From 28660de4ed756aae6205795feda0e19a27ba3b12 Mon Sep 17 00:00:00 2001 From: zhangheng Date: Sat, 28 Mar 2026 15:23:08 +0800 Subject: [PATCH] fix(qa): bug fix --- .env.development | 4 +- .../label/components/EditorContainer.tsx | 5 +- components/label/components/RightQATools.tsx | 101 +++++++++++++----- 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/.env.development b/.env.development index 7e209f2..c0db470 100644 --- a/.env.development +++ b/.env.development @@ -6,9 +6,9 @@ NEXT_PUBLIC_ENV=development # redis # NEXT_PUBLIC_REDIS_URL="172.16.115.128" -NEXT_PUBLIC_REDIS_URL="localhost" +NEXT_PUBLIC_REDIS_URL="172.30.21.213" NEXT_PUBLIC_REDIS_PORT=9765 NEXT_PUBLIC_REDIS_PASSWORD=password NEXT_PUBLIC_REDIS_USERNAME=default -NEXT_PUBLIC_REDIS_DB=0 +NEXT_PUBLIC_REDIS_DB=1 diff --git a/components/label/components/EditorContainer.tsx b/components/label/components/EditorContainer.tsx index b72d431..1c0a26d 100644 --- a/components/label/components/EditorContainer.tsx +++ b/components/label/components/EditorContainer.tsx @@ -9,7 +9,7 @@ import Placeholder from "@tiptap/extension-placeholder" import { RichTextEditor } from "@mantine/tiptap" import { useTopToolsStore } from "../useTopToolsStore" import { useKeyEventStore } from "../store" -import { useEffect, useRef } from "react" +import { useEffect, useRef, type ChangeEvent } from "react" import { Box } from "@mantine/core" import { IconPhoto } from "@tabler/icons-react" import "@mantine/tiptap/styles.css" @@ -33,6 +33,7 @@ const EditorContainer = ({ const fileInputRef = useRef(null) const editor = useEditor({ + immediatelyRender: false, extensions: [ StarterKit, Underline, @@ -72,7 +73,7 @@ const EditorContainer = ({ } }, [value, editor]) - const handleImageUpload = (e: React.ChangeEvent) => { + const handleImageUpload = (e: ChangeEvent) => { const file = e.target.files?.[0] if (file && editor) { const reader = new FileReader() diff --git a/components/label/components/RightQATools.tsx b/components/label/components/RightQATools.tsx index 68f1de5..62b03df 100644 --- a/components/label/components/RightQATools.tsx +++ b/components/label/components/RightQATools.tsx @@ -19,6 +19,7 @@ import { IconEdit } from "@tabler/icons-react" import dayjs from "dayjs" import parse from "html-react-parser" import { ArrowUpIcon, ArrowUpToLineIcon } from "lucide-react" +import dynamic from "next/dynamic" import { forwardRef, useCallback, @@ -36,7 +37,11 @@ import { useDescToolsStore } from "../useDescToolsStore" import { useTopToolsStore } from "../useTopToolsStore" import { safeClone } from "../utils/clone" import CustomModal from "./CustomModal" -import EditorContainer, { splitWord } from "./EditorContainer" +import { splitWord } from "./EditorContainer" + +const EditorContainer = dynamic(() => import("./EditorContainer"), { + ssr: false, +}) interface ComponentProps { taskDetail?: Task.DataProps @@ -54,6 +59,25 @@ const escapeRegExp = (str: string) => { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") } +const getHtmlFromStoredValue = (value?: string) => { + if (!value) return "" + const [html] = value.split(splitWord) + return html ?? value +} + +const getTextFromHtml = (html: string) => { + return html + .replace(/<[^>]+>/g, "") + .replace(/ /g, " ") + .trim() +} + +const ensureStoredValue = (value?: string) => { + if (!value) return "" + if (value.includes(splitWord)) return value + return `${value}${splitWord}${getTextFromHtml(value)}` +} + const RightQAContent = forwardRef( ({ taskDetail }: ComponentProps, ref: React.Ref) => { const { isView } = useTopToolsStore() @@ -114,11 +138,21 @@ const RightQAContent = forwardRef( type QaObj = Record - const handleFormUpdate = (changeValues: any) => { + const handleFormUpdate = ( + changeValues: Record, + rawEditorValue?: string + ) => { if (!selectedQuestion) return const keys = Object.keys(changeValues) const hasTagChange = keys.includes("tag") - const formData = form.values + const formData = { + ...form.values, + ...changeValues, + } + const nextStoredValue = + typeof rawEditorValue === "string" + ? ensureStoredValue(rawEditorValue) + : ensureStoredValue(formData.value) const { label, type, category } = selectedQuestion const currentQaDataArr = currentQaData[category] ?? [] let newArr: { [x: string]: any }[] = [] @@ -127,7 +161,7 @@ const RightQAContent = forwardRef( if (question_name === label) { newArr.push({ [question_name]: { - value: type === "value" ? formData.value : detail.value, + value: type === "value" ? nextStoredValue : detail.value, id: formData.round, is_pre: detail.is_pre, tag: formData.tag, @@ -139,7 +173,7 @@ const RightQAContent = forwardRef( : dayjs().unix(), modify_uid: usePermissionStore.getState().user_id, modify_timestamp: dayjs().unix(), - comment: type === "comment" ? formData.value : detail.comment, + comment: type === "comment" ? nextStoredValue : detail.comment, flag: hasTagChange ? 0 : detail.tag === 2 ? 1 : 0, }, }) @@ -158,9 +192,13 @@ const RightQAContent = forwardRef( } } - const onFieldChange = (field: string, value: any) => { + const onFieldChange = ( + field: string, + value: any, + rawEditorValue?: string + ) => { form.setFieldValue(field, value) - handleFormUpdate({ [field]: value }) + handleFormUpdate({ [field]: value }, rawEditorValue) } useEffect(() => { @@ -170,12 +208,10 @@ const RightQAContent = forwardRef( selectedQuestion.type === "comment" ) { const { value: v } = selectedQuestion - let htmlText = "" - if (selectedQuestion.type === "value") { - htmlText = v.value ? v.value.split(splitWord)[0] : "" - } else { - htmlText = v.comment ? v.comment.split(splitWord)[0] : "" - } + const htmlText = + selectedQuestion.type === "value" + ? getHtmlFromStoredValue(v.value) + : getHtmlFromStoredValue(v.comment) const obj = { value: htmlText, round: v.id, @@ -183,7 +219,8 @@ const RightQAContent = forwardRef( } form.setValues(obj) } - }, [selectedQuestion, form]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedQuestion]) useEffect(() => { if (updateDescDataFlag) { @@ -230,9 +267,10 @@ const RightQAContent = forwardRef( const newQaData = safeClone(currentQaData) const handleText = (v: string) => { - // 提取文本内容 - const text = v.split(splitWord)[1] - let textContent = text.replace(/<[^>]+>/g, "") + // 优先使用存储中的纯文本部分;若历史数据不规范则兜底从 HTML 中提取 + const text = + v.split(splitWord)[1] ?? getTextFromHtml(getHtmlFromStoredValue(v)) + let textContent = text // 替换错词 for (let i = 0; i < wrongWords.length; i++) { const wrong = escapeRegExp(wrongWords[i]) @@ -256,17 +294,20 @@ const RightQAContent = forwardRef( const { value, comment } = questionData const update = (v: string, key: "value" | "comment") => { - const checkValue = v.split(splitWord)[1] + const checkValue = + v.split(splitWord)[1] ?? + getTextFromHtml(getHtmlFromStoredValue(v)) const updatedValue = handleText(v) if (checkValue !== updatedValue) { - let newValue = `

