diff --git a/src/views/Apply.vue b/src/views/Apply.vue index 4f7662d..d8e464a 100644 --- a/src/views/Apply.vue +++ b/src/views/Apply.vue @@ -8,11 +8,10 @@ * 于是明明提交成功也弹失败提示。现在按 flowEnabled 分流 + 短轮询收敛: * flowEnabled=false → 流程中心未接入,真的不会翻转:如实告知"已保存为待提交"; * flowEnabled=true → 排队中:轮询详情等状态翻转(10=审批中),翻转即报成功; - * 超时未翻转才提示"已保存为待提交,可在我的办件重试"(此时多半真失败了)。 + * 超时未翻转才告知"已保存为待提交,可在我的办事重试"(此时多半真失败了)。 */ import { computed, onMounted, reactive, ref } from "vue"; import { useRoute, useRouter } from "vue-router"; -import { Message } from "view-ui-plus"; import { applyApi } from "@/api/module/svc"; import type { ApplyAttachmentItem } from "@/types/svc"; import { useSite } from "@/utils/site"; @@ -33,8 +32,49 @@ const siteCode = ref(""); const formData = reactive>({}); const dynFormRef = ref | null>(null); // DynamicForm.validate() +/** 提交后的状态告知(P0-5):成功页区块取代一次性 Message,用户看得到"下一步与预计完成" */ +const done = ref<{ + applyId: number; + itemTitle: string; + headline: string; + tip: string; + tone: "success" | "warning"; + currentNode: string; + promise: string; + eta: string; +} | null>(null); + const schemaJson = computed(() => (item.value?.schemaJson as string) || ""); +/** 需跑现场次数(0=零跑动):/api/apply/item 未下发该字段时整段隐藏,不编造 */ +const runText = computed(() => { + const n = item.value?.runCount; + if (n === null || n === undefined || n === "") return ""; + const num = Number(n); + if (Number.isNaN(num)) return ""; + return num === 0 ? "零跑动" : `需跑 ${num} 次`; +}); + +/** + * 承诺时限 → 天数:取文案里最大的数字("1-3个工作日" 按上限 3 估,不把承诺说短)。 + * 解析不出("当日办结""次周内")返回 null,由调用方回落为原文展示。 + */ +function parsePromiseDays(text: any): number | null { + const nums = String(text || "").match(/\d+/g); + if (!nums || !nums.length) return null; + const n = Math.max(...nums.map(Number)); + return n > 0 && n <= 365 ? n : null; +} + +const pad2 = (n: number) => String(n).padStart(2, "0"); + +/** 提交日 + N 天(按自然日加,工作日口径待后端确认,见交付说明) */ +function addDays(base: Date, n: number): string { + const d = new Date(base.getTime()); + d.setDate(d.getDate() + n); + return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`; +} + async function load() { siteCode.value = resolveSite(route.query.siteCode); // CMS 指南页来路(T4):校验通过即记入 session,App.vue 顶栏渲染"返回门户" @@ -100,6 +140,27 @@ async function awaitFlowStart(applyId: number, first: any) { return latest; } +/** 后端时间为 `yyyy-MM-dd HH:mm:ss` / ISO 两种形态,统一转 Date;解析失败返回 null */ +function toDate(s?: string | null): Date | null { + if (!s) return null; + const d = new Date(String(s).trim().replace(" ", "T")); + return Number.isNaN(d.getTime()) ? null : d; +} + +/** 当前节点:进度接口里 active 节点的名称;进度不可见时留空(该行整体不显示) */ +async function currentNodeName(applyId: number): Promise { + try { + const p: any = await applyApi.progress(applyId).send(); + const names = (p?.steps || []) + .filter((st: any) => st.active) + .map((st: any) => st.nodeName || st.label) + .filter(Boolean); + return Array.from(new Set(names)).join("、"); + } catch { + return ""; + } +} + async function submit() { if (!item.value) return; // 前端先按 schema 校验(字段下方内联红字 + 滚到第一个错误),不再把"必填项未填写" @@ -116,21 +177,41 @@ async function submit() { }) .send(); + // 分流语义与提示文案保持不变(BP-R4-1),只是把结果落成成功页区块而非一闪而过的 Message + let latest = apply; + let headline = "已提交,进入审批流程"; + let tip = "已进入审批流程,可在办件详情查看进度"; + let tone: "success" | "warning" = "success"; // ① 流程中心未接入:不会翻转,如实告知(不说"暂不可用",因为压根没接) if (apply.flowEnabled === false) { - Message.warning("流程中心未接入,申请已保存为待提交;请联系管理员开通后重试"); + headline = "申请已保存为待提交"; + tip = "流程中心未接入,请联系管理员开通后在「我的办事」重试提交"; + tone = "warning"; } else if (apply.flowStatus === 0) { // ② 排队中:等状态翻转(成功即成功提示,不再误报) - const latest = await awaitFlowStart(apply.id, apply); - if (latest && latest.flowStatus && latest.flowStatus !== 0) { - Message.success("已提交,进入审批流程"); - } else { - Message.warning("申请已保存为待提交:流程中心响应较慢,可在「我的办件」中重试"); + latest = (await awaitFlowStart(apply.id, apply)) || apply; + if (!(latest && latest.flowStatus && latest.flowStatus !== 0)) { + headline = "申请已保存为待提交"; + tip = "流程中心响应较慢,可在「我的办事」中重试提交"; + tone = "warning"; } - } else { - Message.success("已提交,进入审批流程"); } - router.replace(`/apply-detail?id=${apply.id}`); + + const promise = String(item.value?.promiseTimeLimit || ""); + const days = parsePromiseDays(promise); + const submittedAt = toDate(latest?.submitTime || latest?.createTime) || new Date(); + done.value = { + applyId: Number(apply.id), + itemTitle: String(item.value?.title || ""), + headline, + tip, + tone, + currentNode: tone === "success" ? await currentNodeName(apply.id) : "", + promise, + // 解析不出数字时不猜日期:该行直接显示承诺时限原文 + eta: days ? addDays(submittedAt, days) : promise, + }; + window.scrollTo(0, 0); } finally { submitting.value = false; } @@ -141,7 +222,38 @@ onMounted(load);