diff --git a/src/views/Fill.vue b/src/views/Fill.vue index ba2c40f..4c4167c 100644 --- a/src/views/Fill.vue +++ b/src/views/Fill.vue @@ -14,7 +14,7 @@ import { useRoute } from "vue-router"; import { fieldWidget } from "@/components/fields"; import type { DesignField } from "@/types/formSchema"; import type { ResolvedForm, MyAnswerItem } from "@/types/answer"; -import { resolveForm, submitAnswer, listMyAnswers, deleteMyAnswer, editMyAnswer } from "@/api/module/fill"; +import { resolveForm, submitAnswer, listMyAnswers, deleteMyAnswer, editMyAnswer, getMyAnswerDetail } from "@/api/module/fill"; import { startHeightReporting, reportSubmitResult, reportNeedLogin } from "@/utils/embed"; import Utils from "@/utils"; import { Message, Modal } from "view-ui-plus"; @@ -107,9 +107,8 @@ function validateField(field: DesignField): string | null { function validateAll(): boolean { const errors: Record = {}; - if (!form.value?.groups) return true; - const groups = form.value.groups as any[]; - for (const group of groups) { + // 校验与渲染同源:编辑态吃钉住版 groups(renderGroups),否则按当前发布版(Bug#1) + for (const group of renderGroups.value) { for (const field of group.fields || []) { const err = validateField(field); if (err) errors[field.key] = err; @@ -172,6 +171,14 @@ async function handleSubmit() { const myAnswers = ref([]); const editingAnswer = ref(null); +/** 编辑态渲染用的 groups = 答卷钉住的那一版(/my/detail);null/空=未进入编辑 */ +const editingGroups = ref(null); + +/** 渲染与校验同源的 groups:编辑态吃钉住版,否则吃当前发布版 */ +const renderGroups = computed(() => { + if (editingAnswer.value) return editingGroups.value || []; + return (form.value?.groups as any[]) || []; +}); async function loadMyAnswers() { try { @@ -181,9 +188,21 @@ async function loadMyAnswers() { } } -function handleEditAnswer(answer: MyAnswerItem) { +async function handleEditAnswer(answer: MyAnswerItem) { + if (submitting.value) return; + try { + // Bug#1:编辑必须按答卷**钉住的版本**渲染(/my/detail 返回该版本的 groups)。 + // 用当前发布版渲染,客户端会带上/漏掉升版后新增或删除的字段,而服务端按钉住旧版校验, + // 结果必然是"含未知字段"或"缺必填"——表单升过一次版,编辑就永久 400。 + const detail = await getMyAnswerDetail(answer.answerId); + editingGroups.value = (detail.groups as any[]) || []; + } catch (e: any) { + Message.error(e?.message || "加载答卷失败,无法编辑"); + return; + } editingAnswer.value = answer; formValues.value = { ...answer.values }; + formErrors.value = {}; window.scrollTo(0, 0); } @@ -198,6 +217,7 @@ async function handleUpdateAnswer() { await editMyAnswer(editingAnswer.value.answerId, formValues.value); Message.success("更新成功"); editingAnswer.value = null; + editingGroups.value = null; formValues.value = {}; await loadMyAnswers(); } catch (e: any) { @@ -225,6 +245,7 @@ function handleDeleteAnswer(answer: MyAnswerItem) { function handleCancelEdit() { editingAnswer.value = null; + editingGroups.value = null; formValues.value = {}; formErrors.value = {}; } @@ -327,7 +348,7 @@ const readonlyReason = computed(() => {
-
+

{{ group.label }}

diff --git a/src/views/admin/Data.vue b/src/views/admin/Data.vue index 2836596..ffb86fc 100644 --- a/src/views/admin/Data.vue +++ b/src/views/admin/Data.vue @@ -271,10 +271,10 @@ const tableColumns = computed(() => { width: 170, render: (h2: any, p: any) => h2(Tooltip, { - content: `submitter_id=${p.row.submitterId}${p.row.subjectId ? `,subject_id=${p.row.subjectId}(他人代填)` : ",无代填主体"}`, + content: `submitter_id=${p.row.submitterId}${p.row.subjectId && p.row.subjectId !== p.row.submitterId ? `,subject_id=${p.row.subjectId}(他人代填)` : ",无代填主体"}`, transfer: true }, { - default: () => [h2("span", `提交人 ${p.row.submitterId}${p.row.subjectId ? `|主体 ${p.row.subjectId}` : ""}`)] + default: () => [h2("span", `提交人 ${p.row.submitterId}${p.row.subjectId && p.row.subjectId !== p.row.submitterId ? `|主体 ${p.row.subjectId}` : ""}`)] }) }); cols.push(