${updatedValue}

${splitWord}${updatedValue}` + const htmlValue = `

${updatedValue}

` + let newValue = `${htmlValue}${splitWord}${updatedValue}` questionData[key] = newValue if ( selectedQuestion?.label === name && selectedQuestion?.type === key ) - form.setFieldValue("value", `

${updatedValue}

`) + form.setFieldValue("value", htmlValue) } } @@ -312,12 +353,10 @@ const RightQAContent = forwardRef( {qaDataArr.map((qaObj, index) => { const [question_name, detail] = Object.entries(qaObj)[0] - const htmlText = detail.value - ? detail.value.split(splitWord)[0] - : "" - const commentHtmlText = detail.comment - ? detail.comment.split(splitWord)[0] - : "" + const htmlText = getHtmlFromStoredValue(detail.value) + const commentHtmlText = getHtmlFromStoredValue( + detail.comment + ) return ( onFieldChange("value", val)} + onChange={(val: string) => + onFieldChange("value", getHtmlFromStoredValue(val), val) + } disabled={!selectedQuestion} /> @@ -601,7 +642,7 @@ const RightQAContent = forwardRef( const finalData = safeClone(currentQaData) const updateData = safeClone(currentOperation) let obj = { - value: formData.value ?? "

", + value: ensureStoredValue(formData.value ?? "

"), id: formData.round_id, is_pre: false, tag: 0, @@ -657,7 +698,9 @@ const RightQAContent = forwardRef( textForm.setFieldValue("value", val)} + onChange={(val: string) => + textForm.setFieldValue("value", getHtmlFromStoredValue(val)) + } />