fix(ci): add file
This commit is contained in:
28
.gitlab-ci.yml
Normal file
28
.gitlab-ci.yml
Normal file
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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 : "<p></p>"
|
||||
const incomingHtml = getEditorHtml(value) || "<p></p>"
|
||||
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",
|
||||
}}>
|
||||
<RichTextEditor editor={editor} style={{ border: 0 }}>
|
||||
<RichTextEditor.Toolbar sticky stickyOffset={60}>
|
||||
<RichTextEditor.Toolbar>
|
||||
<RichTextEditor.ControlsGroup>
|
||||
<RichTextEditor.Bold />
|
||||
<RichTextEditor.Italic />
|
||||
|
||||
@@ -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,23 +41,29 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
|
||||
})
|
||||
|
||||
const isUpdatingFromStore = useRef(false)
|
||||
const syncTimerRef = useRef<ReturnType<typeof setTimeout> | 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])
|
||||
)
|
||||
}
|
||||
} else {
|
||||
const currentDescData = safeClone(useDescToolsStore.getState().descData)
|
||||
const updateMap = new Map()
|
||||
Object.entries(data).forEach(([label_class, val]: any) => {
|
||||
Object.entries(values).forEach(([label_class, val]: any) => {
|
||||
let value = { ...val }
|
||||
if (value.is_correct !== undefined) {
|
||||
value.is_correct = value.is_correct === "true"
|
||||
@@ -68,18 +75,33 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
|
||||
updateMap.set(label_class, { value: value })
|
||||
}
|
||||
})
|
||||
dataMap.set(activeImage, updateMap)
|
||||
}
|
||||
})
|
||||
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 (
|
||||
<Box h="100%">
|
||||
<Box
|
||||
@@ -156,7 +191,9 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
|
||||
activeImage,
|
||||
operation.label_class
|
||||
)
|
||||
const hasComment = currentData?.comment
|
||||
const hasComment =
|
||||
!!(form.values as any)?.[operation.label_class]?.comment ||
|
||||
!!currentData?.comment
|
||||
|
||||
return (
|
||||
<Box key={operation.label_class}>
|
||||
@@ -171,9 +208,17 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
|
||||
{operation.label_class}
|
||||
</Text>
|
||||
<Radio.Group
|
||||
{...form.getInputProps(
|
||||
`${operation.label_class}.is_correct`
|
||||
)}
|
||||
value={
|
||||
(form.values as any)[operation.label_class]
|
||||
?.is_correct || ""
|
||||
}
|
||||
onChange={(val) => {
|
||||
setOperationField(
|
||||
operation.label_class,
|
||||
"is_correct",
|
||||
val
|
||||
)
|
||||
}}
|
||||
size="xs">
|
||||
<Group gap="xs">
|
||||
<Radio
|
||||
@@ -195,8 +240,9 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
|
||||
variant="subtle"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
form.setFieldValue(
|
||||
`${operation.label_class}.comment`,
|
||||
setOperationField(
|
||||
operation.label_class,
|
||||
"comment",
|
||||
"<p></p>"
|
||||
)
|
||||
}}>
|
||||
@@ -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
|
||||
)
|
||||
}}>
|
||||
<Group gap="xs" mt={4}>
|
||||
{subDesc.optional_item.map((option) => (
|
||||
<Radio
|
||||
@@ -244,9 +308,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
|
||||
)
|
||||
}}>
|
||||
<Group gap="xs" mt={4}>
|
||||
{subDesc.optional_item.map((option) => (
|
||||
<Checkbox
|
||||
@@ -266,13 +339,11 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
|
||||
<Box mb="xs">
|
||||
<EditorContainer
|
||||
value={
|
||||
(form.values as any)[operation.label_class]?.value
|
||||
(form.values as any)[operation.label_class]?.value ||
|
||||
""
|
||||
}
|
||||
onChange={(val: any) =>
|
||||
form.setFieldValue(
|
||||
`${operation.label_class}.value`,
|
||||
val
|
||||
)
|
||||
setOperationField(operation.label_class, "value", val)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
@@ -282,14 +353,16 @@ const RightDescTools = ({ taskDetail }: ComponentProps) => {
|
||||
<EditorContainer
|
||||
color="border-danger"
|
||||
value={
|
||||
(form.values as any)[operation.label_class]?.comment
|
||||
(form.values as any)[operation.label_class]
|
||||
?.comment || ""
|
||||
}
|
||||
onChange={(val: any) =>
|
||||
form.setFieldValue(
|
||||
`${operation.label_class}.comment`,
|
||||
onChange={(val: any) => {
|
||||
setOperationField(
|
||||
operation.label_class,
|
||||
"comment",
|
||||
val
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user