145 lines
4.5 KiB
JavaScript
145 lines
4.5 KiB
JavaScript
import { isWriteOutcomeUnknown } from '@/utils/request-outcome.js'
|
|
|
|
const messages = {
|
|
submitted:
|
|
"反馈已提交。以下内容为本次提交记录;修改任一项后可提交新反馈。",
|
|
submittedWithEdits: "上一份反馈已提交,当前修改尚未提交。",
|
|
uncertain:
|
|
"暂时无法确认是否提交成功,请不要重复提交相同内容。你可以修改内容后再提交一份反馈。",
|
|
uncertainWithEdits: "上一份反馈暂未确认,当前修改尚未提交。",
|
|
};
|
|
|
|
const normalizeFeedback = (form) => ({
|
|
feedbackType: String(form?.feedbackType || "").trim(),
|
|
feedbackContent: String(form?.feedbackContent || "").trim(),
|
|
contactInfo: String(form?.contactInfo || "").trim(),
|
|
});
|
|
|
|
// 字段顺序固定的快照既用于脏数据判断,也用于阻止非幂等 POST 重复提交。
|
|
// 不能改成对象引用比较,否则 Vue 表单原地修改时无法识别同一份内容。
|
|
const snapshotOf = (form) => JSON.stringify(normalizeFeedback(form));
|
|
|
|
const errorMessage = (error) => {
|
|
if (error?.code === "WRITE_UNAVAILABLE") {
|
|
return "当前为本地预览,反馈没有发出。";
|
|
}
|
|
if (error?.httpStatus === 401) {
|
|
return "登录状态已失效,请重新登录后再提交。";
|
|
}
|
|
return "提交未完成,请稍后再试。";
|
|
};
|
|
|
|
const submitLabelFor = (phase) => {
|
|
if (phase === "submitting") return "正在提交";
|
|
if (phase === "success") return "反馈已提交";
|
|
if (phase === "uncertain") return "暂未确认";
|
|
if (phase === "error") return "重新提交";
|
|
return "提交反馈";
|
|
};
|
|
|
|
export const createFeedbackSubmissionSession = (initialForm) => {
|
|
let baselineSnapshot = snapshotOf(initialForm);
|
|
let lastSubmittedSnapshot = "";
|
|
let lastUncertainSnapshot = "";
|
|
let phase = "ready";
|
|
let message = "";
|
|
let tone = "";
|
|
|
|
const view = (currentForm) => {
|
|
const currentSnapshot = snapshotOf(currentForm);
|
|
const isRepeatedSubmission =
|
|
currentSnapshot === lastSubmittedSnapshot ||
|
|
currentSnapshot === lastUncertainSnapshot;
|
|
return {
|
|
phase,
|
|
message,
|
|
tone,
|
|
isDirty: currentSnapshot !== baselineSnapshot,
|
|
isSubmitDisabled: phase === "submitting" || isRepeatedSubmission,
|
|
submitLabel: submitLabelFor(phase),
|
|
};
|
|
};
|
|
|
|
const begin = (currentForm) => {
|
|
const currentSnapshot = snapshotOf(currentForm);
|
|
if (
|
|
phase === "submitting" ||
|
|
currentSnapshot === lastSubmittedSnapshot ||
|
|
currentSnapshot === lastUncertainSnapshot
|
|
) {
|
|
return null;
|
|
}
|
|
phase = "submitting";
|
|
message = "";
|
|
tone = "";
|
|
return {
|
|
snapshot: currentSnapshot,
|
|
payload: normalizeFeedback(currentForm),
|
|
};
|
|
};
|
|
|
|
const succeed = (submission, currentForm) => {
|
|
baselineSnapshot = submission.snapshot;
|
|
lastSubmittedSnapshot = submission.snapshot;
|
|
tone = "success";
|
|
if (snapshotOf(currentForm) === submission.snapshot) {
|
|
phase = "success";
|
|
message = messages.submitted;
|
|
} else {
|
|
phase = "ready";
|
|
message = messages.submittedWithEdits;
|
|
}
|
|
};
|
|
|
|
const fail = (submission, error, currentForm) => {
|
|
if (!isWriteOutcomeUnknown(error)) {
|
|
phase = "error";
|
|
message = errorMessage(error);
|
|
tone = "error";
|
|
return;
|
|
}
|
|
|
|
// 超时或异常 2xx 可能已经被后端写入。记录原快照并禁用原样重试,
|
|
// 避免用户在网络恢复后生成两条内容完全相同的反馈。
|
|
lastUncertainSnapshot = submission.snapshot;
|
|
tone = "uncertain";
|
|
if (snapshotOf(currentForm) === submission.snapshot) {
|
|
phase = "uncertain";
|
|
message = messages.uncertain;
|
|
} else {
|
|
phase = "ready";
|
|
message = messages.uncertainWithEdits;
|
|
}
|
|
};
|
|
|
|
const reconcile = (currentForm) => {
|
|
if (phase === "submitting") return;
|
|
const currentSnapshot = snapshotOf(currentForm);
|
|
if (lastSubmittedSnapshot && currentSnapshot === lastSubmittedSnapshot) {
|
|
phase = "success";
|
|
message = messages.submitted;
|
|
tone = "success";
|
|
return;
|
|
}
|
|
if (lastUncertainSnapshot && currentSnapshot === lastUncertainSnapshot) {
|
|
phase = "uncertain";
|
|
message = messages.uncertain;
|
|
tone = "uncertain";
|
|
return;
|
|
}
|
|
phase = "ready";
|
|
if (lastUncertainSnapshot) {
|
|
message = messages.uncertainWithEdits;
|
|
tone = "uncertain";
|
|
} else if (lastSubmittedSnapshot) {
|
|
message = messages.submittedWithEdits;
|
|
tone = "success";
|
|
} else {
|
|
message = "";
|
|
tone = "";
|
|
}
|
|
};
|
|
|
|
return { begin, succeed, fail, reconcile, view };
|
|
};
|