Vue3 + Vite + view-ui-plus 前端源码,含流程工作台、实例详情、 任务处理、管理端(定义/实例/映射/回调)等页面。 保留远程 .gitea/workflows/build.yml CI 配置。
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
// 办件详情:状态 + 业务摘要 + 流程轨迹时间线 + 分支会签进度(概览 §9.4 进度图数据源)
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { flowApi, INSTANCE_STATUS } from "@/api/module/flow";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const loading = ref(false);
|
||||
const detail = ref<any>(null);
|
||||
|
||||
const snapshotRows = computed(() => {
|
||||
const raw = detail.value?.bizSnapshot;
|
||||
if (!raw) return [];
|
||||
let obj: any = null;
|
||||
try {
|
||||
obj = typeof raw === "string" ? JSON.parse(raw) : raw;
|
||||
} catch {
|
||||
return [{ k: "摘要", v: String(raw) }];
|
||||
}
|
||||
if (obj && typeof obj === "object") {
|
||||
return Object.entries(obj).map(([k, v]) => ({ k, v: v == null ? "-" : String(v) }));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
/** 业务表单数据(§3.4 只读渲染;解析失败降级一行原文;无 formData 时整块隐藏) */
|
||||
const formRows = computed(() => {
|
||||
const raw = detail.value?.formDataJson;
|
||||
if (raw === null || raw === undefined || raw === "") return [];
|
||||
let obj: any = null;
|
||||
try {
|
||||
obj = typeof raw === "string" ? JSON.parse(raw) : raw;
|
||||
} catch {
|
||||
return [{ k: "表单原文", v: String(raw) }];
|
||||
}
|
||||
if (obj && typeof obj === "object" && !Array.isArray(obj)) {
|
||||
return Object.entries(obj).map(([k, v]) => ({
|
||||
k,
|
||||
v: v == null ? "-"
|
||||
: Array.isArray(v)
|
||||
? (v.map((item: any) => item && typeof item === "object" ? item.fileName || JSON.stringify(item) : String(item)).join("、") || "-")
|
||||
: (typeof v === "object" ? JSON.stringify(v) : String(v)),
|
||||
}));
|
||||
}
|
||||
return [{ k: "表单原文", v: String(raw) }];
|
||||
});
|
||||
|
||||
const branchText = computed(() => {
|
||||
const branches = detail.value?.branches || [];
|
||||
if (!branches.length) return "";
|
||||
const arrived = branches.filter((b: any) => b.status !== 0).length;
|
||||
return `${arrived}/${branches.length} 分支已到达汇聚点`;
|
||||
});
|
||||
|
||||
function statusName(s: number) {
|
||||
return INSTANCE_STATUS[s] || s;
|
||||
}
|
||||
|
||||
function statusColor(s: number) {
|
||||
return s === 10 ? "blue" : s === 20 ? "green" : s === 30 ? "red" : "gray";
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
detail.value = await flowApi.detail(String(route.params.instanceId)).send();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page" v-if="detail">
|
||||
<div class="head">
|
||||
<span class="title">{{ detail.bizTitle }}</span>
|
||||
<Tag :color="statusColor(detail.status)">{{ statusName(detail.status) }}</Tag>
|
||||
<a @click="router.push({ path: '/diagram', query: { instanceId: String(route.params.instanceId) } })">流程示意图</a>
|
||||
<span class="muted" v-if="branchText">{{ branchText }}</span>
|
||||
</div>
|
||||
|
||||
<Card class="block" :bordered="false" dis-hover>
|
||||
<template #title>基本信息</template>
|
||||
<div class="grid">
|
||||
<div class="row"><span class="k">流程</span><span>{{ detail.defCode }} v{{ detail.defVersion }}</span></div>
|
||||
<div class="row"><span class="k">提交人</span><span>{{ detail.submitterName }}</span></div>
|
||||
<div class="row"><span class="k">提交时间</span><span>{{ (detail.submitTime || "").replace("T", " ") }}</span></div>
|
||||
<div class="row"><span class="k">办结时间</span><span>{{ (detail.finishTime || "").replace("T", " ") || "-" }}</span></div>
|
||||
<div class="row" v-if="detail.bizUrl"><span class="k">业务详情</span>
|
||||
<a :href="detail.bizUrl" target="_blank">查看业务详情</a></div>
|
||||
</div>
|
||||
<template v-if="snapshotRows.length">
|
||||
<Divider size="small" />
|
||||
<div class="snapshot">
|
||||
<div v-for="row in snapshotRows" :key="row.k" class="row">
|
||||
<span class="k">{{ row.k }}</span>
|
||||
<span class="v">{{ row.v }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="formRows.length">
|
||||
<Divider size="small">业务表单数据</Divider>
|
||||
<div class="snapshot">
|
||||
<div v-for="row in formRows" :key="row.k" class="row">
|
||||
<span class="k">{{ row.k }}</span>
|
||||
<span class="v">{{ row.v }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<Card class="block" :bordered="false" dis-hover>
|
||||
<template #title>流程轨迹</template>
|
||||
<Timeline>
|
||||
<TimelineItem v-for="t in detail.trace" :key="t.taskId"
|
||||
:color="t.status === 1 ? 'green' : t.status === 2 ? 'red' : t.status === 0 ? 'blue' : 'gray'">
|
||||
<span class="node">{{ t.nodeName || t.nodeId }}</span>
|
||||
<Tag size="small">{{ t.assigneeName || t.assigneeId }}</Tag>
|
||||
<Tag size="small" v-if="t.status !== 1">{{ taskStatusName(t.status) }}</Tag>
|
||||
<span class="opinion" v-if="t.opinion">「{{ t.opinion }}」</span>
|
||||
<div class="time">{{ (t.handleTime || t.arriveTime || "").replace("T", " ") }}</div>
|
||||
</TimelineItem>
|
||||
</Timeline>
|
||||
</Card>
|
||||
</div>
|
||||
<div v-else class="page muted">加载中…</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
// 任务状态名(避免引入整表;口径同 api/module/flow TASK_STATUS)
|
||||
function taskStatusName(s: number) {
|
||||
return ({ 0: "待办", 2: "拒绝", 3: "失效" } as Record<number, string>)[s] || "同意";
|
||||
}
|
||||
export default { methods: { taskStatusName } };
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page { padding: 12px 16px; }
|
||||
.head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
|
||||
.head a { cursor: pointer; }
|
||||
.title { font-size: 16px; font-weight: 600; }
|
||||
.block { margin-bottom: 12px; }
|
||||
.grid .row { display: flex; padding: 3px 0; }
|
||||
.k { width: 120px; color: #888; flex-shrink: 0; }
|
||||
.v { white-space: pre-wrap; word-break: break-all; }
|
||||
.muted { color: #999; }
|
||||
.node { font-weight: 600; margin-right: 6px; }
|
||||
.opinion { color: #666; }
|
||||
.time { color: #aaa; font-size: 12px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user