From b59518d5c068ae8e6e32cce90ccf281bf81dc54a Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 19 八月 2026 14:34:39 +0800
Subject: [PATCH] feat(collaborativeApproval): 添加用印审批功能支持

---
 src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue |   95 +++++++++++++
 src/views/collaborativeApproval/approvalProcess/index.vue                  |   12 +
 src/api/collaborativeApproval/sealManagement.js                            |    9 +
 vite.config.js                                                             |    2 
 src/views/collaborativeApproval/approvalManagement/index.vue               |    4 
 src/views/collaborativeApproval/sealManagement/index.vue                   |  290 ++++++++++++++++++++++++-----------------
 6 files changed, 287 insertions(+), 125 deletions(-)

diff --git a/src/api/collaborativeApproval/sealManagement.js b/src/api/collaborativeApproval/sealManagement.js
index cb990b3..0949e94 100644
--- a/src/api/collaborativeApproval/sealManagement.js
+++ b/src/api/collaborativeApproval/sealManagement.js
@@ -38,6 +38,15 @@
   });
 }
 
+// 鐢ㄥ嵃鐢宠璇︽儏锛堟寜瀹℃壒娴佺▼id锛�
+export function getSealDetail(approveProcessId) {
+  return request({
+    url: "/sealApplicationManagement/detail",
+    method: "get",
+    params: { approveProcessId },
+  });
+}
+
 // 鏂板鍗扮珷鐢宠
 export function addSealApplication(data) {
   return request({
diff --git a/src/views/collaborativeApproval/approvalManagement/index.vue b/src/views/collaborativeApproval/approvalManagement/index.vue
index 4638392..e265cfe 100644
--- a/src/views/collaborativeApproval/approvalManagement/index.vue
+++ b/src/views/collaborativeApproval/approvalManagement/index.vue
@@ -203,6 +203,7 @@
   { value: '5', label: '閲囪喘瀹℃壒', icon: 'ShoppingCart', color: '#909399' },
   { value: '6', label: '鎶ヤ环瀹℃壒', icon: 'DocumentChecked', color: '#9B59B6' },
   { value: '7', label: '鍙戣揣瀹℃壒', icon: 'Van', color: '#1ABC9C' },
+  { value: '9', label: '鐢ㄥ嵃瀹℃壒', icon: 'Stamp', color: '#7D3C98' },
 ];
 
 // 瀹℃壒绫诲瀷鍚嶇О鏄犲皠
@@ -214,6 +215,7 @@
   5: '閲囪喘瀹℃壒',
   6: '鎶ヤ环瀹℃壒',
   7: '鍙戣揣瀹℃壒',
+  9: '鐢ㄥ嵃瀹℃壒',
 };
 
 // 瀹℃壒绫诲瀷鍥炬爣鏄犲皠
@@ -225,6 +227,7 @@
   5: 'ShoppingCart',
   6: 'DocumentChecked',
   7: 'Van',
+  9: 'Stamp',
 };
 
 // 瀹℃壒绫诲瀷棰滆壊鏄犲皠
@@ -236,6 +239,7 @@
   5: '#909399',
   6: '#9B59B6',
   7: '#1ABC9C',
+  9: '#7D3C98',
 };
 
 // 澶村儚棰滆壊姹�
diff --git a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
index 1269621..90802a5 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -135,6 +135,43 @@
         </el-skeleton>
       </div>
 
