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-list/index.vue | 438 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 431 insertions(+), 7 deletions(-)
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
index d4ff149..774b322 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
@@ -1,12 +1,436 @@
-<!--
- 妯″潡涓枃鍚嶏細瀹℃壒鍒楄〃
- 鐩綍鏍囪瘑锛欰pproveManage/approve-list锛坅pprove-list 鈫� 涓枃锛氬鎵瑰垪琛級
- 澶嶇敤椤甸潰锛欯/views/procurementManagement/procurementLedger/index.vue锛堥噰璐彴璐︼紱鏂囦欢鍚� index.vue 鈫� 鍏ュ彛椤碉級
--->
+<!--OA妯″潡锛氬鎵瑰垪琛�-->
<template>
- <ProcurementLedger />
+ <div class="app-container">
+ <div class="search_form mb20">
+ <div class="search_fields">
+ <span class="search_title">瀹℃壒绫诲瀷锛�</span>
+ <el-select
+ v-model="searchForm.approvalType"
+ placeholder="璇烽�夋嫨瀹℃壒绫诲瀷"
+ clearable
+ filterable
+ style="width: 200px"
+ >
+ <el-option
+ v-for="opt in APPROVAL_TYPE_OPTIONS"
+ :key="opt.value"
+ :label="opt.label"
+ :value="opt.value"
+ />
+ </el-select>
+ <span class="search_title" style="margin-left: 12px">鐢宠浜哄悕绉帮細</span>
+ <el-input
+ v-model="searchForm.applicantKeyword"
+ style="width: 200px"
+ placeholder="璇疯緭鍏ョ敵璇蜂汉鍚嶇О"
+ clearable
+ :prefix-icon="Search"
+ @keyup.enter="handleQuery"
+ />
+ <span class="search_title" style="margin-left: 12px">鍒涘缓鏃堕棿锛�</span>
+ <el-date-picker
+ v-model="searchForm.createTimeRange"
+ type="daterange"
+ range-separator="-"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ style="width: 260px"
+ clearable
+ />
+ <el-button type="primary" :icon="Search" style="margin-left: 10px" @click="handleQuery">鎼滅储</el-button>
+ <el-button :icon="RefreshRight" @click="resetSearch">閲嶇疆</el-button>
+ </div>
+ <div class="search_actions">
+ <el-button type="primary" :icon="Plus" @click="openSubmitDialog">鎻愪氦瀹℃壒</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"
+ >
+ <template #approveType="{ row }">
+ <span class="approve-type-cell" :style="approvalTypeStyle(row.approvalType)">
+ {{ approvalTypeLabel(row.approvalType) }}
+ </span>
+ </template>
+ <template #approvalMethod="{ row }">
+ <span class="approval-method-text">{{ approvalModeLabel(row.approvalMode) }}</span>
+ </template>
+ </PIMTable>
+ </div>
+
+ <!-- 鎻愪氦瀹℃壒锛堟寜妯℃澘锛� -->
+ <el-dialog
+ v-model="submitDialog.visible"
+ :title="submitDialog.step === 1 ? '閫夋嫨瀹℃壒妯℃澘' : `鎻愪氦${activeTemplate?.label || '瀹℃壒'}`"
+ width="720px"
+ append-to-body
+ destroy-on-close
+ class="approve-submit-dialog"
+ @closed="submitDialog.step = 1"
+ >
+ <template v-if="submitDialog.step === 1">
+ <p class="template-hint">璇烽�夋嫨瑕佹彁浜ょ殑瀹℃壒绫诲瀷锛岀郴缁熷皢鎸夊搴旀ā鏉垮紩瀵煎~鎶ワ紙瀛楁鍚庢湡涓庡悗绔悓姝ワ級銆�</p>
+ <div class="template-grid">
+ <div
+ v-for="(tpl, key) in SUBMIT_TEMPLATES"
+ :key="key"
+ class="template-card"
+ @click="onTemplatePick(key)"
+ >
+ <span class="template-card-type" :style="approvalTypeStyle(tpl.approvalType)">
+ {{ tpl.label }}
+ </span>
+ <span class="template-card-desc">{{ tpl.summaryPlaceholder || "鐐瑰嚮濉啓骞舵彁浜�" }}</span>
+ </div>
+ </div>
+ </template>
+
+ <template v-else>
+ <el-form ref="submitFormRef" :model="submitForm" :rules="submitFormRules" label-width="120px">
+ <el-form-item label="瀹℃壒绫诲瀷">
+ <span class="approve-type-cell" :style="approvalTypeStyle(activeTemplate.approvalType)">
+ {{ activeTemplate.label }}
+ </span>
+ <el-button type="primary" link class="ml12" @click="backToTemplatePick">鏇存崲妯℃澘</el-button>
+ </el-form-item>
+ <el-form-item label="瀹℃壒鏂瑰紡" prop="approvalMode">
+ <el-radio-group v-model="submitForm.approvalMode">
+ <el-radio value="parallel">涓庣</el-radio>
+ <el-radio value="or_sign">鎴栫</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <template v-for="field in activeTemplate.fields" :key="field.key">
+ <el-form-item :label="field.label" :prop="`formPayload.${field.key}`">
+ <el-input
+ v-if="field.type === 'text'"
+ v-model="submitForm.formPayload[field.key]"
+ :placeholder="`璇疯緭鍏�${field.label}`"
+ maxlength="200"
+ />
+ <el-input
+ v-else-if="field.type === 'textarea'"
+ v-model="submitForm.formPayload[field.key]"
+ type="textarea"
+ :rows="field.rows || 3"
+ :placeholder="`璇峰~鍐�${field.label}`"
+ maxlength="2000"
+ show-word-limit
+ />
+ <el-input-number
+ v-else-if="field.type === 'number'"
+ v-model="submitForm.formPayload[field.key]"
+ :min="field.min ?? 0"
+ :precision="field.precision ?? 0"
+ controls-position="right"
+ style="width: 100%"
+ />
+ <el-date-picker
+ v-else-if="field.type === 'date'"
+ v-model="submitForm.formPayload[field.key]"
+ type="date"
+ :placeholder="`璇烽�夋嫨${field.label}`"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ style="width: 100%"
+ />
+ <el-date-picker
+ v-else-if="field.type === 'datetimerange'"
+ v-model="submitForm.formPayload[field.key]"
+ type="datetimerange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫椂闂�"
+ end-placeholder="缁撴潫鏃堕棿"
+ format="YYYY-MM-DD HH:mm:ss"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ style="width: 100%"
+ />
+ <el-select
+ v-else-if="field.type === 'select'"
+ v-model="submitForm.formPayload[field.key]"
+ :placeholder="`璇烽�夋嫨${field.label}`"
+ style="width: 100%"
+ clearable
+ >
+ <el-option v-for="o in field.options" :key="o.value" :label="o.label" :value="o.value" />
+ </el-select>
+ </el-form-item>
+ </template>
+ <el-form-item label="瀹℃壒娴佺▼">
+ <ApprovalFlowEditor v-model="submitForm.approvalFlowNodes" :user-options="flowUserOptions" />
+ <p class="flow-tip">鑷冲皯淇濈暀涓�涓鎵硅妭鐐癸紱鎻愪氦鍚庤繘鍏ャ�屽鏍镐腑銆嶇姸鎬併��</p>
+ </el-form-item>
+ </el-form>
+ </template>
+
+ <template #footer>
+ <el-button v-if="submitDialog.step === 2" type="primary" @click="onSubmitNew">鎻� 浜�</el-button>
+ <el-button @click="submitDialog.visible = false">{{ submitDialog.step === 1 ? "鍙� 娑�" : "鍏� 闂�" }}</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 璇︽儏 -->
+ <el-dialog
+ v-model="detailDialog.visible"
+ title="瀹℃壒璇︽儏"
+ width="920px"
+ append-to-body
+ destroy-on-close
+ >
+ <ApproveDetailPanel :row="detailRow" />
+ <el-divider content-position="left">瀹℃壒娴佺▼</el-divider>
+ <ApprovalFlowProgress
+ :nodes="detailRow.approvalFlowNodes"
+ :current-index="detailRow.currentNodeIndex ?? 0"
+ />
+ <el-divider content-position="left">瀹℃壒璁板綍</el-divider>
+ <el-timeline v-if="detailRow.approvalRecords?.length">
+ <el-timeline-item
+ v-for="(rec, i) in detailRow.approvalRecords"
+ :key="i"
+ :type="rec.result === 'approved' ? 'success' : rec.result === 'rejected' ? 'danger' : 'primary'"
+ :timestamp="rec.time"
+ >
+ {{ rec.operatorName }} 鈥� {{ approvalActionLabel(rec.result) }}锛歿{ rec.opinion || "鏃犳剰瑙�" }}
+ </el-timeline-item>
+ </el-timeline>
+ <el-empty v-else description="鏆傛棤瀹℃壒璁板綍" :image-size="60" />
+ <template #footer>
+ <el-button
+ v-if="detailRow.approvalStatus === 'pending'"
+ type="primary"
+ @click="openApproveFromDetail"
+ >
+ 鍘诲鎵�
+ </el-button>
+ <el-button @click="detailDialog.visible = false">鍏� 闂�</el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 瀹℃壒鎿嶄綔 -->
+ <el-dialog
+ v-model="approveDialog.visible"
+ title="瀹℃壒澶勭悊"
+ width="960px"
+ append-to-body
+ destroy-on-close
+ @closed="approveOpinion = ''"
+ >
+ <ApproveDetailPanel :row="approveDialog.row" />
+ <el-divider content-position="left">娴佺▼杩涘害</el-divider>
+ <ApprovalFlowProgress
+ :nodes="approveDialog.row?.approvalFlowNodes"
+ :current-index="approveDialog.row?.currentNodeIndex ?? 0"
+ />
+ <el-form label-width="100px" class="mt16">
+ <el-form-item label="瀹℃壒鎰忚" required>
+ <el-input
+ v-model="approveOpinion"
+ type="textarea"
+ :rows="3"
+ maxlength="500"
+ show-word-limit
+ placeholder="閫氳繃鍙暀绌猴紱椹冲洖璇峰~鍐欏叿浣撳師鍥�"
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button type="success" @click="onApprove('approved')">閫� 杩�</el-button>
+ <el-button type="danger" @click="onApprove('rejected')">椹� 鍥�</el-button>
+ <el-button @click="approveDialog.visible = false">鍙� 娑�</el-button>
+ </template>
+ </el-dialog>
+ </div>
</template>
<script setup>
-import ProcurementLedger from '@/views/procurementManagement/procurementLedger/index.vue'
+import { 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 ApprovalFlowEditor from "@/views/officeProcessAutomation/AttendManage/overtime-apply/components/ApprovalFlowEditor.vue";
+import ApprovalFlowProgress from "@/views/officeProcessAutomation/ReimburseManage/travel-reimburse/components/ApprovalFlowProgress.vue";
+import { approvalTypeStyle } from "./approveListConstants.js";
+import ApproveDetailPanel from "./components/ApproveDetailPanel.vue";
+import { useApproveList } from "./useApproveList.js";
+
+const al = useApproveList();
+const {
+ Search,
+ APPROVAL_TYPE_OPTIONS,
+ SUBMIT_TEMPLATES,
+ approvalTypeLabel,
+ approvalModeLabel,
+ approvalActionLabel,
+ searchForm,
+ tableLoading,
+ page,
+ tableData,
+ tableColumn,
+ detailDialog,
+ detailRow,
+ approveDialog,
+ approveOpinion,
+ submitDialog,
+ submitForm,
+ submitFormRef,
+ activeTemplate,
+ submitFormRules,
+ handleQuery,
+ resetSearch,
+ pagination,
+ openSubmitDialog,
+ onTemplatePick,
+ backToTemplatePick,
+ submitNewApproval,
+ submitApprove,
+ openDetail,
+ openApprove,
+} = al;
+
+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 onSubmitNew() {
+ const ok = await submitNewApproval();
+ if (ok) ElMessage.success("瀹℃壒宸叉彁浜�");
+}
+
+function onApprove(result) {
+ const ret = submitApprove(result);
+ if (ret?.needOpinion) {
+ ElMessage.warning("椹冲洖鏃惰濉啓瀹℃壒鎰忚");
+ return;
+ }
+ if (ret?.ok) {
+ ElMessage.success(result === "approved" ? "宸查�氳繃" : "宸查┏鍥�");
+ }
+}
+
+function openApproveFromDetail() {
+ const row = detailRow.value;
+ detailDialog.visible = false;
+ openApprove(row);
+}
+
+onMounted(() => {
+ loadUsers();
+ handleQuery();
+});
</script>
+
+<style scoped>
+.mb20 {
+ margin-bottom: 20px;
+}
+.ml12 {
+ margin-left: 12px;
+}
+.mt16 {
+ margin-top: 16px;
+}
+.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;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+.approve-type-cell {
+ display: inline-block;
+ padding: 2px 10px;
+ border-radius: 4px;
+ font-size: 13px;
+ line-height: 1.5;
+}
+.approval-method-text {
+ color: var(--el-color-danger);
+ font-weight: 500;
+}
+.template-hint {
+ font-size: 13px;
+ color: var(--el-text-color-secondary);
+ margin: 0 0 16px;
+}
+.template-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 12px;
+}
+.template-card {
+ padding: 14px 16px;
+ border: 1px solid var(--el-border-color-lighter);
+ border-radius: var(--radius-md, 8px);
+ cursor: pointer;
+ transition: border-color 0.2s, box-shadow 0.2s;
+ background: var(--el-fill-color-blank);
+}
+.template-card:hover {
+ border-color: var(--el-color-primary);
+ box-shadow: var(--shadow-sm, 0 2px 8px rgba(0, 0, 0, 0.06));
+}
+.template-card-type {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 13px;
+ font-weight: 600;
+ margin-bottom: 8px;
+}
+.template-card-desc {
+ display: block;
+ font-size: 12px;
+ color: var(--el-text-color-secondary);
+ line-height: 1.5;
+}
+.flow-tip {
+ font-size: 12px;
+ color: var(--el-text-color-secondary);
+ margin-top: 8px;
+}
+.approve-submit-dialog :deep(.el-dialog__body) {
+ padding-top: 12px;
+}
+</style>
--
Gitblit v1.9.3