| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | |
| | | /** 审批记录 approveAction → 页面 result */ |
| | | export function mapRecordResultFromApi(action) { |
| | | const s = String(action || "").toUpperCase(); |
| | | // 发起申请:申请人是本人,不是审批动作,单独标识,避免显示成「待处理」 |
| | | if (s === "SUBMIT" || s === "SUBMITTED" || s === "CREATE") return "submitted"; |
| | | if (s === "APPROVED" || s === "APPROVE" || s === "PASS") return "approved"; |
| | | if (s === "REJECTED" || s === "REJECT" || s === "REFUSE") return "rejected"; |
| | | return "pending"; |
| | | } |
| | | |
| | | /** 审批记录 result → 文案 */ |
| | | export function approvalRecordResultLabel(result) { |
| | | if (result === "submitted") return "申请"; |
| | | if (result === "approved") return "通过"; |
| | | if (result === "rejected") return "驳回"; |
| | | return "待处理"; |
| | | } |
| | | |
| | | /** 后端 records → 时间线展示结构 */ |
| | | export function mapRecordsFromApi(records) { |
| | | const list = Array.isArray(records) ? records : []; |
| | |
| | | <div class="flow-step-badge">{{ index + 1 }}</div> |
| | | <div class="flow-step-main"> |
| | | <div class="flow-step-head"> |
| | | <span class="flow-step-name">节点 {{ index + 1 }}</span> |
| | | <span class="flow-step-name">{{ nodeDisplayName(props.typeName, index) }}</span> |
| | | <el-tag size="small" :type="node.signMode === 'or_sign' ? 'warning' : 'primary'" effect="plain"> |
| | | {{ nodeSignModeLabel(node.signMode) }} |
| | | </el-tag> |
| | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | import { ArrowRight } from "@element-plus/icons-vue"; |
| | | import { nodeSignModeLabel } from "../../approve-template/approveTemplateConstants.js"; |
| | | import { nodeDisplayName, nodeSignModeLabel } from "../../approve-template/approveTemplateConstants.js"; |
| | | import { |
| | | mapTaskStatusLabel, |
| | | mapTaskStatusTagType, |
| | |
| | | const props = defineProps({ |
| | | tasks: { type: Array, default: () => [] }, |
| | | nodes: { type: Array, default: () => [] }, |
| | | /** 审批类型/模板名称,用于拼节点名(如「发货审批·一级」) */ |
| | | typeName: { type: String, default: "" }, |
| | | }); |
| | | |
| | | const displayNodes = computed(() => { |
| | |
| | | <div class="detail-block-title"> |
| | | 审批流程({{ detailRow.tasks?.length || detailRow.flowNodes?.length || 0 }} 项) |
| | | </div> |
| | | <InstanceFlowDisplay :tasks="detailRow.tasks" :nodes="detailRow.flowNodes" /> |
| | | <InstanceFlowDisplay :tasks="detailRow.tasks" :nodes="detailRow.flowNodes" :type-name="detailRow.templateName" /> |
| | | </div> |
| | | <div class="detail-block"> |
| | | <div class="detail-block-title">审批记录</div> |
| | |
| | | <div class="detail-block-title"> |
| | | 审批流程({{ approveDialog.row?.tasks?.length || approveDialog.row?.flowNodes?.length || 0 }} 项) |
| | | </div> |
| | | <InstanceFlowDisplay :tasks="approveDialog.row?.tasks" :nodes="approveDialog.row?.flowNodes" /> |
| | | <InstanceFlowDisplay :tasks="approveDialog.row?.tasks" :nodes="approveDialog.row?.flowNodes" :type-name="approveDialog.row?.templateName" /> |
| | | </div> |
| | | <el-form label-width="100px" class="mt16"> |
| | | <el-form-item label="审批意见" required> |
| | |
| | | import { |
| | | APPROVAL_STATUS_SEARCH_OPTIONS, |
| | | APPROVAL_TYPE_OPTIONS, |
| | | approvalRecordResultLabel, |
| | | approvalStatusLabel, |
| | | approvalStatusTagType, |
| | | approvalTypeLabel, |
| | |
| | | } |
| | | |
| | | function approvalActionLabel(result) { |
| | | if (result === "approved") return "通过"; |
| | | if (result === "rejected") return "驳回"; |
| | | return "待处理"; |
| | | return approvalRecordResultLabel(result); |
| | | } |
| | | |
| | | return { |
| | |
| | | <div class="detail-block-title"> |
| | | 审批流程({{ row?.tasks?.length || row?.flowNodes?.length || 0 }} 项) |
| | | </div> |
| | | <InstanceFlowDisplay :tasks="row?.tasks" :nodes="row?.flowNodes" /> |
| | | <InstanceFlowDisplay :tasks="row?.tasks" :nodes="row?.flowNodes" :type-name="row?.templateName" /> |
| | | </div> |
| | | <div class="detail-block"> |
| | | <div class="detail-block-title">审批记录</div> |
| | |
| | | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | import { canEditBusinessInstanceRow } from "../../approve-list/approveListConstants.js"; |
| | | import { canEditBusinessInstanceRow, approvalRecordResultLabel } from "../../approve-list/approveListConstants.js"; |
| | | import { formatDisplayTime } from "../../approve-template/approveTemplateConstants.js"; |
| | | import ApproveDetailPanel from "../../approve-list/components/ApproveDetailPanel.vue"; |
| | | import InstanceFlowDisplay from "../../approve-list/components/InstanceFlowDisplay.vue"; |
| | |
| | | }); |
| | | |
| | | function approvalActionLabel(result) { |
| | | if (result === "approved") return "通过"; |
| | | if (result === "rejected") return "驳回"; |
| | | return "待处理"; |
| | | return approvalRecordResultLabel(result); |
| | | } |
| | | |
| | | function formatRecordTime(time) { |
| | |
| | | return { ok: true, nodes, name }; |
| | | } |
| | | |
| | | export function flowNodesSummary(nodes) { |
| | | /** 节点层级中文序号:一级 / 二级 / … */ |
| | | const LEVEL_TEXT = ["一级", "二级", "三级", "四级", "五级", "六级", "七级", "八级", "九级", "十级"]; |
| | | |
| | | export function nodeLevelText(index) { |
| | | return LEVEL_TEXT[index] || `第${index + 1}级`; |
| | | } |
| | | |
| | | /** |
| | | * 审批节点名称:按「审批类型 + 层级」命名,例如「发货审批·一级」。 |
| | | * @param {string} typeName 审批类型/模板名称,如「发货审批」 |
| | | * @param {number} index 0 基节点序号 |
| | | */ |
| | | export function nodeDisplayName(typeName, index) { |
| | | const level = nodeLevelText(index); |
| | | const type = (typeName || "").trim(); |
| | | return type ? `${type}·${level}` : level; |
| | | } |
| | | |
| | | export function flowNodesSummary(nodes, typeName = "") { |
| | | const list = normalizeFlowNodes(nodes); |
| | | if (!list.length) return "—"; |
| | | return list |
| | | .map((n, i) => { |
| | | const names = n.approvers.map((a) => a.approverName || "未命名").join("、") || "未配置"; |
| | | return `节点${i + 1}(${nodeSignModeLabel(n.signMode)}:${names})`; |
| | | return `${nodeDisplayName(typeName, i)}(${nodeSignModeLabel(n.signMode)}:${names})`; |
| | | }) |
| | | .join(" → "); |
| | | } |
| | |
| | | <div class="tfe-card" :class="{ 'tfe-card--empty': !item.approvers?.length }"> |
| | | <div class="tfe-badge">{{ index + 1 }}</div> |
| | | <div class="tfe-head"> |
| | | <span class="tfe-level">{{ levelText(index) }}</span> |
| | | <el-radio-group |
| | | <span class="tfe-level">{{ levelText(index) }}</span> <el-radio-group |
| | | v-if="!readonly" |
| | | v-model="item.signMode" |
| | | size="small" |
| | |
| | | <script setup> |
| | | import { ArrowLeft, ArrowRight, Delete, Plus, User } from "@element-plus/icons-vue"; |
| | | import { ref, watch } from "vue"; |
| | | import { NODE_SIGN_MODE_OPTIONS, normalizeFlowNodes } from "../approveTemplateConstants.js"; |
| | | import { NODE_SIGN_MODE_OPTIONS, normalizeFlowNodes, nodeDisplayName } from "../approveTemplateConstants.js"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { type: Array, default: () => [] }, |
| | | userOptions: { type: Array, default: () => [] }, |
| | | /** 审批类型/模板名称,用于拼节点名(如「发货审批·一级」) */ |
| | | typeName: { type: String, default: "" }, |
| | | /** 选择模板后申请场景:仅展示,不可改审批人/节点 */ |
| | | readonly: { type: Boolean, default: false }, |
| | | }); |
| | |
| | | } |
| | | |
| | | function levelText(i) { |
| | | const t = ["第一级", "第二级", "第三级", "第四级", "第五级", "第六级", "第七级", "第八级"]; |
| | | return t[i] || `第${i + 1}级`; |
| | | // 节点名按审批类型 + 层级显示,如「发货审批·一级」 |
| | | return nodeDisplayName(props.typeName, i); |
| | | } |
| | | |
| | | function optionLabel(u) { |
| | |
| | | |
| | | <el-form-item label="审批流程" required> |
| | | |
| | | <TemplateFlowEditor v-model="form.flowNodes" :user-options="flowUserOptions" /> |
| | | <TemplateFlowEditor v-model="form.flowNodes" :user-options="flowUserOptions" :type-name="form.templateName" /> |
| | | |
| | | <p class="flow-tip"> |
| | | |
| | |
| | | |
| | | <div class="detail-node-head"> |
| | | |
| | | <span class="detail-node-order">节点 {{ index + 1 }}</span> |
| | | <span class="detail-node-order">{{ nodeDisplayName(detailRow.templateName, index) }}</span> |
| | | |
| | | <el-tag size="small" :type="node.signMode === 'or_sign' ? 'warning' : 'primary'"> |
| | | |
| | |
| | | |
| | | nodeSignModeLabel, |
| | | |
| | | nodeDisplayName, |
| | | |
| | | searchForm, |
| | | |
| | | tableLoading, |
| | |
| | | isBuiltinTemplate, |
| | | mapTemplateFromApi, |
| | | mapTemplateToApi, |
| | | nodeDisplayName, |
| | | nodeSignModeLabel, |
| | | formatDisplayTime, |
| | | unwrapTemplateDetail, |
| | |
| | | }, |
| | | { |
| | | label: "流程概要", |
| | | prop: "flowNodes", |
| | | prop: "flowSummary", |
| | | minWidth: 220, |
| | | showOverflowTooltip: true, |
| | | formatData: (v) => flowNodesSummary(v), |
| | | }, |
| | | { |
| | | label: "状态", |
| | |
| | | buildApprovalTemplateListParams({ page, searchForm }) |
| | | ); |
| | | const data = res?.data || {}; |
| | | tableData.value = (data.records || []).map(mapTemplateFromApi); |
| | | // 流程概要按「审批类型·层级」展示,需要把模板名带进文案里 |
| | | tableData.value = (data.records || []).map(mapTemplateFromApi).map((row) => ({ |
| | | ...row, |
| | | flowSummary: flowNodesSummary(row.flowNodes, row.templateName), |
| | | })); |
| | | page.total = Number(data.total || 0); |
| | | } catch { |
| | | tableData.value = []; |
| | |
| | | templateTypeLabel, |
| | | fetchTemplateList, |
| | | nodeSignModeLabel, |
| | | nodeDisplayName, |
| | | flowNodesSummary, |
| | | searchForm, |
| | | tableLoading, |
| | |
| | | } |
| | | |
| | | function approvalActionLabel(v) { |
| | | // 与审批台账保持一致:申请记录显示「申请」,审批动作显示「通过/驳回」 |
| | | if (v === "submitted" || v === "pending") return "申请"; |
| | | if (v === "approved") return "通过"; |
| | | if (v === "rejected") return "驳回"; |
| | | return "提交"; |
| | |
| | | } |
| | | |
| | | function approvalActionLabel(v) { |
| | | // 与审批台账保持一致:申请记录显示「申请」,审批动作显示「通过/驳回」 |
| | | if (v === "submitted" || v === "pending") return "申请"; |
| | | if (v === "approved") return "通过"; |
| | | if (v === "rejected") return "驳回"; |
| | | return "提交"; |