+      <!-- 鐢ㄥ嵃瀹℃壒锛氬睍绀虹敤鍗扮敵璇疯鎯� -->
+      <div v-if="isSealApproval" style="margin: 10px 0 18px;">
+        <el-divider content-position="left">鐢ㄥ嵃璇︽儏</el-divider>
+        <el-skeleton :loading="sealLoading" animated>
+          <template #template>
+            <el-skeleton-item variant="h3" style="width: 30%" />
+            <el-skeleton-item variant="text" style="width: 100%" />
+          </template>
+          <template #default>
+            <el-empty v-if="!currentSeal || !currentSeal.applicationNum" description="鏈煡璇㈠埌瀵瑰簲鐢ㄥ嵃鐢宠璇︽儏" />
+            <template v-else>
+              <el-descriptions :column="2" border>
+                <el-descriptions-item label="鐢宠缂栧彿">{{ currentSeal.applicationNum }}</el-descriptions-item>
+                <el-descriptions-item label="鐢宠鏍囬">{{ currentSeal.title }}</el-descriptions-item>
+                <el-descriptions-item label="鐢ㄥ嵃绫诲瀷">{{ getSealTypeText(currentSeal.sealType) }}</el-descriptions-item>
+                <el-descriptions-item label="绱ф�ョ▼搴�">{{ getUrgencyText(currentSeal.urgency) }}</el-descriptions-item>
+                <el-descriptions-item label="鐢宠鍘熷洜" :span="2">{{ currentSeal.reason }}</el-descriptions-item>
+              </el-descriptions>
+              <div v-if="currentSeal.commonFileList?.length" style="margin-top: 20px;">
+                <h4>闄勪欢</h4>
+                <el-table :data="currentSeal.commonFileList" border style="width: 100%">
+                  <el-table-column label="闄勪欢鍚嶇О" show-overflow-tooltip>
+                    <template #default="scope">{{ scope.row.name }}</template>
+                  </el-table-column>
+                  <el-table-column label="鎿嶄綔" width="150" align="center">
+                    <template #default="scope">
+                      <el-button link type="primary" size="small" @click="previewSealFile(scope.row)">棰勮</el-button>
+                      <el-button link type="primary" size="small" @click="downloadSealFile(scope.row)">涓嬭浇</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </div>
+            </template>
+          </template>
+        </el-skeleton>
+      </div>
+
       <el-form :model="{ activities }" ref="formRef" label-position="top">
         <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
           <el-step
@@ -184,6 +221,7 @@
         </div>
       </template>
     </el-dialog>
+    <filePreview ref="filePreviewRef" />
   </div>
 </template>
 
@@ -198,6 +236,8 @@
 import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
 import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
 import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
+import { getSealDetail } from "@/api/collaborativeApproval/sealManagement.js";
+import FilePreview from '@/components/filePreview/index.vue'
 const emit = defineEmits(['close'])
 const { proxy } = getCurrentInstance()
 
@@ -218,8 +258,12 @@
 const currentQuotation = ref({})
 const purchaseLoading = ref(false)
 const currentPurchase = ref({})
+const sealLoading = ref(false)
+const currentSeal = ref({})
+const filePreviewRef = ref()
 const isQuotationApproval = computed(() => Number(props.approveType) === 6)
 const isPurchaseApproval = computed(() => Number(props.approveType) === 5)
+const isSealApproval = computed(() => Number(props.approveType) === 9)
 
 const data = reactive({
 	form: {
@@ -315,6 +359,19 @@
     }
   }
 
+  // 鐢ㄥ嵃瀹℃壒锛氭寜瀹℃壒娴佺▼ DB 涓婚敭锛坮ow.id锛夋煡鐢ㄥ嵃鐢宠璇︽儏
+  if (isSealApproval.value) {
+    sealLoading.value = true
+    getSealDetail(row.id).then((res) => {
+      currentSeal.value = res.data || {}
+    }).catch((err) => {
+      console.error('鏌ヨ鐢ㄥ嵃璇︽儏澶辫触:', err)
+      proxy.$modal.msgError('鏌ヨ鐢ㄥ嵃璇︽儏澶辫触')
+    }).finally(() => {
+      sealLoading.value = false
+    })
+  }
+
   approveProcessDetails(row.approveId).then((res) => {
     activities.value = res.data
     // 澧炲姞isApproval瀛楁
@@ -339,6 +396,42 @@
 		productOptions.value = res.data;
 	});
 };
