From 8bba0a2d08c7abc07604a0654661efc884e5d751 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期六, 16 五月 2026 15:23:17 +0800
Subject: [PATCH] 审批列表和审批模板页面
---
src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue | 367 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 360 insertions(+), 7 deletions(-)
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
index f88c88f..a79d546 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
@@ -1,12 +1,365 @@
-<!--
- 妯″潡涓枃鍚嶏細瀹℃壒妯℃澘
- 鐩綍鏍囪瘑锛欰pproveManage/approve-template锛坅pprove-template 鈫� 涓枃锛氬鎵规ā鏉匡級
- 澶嶇敤椤甸潰锛欯/views/procurementManagement/procurementLedger/index.vue锛堥噰璐彴璐︼紱鏂囦欢鍚� index.vue 鈫� 鍏ュ彛椤碉級
--->
+<!--OA妯″潡锛氬鎵规ā鏉匡紙绯荤粺甯哥敤 + 鑷畾涔夊鑺傜偣娴佺▼锛�-->
<template>
- <ProcurementLedger />
+ <div class="app-container approve-template-page">
+ <el-tabs v-model="activeTab" class="template-tabs">
+ <el-tab-pane label="绯荤粺甯哥敤瀹℃壒" name="builtin">
+ <el-alert type="info" show-icon :closable="false" class="mb16">
+ <template #title>绯荤粺棰勭疆妯℃澘</template>
+ <template #default>
+ 浠ヤ笅涓� OA 妯″潡鍐呯疆鐨勫父鐢ㄥ鎵圭被鍨嬶紝濉姤瀛楁涓庨粯璁ゅ鎵规柟寮忕敱绯荤粺缁存姢锛涙彁浜ゅ鎵规椂鍙洿鎺ラ�夌敤銆�
+ </template>
+ </el-alert>
+ <div class="builtin-grid">
+ <div v-for="item in builtinTemplates" :key="item.key" class="builtin-card">
+ <span class="builtin-label">{{ item.label }}</span>
+ <p class="builtin-summary">{{ item.summary }}</p>
+ <div class="builtin-meta">
+ <el-tag size="small" effect="plain">{{ item.fieldCount }} 涓~鎶ラ」</el-tag>
+ <el-tag size="small" type="warning" effect="plain">
+ 榛樿{{ item.defaultMode === "or_sign" ? "鎴栫" : "涓庣" }}
+ </el-tag>
+ <el-tag size="small" type="info" effect="plain">鍙</el-tag>
+ </div>
+ </div>
+ </div>
+ </el-tab-pane>
+
+ <el-tab-pane label="鑷畾涔夊鎵规ā鏉�" name="custom">
+ <div class="search_form mb20">
+ <div class="search_fields">
+ <span class="search_title">妯℃澘鍚嶇О锛�</span>
+ <el-input
+ v-model="searchForm.keyword"
+ style="width: 220px"
+ placeholder="鎼滅储鍚嶇О鎴栬鏄�"
+ clearable
+ :prefix-icon="Search"
+ @keyup.enter="handleQuery"
+ />
+ <el-checkbox v-model="searchForm.enabledOnly" class="ml12" @change="handleQuery">
+ 浠呮樉绀哄惎鐢�
+ </el-checkbox>
+ <el-button type="primary" :icon="Search" class="ml10" @click="handleQuery">鎼滅储</el-button>
+ <el-button :icon="RefreshRight" @click="resetSearch">閲嶇疆</el-button>
+ </div>
+ <div class="search_actions">
+ <el-button type="primary" :icon="Plus" @click="openFormDialog('add')">鏂板缓妯℃澘</el-button>
+ </div>
+ </div>
+
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="false"
+ :tableLoading="tableLoading"
+ :total="page.total"
+ @pagination="pagination"
+ />
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+
+ <!-- 鏂板缓 / 缂栬緫 -->
+ <el-dialog
+ v-model="formDialog.visible"
+ :title="formDialog.title"
+ width="960px"
+ append-to-body
+ destroy-on-close
+ class="template-form-dialog"
+ @closed="formRef?.resetFields?.()"
+ >
+ <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="妯℃澘鍚嶇О" prop="templateName">
+ <el-input v-model="form.templateName" placeholder="濡傦細椤圭洰绔嬮」瀹℃壒" maxlength="50" show-word-limit />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍚敤鐘舵��">
+ <el-switch v-model="form.enabled" active-text="鍚敤" inactive-text="鍋滅敤" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-form-item label="妯℃澘璇存槑">
+ <el-input
+ v-model="form.description"
+ type="textarea"
+ :rows="2"
+ placeholder="绠�瑕佽鏄庤妯℃澘鐨勯�傜敤鍦烘櫙"
+ maxlength="200"
+ show-word-limit
+ />
+ </el-form-item>
+ <el-form-item label="瀹℃壒娴佺▼" required>
+ <TemplateFlowEditor v-model="form.flowNodes" :user-options="flowUserOptions" />
+ <p class="flow-tip">
+ 鎸夐『搴忔祦杞細鍙负姣忎釜鑺傜偣娣诲姞澶氬悕瀹℃壒浜猴紱浼氱闇�鍏ㄩ儴閫氳繃锛屾垨绛句换涓�浜洪�氳繃鍗冲彲杩涘叆涓嬩竴鑺傜偣銆�
+ </p>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button type="primary" @click="onSubmitForm">淇� 瀛�</el-button>
+ <el-button @click="formDialog.visible = false">鍙� 娑�</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 璇︽儏 -->
+ <el-dialog v-model="detailDialog.visible" title="妯℃澘璇︽儏" width="880px" append-to-body destroy-on-close>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="妯℃澘鍚嶇О">{{ detailRow.templateName }}</el-descriptions-item>
+ <el-descriptions-item label="鐘舵��">
+ <el-tag :type="detailRow.enabled !== false ? 'success' : 'info'" size="small">
+ {{ detailRow.enabled !== false ? "鍚敤" : "鍋滅敤" }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="璇存槑" :span="2">{{ detailRow.description || "鈥�" }}</el-descriptions-item>
+ <el-descriptions-item label="鍒涘缓鏃堕棿">{{ detailRow.createTime || "鈥�" }}</el-descriptions-item>
+ <el-descriptions-item label="鏇存柊鏃堕棿">{{ detailRow.updateTime || "鈥�" }}</el-descriptions-item>
+ </el-descriptions>
+ <el-divider content-position="left">瀹℃壒娴佺▼锛坽{ detailRow.flowNodes?.length || 0 }} 涓妭鐐癸級</el-divider>
+ <div v-if="detailRow.flowNodes?.length" class="detail-flow">
+ <div v-for="(node, index) in detailRow.flowNodes" :key="index" class="detail-node">
+ <div class="detail-node-head">
+ <span class="detail-node-order">鑺傜偣 {{ index + 1 }}</span>
+ <el-tag size="small" :type="node.signMode === 'or_sign' ? 'warning' : 'primary'">
+ {{ nodeSignModeLabel(node.signMode) }}
+ </el-tag>
+ </div>
+ <div class="detail-approvers">
+ <el-tag
+ v-for="a in node.approvers"
+ :key="String(a.approverId)"
+ class="detail-approver-tag"
+ effect="plain"
+ >
+ {{ a.approverName || "鈥�" }}
+ </el-tag>
+ <span v-if="!node.approvers?.length" class="text-muted">鏈厤缃鎵逛汉</span>
+ </div>
+ <el-icon v-if="index < detailRow.flowNodes.length - 1" class="detail-arrow"><ArrowRight /></el-icon>
+ </div>
+ </div>
+ <el-empty v-else description="鏆傛棤娴佺▼鑺傜偣" :image-size="60" />
+ <template #footer>
+ <el-button @click="detailDialog.visible = false">鍏� 闂�</el-button>
+ <el-button type="primary" @click="editFromDetail">缂� 杈�</el-button>
+ </template>
+ </el-dialog>
+ </div>
</template>
<script setup>
-import ProcurementLedger from '@/views/procurementManagement/procurementLedger/index.vue'
+import { ArrowRight, Document, Plus, RefreshRight } from "@element-plus/icons-vue";
+import { ElMessage } from "element-plus";
+import { onMounted, ref } from "vue";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import TemplateFlowEditor from "./components/TemplateFlowEditor.vue";
+import { useApproveTemplate } from "./useApproveTemplate.js";
+
+const at = useApproveTemplate();
+const {
+ Search,
+ activeTab,
+ builtinTemplates,
+ nodeSignModeLabel,
+ searchForm,
+ tableLoading,
+ page,
+ tableData,
+ tableColumn,
+ formDialog,
+ form,
+ formRef,
+ formRules,
+ detailDialog,
+ detailRow,
+ handleQuery,
+ resetSearch,
+ pagination,
+ openFormDialog,
+ openDetail,
+ submitForm,
+} = at;
+
+const flowUserOptions = ref([]);
+
+function unwrapArray(payload) {
+ if (Array.isArray(payload)) return payload;
+ if (payload?.data && Array.isArray(payload.data)) return payload.data;
+ if (payload?.rows && Array.isArray(payload.rows)) return payload.rows;
+ return [];
+}
+
+function isActiveUser(u) {
+ if (u.delFlag === "2" || u.delFlag === 2) return false;
+ if (u.status == null) return true;
+ return String(u.status) === "0";
+}
+
+async function loadUsers() {
+ try {
+ const res = await userListNoPageByTenantId();
+ flowUserOptions.value = unwrapArray(res).filter(isActiveUser);
+ } catch {
+ flowUserOptions.value = [];
+ }
+}
+
+async function onSubmitForm() {
+ const ret = await submitForm();
+ if (ret?.message) {
+ ElMessage.warning(ret.message);
+ return;
+ }
+ if (ret?.ok) ElMessage.success("淇濆瓨鎴愬姛");
+}
+
+function editFromDetail() {
+ const row = detailRow.value;
+ detailDialog.visible = false;
+ openFormDialog("edit", row);
+}
+
+onMounted(() => {
+ loadUsers();
+ handleQuery();
+});
</script>
+
+<style scoped>
+.mb20 {
+ margin-bottom: 20px;
+}
+.mb16 {
+ margin-bottom: 16px;
+}
+.ml10 {
+ margin-left: 10px;
+}
+.ml12 {
+ margin-left: 12px;
+}
+.page-header .header-title {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 18px;
+ font-weight: 600;
+ color: var(--el-text-color-primary);
+ margin-bottom: 8px;
+}
+.title-icon {
+ font-size: 22px;
+ color: var(--el-color-primary);
+}
+.header-desc {
+ margin: 0;
+ font-size: 13px;
+ color: var(--el-text-color-secondary);
+ line-height: 1.6;
+ max-width: 920px;
+}
+.search_form {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+.search_fields {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 4px;
+}
+.search_actions {
+ display: flex;
+ gap: 8px;
+}
+.builtin-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ gap: 12px;
+}
+.builtin-card {
+ padding: 14px 16px;
+ border: 1px solid var(--el-border-color-lighter);
+ border-radius: var(--radius-md, 8px);
+ background: var(--el-fill-color-blank);
+}
+.builtin-label {
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--el-text-color-primary);
+}
+.builtin-summary {
+ margin: 8px 0 10px;
+ font-size: 12px;
+ color: var(--el-text-color-secondary);
+ line-height: 1.5;
+ min-height: 36px;
+}
+.builtin-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+.flow-tip {
+ font-size: 12px;
+ color: var(--el-text-color-secondary);
+ margin: 8px 0 0;
+ line-height: 1.5;
+}
+.detail-flow {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-start;
+ gap: 8px;
+}
+.detail-node {
+ position: relative;
+ min-width: 180px;
+ max-width: 240px;
+ padding: 12px;
+ border: 1px solid var(--el-border-color-lighter);
+ border-radius: 8px;
+ background: var(--el-fill-color-lighter);
+}
+.detail-node-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 8px;
+}
+.detail-node-order {
+ font-weight: 600;
+ font-size: 13px;
+}
+.detail-approvers {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+}
+.detail-approver-tag {
+ margin: 0;
+}
+.detail-arrow {
+ position: absolute;
+ right: -20px;
+ top: 50%;
+ transform: translateY(-50%);
+ color: var(--el-text-color-placeholder);
+}
+.text-muted {
+ font-size: 12px;
+ color: var(--el-text-color-placeholder);
+}
+.template-form-dialog :deep(.el-dialog__body) {
+ padding-top: 8px;
+}
+</style>
--
Gitblit v1.9.3