From 930d38ed2a3c2131be3305a585602c7a5a275fe3 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期二, 19 五月 2026 17:09:12 +0800
Subject: [PATCH] Merge branch 'dev-new_pro_OA' of http://114.132.189.42:9002/r/product-inventory-management into dev-new_pro_OA
---
src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue | 504 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 497 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..cdae763 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
@@ -1,12 +1,502 @@
-<!--
- 妯″潡涓枃鍚嶏細瀹℃壒鍒楄〃
- 鐩綍鏍囪瘑锛欰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>
+ </PIMTable>
+ </div>
+
+ <!-- 鎻愪氦瀹℃壒锛堟寜妯℃澘锛� -->
+ <el-dialog
+ v-model="submitDialog.visible"
+ :title="submitDialogTitle"
+ width="720px"
+ append-to-body
+ destroy-on-close
+ class="approve-submit-dialog"
+ @closed="resetSubmitDialogState"
+ >
+ <template v-if="submitDialog.step === 1 && !isSubmitEdit">
+ <p class="template-hint">璇烽�夋嫨宸插惎鐢ㄧ殑瀹℃壒妯℃澘锛岀郴缁熷皢鎸夋ā鏉块厤缃紩瀵煎~鎶ャ��</p>
+ <div v-loading="submitTemplatesLoading" class="template-grid">
+ <div
+ v-for="card in submitTemplateCards"
+ :key="card.key"
+ class="template-card"
+ @click="onTemplatePick(card)"
+ >
+ <span class="template-card-type" :style="approvalTypeStyle(card.approvalType)">
+ {{ card.label }}
+ </span>
+ <span class="template-card-desc">{{ card.summaryPlaceholder }}</span>
+ </div>
+ <el-empty
+ v-if="!submitTemplatesLoading && !submitTemplateCards.length"
+ description="鏆傛棤鍙敤瀹℃壒妯℃澘"
+ :image-size="80"
+ class="template-empty"
+ />
+ </div>
+ </template>
+
+ <template v-else>
+ <div v-loading="submitTemplatesLoading && !isSubmitEdit">
+ <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
+ v-if="!isSubmitEdit"
+ type="primary"
+ link
+ class="ml12"
+ @click="backToTemplatePick"
+ >
+ 鏇存崲妯℃澘
+ </el-button>
+ </el-form-item>
+ <FormPayloadFields
+ :fields="submitFormFields"
+ :form-payload="submitForm.formPayload"
+ />
+ <el-form-item label="瀹℃壒娴佺▼" required>
+ <TemplateFlowEditor v-model="submitForm.flowNodes" :user-options="flowUserOptions" />
+ <p class="flow-tip">
+ 鎸夐『搴忔祦杞細鍙负姣忎釜鑺傜偣娣诲姞澶氬悕瀹℃壒浜猴紱浼氱闇�鍏ㄩ儴閫氳繃锛屾垨绛句换涓�浜洪�氳繃鍗冲彲杩涘叆涓嬩竴鑺傜偣銆�
+ </p>
+ </el-form-item>
+ </el-form>
+ </div>
+ </template>
+
+ <template #footer>
+ <el-button
+ v-if="submitDialog.step === 2 || isSubmitEdit"
+ type="primary"
+ :loading="submitSaving"
+ @click="onSubmitInstance"
+ >
+ {{ isSubmitEdit ? "淇� 瀛�" : "鎻� 浜�" }}
+ </el-button>
+ <el-button @click="submitDialog.visible = false">
+ {{ submitDialog.step === 1 && !isSubmitEdit ? "鍙� 娑�" : "鍏� 闂�" }}
+ </el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 璇︽儏 -->
+ <el-dialog
+ v-model="detailDialog.visible"
+ title="瀹℃壒璇︽儏"
+ width="920px"
+ append-to-body
+ destroy-on-close
+ class="approve-detail-dialog"
+ >
+ <div class="approve-detail-body">
+ <ApproveDetailPanel :row="detailRow" />
+ <div class="detail-block">
+ <div class="detail-block-title">
+ 瀹℃壒娴佺▼锛坽{ detailRow.tasks?.length || detailRow.flowNodes?.length || 0 }} 椤癸級
+ </div>
+ <InstanceFlowDisplay :tasks="detailRow.tasks" :nodes="detailRow.flowNodes" />
+ </div>
+ <div class="detail-block">
+ <div class="detail-block-title">瀹℃壒璁板綍</div>
+ <el-timeline v-if="detailRow.approvalRecords?.length" class="approve-record-timeline">
+ <el-timeline-item
+ v-for="(rec, i) in detailRow.approvalRecords"
+ :key="rec.id ?? i"
+ :type="rec.result === 'approved' ? 'success' : rec.result === 'rejected' ? 'danger' : 'primary'"
+ :timestamp="formatRecordTime(rec.time)"
+ placement="top"
+ >
+ <div class="record-item">
+ <span class="record-operator">{{ rec.operatorName || "鈥�" }}</span>
+ <el-tag
+ size="small"
+ :type="rec.result === 'approved' ? 'success' : rec.result === 'rejected' ? 'danger' : 'info'"
+ effect="plain"
+ >
+ {{ approvalActionLabel(rec.result) }}
+ </el-tag>
+ <p class="record-opinion">{{ rec.opinion || "鏃犳剰瑙�" }}</p>
+ </div>
+ </el-timeline-item>
+ </el-timeline>
+ <el-empty v-else description="鏆傛棤瀹℃壒璁板綍" :image-size="48" />
+ </div>
+ </div>
+ <template #footer>
+ <el-button
+ v-if="detailRow.approvalStatus === 'pending'"
+ @click="openEditFromDetail"
+ >
+ 淇� 鏀�
+ </el-button>
+ <el-button
+ v-if="detailRow.approvalStatus === 'pending' && detailRow.isApprove"
+ 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" />
+ <div class="detail-block mt16">
+ <div class="detail-block-title">
+ 瀹℃壒娴佺▼锛坽{ approveDialog.row?.tasks?.length || approveDialog.row?.flowNodes?.length || 0 }} 椤癸級
+ </div>
+ <InstanceFlowDisplay :tasks="approveDialog.row?.tasks" :nodes="approveDialog.row?.flowNodes" />
+ </div>
+ <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"
+ :loading="approveSubmitting"
+ @click="onApprove('approved')"
+ >
+ 閫� 杩�
+ </el-button>
+ <el-button
+ type="danger"
+ :loading="approveSubmitting"
+ @click="onApprove('rejected')"
+ >
+ 椹� 鍥�
+ </el-button>
+ <el-button :disabled="approveSubmitting" @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 TemplateFlowEditor from "../approve-template/components/TemplateFlowEditor.vue";
+import FormPayloadFields from "./components/FormPayloadFields.vue";
+import { formatDisplayTime } from "../approve-template/approveTemplateConstants.js";
+import { approvalTypeStyle } from "./approveListConstants.js";
+import ApproveDetailPanel from "./components/ApproveDetailPanel.vue";
+import InstanceFlowDisplay from "./components/InstanceFlowDisplay.vue";
+import { useApproveList } from "./useApproveList.js";
+
+const al = useApproveList();
+const {
+ Search,
+ APPROVAL_TYPE_OPTIONS,
+ submitTemplateCards,
+ submitTemplatesLoading,
+ approvalTypeLabel,
+ approvalActionLabel,
+ searchForm,
+ tableLoading,
+ page,
+ tableData,
+ tableColumn,
+ detailDialog,
+ detailRow,
+ approveDialog,
+ approveOpinion,
+ approveSubmitting,
+ submitDialog,
+ isSubmitEdit,
+ submitDialogTitle,
+ submitForm,
+ submitFormRef,
+ submitSaving,
+ activeTemplate,
+ submitFormFields,
+ submitFormRules,
+ handleQuery,
+ resetSearch,
+ pagination,
+ resetSubmitDialogState,
+ openSubmitDialog,
+ openEditDialog,
+ onTemplatePick,
+ backToTemplatePick,
+ submitInstanceForm,
+ 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 onSubmitInstance() {
+ const ok = await submitInstanceForm();
+ if (ok) ElMessage.success(isSubmitEdit.value ? "淇敼鎴愬姛" : "瀹℃壒宸叉彁浜�");
+}
+
+async function onApprove(result) {
+ const ret = await submitApprove(result);
+ if (ret?.needOpinion) {
+ ElMessage.warning("椹冲洖鏃惰濉啓瀹℃壒鎰忚");
+ return;
+ }
+ if (ret?.ok) {
+ ElMessage.success(result === "approved" ? "宸查�氳繃" : "宸查┏鍥�");
+ }
+}
+
+function formatRecordTime(time) {
+ return formatDisplayTime(time) || "鈥�";
+}
+
+function openApproveFromDetail() {
+ const row = detailRow.value;
+ detailDialog.visible = false;
+ openApprove(row);
+}
+
+function openEditFromDetail() {
+ const row = detailRow.value;
+ detailDialog.visible = false;
+ openEditDialog(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;
+}
+.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;
+ min-height: 120px;
+}
+.template-empty {
+ grid-column: 1 / -1;
+}
+.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;
+}
+.approve-detail-dialog :deep(.el-dialog__body) {
+ padding-top: 16px;
+ max-height: 70vh;
+ overflow-y: auto;
+}
+.approve-detail-body .detail-block {
+ margin-top: 20px;
+}
+.approve-detail-body .detail-block-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--el-text-color-primary);
+ margin: 0 0 12px;
+ padding-left: 10px;
+ border-left: 3px solid var(--el-color-primary);
+ line-height: 1.4;
+}
+.approve-record-timeline {
+ padding-left: 4px;
+}
+.record-item {
+ padding: 4px 0 2px;
+}
+.record-operator {
+ font-weight: 600;
+ margin-right: 8px;
+ color: var(--el-text-color-primary);
+}
+.record-opinion {
+ margin: 8px 0 0;
+ font-size: 13px;
+ color: var(--el-text-color-regular);
+ line-height: 1.5;
+}
+.detail-block-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--el-text-color-primary);
+ margin: 0 0 12px;
+ padding-left: 10px;
+ border-left: 3px solid var(--el-color-primary);
+ line-height: 1.4;
+}
+</style>
--
Gitblit v1.9.3