+// 鐢ㄥ嵃绫诲瀷鏂囨湰
+const getSealTypeText = (sealType) => {
+  const map = { official: '鍏珷', contract: '鍚堝悓涓撶敤绔�', finance: '璐㈠姟涓撶敤绔�', legal: '娉曚汉绔�' }
+  return map[sealType] || sealType || '鏈煡'
+}
+// 绱ф�ョ▼搴︽枃鏈�
+const getUrgencyText = (urgency) => {
+  const map = { normal: '鏅��', urgent: '绱ф��', 'very-urgent': '鐗规��' }
+  return map[urgency] || urgency || '鏈煡'
+}
+// 棰勮闄勪欢锛圲RL 涓哄畬鏁村湴鍧�锛屽師鏍锋墦寮�锛屼笉鎷� host锛�
+const previewSealFile = (row) => {
+  const url = row.previewURL || row.previewUrl || row.url
+  if (url && filePreviewRef.value) {
+    filePreviewRef.value.openUrl(url)
+  } else {
+    proxy.$modal.msgWarning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曢瑙�')
+  }
+}
+// 涓嬭浇闄勪欢锛堥敋鐐规柟寮忥紝URL 涓哄畬鏁村湴鍧�锛�
+const downloadSealFile = (row) => {
+  const url = row.downloadURL || row.downloadUrl || row.url
+  if (url) {
+    const filename = row.originalFilename || row.name || row.fileName || 'download'
+    const a = document.createElement('a')
+    a.href = url
+    a.download = filename
+    a.target = '_blank'
+    a.rel = 'noopener noreferrer'
+    document.body.appendChild(a)
+    a.click()
+    document.body.removeChild(a)
+  } else {
+    proxy.$modal.msgWarning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曚笅杞�')
+  }
+}
 // 鎻愪氦瀹℃壒
 const submitForm = (status) => {
   const filteredActivities = activities.value.filter(activity => activity.isShen);
@@ -367,6 +460,8 @@
   currentQuotation.value = {}
   purchaseLoading.value = false
   currentPurchase.value = {}
+  sealLoading.value = false
+  currentSeal.value = {}
   emit('close')
 };
 defineExpose({
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index dba6bc1..6ea576b 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -70,7 +70,7 @@
           <el-button
             type="primary"
             @click="openForm('add')"
-            v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
+            v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7 && currentApproveType !== 9"
             class="action-btn primary"
           >
             <el-icon><Plus /></el-icon>
@@ -84,7 +84,7 @@
             type="danger"
             plain
             @click="handleDelete"
-            v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
+            v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7 && currentApproveType !== 9"
             class="action-btn danger"
           >
             <el-icon><Delete /></el-icon>
@@ -159,6 +159,7 @@
   { value: '5', label: '閲囪喘瀹℃壒', icon: 'ShoppingCart', color: '#909399' },
   { value: '6', label: '鎶ヤ环瀹℃壒', icon: 'DocumentChecked', color: '#9B59B6' },
   { value: '7', label: '鍙戣揣瀹℃壒', icon: 'Van', color: '#1ABC9C' },
+  { value: '9', label: '鐢ㄥ嵃瀹℃壒', icon: 'Stamp', color: '#7D3C98' },
 ];
 
 // 褰撳墠瀹℃壒绫诲瀷淇℃伅
@@ -305,6 +306,7 @@
         currentApproveType.value === 5 ||
         currentApproveType.value === 6 ||
         currentApproveType.value === 7 ||
+        currentApproveType.value === 9 ||
         row.approveStatus == 2 ||
         row.approveStatus == 1 ||
         row.approveStatus == 4
@@ -331,8 +333,8 @@
     },
   ];
 
