From 04b1a9cfde4049be9a38b9832d5289d4a192c883 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期五, 15 五月 2026 16:29:33 +0800
Subject: [PATCH] 加班申请模块和审批流程公共组件
---
src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue | 2
src/views/officeProcessAutomation/AttendManage/overtime-apply/components/ApprovalFlowEditor.vue | 360 ++++++++++++++++++++++++++++++++++++
src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue | 201 +++++++++----------
3 files changed, 458 insertions(+), 105 deletions(-)
diff --git a/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue b/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
index f52d57d..7c3849b 100644
--- a/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
+++ b/src/views/officeProcessAutomation/AttendManage/leave-apply/index.vue
@@ -1,4 +1,4 @@
-<!--OA妯″潡锛氳鍋囩敵璇凤紙瀛楁涓哄墠绔崰浣嶏紝鍚庢湡涓庡悗绔帴鍙e榻愶級-->
+<!--OA妯″潡锛氳鍋囩敵璇�-->
<template>
<div class="app-container">
<div class="search_form mb20">
diff --git a/src/views/officeProcessAutomation/AttendManage/overtime-apply/components/ApprovalFlowEditor.vue b/src/views/officeProcessAutomation/AttendManage/overtime-apply/components/ApprovalFlowEditor.vue
new file mode 100644
index 0000000..9e3ada5
--- /dev/null
+++ b/src/views/officeProcessAutomation/AttendManage/overtime-apply/components/ApprovalFlowEditor.vue
@@ -0,0 +1,360 @@
+<!-- 鍔犵彮鐢宠妯″潡鍐咃細鍙鍒犲鎵硅妭鐐癸紝姣忚妭鐐瑰繀閫� 1 浜� -->
+<template>
+ <div class="afe">
+ <div v-if="innerList.length" class="afe-flow">
+ <div v-for="(item, index) in innerList" :key="item._uid" class="afe-flow-item">
+ <div class="afe-card" :class="{ 'afe-card--empty': !item.approverId }">
+ <div class="afe-badge">{{ index + 1 }}</div>
+ <div class="afe-avatar-wrap">
+ <div
+ class="afe-avatar"
+ :class="{ 'afe-avatar--on': item.approverId }"
+ :style="item.approverId ? { backgroundColor: avatarColor(item.approverName) } : {}"
+ >
+ <span v-if="item.approverId">{{ (item.approverName || '?').charAt(0) }}</span>
+ <el-icon v-else :size="22"><User /></el-icon>
+ </div>
+ <div class="afe-level">{{ levelText(index) }}</div>
+ </div>
+ <div class="afe-select">
+ <el-select
+ v-model="item.approverId"
+ placeholder="璇烽�夋嫨瀹℃壒浜�"
+ filterable
+ clearable
+ style="width: 100%"
+ @change="(v) => onPick(v, item)"
+ >
+ <el-option
+ v-for="u in userOptions"
+ :key="String(u.userId ?? u.id)"
+ :label="optionLabel(u)"
+ :value="u.userId ?? u.id"
+ />
+ </el-select>
+ </div>
+ <div class="afe-actions">
+ <el-button type="primary" circle size="small" :disabled="index === 0" title="鍓嶇Щ" @click="moveLeft(index)">
+ <el-icon><ArrowLeft /></el-icon>
+ </el-button>
+ <el-button
+ type="primary"
+ circle
+ size="small"
+ :disabled="index === innerList.length - 1"
+ title="鍚庣Щ"
+ @click="moveRight(index)"
+ >
+ <el-icon><ArrowRight /></el-icon>
+ </el-button>
+ <el-button type="danger" circle size="small" title="鍒犻櫎鑺傜偣" @click="remove(index)">
+ <el-icon><Delete /></el-icon>
+ </el-button>
+ </div>
+ </div>
+ <div v-if="index < innerList.length - 1" class="afe-conn">
+ <div class="afe-conn-line"></div>
+ <el-icon class="afe-conn-icon"><ArrowRight /></el-icon>
+ </div>
+ </div>
+
+ <div class="afe-add-wrap">
+ <div class="afe-conn" v-if="innerList.length">
+ <div class="afe-conn-line"></div>
+ <el-icon class="afe-conn-icon"><ArrowRight /></el-icon>
+ </div>
+ <div class="afe-add-card" @click="addNode">
+ <div class="afe-add-icon"><el-icon :size="26"><Plus /></el-icon></div>
+ <span>鏂板鑺傜偣</span>
+ </div>
+ </div>
+ </div>
+
+ <div v-else class="afe-empty">
+ <el-icon :size="44" color="#c0c4cc"><User /></el-icon>
+ <p>鏆傛棤瀹℃壒鑺傜偣</p>
+ <el-button type="primary" @click="addNode">娣诲姞绗竴涓妭鐐�</el-button>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ArrowLeft, ArrowRight, Delete, Plus, User } from "@element-plus/icons-vue";
+import { ref, watch } from "vue";
+
+const props = defineProps({
+ modelValue: { type: Array, default: () => [] },
+ /** 涓庣埗椤� userList 缁撴瀯涓�鑷达細userId / id銆乶ickName銆乽serName */
+ userOptions: { type: Array, default: () => [] },
+});
+
+const emit = defineEmits(["update:modelValue"]);
+
+const innerList = ref([]);
+
+const palette = ["#409EFF", "#67C23A", "#E6A23C", "#F56C6C", "#9B59B6", "#1ABC9C"];
+
+function avatarColor(name) {
+ if (!name) return "#c0c4cc";
+ let h = 0;
+ for (let i = 0; i < name.length; i++) h = name.charCodeAt(i) + ((h << 5) - h);
+ return palette[Math.abs(h) % palette.length];
+}
+
+function levelText(i) {
+ const t = ["绗竴绾�", "绗簩绾�", "绗笁绾�", "绗洓绾�", "绗簲绾�", "绗叚绾�", "绗竷绾�", "绗叓绾�"];
+ return t[i] || `绗�${i + 1}绾;
+}
+
+function optionLabel(u) {
+ const nick = u.nickName || "";
+ const un = u.userName || "";
+ if (nick && un && nick !== un) return `${nick}锛�${un}锛塦;
+ return nick || un || `鐢ㄦ埛${u.userId ?? u.id ?? ""}`;
+}
+
+function newUid() {
+ return `n_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
+}
+
+function mapIn(rows) {
+ if (!Array.isArray(rows)) return [];
+ return rows.map((r, i) => ({
+ _uid: newUid(),
+ approverId: r.approverId ?? r.approver_id ?? null,
+ approverName: r.approverName ?? r.approver_name ?? "",
+ sortOrder: r.sortOrder ?? r.nodeOrder ?? i + 1,
+ nodeOrder: r.nodeOrder ?? r.sortOrder ?? i + 1,
+ roleName: r.roleName ?? "",
+ roleCode: r.roleCode ?? "",
+ }));
+}
+
+function publicShape(rows) {
+ const arr = Array.isArray(rows) ? rows : [];
+ return arr.map((r, i) => ({
+ approverId: r.approverId ?? null,
+ approverName: r.approverName ?? "",
+ roleName: r.roleName ?? "",
+ roleCode: r.roleCode ?? "",
+ sortOrder: i + 1,
+ }));
+}
+
+function emitOut() {
+ const out = innerList.value.map((r, i) => ({
+ approverId: r.approverId ?? null,
+ approverName: r.approverName ?? "",
+ sortOrder: i + 1,
+ nodeOrder: i + 1,
+ roleName: r.roleName ?? "",
+ roleCode: r.roleCode ?? "",
+ }));
+ emit("update:modelValue", out);
+}
+
+watch(
+ () => props.modelValue,
+ (v) => {
+ const next = publicShape(v || []);
+ if (JSON.stringify(next) === JSON.stringify(publicShape(innerList.value))) return;
+ innerList.value = mapIn(v || []);
+ },
+ { deep: true, immediate: true }
+);
+
+function findUser(id) {
+ if (id == null || id === "") return null;
+ return props.userOptions.find((u) => String(u.userId ?? u.id) === String(id)) ?? null;
+}
+
+function onPick(userId, row) {
+ if (!userId) {
+ row.approverName = "";
+ emitOut();
+ return;
+ }
+ const u = findUser(userId);
+ row.approverName = u ? u.nickName || u.userName || "" : "";
+ emitOut();
+}
+
+function addNode() {
+ innerList.value.push({
+ _uid: newUid(),
+ approverId: null,
+ approverName: "",
+ roleName: "",
+ roleCode: "",
+ });
+ emitOut();
+}
+
+function remove(index) {
+ innerList.value.splice(index, 1);
+ emitOut();
+}
+
+function moveLeft(index) {
+ if (index < 1) return;
+ const t = innerList.value[index];
+ innerList.value[index] = innerList.value[index - 1];
+ innerList.value[index - 1] = t;
+ emitOut();
+}
+
+function moveRight(index) {
+ if (index >= innerList.value.length - 1) return;
+ const t = innerList.value[index];
+ innerList.value[index] = innerList.value[index + 1];
+ innerList.value[index + 1] = t;
+ emitOut();
+}
+</script>
+
+<style scoped>
+.afe {
+ width: 100%;
+}
+.afe-flow {
+ display: flex;
+ align-items: flex-start;
+ flex-wrap: nowrap;
+ overflow-x: auto;
+ padding: 6px 0 10px;
+ gap: 0;
+}
+.afe-flow-item {
+ display: flex;
+ align-items: center;
+}
+.afe-card {
+ width: 200px;
+ flex-shrink: 0;
+ border: 2px solid var(--el-border-color);
+ border-radius: 12px;
+ padding: 14px 12px 12px;
+ position: relative;
+ background: var(--el-bg-color);
+}
+.afe-card--empty {
+ border-style: dashed;
+ background: var(--el-fill-color-lighter);
+}
+.afe-badge {
+ position: absolute;
+ top: -8px;
+ left: 12px;
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: var(--el-color-primary);
+ color: #fff;
+ font-size: 12px;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.afe-avatar-wrap {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 6px 0 10px;
+}
+.afe-avatar {
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ background: var(--el-fill-color);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--el-text-color-placeholder);
+ margin-bottom: 6px;
+ font-size: 18px;
+ font-weight: 600;
+}
+.afe-avatar--on {
+ color: #fff;
+}
+.afe-level {
+ font-size: 12px;
+ color: var(--el-text-color-secondary);
+}
+.afe-select {
+ margin-bottom: 10px;
+}
+.afe-actions {
+ display: flex;
+ justify-content: center;
+ gap: 8px;
+ padding-top: 10px;
+ border-top: 1px solid var(--el-border-color-lighter);
+}
+.afe-conn {
+ display: flex;
+ align-items: center;
+ width: 40px;
+ flex-shrink: 0;
+ align-self: center;
+}
+.afe-conn-line {
+ flex: 1;
+ height: 2px;
+ background: var(--el-border-color);
+}
+.afe-conn-icon {
+ font-size: 14px;
+ color: var(--el-text-color-placeholder);
+ margin-left: -2px;
+}
+.afe-add-wrap {
+ display: flex;
+ align-items: center;
+}
+.afe-add-card {
+ width: 120px;
+ min-height: 168px;
+ flex-shrink: 0;
+ border: 2px dashed var(--el-border-color);
+ border-radius: 12px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ cursor: pointer;
+ color: var(--el-text-color-regular);
+ font-size: 13px;
+ background: var(--el-fill-color-lighter);
+ transition: border-color 0.2s, background 0.2s;
+}
+.afe-add-card:hover {
+ border-color: var(--el-color-primary);
+ background: var(--el-color-primary-light-9);
+ color: var(--el-color-primary);
+}
+.afe-add-icon {
+ width: 44px;
+ height: 44px;
+ border-radius: 50%;
+ background: var(--el-color-primary);
+ color: #fff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.afe-empty {
+ text-align: center;
+ padding: 28px 16px;
+ border: 1px dashed var(--el-border-color);
+ border-radius: 12px;
+ background: var(--el-fill-color-lighter);
+}
+.afe-empty p {
+ margin: 10px 0 14px;
+ color: var(--el-text-color-secondary);
+ font-size: 14px;
+}
+</style>
diff --git a/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue b/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue
index 477bc06..0bdd83f 100644
--- a/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue
+++ b/src/views/officeProcessAutomation/AttendManage/overtime-apply/index.vue
@@ -1,4 +1,4 @@
-<!--OA妯″潡锛氬姞鐝敵璇凤紙瀛楁涓哄墠绔崰浣嶏紝鍚庢湡涓庡悗绔帴鍙e榻愶級-->
+<!--OA妯″潡锛氬姞鐝敵璇�-->
<template>
<div class="app-container">
<div class="search_form mb20">
@@ -44,7 +44,7 @@
<el-dialog
v-model="formDialog.visible"
:title="formDialog.title"
- width="960px"
+ width="1040px"
append-to-body
destroy-on-close
class="overtime-apply-form-dialog"
@@ -136,23 +136,13 @@
</el-row>
<el-row :gutter="24">
<el-col :span="24">
- <el-form-item label="棰勮瀹℃壒娴�">
- <div class="approval-flow-preview">
- <div
- v-for="(node, index) in PRESET_APPROVAL_FLOW_NODES"
- :key="node.roleCode"
- class="flow-node-wrap"
- >
- <div class="flow-node">
- <span class="flow-node-order">{{ index + 1 }}</span>
- <span class="flow-node-name">{{ node.roleName }}</span>
- </div>
- <el-icon v-if="index < PRESET_APPROVAL_FLOW_NODES.length - 1" class="flow-arrow">
- <ArrowRight />
- </el-icon>
- </div>
- </div>
- <p class="flow-tip">鎸夐『搴忛�愮骇瀹℃壒锛屽悇鑺傜偣瀹℃壒浜虹敱绯荤粺鏍规嵁缁勭粐鏋舵瀯鑷姩鍖归厤</p>
+ <el-form-item label="瀹℃壒娴佺▼" prop="approvalFlowNodes">
+ <ApprovalFlowEditor
+ v-model="form.approvalFlowNodes"
+ :user-options="flowUserOptions"
+ @update:model-value="onApprovalFlowChange"
+ />
+ <p class="flow-tip">鑷冲皯淇濈暀涓�涓妭鐐癸紱姣忎釜鑺傜偣閫夋嫨涓�鍚嶅鎵逛汉锛涘彲鏂板銆佸垹闄ゆ垨璋冩暣椤哄簭銆�</p>
</el-form-item>
</el-col>
</el-row>
@@ -199,22 +189,16 @@
<el-descriptions-item label="鍔犵彮缁撴潫鏃ユ湡">{{ detailRow.overtimeEndTime || "鈥�" }}</el-descriptions-item>
<el-descriptions-item label="鍔犵彮鏃堕暱">{{ formatHours(detailRow.overtimeHours) }}</el-descriptions-item>
<el-descriptions-item label="鍔犵彮浜嬬敱">{{ detailRow.overtimeReason }}</el-descriptions-item>
- <el-descriptions-item label="棰勮瀹℃壒娴�">
- <div class="approval-flow-preview approval-flow-detail">
- <div
- v-for="(node, index) in detailApprovalFlowNodes"
- :key="node.roleCode"
- class="flow-node-wrap"
- >
- <div class="flow-node flow-node--compact">
- <span class="flow-node-order">{{ index + 1 }}</span>
- <span class="flow-node-name">{{ node.roleName }}</span>
- </div>
- <el-icon v-if="index < detailApprovalFlowNodes.length - 1" class="flow-arrow">
- <ArrowRight />
- </el-icon>
+ <el-descriptions-item label="瀹℃壒娴佺▼">
+ <template v-if="sortedApprovalNodes(detailRow).length">
+ <div class="detail-flow-chain">
+ <template v-for="(n, i) in sortedApprovalNodes(detailRow)" :key="i">
+ <span class="detail-flow-step">{{ i + 1 }}. {{ approvalNodeLabel(n) }}</span>
+ <span v-if="i < sortedApprovalNodes(detailRow).length - 1" class="detail-flow-sep">鈫�</span>
+ </template>
</div>
- </div>
+ </template>
+ <span v-else>鈥�</span>
</el-descriptions-item>
<el-descriptions-item label="瀹℃壒缁撴灉">{{ approvalResultLabel(detailRow.approvalResult) }}</el-descriptions-item>
<el-descriptions-item label="鍒涘缓鏃堕棿">{{ detailRow.createTime || "鈥�" }}</el-descriptions-item>
@@ -256,9 +240,10 @@
</template>
<script setup>
-import { ArrowRight, Search } from "@element-plus/icons-vue";
+import { Search } from "@element-plus/icons-vue";
import dayjs from "dayjs";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
+import ApprovalFlowEditor from "./components/ApprovalFlowEditor.vue";
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { computed, getCurrentInstance, nextTick, reactive, ref, watch } from "vue";
@@ -269,22 +254,24 @@
{ label: "娉曞畾鑺傚亣鏃ュ姞鐝�", value: "holiday" },
];
-/** 棰勮瀹℃壒娴佽妭鐐癸紙涓庢祦绋嬪紩鎿庨厤缃榻愬崰浣嶏級 */
-const PRESET_APPROVAL_FLOW_NODES = [
- { roleCode: "direct_leader", roleName: "鐩村睘涓婄骇", sortOrder: 1 },
- { roleCode: "dept_leader", roleName: "閮ㄩ棬璐熻矗浜�", sortOrder: 2 },
-];
-
-function resolveApprovalFlowNodes(row) {
- const nodes = row?.approvalFlowNodes;
- if (Array.isArray(nodes) && nodes.length) {
- return [...nodes].sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
- }
- return PRESET_APPROVAL_FLOW_NODES;
+/** 鏈湴婕旂ず锛氫袱鏉$┖鑺傜偣锛屾彁浜ゅ墠椤讳负姣忚妭鐐归�夋嫨瀹℃壒浜� */
+function demoApprovalFlowNodes() {
+ return [
+ { approverId: null, approverName: "", sortOrder: 1, nodeOrder: 1, roleName: "", roleCode: "" },
+ { approverId: null, approverName: "", sortOrder: 2, nodeOrder: 2, roleName: "", roleCode: "" },
+ ];
}
-function cloneApprovalFlowNodes() {
- return PRESET_APPROVAL_FLOW_NODES.map((n) => ({ ...n }));
+function sortedApprovalNodes(row) {
+ const list = row?.approvalFlowNodes;
+ if (!Array.isArray(list) || !list.length) return [];
+ return [...list].sort((a, b) => (a.sortOrder ?? a.nodeOrder ?? 0) - (b.sortOrder ?? b.nodeOrder ?? 0));
+}
+
+function approvalNodeLabel(n) {
+ const name = (n.approverName || "").trim();
+ if (name) return name;
+ return "鏈�夋嫨瀹℃壒浜�";
}
function overtimeTypeLabel(v) {
@@ -303,6 +290,9 @@
overtimeEndTime: "",
overtimeReason: "",
attachmentList: [],
+ approvalFlowNodes: [
+ { approverId: null, approverName: "", sortOrder: 1, nodeOrder: 1, roleName: "", roleCode: "" },
+ ],
});
const { proxy } = getCurrentInstance();
@@ -426,7 +416,7 @@
overtimeEndTime: "2026-05-10 21:30:00",
overtimeHours: 3.5,
overtimeReason: "椤圭洰涓婄嚎淇濋殰銆�",
- approvalFlowNodes: cloneApprovalFlowNodes(),
+ approvalFlowNodes: demoApprovalFlowNodes(),
approvalResult: "pending",
attachmentList: [{ name: "浠诲姟鍗�.pdf" }],
createTime: "2026-05-09 10:20:00",
@@ -442,7 +432,7 @@
overtimeEndTime: "2026-05-11 12:15:00",
overtimeHours: 3.25,
overtimeReason: "瀹㈡埛鐜板満鏀寔銆�",
- approvalFlowNodes: cloneApprovalFlowNodes(),
+ approvalFlowNodes: demoApprovalFlowNodes(),
approvalResult: "approved",
attachmentList: [],
createTime: "2026-05-10 16:00:00",
@@ -555,6 +545,8 @@
const formRef = ref();
const form = reactive(createEmptyForm());
+const flowUserOptions = computed(() => allUsersCache.value.filter((u) => isActiveUser(u)));
+
const overtimeHoursDisplay = computed(() => {
const h = computeOvertimeHours(form.overtimeStartTime, form.overtimeEndTime);
return h == null ? "" : String(h);
@@ -564,6 +556,10 @@
nextTick(() => {
formRef.value?.validateField?.("overtimeEndTime");
});
+}
+
+function onApprovalFlowChange() {
+ nextTick(() => formRef.value?.validateField?.("approvalFlowNodes"));
}
const formRules = {
@@ -590,11 +586,32 @@
},
],
overtimeReason: [{ required: true, message: "璇峰~鍐欏姞鐝簨鐢�", trigger: "blur" }],
+ approvalFlowNodes: [
+ {
+ validator: (_rule, _val, callback) => {
+ const nodes = form.approvalFlowNodes || [];
+ if (!nodes.length) {
+ callback(new Error("璇疯嚦灏戜繚鐣欎竴涓鎵硅妭鐐�"));
+ return;
+ }
+ if (nodes.some((n) => n.approverId == null || n.approverId === "")) {
+ callback(new Error("姣忎釜瀹℃壒鑺傜偣蹇呴』閫夋嫨涓�鍚嶅鎵逛汉"));
+ return;
+ }
+ const ids = nodes.map((n) => String(n.approverId));
+ if (new Set(ids).size !== ids.length) {
+ callback(new Error("鍚屼竴瀹℃壒浜轰笉鑳介噸澶嶅嚭鐜板湪澶氫釜鑺傜偣"));
+ return;
+ }
+ callback();
+ },
+ trigger: "change",
+ },
+ ],
};
const detailDialog = reactive({ visible: false });
const detailRow = ref({});
-const detailApprovalFlowNodes = computed(() => resolveApprovalFlowNodes(detailRow.value));
const filesDialog = reactive({ visible: false, row: null });
@@ -673,7 +690,7 @@
overtimeReason: raw.overtimeReason ?? "",
approvalFlowNodes: Array.isArray(raw.approvalFlowNodes) && raw.approvalFlowNodes.length
? raw.approvalFlowNodes.map((n) => ({ ...n }))
- : cloneApprovalFlowNodes(),
+ : [],
approvalResult: raw.approvalResult && ["pending", "approved", "rejected", "cancelled"].includes(raw.approvalResult)
? raw.approvalResult
: "pending",
@@ -730,6 +747,9 @@
overtimeEndTime: row.overtimeEndTime,
overtimeReason: row.overtimeReason,
attachmentList: JSON.parse(JSON.stringify(row.attachmentList || [])),
+ approvalFlowNodes: row.approvalFlowNodes?.length
+ ? JSON.parse(JSON.stringify(row.approvalFlowNodes))
+ : [],
});
const u = userById(row.applicantId);
if (u) {
@@ -775,7 +795,15 @@
overtimeEndTime: form.overtimeEndTime,
overtimeHours: hours,
overtimeReason: form.overtimeReason,
- approvalFlowNodes: cloneApprovalFlowNodes(),
+ approvalFlowNodes: (form.approvalFlowNodes || []).map((n, i) => ({
+ approverId: n.approverId,
+ approverName:
+ n.approverName || userById(n.approverId)?.nickName || userById(n.approverId)?.userName || "",
+ sortOrder: i + 1,
+ nodeOrder: i + 1,
+ roleName: n.roleName || "",
+ roleCode: n.roleCode || "",
+ })),
attachmentList: JSON.parse(JSON.stringify(form.attachmentList || [])),
};
if (formDialog.mode === "add") {
@@ -857,60 +885,25 @@
.overtime-apply-form-dialog :deep(.el-dialog__body) {
padding-top: 12px;
}
-.approval-flow-preview {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 8px;
- width: 100%;
-}
-.approval-flow-detail {
- padding: 4px 0;
-}
-.flow-node-wrap {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-.flow-node {
- display: flex;
- align-items: center;
- gap: 10px;
- min-width: 140px;
- padding: 10px 16px;
- background: var(--el-fill-color-light);
- border: 1px solid var(--el-border-color-lighter);
- border-radius: 8px;
-}
-.flow-node--compact {
- min-width: 120px;
- padding: 8px 12px;
-}
-.flow-node-order {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 22px;
- height: 22px;
- font-size: 12px;
- font-weight: 600;
- color: #fff;
- background: var(--el-color-primary);
- border-radius: 50%;
- flex-shrink: 0;
-}
-.flow-node-name {
- font-size: 14px;
- color: var(--el-text-color-primary);
-}
-.flow-arrow {
- font-size: 18px;
- color: var(--el-text-color-secondary);
-}
.flow-tip {
margin: 10px 0 0;
font-size: 12px;
line-height: 1.5;
color: var(--el-text-color-secondary);
}
+.detail-flow-chain {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 6px 8px;
+ line-height: 1.6;
+}
+.detail-flow-step {
+ font-size: 14px;
+ color: var(--el-text-color-primary);
+}
+.detail-flow-sep {
+ color: var(--el-text-color-secondary);
+ font-size: 13px;
+}
</style>
--
Gitblit v1.9.3