feat: 办事大厅用户面前端(hall)首版入库——由 app/public 改名去 app 层;build.yml 修并发撞名容exit137
build / build (push) Successful in 3m6s
build / build (push) Successful in 3m6s
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user