-  // 鎶ヤ环瀹℃壒锛堢被鍨� 6锛変笉灞曠ず"闄勪欢"鎿嶄綔
-  if (!isQuotationType) {
+  // 鎶ヤ环瀹℃壒锛堢被鍨� 6锛夈�佺敤鍗板鎵癸紙绫诲瀷 9锛変笉灞曠ず"闄勪欢"鎿嶄綔锛堥檮浠惰蛋 common_file锛屼笉璧� storage_attachment锛�
+  if (!isQuotationType && currentApproveType.value !== 9) {
     actionOperations.push({
       name: "闄勪欢",
       type: "text",
@@ -405,6 +407,7 @@
     5: "/approveProcess/exportFive",
     6: "/approveProcess/exportSix",
     7: "/approveProcess/exportSeven",
+    9: "/approveProcess/exportNine",
   }
   const url = urlMap[type] || urlMap[0]
   const nameMap = {
@@ -416,6 +419,7 @@
     5: "閲囪喘鐢宠瀹℃壒琛�",
     6: "鎶ヤ环瀹℃壒琛�",
     7: "鍙戣揣瀹℃壒琛�",
+    9: "鐢ㄥ嵃瀹℃壒琛�",
   }
   const fileName = nameMap[type] || nameMap[0]
   proxy.download(url, {}, `${fileName}.xlsx`)
diff --git a/src/views/collaborativeApproval/sealManagement/index.vue b/src/views/collaborativeApproval/sealManagement/index.vue
index 7a02751..e4a08ef 100644
--- a/src/views/collaborativeApproval/sealManagement/index.vue
+++ b/src/views/collaborativeApproval/sealManagement/index.vue
@@ -7,7 +7,6 @@
         </div>
       </template>
 
-
    <!-- 鐢ㄥ嵃鐢宠绠$悊 -->
         <div class="tab-content">
             <el-row :gutter="20" class="mb-20 ">
@@ -75,22 +74,39 @@
         <el-form-item label="鐢宠鍘熷洜" prop="reason">
           <el-input v-model="sealForm.reason" type="textarea" :rows="4" placeholder="璇疯缁嗚鏄庣敤鍗板師鍥�" />
         </el-form-item>
-        <el-form-item label="瀹℃壒浜�" prop="approveUserId">
-          <el-select v-model="sealForm.approveUserId" placeholder="璇烽�夋嫨瀹℃壒浜�" style="width: 100%" filterable>
-            <el-option
-                v-for="user in userList"
-                :key="user.userId"
-                :label="user.nickName"
-                :value="user.userId"
-            />
-          </el-select>
-        </el-form-item>
         <el-form-item label="绱ф�ョ▼搴�" prop="urgency">
           <el-radio-group v-model="sealForm.urgency">
-            <el-radio label="normal">鏅��</el-radio>
-            <el-radio label="urgent">绱ф��</el-radio>
-            <el-radio label="very-urgent">鐗规��</el-radio>
+            <el-radio value="normal">鏅��</el-radio>
+            <el-radio value="urgent">绱ф��</el-radio>
+            <el-radio value="very-urgent">鐗规��</el-radio>
           </el-radio-group>
+        </el-form-item>
+        <el-form-item label="闄勪欢涓婁紶">
+          <el-upload
+            multiple
+            :show-file-list="false"
+            :action="sealUploadUrl"
+            name="files"
+            :headers="uploadHeaders"
+            :limit="10"
+            :before-upload="handleBeforeUpload"
+            :on-success="handleUploadSuccess"
+            :on-error="handleUploadError"
+            :on-exceed="handleUploadExceed"
+          >
+            <el-button type="primary">鐐瑰嚮涓婁紶闄勪欢</el-button>
+          </el-upload>
+          <el-table v-if="sealForm.attachmentList.length" :data="sealForm.attachmentList" border class="attachment-table">
+            <el-table-column label="闄勪欢鍚嶇О" show-overflow-tooltip>
+              <template #default="scope">{{ scope.row.name }}</template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔" width="140" align="center">
+              <template #default="scope">
+                <el-button link type="primary" size="small" @click="previewFile(scope.row)">棰勮</el-button>
+                <el-button link type="danger" size="small" @click="removeAttachment(scope.$index)">鍒犻櫎</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
         </el-form-item>
       </el-form>
     </FormDialog>
@@ -119,21 +135,41 @@
           </el-descriptions-item>
           <el-descriptions-item label="鐢宠鍘熷洜" :span="2">{{ currentSealDetail.reason }}</el-descriptions-item>
         </el-descriptions>
+        <!-- 闄勪欢鍒楄〃 -->
+        <div v-if="currentSealDetail.commonFileList?.length" class="attachment-section">
+          <div class="attachment-title">闄勪欢鍒楄〃锛�</div>
+          <el-table :data="currentSealDetail.commonFileList" border class="attachment-table">
+            <el-table-column label="闄勪欢鍚嶇О" show-overflow-tooltip>
+              <template #default="scope">
+                {{ scope.row.originalFilename || scope.row.name || scope.row.fileName || '鏈懡鍚嶆枃浠�' }}
+              </template>
+            </el-table-column>
+            <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
+              <template #default="scope">
+                <el-button link type="primary" size="small" @click="previewFile(scope.row)">棰勮</el-button>
+                <el-button link type="primary" size="small" @click="downloadFile(scope.row)">涓嬭浇</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
       </div>
     </FormDialog>
+    <!-- 鏂囦欢棰勮缁勪欢 -->
+    <FilePreview ref="filePreviewRef" />
 
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted, getCurrentInstance, watch } from 'vue'
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
 import { useRoute } from 'vue-router'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { listSealApplication, addSealApplication, updateSealApplication } from '@/api/collaborativeApproval/sealManagement.js'
-import { userListNoPageByTenantId } from '@/api/system/user.js'
+import { ElMessage } from 'element-plus'
+import { listSealApplication, addSealApplication } from '@/api/collaborativeApproval/sealManagement.js'
 import useUserStore from '@/store/modules/user'
+import { getToken } from '@/utils/auth'
 import FormDialog from '@/components/Dialog/FormDialog.vue'
 import PIMTable from '@/components/PIMTable/PIMTable.vue'
+import FilePreview from '@/components/filePreview/index.vue'
 
 // 鍝嶅簲寮忔暟鎹�
 // 鐢ㄥ嵃鐢宠鐩稿叧
@@ -143,24 +179,27 @@
 const tableLoading = ref(false)
 const showSealDetailDialog = ref(false)
 const currentSealDetail = ref(null)
+const filePreviewRef = ref(null)
 const sealFormRef = ref()
-const userList = ref([])
+// 闄勪欢涓婁紶锛堟湰鍦颁笂浼狅紝闈� minio锛岃繑鍥� [{ name, url }]锛�
+const sealUploadUrl = import.meta.env.VITE_APP_BASE_API + '/common/uploadsList'
+const uploadHeaders = ref({ Authorization: 'Bearer ' + getToken() })
 const sealForm = reactive({
+  id: null,
   applicationNum: '',
   title: '',
   sealType: '',
   reason: '',
-  approveUserId: '',
   urgency: 'normal',
-  status: 'pending'
+  status: 'pending',
+  attachmentList: []
 })
 
 const sealRules = {
   applicationNum: [{ required: true, message: '璇疯緭鍏ョ敵璇风紪鍙�', trigger: 'blur' }],
   title: [{ required: true, message: '璇疯緭鍏ョ敵璇锋爣棰�', trigger: 'blur' }],
   sealType: [{ required: true, message: '璇烽�夋嫨鐢ㄥ嵃绫诲瀷', trigger: 'change' }],
-  reason: [{ required: true, message: '璇疯緭鍏ョ敵璇峰師鍥�', trigger: 'blur' }],
-  approveUserId: [{ required: true, message: '璇烽�夋嫨瀹℃壒浜�', trigger: 'change' }]
+  reason: [{ required: true, message: '璇疯緭鍏ョ敵璇峰師鍥�', trigger: 'blur' }]
 }
 
 const sealSearchForm = reactive({
@@ -209,9 +248,9 @@
 
 // 鐢ㄥ嵃鐢宠琛ㄦ牸鍒楅厤缃紙闇�鍦� getStatusText/getSealTypeText 绛変箣鍚庡畾涔夛級
 const sealTableColumn = ref([
-  { label: '鐢宠缂栧彿', prop: 'applicationNum',},
+  { label: '鐢宠缂栧彿', prop: 'applicationNum' },
   { label: '鐢宠鏍囬', prop: 'title', showOverflowTooltip: true },
-  { label: '鐢宠浜�', prop: 'createUserName', },
+  { label: '鐢宠浜�', prop: 'createUserName' },
   { label: '鎵�灞為儴闂�', prop: 'department', width: 150 },
   {
     label: '鐢ㄥ嵃绫诲瀷',
@@ -229,40 +268,27 @@
     formatData: (v) => getStatusText(v),
     formatType: (v) => getStatusType(v)
   },
-  { label: '瀹℃壒浜�', prop: 'approveUserName', width: 120 },
   {
     dataType: 'action',
     label: '鎿嶄綔',
-    width: 250,
+    width: 150,
     fixed: 'right',
     align: 'center',
     operation: [
-      { name: '鏌ョ湅', clickFun: (row) => viewSealDetail(row) },
-      {
-        name: '瀹℃壒',
-        clickFun: (row) => approveSeal(row),
-        showHide: (row) => row.status === 'pending' && row.approveUserId === userStore.id
-      },
-      {
-        name: '鎷掔粷',
-        clickFun: (row) => rejectSeal(row),
-        showHide: (row) => row.status === 'pending' && row.approveUserId === userStore.id
-      },
       {
         name: '閲嶆柊鐢宠',
         clickFun: (row) => reapplySeal(row),
-        showHide: (row) => row.status === 'rejected' && row.createUser === userStore.id
-      }
+        showHide: (row) => row.status === 'rejected' && Number(userStore.id) === row.createUser
+      },
+      { name: '璇︽儏', clickFun: (row) => viewSealDetail(row) }
     ]
   }
 ])
 
 // 鎼滅储鍗扮珷鐢宠
 const searchSealApplications = () => {
-  page.current=1
+  page.current = 1
   getSealApplicationList()
-
-  // ElMessage.success('鎼滅储瀹屾垚')
 }
 // 閲嶇疆鍗扮珷鐢宠鎼滅储
 const resetSealSearch = () => {
@@ -271,43 +297,52 @@
   sealSearchForm.applicationNum = ''
   searchSealApplications()
 }
-// 鎻愪氦鐢ㄥ嵃鐢宠
+
+// 閲嶆柊鐢宠鐢ㄥ嵃
+const reapplySeal = (row) => {
+  // 棰勫~琛ㄥ崟鏁版嵁
+  Object.assign(sealForm, {
+    id: row.id,
+    applicationNum: row.applicationNum,
+    title: row.title,
+    sealType: row.sealType,
+    reason: row.reason,
+    urgency: row.urgency || 'normal',
+    status: 'pending',
+    attachmentList: row.commonFileList || []
+  })
+  showSealApplyDialog.value = true
+}
+
+// 鎻愪氦鐢ㄥ嵃鐢宠锛堟柊澧炰笌閲嶆柊鐢宠鍧囪蛋 add 鎺ュ彛锛氶噸鏂扮敵璇蜂細閲嶆柊鍙戣捣鍗忓悓瀹℃壒娴佺▼锛�
 const submitSealApplication = async () => {
   try {
     await sealFormRef.value.validate()
     addSealApplication(sealForm).then(res => {
-      if(res.code == 200){
-        ElMessage.success('鐢宠鎻愪氦鎴愬姛')
+      if (res.code == 200) {
+        ElMessage.success(sealForm.id ? '閲嶆柊鐢宠鎴愬姛' : '鐢宠鎻愪氦鎴愬姛')
         closeSealApplyDialog()
         getSealApplicationList()
-        Object.assign(sealForm, {
-        applicationNum: '',
-        title: '',
-        sealType: '',
-        reason: '',
-        approveUserId: '',
-        urgency: 'normal',
-        status: 'pending'
-      })
       }
     }).catch(err => {
       console.log(err.msg)
     })
-
   } catch (error) {
   }
 }
+
 // 鍏抽棴鐢ㄥ嵃鐢宠瀵硅瘽妗�
 const closeSealApplyDialog = () => {
   // 娓呯┖琛ㄥ崟鏁版嵁
   Object.assign(sealForm, {
+    id: null,
     applicationNum: '',
     title: '',
     sealType: '',
     reason: '',
-    approveUserId: '',
     urgency: 'normal',
-    status: 'pending'
+    status: 'pending',
+    attachmentList: []
   })
   // 娓呴櫎琛ㄥ崟楠岃瘉鐘舵��
   if (sealFormRef.value) {
@@ -325,55 +360,64 @@
   currentSealDetail.value = row
   showSealDetailDialog.value = true
 }
-// 瀹℃壒鐢ㄥ嵃鐢宠
-const approveSeal = (row) => {
-  ElMessageBox.confirm('纭閫氳繃璇ョ敤鍗扮敵璇凤紵', '鎻愮ず', {
-    confirmButtonText: '纭畾',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  }).then(() => {
-    row.status = 'approved'
-    updateSealApplication(row).then(res => {
-      if(res.code == 200){
-        ElMessage.success('瀹℃壒閫氳繃')
-        getSealApplicationList()
-      }
-    })
-  })
+
+// 闄勪欢涓婁紶鍓嶆牎楠岋紙鍗曚釜鏂囦欢涓嶈秴杩� 50MB锛�
+const handleBeforeUpload = (file) => {
+  if (file.size / 1024 / 1024 > 50) {
+    ElMessage.error('鏂囦欢澶у皬涓嶈兘瓒呰繃 50MB')
+    return false
+  }
+  return true
 }
-// 鎷掔粷鐢ㄥ嵃鐢宠
-const rejectSeal = (row) => {
-  ElMessageBox.prompt('璇疯緭鍏ユ嫆缁濆師鍥�', '鎻愮ず', {
-    confirmButtonText: '纭畾',
-    cancelButtonText: '鍙栨秷',
-    inputPattern: /\S+/,
-    inputErrorMessage: '鎷掔粷鍘熷洜涓嶈兘涓虹┖'
-  }).then(({ value }) => {
-    row.status = 'rejected'
-    updateSealApplication(row).then(res => {
-      if(res.code == 200){
-        ElMessage.success('宸叉嫆缁濈敵璇�')
-        getSealApplicationList()
-      }
-    })
-  })
+// 闄勪欢涓婁紶鎴愬姛鍥炶皟
+const handleUploadSuccess = (res) => {
+  if (res.code === 200) {
+    const list = res.data || []
+    list.forEach(item => sealForm.attachmentList.push({ name: item.name, url: item.url }))
+    ElMessage.success('涓婁紶鎴愬姛')
+  } else {
+    ElMessage.error(res.msg || '涓婁紶澶辫触')
+  }
+}
+// 闄勪欢涓婁紶澶辫触鍥炶皟
+const handleUploadError = () => {
+  ElMessage.error('闄勪欢涓婁紶澶辫触')
+}
+// 闄勪欢鏁伴噺瓒呭嚭
+const handleUploadExceed = () => {
+  ElMessage.warning('鏈�澶氫笂浼� 10 涓檮浠�')
+}
+// 鍒犻櫎寰呮彁浜ら檮浠�
+const removeAttachment = (index) => {
+  sealForm.attachmentList.splice(index, 1)
+}
+// 棰勮鏂囦欢锛堝畬鏁村彲璁块棶 URL锛岀洿鎺ユ墦寮�锛屼笉鎷� host锛�
+const previewFile = (row) => {
+  const url = row.previewURL || row.previewUrl || row.url
+  if (url && filePreviewRef.value) {
+    filePreviewRef.value.openUrl(url)
+  } else {
+    ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曢瑙�')
+  }
 }
 
-// 閲嶆柊鐢宠鐢ㄥ嵃
-const reapplySeal = (row) => {
-  // 棰勫~琛ㄥ崟鏁版嵁
-  Object.assign(sealForm, {
-    applicationNum: row.applicationNum,
-    title: row.title,
-    sealType: row.sealType,
-    reason: row.reason,
-    approveUserId: row.approveUserId,
-    urgency: row.urgency || 'normal',
-    status: 'pending'
-  })
-  showSealApplyDialog.value = true
+// 涓嬭浇鏂囦欢
+const downloadFile = (row) => {
+  const url = row.downloadURL || row.downloadUrl || row.url
+  if (url) {
+    const filename = row.originalFilename || row.name || row.fileName || 'download'
+    const a = document.createElement('a')
+    a.href = url
+    a.download = filename
+    a.target = '_blank'
+    a.rel = 'noopener noreferrer'
+    document.body.appendChild(a)
+    a.click()
+    document.body.removeChild(a)
+  } else {
+    ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曚笅杞�')
+  }
 }
-
 // 瀵煎嚭鐢ㄥ嵃鐢宠
 const { proxy } = getCurrentInstance()
 const handleExport = () => {
@@ -384,13 +428,13 @@
 const getSealApplicationList = async () => {
   tableLoading.value = true
   listSealApplication(page, sealSearchForm)
-  .then(res => {
-    sealApplications.value = res.data.records
-    page.total = res.data.total
-    tableLoading.value = false
-  }).catch(err => {
-    tableLoading.value = false
-  })
+    .then(res => {
+      sealApplications.value = res.data.records
+      page.total = res.data.total
+      tableLoading.value = false
+    }).catch(err => {
+      tableLoading.value = false
+    })
 }
 // 鍒嗛〉鍙樺寲澶勭悊
 const paginationChange = (obj) => {
@@ -398,15 +442,6 @@
   page.size = obj.limit;
   getSealApplicationList();
 };
-
-// 鐩戝惉瀵硅瘽妗嗘墦寮�锛岃幏鍙栫敤鎴峰垪琛�
-watch(showSealApplyDialog, (newVal) => {
-  if (newVal) {
-    userListNoPageByTenantId().then((res) => {
-      userList.value = res.data;
-    });
-  }
-});
 
 onMounted(() => {
   // 璺敱鎼哄甫 applicationNum 鏃讹紝棰勫~骞舵煡璇�
@@ -442,4 +477,19 @@
 .ml-10 {
   margin-left: 10px;
 }
-</style>
+
+.attachment-section {
+  margin-top: 20px;
+}
+
+.attachment-title {
+  font-size: 14px;
+  color: #606266;
+  margin-bottom: 10px;
+  font-weight: 500;
+}
+
+.attachment-table {
+  border-radius: 4px;
+}
+</style>
\ No newline at end of file
diff --git a/vite.config.js b/vite.config.js
index 4e392ae..ff4745d 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -8,7 +8,7 @@
   const { VITE_APP_ENV } = env;
   const baseUrl =
       env.VITE_APP_ENV === "development"
-          ? "http://1.15.17.182:9038"
+          ? "http://192.168.0.10:7003"
           : env.VITE_BASE_API;
   const javaUrl =
       env.VITE_APP_ENV === "development"

--
Gitblit v1.9.3