feat: 办事大厅用户面前端(hall)首版入库——由 app/public 改名去 app 层;build.yml 修并发撞名容exit137
build / build (push) Successful in 3m6s

This commit is contained in:
chenxing
2026-09-17 09:12:03 +08:00
parent 6511923327
commit f87523c524
89 changed files with 16000 additions and 14 deletions
+172
View File
@@ -0,0 +1,172 @@
<script setup lang="ts">
/**
* 在线办理(发起页):CMS 指南页"我要办理"跳入 → /#/apply?siteCode=&itemCode=
*
* 提交反馈纪律(BP-R4-1 修复,务必保留语义):
* 后端代发起是"落库(PENDING=0) → AFTER_COMMIT 异步出站 → 回调推进 10"。所以**提交成功的那一刻
* flowStatus 必然是 0**,随后秒级翻转。原实现把 flowStatus===0 一律当"流程中心暂不可用",
* 于是明明提交成功也弹失败提示。现在按 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 { useSite } from "@/utils/site";
import DynamicForm from "@/components/DynamicForm.vue";
const route = useRoute();
const router = useRouter();
const { resolveSite, rememberItem } = useSite();
/** 轮询参数:代发起 + 回调一般在 1s 内完成,给到 ~9s 足够,避免用户盯着"待提交"发懵 */
const POLL_TIMES = 12;
const POLL_INTERVAL_MS = 750;
const loading = ref(false);
const submitting = ref(false);
const item = ref<Record<string, any> | null>(null);
const loadError = ref("");
const siteCode = ref("");
const formData = reactive<Record<string, any>>({});
const schemaJson = computed(() => (item.value?.schemaJson as string) || "");
async function load() {
siteCode.value = resolveSite(route.query.siteCode);
const itemCode = String(route.query.itemCode || "");
if (!siteCode.value || !itemCode) {
loadError.value = "缺少 siteCode/itemCode 参数";
return;
}
rememberItem(itemCode);
loading.value = true;
try {
item.value = (await applyApi.item(siteCode.value, itemCode).send()) as any;
loadError.value = "";
} catch (e: any) {
// 区分加载失败与业务性不可办理:网络/服务异常不当成"事项不可办理"
loadError.value = String(e?.message || e || "网络或服务异常");
} finally {
loading.value = false;
}
}
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
/** 轮询办件状态直到离开 PENDING;超时返回最后一次看到的办件(可能仍是待提交) */
async function awaitFlowStart(applyId: number, first: any) {
let latest = first;
for (let i = 0; i < POLL_TIMES; i++) {
if (latest && latest.flowStatus !== 0 && latest.flowStatus !== undefined && latest.flowStatus !== null) {
return latest;
}
await sleep(POLL_INTERVAL_MS);
try {
latest = (await applyApi.detail(applyId).send()) as any;
} catch {
// 轮询期间的偶发失败不打断流程,继续下一轮
}
}
return latest;
}
async function submit() {
if (!item.value) return;
submitting.value = true;
try {
const apply: any = await applyApi
.submit({
siteCode: siteCode.value,
itemCode: String(route.query.itemCode),
formData: Object.keys(formData).length ? JSON.stringify(formData) : undefined,
})
.send();
// ① 流程中心未接入:不会翻转,如实告知(不说"暂不可用",因为压根没接)
if (apply.flowEnabled === false) {
Message.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("申请已保存为待提交:流程中心响应较慢,可在「我的办件」中重试");
}
} else {
Message.success("已提交,进入审批流程");
}
router.replace(`/apply-detail?id=${apply.id}`);
} finally {
submitting.value = false;
}
}
onMounted(load);
</script>
<template>
<div class="page">
<Card :bordered="false" dis-hover v-if="item">
<template #title>在线办理 · {{ item.title }}</template>
<div class="summary" v-if="item.summary || item.deptName || item.handleTimeLimit">
<div v-if="item.summary">{{ item.summary }}</div>
<div class="meta">
<span v-if="item.deptName">办理部门:{{ item.deptName }}</span>
<span v-if="item.handleTimeLimit">办理时限:{{ item.handleTimeLimit }}</span>
<span v-if="item.contactPhone">咨询电话:{{ item.contactPhone }}</span>
</div>
</div>
<Divider v-if="schemaJson" orientation="left">申请表单</Divider>
<DynamicForm v-if="schemaJson" :schema-json="schemaJson" v-model:form-data="formData" />
<div v-else class="no-form">该事项无需填写表单,提交后由办理部门联系确认。</div>
<div class="actions">
<Button class="sh-btn-primary" type="primary" :loading="submitting" @click="submit">
提交申请
</Button>
<Button @click="router.push('/my')">我的办件</Button>
<Button @click="router.push('/hall')">返回大厅</Button>
</div>
</Card>
<Card :bordered="false" dis-hover v-else>
<div class="state-tip">
<template v-if="loading">加载中…</template>
<template v-else-if="loadError">
事项加载失败:{{ loadError }}
</template>
<template v-else>事项不可办理(未发布或未开通在线办理)</template>
</div>
</Card>
</div>
</template>
<style scoped>
.summary {
padding-bottom: 12px;
}
.meta {
margin-top: 8px;
display: flex;
gap: 16px;
color: var(--sh-text-weak);
font-size: 13px;
flex-wrap: wrap;
}
.no-form {
color: var(--sh-text-weak);
padding: 12px 0;
}
.actions {
margin-top: 16px;
display: flex;
gap: 8px;
}
.state-tip {
text-align: center;
padding: 40px;
color: var(--sh-text-weak);
}
</style>