diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..610a3a5
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,28 @@
+stages:
+ - sync_github
+
+sync_github:
+ stage: sync_github
+ tags:
+ - webfront
+ image: harbor.cowarobot.cn/softrepo/rust_build:1.94
+ script:
+ - echo "代码已合并到 main, 开始执行发布逻辑..."
+ - |
+ for i in {1..10}; do
+ if command -v docker >/dev/null 2>&1 && [ -S /var/run/docker.sock ]; then
+ echo "Docker is ready!"
+ break
+ fi
+ echo "Waiting for Docker..."
+ sleep 2
+ done
+ - docker info
+ - echo ${CI_PROJECT_NAME} ${CI_PROJECT_PATH} ${CI_PROJECT_DIR} ${CI_PROJECT_URL} ${CI_COMMIT_SHA}
+ - echo ${HARBORKEY} | docker login harbor.cowarobot.cn -u 'robot$softpro+cibuild' --password-stdin
+ - rm -rf ${CI_PROJECT_NAME}
+ - git clone https://oauth2:glpat-EdU8a_84jpSH2nOlNTkpnW86MQp1OjY1CA.01.0y1rtht99@git.cowarobot.com/${CI_PROJECT_PATH}
+ - cd ${CI_PROJECT_NAME}
+ - bash build.sh prod
+ rules:
+ - if: $CI_COMMIT_BRANCH == "main"
diff --git a/components/label/api/const.ts b/components/label/api/const.ts
index 85a96fa..5e1fb07 100644
--- a/components/label/api/const.ts
+++ b/components/label/api/const.ts
@@ -1,6 +1,6 @@
export const BASE_LABEL_API =
process.env.NEXT_PUBLIC_ENV === "production"
- ? "https://label.cowarobot.com"
+ ? "https://label.soft.cowarobot.com"
: process.env.NEXT_PUBLIC_ENV === "staging"
? "http://172.16.115.128:9110"
: "https://label.softtest.cowarobot.com"
diff --git a/components/label/components/EditorContainer.tsx b/components/label/components/EditorContainer.tsx
index 7cadec3..89813b1 100644
--- a/components/label/components/EditorContainer.tsx
+++ b/components/label/components/EditorContainer.tsx
@@ -16,6 +16,12 @@ import "@mantine/tiptap/styles.css"
export const splitWord = "@1024-"
+const getEditorHtml = (rawValue?: string) => {
+ if (!rawValue) return ""
+ const [html] = rawValue.split(splitWord)
+ return html ?? rawValue
+}
+
interface EditorContainerProps {
value: string
onChange: (value: string) => void
@@ -41,11 +47,11 @@ const EditorContainer = ({
Image,
Placeholder.configure({ placeholder: "请输入内容..." }),
],
- content: value,
+ content: getEditorHtml(value),
editable: !disabled && !isView,
onUpdate: ({ editor }) => {
- // Preserve splitWord format
- onChange(`${editor.getHTML()}${splitWord}${editor.getText()}`)
+ const html = getEditorHtml(editor.getHTML())
+ onChange(`${html}${splitWord}${editor.getText()}`)
},
onFocus: () => {
useKeyEventStore.getState().setFocusInput(true)
@@ -63,7 +69,7 @@ const EditorContainer = ({
useEffect(() => {
if (!editor) return
- const incomingHtml = value ? value : "
"
+ const incomingHtml = getEditorHtml(value) || ""
if (editor.getHTML() !== incomingHtml) {
// Keep editor content in sync with external state, including empty value switch.
editor.commands.setContent(incomingHtml, { emitUpdate: false })
@@ -91,7 +97,7 @@ const EditorContainer = ({
padding: "4px",
}}>
-
+
diff --git a/components/label/components/RightDescTools.tsx b/components/label/components/RightDescTools.tsx
index caa8de8..ede4b50 100644
--- a/components/label/components/RightDescTools.tsx
+++ b/components/label/components/RightDescTools.tsx
@@ -18,6 +18,7 @@ import { Task } from "../api/task/typing"
import { useBottomToolsStore } from "../useBottomToolsStore"
import { useDescToolsStore } from "../useDescToolsStore"
import { useTopToolsStore } from "../useTopToolsStore"
+import { safeClone } from "../utils/clone"
import EditorContainer from "./EditorContainer"
interface ComponentProps {
@@ -40,46 +41,67 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
})
const isUpdatingFromStore = useRef(false)
+ const syncTimerRef = useRef | null>(null)
- const handleFormUpdate = () => {
+ const setOperationField = (
+ labelClass: string,
+ field: string,
+ value: string | string[]
+ ) => {
+ const current = ((form.values as any)[labelClass] ?? {}) as Record<
+ string,
+ any
+ >
+ form.setFieldValue(labelClass, {
+ ...current,
+ [field]: value,
+ })
+ }
+
+ const handleFormUpdate = (values: any) => {
if (isUpdatingFromStore.current) return
- const data = form.values as any
- let dataMap = new Map()
- taskDetail?.data_name.forEach((detail_name) => {
- if (detail_name.name[0] !== activeImage) {
- if (useDescToolsStore.getState().descData.has(detail_name.name[0])) {
- dataMap.set(
- detail_name.name[0],
- useDescToolsStore.getState().descData.get(detail_name.name[0])
- )
- }
+ const currentDescData = safeClone(useDescToolsStore.getState().descData)
+ const updateMap = new Map()
+ Object.entries(values).forEach(([label_class, val]: any) => {
+ let value = { ...val }
+ if (value.is_correct !== undefined) {
+ value.is_correct = value.is_correct === "true"
+ }
+ const keys = Object.keys(value)
+ if (keys.includes("value")) {
+ updateMap.set(label_class, value)
} else {
- const updateMap = new Map()
- Object.entries(data).forEach(([label_class, val]: any) => {
- let value = { ...val }
- if (value.is_correct !== undefined) {
- value.is_correct = value.is_correct === "true"
- }
- const keys = Object.keys(value)
- if (keys.includes("value")) {
- updateMap.set(label_class, value)
- } else {
- updateMap.set(label_class, { value: value })
- }
- })
- dataMap.set(activeImage, updateMap)
+ updateMap.set(label_class, { value: value })
}
})
- setDescData(dataMap)
+ currentDescData.set(activeImage, updateMap)
+ setDescData(currentDescData)
}
useEffect(() => {
- handleFormUpdate()
+ if (isUpdatingFromStore.current) return
+ if (syncTimerRef.current) {
+ clearTimeout(syncTimerRef.current)
+ }
+ syncTimerRef.current = setTimeout(() => {
+ handleFormUpdate(form.values)
+ syncTimerRef.current = null
+ }, 180)
+ return () => {
+ if (syncTimerRef.current) {
+ clearTimeout(syncTimerRef.current)
+ syncTimerRef.current = null
+ }
+ }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [form.values])
useEffect(() => {
+ if (syncTimerRef.current) {
+ clearTimeout(syncTimerRef.current)
+ syncTimerRef.current = null
+ }
isUpdatingFromStore.current = true
form.reset()
if (activeImage) {
@@ -109,6 +131,10 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
useEffect(() => {
if (updateDescDataFlag) {
+ if (syncTimerRef.current) {
+ clearTimeout(syncTimerRef.current)
+ syncTimerRef.current = null
+ }
isUpdatingFromStore.current = true
const formData = useDescToolsStore
.getState()
@@ -133,6 +159,15 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updateDescDataFlag])
+ useEffect(() => {
+ return () => {
+ if (syncTimerRef.current) {
+ clearTimeout(syncTimerRef.current)
+ syncTimerRef.current = null
+ }
+ }
+ }, [])
+
return (
{
activeImage,
operation.label_class
)
- const hasComment = currentData?.comment
+ const hasComment =
+ !!(form.values as any)?.[operation.label_class]?.comment ||
+ !!currentData?.comment
return (
@@ -171,9 +208,17 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
{operation.label_class}
{
+ setOperationField(
+ operation.label_class,
+ "is_correct",
+ val
+ )
+ }}
size="xs">
{
variant="subtle"
size="xs"
onClick={() => {
- form.setFieldValue(
- `${operation.label_class}.comment`,
+ setOperationField(
+ operation.label_class,
+ "comment",
""
)
}}>
@@ -215,9 +261,18 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
placeholder={`请输入${subDesc.chinese_name}`}
size="xs"
required={!!subDesc.isrequired}
- {...form.getInputProps(
- `${operation.label_class}.${subDesc.chinese_name}`
- )}
+ value={
+ (form.values as any)[operation.label_class]?.[
+ subDesc.chinese_name
+ ] || ""
+ }
+ onChange={(event) => {
+ setOperationField(
+ operation.label_class,
+ subDesc.chinese_name,
+ event.currentTarget.value
+ )
+ }}
disabled={!!isView}
/>
) : subDesc.select_mode === 1 ? (
@@ -225,9 +280,18 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
label={subDesc.chinese_name}
size="xs"
required={!!subDesc.isrequired}
- {...form.getInputProps(
- `${operation.label_class}.${subDesc.chinese_name}`
- )}>
+ value={
+ (form.values as any)[operation.label_class]?.[
+ subDesc.chinese_name
+ ] || ""
+ }
+ onChange={(val) => {
+ setOperationField(
+ operation.label_class,
+ subDesc.chinese_name,
+ val
+ )
+ }}>
{subDesc.optional_item.map((option) => (
{
label={subDesc.chinese_name}
size="xs"
required={!!subDesc.isrequired}
- {...form.getInputProps(
- `${operation.label_class}.${subDesc.chinese_name}`
- )}>
+ value={
+ (form.values as any)[operation.label_class]?.[
+ subDesc.chinese_name
+ ] || []
+ }
+ onChange={(val) => {
+ setOperationField(
+ operation.label_class,
+ subDesc.chinese_name,
+ val
+ )
+ }}>
{subDesc.optional_item.map((option) => (
{
- form.setFieldValue(
- `${operation.label_class}.value`,
- val
- )
+ setOperationField(operation.label_class, "value", val)
}
/>
@@ -282,14 +353,16 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
- form.setFieldValue(
- `${operation.label_class}.comment`,
+ onChange={(val: any) => {
+ setOperationField(
+ operation.label_class,
+ "comment",
val
)
- }
+ }}
/>
>
diff --git a/components/login/api/index.ts b/components/login/api/index.ts
index c3d63e8..2075b43 100644
--- a/components/login/api/index.ts
+++ b/components/login/api/index.ts
@@ -4,7 +4,7 @@ import { Login } from "./types"
const BASE_PATH =
process.env.NEXT_PUBLIC_ENV === "production"
- ? "https://label.cowarobot.com"
+ ? "https://label.soft.cowarobot.com"
: process.env.NEXT_PUBLIC_ENV === "staging"
? "http://172.16.115.128:9110"
: "https://label.softtest.cowarobot.com"