From 69a5fcec5068f7f5d2e2cade4389651e73e26aef Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 27 八月 2025 10:43:44 +0800
Subject: [PATCH] 1.协同审批开发联调

---
 src/pages/cooperativeOffice/collaborativeApproval/approve.vue       |  519 +++++++++++
 src/api/system/user.js                                              |    7 
 src/pages.json                                                      |   14 
 src/pages/cooperativeOffice/collaborativeApproval/index.vue         |  260 ++++-
 src/pages/cooperativeOffice/collaborativeApproval/detail.vue        |  931 +++++++++++++--------
 src/api/equipmentManagement/upkeep.js                               |   72 +
 src/api/equipmentManagement/ledger.js                               |   44 +
 src/api/equipmentManagement/measurementEquipment.js                 |   35 
 src/api/collaborativeApproval/approvalProcess.js                    |   63 +
 src/api/equipmentManagement/repair.js                               |   72 +
 src/api/collaborativeApproval/noticeManagement.js                   |   69 +
 src/pages/cooperativeOffice/collaborativeApproval/contactSelect.vue |  391 ++++++++
 src/api/collaborativeApproval/rpaManagement.js                      |   77 +
 src/api/equipmentManagement/calibration.js                          |   27 
 14 files changed, 2,169 insertions(+), 412 deletions(-)

diff --git a/src/api/collaborativeApproval/approvalProcess.js b/src/api/collaborativeApproval/approvalProcess.js
new file mode 100644
index 0000000..415bed8
--- /dev/null
+++ b/src/api/collaborativeApproval/approvalProcess.js
@@ -0,0 +1,63 @@
+// 鍗忓悓瀹℃壒
+import request from "@/utils/request";
+
+export function approveProcessListPage(query) {
+    return request({
+        url: '/approveProcess/list',
+        method: 'get',
+        params: query,
+    })
+}
+export function getDept(query) {
+    return request({
+        url: '/approveProcess/getDept',
+        method: 'get',
+        params: query,
+    })
+}
+export function approveProcessGetInfo(query) {
+    return request({
+        url: '/approveProcess/get',
+        method: 'get',
+        params: query,
+    })
+}
+// 鏂板瀹℃壒娴佺▼
+export function approveProcessAdd(query) {
+    return request({
+        url: '/approveProcess/add',
+        method: 'post',
+        data: query,
+    })
+}
+// 淇敼瀹℃壒娴佺▼
+export function approveProcessUpdate(query) {
+    return request({
+        url: '/approveProcess/update',
+        method: 'post',
+        data: query,
+    })
+}
+// 鎻愪氦瀹℃壒
+export function updateApproveNode(query) {
+    return request({
+        url: '/approveNode/updateApproveNode',
+        method: 'post',
+        data: query,
+    })
+}
+// 鍒犻櫎瀹℃壒娴佺▼
+export function approveProcessDelete(query) {
+    return request({
+        url: '/approveProcess/deleteIds',
+        method: 'delete',
+        data: query,
+    })
+}
+// 鏌ヨ瀹℃壒娴佺▼
+export function approveProcessDetails(query) {
+    return request({
+        url: '/approveNode/details/' + query,
+        method: 'get',
+    })
+}
\ No newline at end of file
diff --git a/src/api/collaborativeApproval/noticeManagement.js b/src/api/collaborativeApproval/noticeManagement.js
new file mode 100644
index 0000000..fa1caec
--- /dev/null
+++ b/src/api/collaborativeApproval/noticeManagement.js
@@ -0,0 +1,69 @@
+import request from '@/utils/request'
+
+// 鏌ヨ鍏憡鍒楄〃
+export function listNotice(query) {
+  return request({
+    url: '/collaborativeApproval/notice/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ鍏憡璇︾粏
+export function getNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/' + noticeId,
+    method: 'get'
+  })
+}
+
+// 鏂板鍏憡
+export function addNotice(data) {
+  return request({
+    url: '/collaborativeApproval/notice',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼鍏憡
+export function updateNotice(data) {
+  return request({
+    url: '/collaborativeApproval/notice',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎鍏憡
+export function delNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/' + noticeId,
+    method: 'delete'
+  })
+}
+
+// 鎵归噺鍒犻櫎鍏憡
+export function delNoticeBatch(noticeIds) {
+  return request({
+    url: '/collaborativeApproval/notice/batch',
+    method: 'delete',
+    data: noticeIds
+  })
+}
+
+// 鍙戝竷鍏憡
+export function publishNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/publish/' + noticeId,
+    method: 'put'
+  })
+}
+
+// 涓嬬嚎鍏憡
+export function offlineNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/offline/' + noticeId,
+    method: 'put'
+  })
+}
diff --git a/src/api/collaborativeApproval/rpaManagement.js b/src/api/collaborativeApproval/rpaManagement.js
new file mode 100644
index 0000000..6fc3368
--- /dev/null
+++ b/src/api/collaborativeApproval/rpaManagement.js
@@ -0,0 +1,77 @@
+import request from "@/utils/request";
+
+// 鏌ヨRPA鍒楄〃
+export function listRpa(query) {
+  return request({
+    url: "/collaborativeApproval/rpa/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鏌ヨRPA璇︾粏
+export function getRpa(rpaId) {
+  return request({
+    url: "/collaborativeApproval/rpa/" + rpaId,
+    method: "get",
+  });
+}
+
+// 鏂板RPA
+export function addRpa(data) {
+  return request({
+    url: "/collaborativeApproval/rpa",
+    method: "post",
+    data: data,
+  });
+}
+
+// 淇敼RPA
+export function updateRpa(data) {
+  return request({
+    url: "/collaborativeApproval/rpa",
+    method: "put",
+    data: data,
+  });
+}
+
+// 鍒犻櫎RPA
+export function delRpa(rpaId) {
+  return request({
+    url: "/collaborativeApproval/rpa/" + rpaId,
+    method: "delete",
+  });
+}
+
+// 鎵归噺鍒犻櫎RPA
+export function delRpaBatch(rpaIds) {
+  return request({
+    url: "/collaborativeApproval/rpa/batch",
+    method: "delete",
+    data: rpaIds,
+  });
+}
+
+// 鍚姩RPA
+export function startRpa(rpaId) {
+  return request({
+    url: "/collaborativeApproval/rpa/start/" + rpaId,
+    method: "post",
+  });
+}
+
+// 鍋滄RPA
+export function stopRpa(rpaId) {
+  return request({
+    url: "/collaborativeApproval/rpa/stop/" + rpaId,
+    method: "post",
+  });
+}
+
+// 鑾峰彇RPA鐘舵��
+export function getRpaStatus(rpaId) {
+  return request({
+    url: "/collaborativeApproval/rpa/status/" + rpaId,
+    method: "get",
+  });
+}
diff --git a/src/api/equipmentManagement/calibration.js b/src/api/equipmentManagement/calibration.js
new file mode 100644
index 0000000..54b99f9
--- /dev/null
+++ b/src/api/equipmentManagement/calibration.js
@@ -0,0 +1,27 @@
+// 妫�瀹氭牎鍑嗚褰�
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function ledgerRecordListPage(query) {
+  return request({
+    url: "/measuringInstrumentLedgerRecord/listPage",
+    method: "get",
+    params: query,
+  });
+}
+// 鏍″噯
+export function ledgerRecordVerifying(query) {
+  return request({
+    url: "/measuringInstrumentLedger/verifying",
+    method: "post",
+    data: query,
+  });
+}
+// 淇敼鏍″噯
+export function ledgerRecordUpdate(query) {
+  return request({
+    url: "/measuringInstrumentLedgerRecord/update",
+    method: "post",
+    data: query,
+  });
+}
\ No newline at end of file
diff --git a/src/api/equipmentManagement/ledger.js b/src/api/equipmentManagement/ledger.js
new file mode 100644
index 0000000..d1b65b0
--- /dev/null
+++ b/src/api/equipmentManagement/ledger.js
@@ -0,0 +1,44 @@
+import request from "@/utils/request";
+
+export const getLedgerPage = (params) => {
+  return request({
+    url: "/device/ledger/page",
+    method: "get",
+    params,
+  });
+};
+export const getLedgerById = (id) => {
+  return request({
+    url: `/device/ledger/${id}`,
+    method: "get",
+  });
+};
+
+export const addLedger = (data) => {
+  return request({
+    url: "/device/ledger",
+    method: "post",
+    data,
+  });
+};
+export const editLedger = (data) => {
+  return request({
+    url: "/device/ledger",
+    method: "put",
+    data,
+  });
+};
+
+export const delLedger = (id) => {
+  return request({
+    url: `/device/ledger/${id}`,
+    method: "delete",
+  });
+};
+
+export const getDeviceLedger = () => {
+  return request({
+    url: "/device/ledger/getDeviceLedger",
+    method: "get",
+  });
+};
diff --git a/src/api/equipmentManagement/measurementEquipment.js b/src/api/equipmentManagement/measurementEquipment.js
new file mode 100644
index 0000000..a22c034
--- /dev/null
+++ b/src/api/equipmentManagement/measurementEquipment.js
@@ -0,0 +1,35 @@
+// 璁¢噺鍣ㄥ叿鍙拌处
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function measuringInstrumentListPage(query) {
+  return request({
+    url: "/measuringInstrumentLedger/listPage",
+    method: "get",
+    params: query,
+  });
+}
+// 鍒犻櫎
+export function measuringInstrumentDelete(query) {
+  return request({
+    url: "/measuringInstrumentLedger/delete",
+    method: "delete",
+    data: query,
+  });
+}
+// 鏂板
+export function measuringInstrumentAdd(query) {
+  return request({
+    url: "/measuringInstrumentLedger/add",
+    method: "post",
+    data: query,
+  });
+}
+// 淇敼
+export function measuringInstrumentUpdate(query) {
+  return request({
+    url: "/measuringInstrumentLedger/update",
+    method: "post",
+    data: query,
+  });
+}
\ No newline at end of file
diff --git a/src/api/equipmentManagement/repair.js b/src/api/equipmentManagement/repair.js
new file mode 100644
index 0000000..0233ae6
--- /dev/null
+++ b/src/api/equipmentManagement/repair.js
@@ -0,0 +1,72 @@
+import request from "@/utils/request";
+
+/**
+ * @desc 璁惧鎶ヤ慨鍒楄〃
+ * @param {鍒嗛〉鏌ヨ} params
+ * @returns
+ */
+export const getRepairPage = (params) => {
+  return request({
+    url: "/device/repair/page",
+    method: "get",
+    params,
+  });
+};
+
+/**
+ * @desc 鏂板鎶ヤ慨
+ * @param {鎶ヤ慨鍙傛暟} data
+ * @returns
+ */
+export const addRepair = (data) => {
+  return request({
+    url: "/device/repair",
+    method: "post",
+    data,
+  });
+};
+
+/**
+ * @desc 缂栬緫鎶ヤ慨
+ * @param {鎶ヤ慨鍙傛暟} data
+ * @returns
+ */
+export const editRepair = (data) => {
+  return request({
+    url: "/device/repair",
+    method: "put",
+    data,
+  });
+};
+
+/**
+ * @desc 鏍规嵁id鏌ヨ涓�鏉℃姤淇�
+ * @param {鎶ヤ慨id} id
+ * @returns
+ */
+export const getRepairById = (id) => {
+  return request({
+    url: `/device/repair/${id}`,
+    method: "get",
+  });
+};
+
+/**
+ * @desc 鍒犻櫎鎶ヤ慨
+ * @param {缂栧彿} ids
+ * @returns
+ */
+export const delRepair = (ids) => {
+  return request({
+    url: `/device/repair/${ids}`,
+    method: "delete",
+  });
+};
+
+export const addMaintain = (data) => {
+  return request({
+    url: `/device/repair/repair`,
+    method: "post",
+    data,
+  });
+};
diff --git a/src/api/equipmentManagement/upkeep.js b/src/api/equipmentManagement/upkeep.js
new file mode 100644
index 0000000..c091670
--- /dev/null
+++ b/src/api/equipmentManagement/upkeep.js
@@ -0,0 +1,72 @@
+import request from "@/utils/request";
+
+/**
+ * @desc 璁惧淇濆吇鍒楄〃鍒嗛〉鏌ヨ
+ * @param {鍒嗛〉鏌ヨ鍏ュ弬} params
+ * @returns
+ */
+export const getUpkeepPage = (params) => {
+  return request({
+    url: "/device/maintenance/page",
+    method: "get",
+    params,
+  });
+};
+
+/**
+ * @desc 璁惧淇濆吇璇︽儏
+ * @param {淇濆吇浣嗙紪鍙穧 id
+ * @returns
+ */
+export const getUpkeepById = (id) => {
+  return request({
+    url: `/device/maintenance/${id}`,
+    method: "get",
+  });
+};
+
+/**
+ * @desc 璁惧淇濆吇鏂板
+ * @param {鏂板淇濆吇琛ㄥ崟} data
+ * @returns
+ */
+export const addUpkeep = (data) => {
+  return request({
+    url: "/device/maintenance",
+    method: "post",
+    data,
+  });
+};
+
+/**
+ * @desc 璁惧淇濆吇缂栬緫
+ * @param {缂栬緫淇濆吇琛ㄥ崟} data
+ * @returns
+ */
+export const editUpkeep = (data) => {
+  return request({
+    url: "/device/maintenance",
+    method: "put",
+    data,
+  });
+};
+
+/**
+ * @desc 鏂板淇濆吇琛ㄥ崟
+ * @param {鏂板淇濆吇琛ㄥ崟} data
+ * @returns
+ */
+export const addMaintenance = (data) => {
+  return request({
+    url: "/device/maintenance/maintenance",
+    method: "post",
+    data,
+  });
+};
+
+export const delUpkeep = (id) => {
+  return request({
+    url: `/device/maintenance/${id}`,
+    method: "delete",
+  });
+};
diff --git a/src/api/system/user.js b/src/api/system/user.js
index 21d60bc..c2553cb 100644
--- a/src/api/system/user.js
+++ b/src/api/system/user.js
@@ -45,4 +45,11 @@
     url: '/system/user/userListNoPage',
     method: 'get'
   })
+}
+// 鏌ヨ鐢ㄦ埛鍒楄〃
+export function userListNoPageByTenantId() {
+  return request({
+    url: '/system/user/userListNoPageByTenantId',
+    method: 'get'
+  })
 }
\ No newline at end of file
diff --git a/src/pages.json b/src/pages.json
index 9e29df3..fc70bde 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -273,6 +273,20 @@
       }
     },
     {
+      "path": "pages/cooperativeOffice/collaborativeApproval/approve",
+      "style": {
+        "navigationBarTitleText": "瀹℃牳",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/cooperativeOffice/collaborativeApproval/contactSelect",
+      "style": {
+        "navigationBarTitleText": "閫夋嫨鑱旂郴浜�",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/cooperativeOffice/clientVisit/index",
       "style": {
         "navigationBarTitleText": "瀹㈡埛鎷滆",
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/approve.vue b/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
new file mode 100644
index 0000000..6011e7b
--- /dev/null
+++ b/src/pages/cooperativeOffice/collaborativeApproval/approve.vue
@@ -0,0 +1,519 @@
+<template>
+  <view class="approve-page">
+
+    <PageHeader title="瀹℃牳" @back="goBack" />
+
+    <!-- 鐢宠淇℃伅 -->
+    <view class="application-info">
+      <view class="info-header">
+        <text class="info-title">鐢宠淇℃伅</text>
+      </view>
+      <view class="info-content">
+        <view class="info-row">
+          <text class="info-label">鐢宠浜�</text>
+          <text class="info-value">{{ approvalData.approveUserName }}</text>
+        </view>
+        <view class="info-row">
+          <text class="info-label">鐢宠閮ㄩ棬</text>
+          <text class="info-value">{{ approvalData.approveDeptName }}</text>
+        </view>
+        <view class="info-row">
+          <text class="info-label">鐢宠浜嬬敱</text>
+          <text class="info-value">{{ approvalData.approveReason }}</text>
+        </view>
+        <view class="info-row">
+          <text class="info-label">鐢宠鏃ユ湡</text>
+          <text class="info-value">{{ approvalData.approveTime }}</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 瀹℃壒娴佺▼ -->
+    <view class="approval-process">
+      <view class="process-header">
+        <text class="process-title">瀹℃壒娴佺▼</text>
+      </view>
+      
+      <view class="process-steps">
+        <view 
+          v-for="(step, index) in approvalSteps" 
+          :key="index" 
+          class="process-step"
+          :class="{
+            'completed': step.status === 'completed',
+            'current': step.status === 'current',
+            'pending': step.status === 'pending',
+            'rejected': step.status === 'rejected'
+          }"
+        >
+          <view class="step-indicator">
+            <view class="step-dot">
+              <text v-if="step.status === 'completed'" class="step-icon">鉁�</text>
+              <text v-else-if="step.status === 'rejected'" class="step-icon">鉁�</text>
+              <text v-else class="step-number">{{ index + 1 }}</text>
+            </view>
+            <view v-if="index < approvalSteps.length - 1" class="step-line"></view>
+          </view>
+          
+          <view class="step-content">
+            <view class="step-info">
+              <text class="step-title">{{ step.title }}</text>
+              <text class="step-approver">{{ step.approverName }}</text>
+              <text v-if="step.approveTime" class="step-time">{{ step.approveTime }}</text>
+            </view>
+            
+            <view v-if="step.opinion" class="step-opinion">
+              <text class="opinion-label">瀹℃壒鎰忚锛�</text>
+              <text class="opinion-content">{{ step.opinion }}</text>
+            </view>
+            <!-- 绛惧悕灞曠ず -->
+            <view v-if="step.urlTem" class="step-opinion" style="margin-top:8px;">
+              <text class="opinion-label">绛惧悕锛�</text>
+              <image :src="step.urlTem" mode="widthFix" style="width:180px;border-radius:6px;border:1px solid #eee;" />
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 瀹℃牳鎰忚杈撳叆 -->
+    <view v-if="canApprove" class="approval-input">
+      <view class="input-header">
+        <text class="input-title">瀹℃牳鎰忚</text>
+      </view>
+      
+      <view class="input-content">
+        <van-field
+          v-model="approvalOpinion"
+          type="textarea"
+          rows="4"
+          placeholder="璇疯緭鍏ュ鏍告剰瑙�"
+          maxlength="200"
+          show-word-limit
+        />
+      </view>
+    </view>
+
+    <!-- 搴曢儴鎿嶄綔鎸夐挳 -->
+    <view v-if="canApprove" class="footer-actions">
+      <van-button class="reject-btn" @click="handleReject">椹冲洖</van-button>
+      <van-button class="approve-btn" @click="handleApprove">閫氳繃</van-button>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { ref, onMounted, computed } from 'vue'
+import { approveProcessGetInfo, approveProcessDetails, updateApproveNode } from '@/api/collaborativeApproval/approvalProcess'
+import useUserStore from '@/store/modules/user'
+import { showToast } from 'vant'
+import PageHeader from "@/components/PageHeader.vue";
+
+const userStore = useUserStore()
+const approvalData = ref({})
+const approvalSteps = ref([])
+const approvalOpinion = ref('')
+const approveId = ref('')
+
+// 浠庤鎯呮帴鍙e瓧娈靛榻� canApprove锛氫粎褰撴湁 isShen 鐨勮妭鐐规椂鍙鎵�
+const canApprove = computed(() => {
+  return approvalSteps.value.some(step => step.isShen === true)
+})
+
+onMounted(() => {
+  const pages = getCurrentPages()
+  const currentPage = pages[pages.length - 1]
+  approveId.value = currentPage.options.approveId
+  if (approveId.value) {
+    loadApprovalData()
+  }
+})
+
+const loadApprovalData = () => {
+  // 鍩烘湰鐢宠淇℃伅
+  approveProcessGetInfo({ id: approveId.value }).then(res => {
+    approvalData.value = res.data || {}
+  })
+  // 瀹℃壒鑺傜偣璇︽儏
+  approveProcessDetails(approveId.value).then(res => {
+    const list = Array.isArray(res.data) ? res.data : []
+    // 淇濆瓨鍘熷鑺傜偣鏁版嵁渚涙彁浜や娇鐢�
+    activities.value = list
+
+    approvalSteps.value = list.map((it, idx) => {
+      // 鑺傜偣鐘舵�佹槧灏勶細1=閫氳繃锛�2=涓嶉�氳繃锛屽惁鍒欑湅鏄惁褰撳墠(isShen)锛屽啀榛樿涓哄緟澶勭悊
+      let status = 'pending'
+      if (it.approveNodeStatus === 1) status = 'completed'
+      else if (it.approveNodeStatus === 2) status = 'rejected'
+      else if (it.isShen) status = 'current'
+      return {
+        title: `绗�${idx + 1}姝ュ鎵筦,
+        approverName: it.approveNodeUser || '鏈煡鐢ㄦ埛',
+        status,
+        approveTime: it.approveTime || null,
+        opinion: it.approveNodeReason || '',
+        urlTem: it.urlTem || '',
+        isShen: !!it.isShen
+      }
+    })
+  })
+}
+
+const goBack = () => {
+  uni.navigateBack()
+}
+
+const submitForm = (status) => {
+  // 鍙�夛細鏍¢獙瀹℃牳鎰忚
+  if (!approvalOpinion.value?.trim()) {
+    showToast('璇疯緭鍏ュ鏍告剰瑙�')
+    return
+  }
+  // 鎵惧埌褰撳墠鍙鎵硅妭鐐�
+  const filteredActivities = activities.value.filter(activity => activity.isShen)
+  if (!filteredActivities.length) {
+    showToast('褰撳墠鏃犲彲瀹℃壒鑺傜偣')
+    return
+  }
+  // 鍐欏叆鐘舵�佸拰鎰忚
+  filteredActivities[0].approveNodeStatus = status
+  filteredActivities[0].approveNodeReason = approvalOpinion.value || ''
+  // 璁$畻鏄惁涓烘渶鍚庝竴姝�
+  const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length - 1
+  // 璋冪敤鍚庣
+  updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
+    const msg = status === 1 ? '瀹℃壒閫氳繃' : '瀹℃壒宸查┏鍥�'
+    showToast(msg)
+    // 鎻愮ず鍚庤繑鍥炰笂涓�涓〉闈�
+    setTimeout(() => {
+      goBack() // 鍐呴儴鏄� uni.navigateBack()
+    }, 800)
+  })
+}
+
+const handleApprove = () => {
+  uni.showModal({
+    title: '纭鎿嶄綔',
+    content: '纭畾瑕侀�氳繃姝ゅ鎵瑰悧锛�',
+    success: (res) => {
+      if (res.confirm) submitForm(1)
+    }
+  })
+}
+
+const handleReject = () => {
+  uni.showModal({
+    title: '纭鎿嶄綔',
+    content: '纭畾瑕侀┏鍥炴瀹℃壒鍚楋紵',
+    success: (res) => {
+      if (res.confirm) submitForm(2)
+    }
+  })
+}
+// 鍘熷鑺傜偣鏁版嵁锛堢敤浜庢彁浜ら�昏緫锛�
+const activities = ref([])
+</script>
+
+<style scoped lang="scss">
+.approve-page {
+  min-height: 100vh;
+  background: #f8f9fa;
+  padding-bottom: 80px;
+}
+
+.header {
+  display: flex;
+  align-items: center;
+  background: #fff;
+  padding: 16px 20px;
+  border-bottom: 1px solid #f0f0f0;
+  position: sticky;
+  top: 0;
+  z-index: 100;
+}
+
+.title {
+  flex: 1;
+  text-align: center;
+  font-size: 18px;
+  font-weight: 600;
+  color: #333;
+}
+
+.application-info {
+  background: #fff;
+  margin: 16px;
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.info-header {
+  padding: 16px;
+  border-bottom: 1px solid #f0f0f0;
+  background: #f8f9fa;
+}
+
+.info-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #333;
+}
+
+.info-content {
+  padding: 16px;
+}
+
+.info-row {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  
+  &:last-child {
+    margin-bottom: 0;
+  }
+}
+
+.info-label {
+  font-size: 14px;
+  color: #666;
+  width: 80px;
+  flex-shrink: 0;
+}
+
+.info-value {
+  font-size: 14px;
+  color: #333;
+  flex: 1;
+}
+
+.approval-process {
+  background: #fff;
+  margin: 16px;
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.process-header {
+  padding: 16px;
+  border-bottom: 1px solid #f0f0f0;
+  background: #f8f9fa;
+}
+
+.process-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #333;
+}
+
+.process-steps {
+  padding: 20px;
+}
+
+.process-step {
+  display: flex;
+  position: relative;
+  margin-bottom: 24px;
+  
+  &:last-child {
+    margin-bottom: 0;
+    
+    .step-line {
+      display: none;
+    }
+  }
+}
+
+.step-indicator {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-right: 16px;
+}
+
+.step-dot {
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 14px;
+  font-weight: 600;
+  position: relative;
+  z-index: 2;
+}
+
+.process-step.completed .step-dot {
+  background: #52c41a;
+  color: #fff;
+}
+
+.process-step.current .step-dot {
+  background: #1890ff;
+  color: #fff;
+  animation: pulse 2s infinite;
+}
+
+.process-step.pending .step-dot {
+  background: #d9d9d9;
+  color: #999;
+}
+
+.step-line {
+  width: 2px;
+  height: 40px;
+  background: #d9d9d9;
+  margin-top: 8px;
+}
+
+.process-step.completed .step-line {
+  background: #52c41a;
+}
+
+.process-step.rejected .step-dot {
+  background: #ff4d4f;
+  color: #fff;
+}
+.process-step.rejected .step-line {
+  background: #ff4d4f;
+}
+
+.step-content {
+  flex: 1;
+  padding-top: 4px;
+}
+
+.step-info {
+  margin-bottom: 8px;
+}
+
+.step-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #333;
+  display: block;
+  margin-bottom: 4px;
+}
+
+.step-approver {
+  font-size: 14px;
+  color: #666;
+  display: block;
+  margin-bottom: 4px;
+}
+
+.step-time {
+  font-size: 12px;
+  color: #999;
+  display: block;
+}
+
+.step-opinion {
+  background: #f8f9fa;
+  padding: 12px;
+  border-radius: 8px;
+  border-left: 4px solid #52c41a;
+}
+
+.opinion-label {
+  font-size: 12px;
+  color: #666;
+  display: block;
+  margin-bottom: 4px;
+}
+
+.opinion-content {
+  font-size: 14px;
+  color: #333;
+  line-height: 1.5;
+}
+
+.approval-input {
+  background: #fff;
+  margin: 16px;
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.input-header {
+  padding: 16px;
+  border-bottom: 1px solid #f0f0f0;
+  background: #f8f9fa;
+}
+
+.input-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #333;
+}
+
+.input-content {
+  padding: 16px;
+}
+
+.footer-actions {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: #fff;
+  display: flex;
+  justify-content: space-around;
+  align-items: center;
+  padding: 16px;
+  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
+  z-index: 1000;
+}
+
+.reject-btn {
+  width: 120px;
+  background: #ff4d4f;
+  color: #fff;
+  border: none;
+}
+
+.approve-btn {
+  width: 120px;
+  background: #52c41a;
+  color: #fff;
+  border: none;
+}
+
+@keyframes pulse {
+  0% {
+    box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
+  }
+  70% {
+    box-shadow: 0 0 0 10px rgba(24, 144, 255, 0);
+  }
+  100% {
+    box-shadow: 0 0 0 0 rgba(24, 144, 255, 0);
+  }
+}
+.signature-section {
+  background: #fff;
+  padding: 12px 16px 16px;
+  border-top: 1px solid #f0f0f0;
+}
+.signature-header {
+  margin-bottom: 8px;
+}
+.signature-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+}
+.signature-box {
+  width: 100%;
+  height: 180px;
+  background: #fff;
+  border: 1px dashed #d9d9d9;
+  border-radius: 8px;
+  overflow: hidden;
+}
+.signature-actions {
+  margin-top: 8px;
+  display: flex;
+  justify-content: flex-end;
+}
+</style>
\ No newline at end of file
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/contactSelect.vue b/src/pages/cooperativeOffice/collaborativeApproval/contactSelect.vue
new file mode 100644
index 0000000..9259068
--- /dev/null
+++ b/src/pages/cooperativeOffice/collaborativeApproval/contactSelect.vue
@@ -0,0 +1,391 @@
+<template>
+  <view class="contact-select">
+    <!-- 椤堕儴鏍囬鏍� -->
+    <view class="header">
+      <up-icon name="arrow-left" size="20" color="#333" @click="goBack" />
+      <text class="title">閫夋嫨鑱旂郴浜�</text>
+      <text class="confirm-btn" @click="confirmSelect">纭畾</text>
+    </view>
+
+    <!-- 鎼滅储妗� -->
+<!--    <view class="search-section">-->
+<!--      <van-search-->
+<!--        v-model="searchValue"-->
+<!--        placeholder="鎼滅储鑱旂郴浜�"-->
+<!--        @search="onSearch"-->
+<!--        @input="onSearch"-->
+<!--      />-->
+<!--    </view>-->
+
+    <!-- 宸查�夋嫨鐨勮仈绯讳汉 -->
+    <view class="selected-section" v-if="selectedContact">
+      <view class="selected-header">
+        <text class="selected-title">宸查�夋嫨</text>
+        <text class="clear-btn" @click="clearSelected">娓呯┖</text>
+      </view>
+      <view class="selected-item">
+        <view class="contact-avatar">
+          <text class="avatar-text">{{ selectedContact.nickName.charAt(0) }}</text>
+        </view>
+        <view class="contact-details">
+          <text class="contact-name">{{ selectedContact.nickName }}</text>
+        </view>
+        <van-icon name="cross" size="16" color="#999" @click="clearSelected" />
+      </view>
+    </view>
+
+    <!-- 鑱旂郴浜哄垪琛� -->
+    <view class="contact-list">
+      <view class="list-header">
+        <text class="list-title">鍏ㄩ儴鑱旂郴浜�</text>
+      </view>
+      
+      <van-list
+        v-model:loading="loading"
+        :finished="finished"
+        finished-text="娌℃湁鏇村浜�"
+        @load="onLoad"
+      >
+        <view 
+          v-for="contact in userList"
+          :key="contact.userId"
+          class="contact-item"
+          :class="{ 'selected': isSelected(contact) }"
+          @click="selectContact(contact)"
+        >
+          <view class="contact-info">
+            <view class="contact-avatar">
+              <text class="avatar-text">{{ contact.nickName.charAt(0) }}</text>
+            </view>
+            <view class="contact-details">
+              <text class="contact-name">{{ contact.nickName }}</text>
+<!--              <text class="contact-dept">{{ contact.department }}</text>-->
+            </view>
+          </view>
+        </view>
+      </van-list>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { userListNoPageByTenantId } from "@/api/system/user"
+
+const loading = ref(false)
+const finished = ref(false)
+const selectedContact = ref(null)
+const userList = ref([])
+
+// 鎺ユ敹浼犻�掔殑鍙傛暟
+const stepIndex = ref(0)
+
+onMounted(() => {
+  // 鑾峰彇椤甸潰鍙傛暟
+  const pages = getCurrentPages()
+  const currentPage = pages[pages.length - 1]
+  if (currentPage.options.stepIndex !== undefined) {
+    stepIndex.value = parseInt(currentPage.options.stepIndex)
+  }
+  
+  // 鍒濆鍖栬仈绯讳汉鏁版嵁
+  initContacts()
+})
+
+const initContacts = () => {
+  userListNoPageByTenantId().then((res) => {
+    userList.value = res.data
+  })
+  finished.value = true
+}
+
+const onLoad = () => {
+  // 妯℃嫙鍔犺浇鏇村鏁版嵁
+  setTimeout(() => {
+    loading.value = false
+    finished.value = true
+  }, 1000)
+}
+
+const isSelected = (contact) => {
+  return selectedContact.value && selectedContact.value.userId === contact.userId
+}
+
+const selectContact = (contact) => {
+  // 鍗曢�夋ā寮忥紝鐩存帴鏇挎崲閫変腑鐨勮仈绯讳汉
+  selectedContact.value = contact
+}
+
+const clearSelected = () => {
+  selectedContact.value = null
+}
+
+const goBack = () => {
+  uni.navigateBack()
+}
+
+const confirmSelect = () => {
+  if (!selectedContact.value) {
+    uni.showToast({
+      title: '璇烽�夋嫨涓�涓仈绯讳汉',
+      icon: 'none'
+    })
+    return
+  }
+  // 浣跨敤 uni.$emit 鍙戦�佹暟鎹�
+  uni.$emit('selectContact', {
+    stepIndex: stepIndex.value,
+    contact: selectedContact.value
+  })
+  uni.navigateBack()
+}
+</script>
+
+<style scoped lang="scss">
+.contact-select {
+  min-height: 100vh;
+  background: #f8f9fa;
+}
+
+.header {
+  background: #ffffff;
+	padding: 16px 20px;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	border-bottom: 1px solid #f0f0f0;
+	position: sticky;
+	/* 鍏煎 iOS 鍒樻捣/鐏靛姩宀涘畨鍏ㄥ尯 */
+	padding-top: calc(env(safe-area-inset-top));
+	top: 0;
+	z-index: 100;
+	position: relative;
+}
+
+.title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #333;
+}
+
+.confirm-btn {
+  color: #006cfb;
+  font-size: 16px;
+  font-weight: 500;
+}
+
+.search-section {
+  background: #fff;
+  padding: 12px 16px;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.selected-section {
+  background: #fff;
+  margin-top: 8px;
+  padding: 16px;
+}
+
+.selected-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 12px;
+}
+
+.selected-title {
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+}
+
+.clear-btn {
+  color: #006cfb;
+  font-size: 14px;
+}
+
+.selected-item {
+  display: flex;
+  align-items: center;
+  background: #f0f8ff;
+  border: 1px solid #006cfb;
+  border-radius: 12px;
+  padding: 12px;
+  gap: 12px;
+  position: relative;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    top: -2px;
+    right: -2px;
+    width: 16px;
+    height: 16px;
+    background: #52c41a;
+    border-radius: 50%;
+    border: 2px solid #fff;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  }
+  
+  &::after {
+    content: '鉁�';
+    position: absolute;
+    top: -1px;
+    right: -1px;
+    width: 16px;
+    height: 16px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 10px;
+    color: #fff;
+    font-weight: bold;
+  }
+}
+
+.contact-list {
+  background: #fff;
+  margin-top: 8px;
+}
+
+.list-header {
+  padding: 16px;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.list-title {
+  font-size: 14px;
+  color: #666;
+}
+
+.contact-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 12px 16px;
+  border-bottom: 1px solid #f8f9fa;
+  transition: all 0.2s;
+  position: relative;
+  
+  &.selected {
+    background-color: #f0f8ff;
+    
+    &::before {
+      content: '';
+      position: absolute;
+      left: 8px;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 4px;
+      height: 4px;
+      background: #006cfb;
+      border-radius: 50%;
+      box-shadow: 0 0 0 4px rgba(0, 108, 251, 0.2);
+    }
+  }
+  
+  &:active {
+    background-color: #f5f5f5;
+  }
+}
+
+.contact-info {
+  display: flex;
+  align-items: center;
+  flex: 1;
+  padding-left: 16px;
+}
+
+.contact-avatar {
+  width: 40px;
+  height: 40px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 12px;
+  position: relative;
+}
+
+.avatar-text {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 500;
+}
+
+.contact-details {
+  flex: 1;
+}
+
+.contact-name {
+  display: block;
+  font-size: 16px;
+  color: #333;
+}
+
+.contact-dept {
+  font-size: 12px;
+  color: #999;
+}
+
+// 鑷畾涔夊崟閫夋寜閽牱寮�
+:deep(.van-radio) {
+  .van-radio__icon {
+    width: 20px;
+    height: 20px;
+    border: 2px solid #ddd;
+    border-radius: 50%;
+    background: #fff;
+    position: relative;
+    transition: all 0.2s;
+    
+    &::before {
+      content: '';
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%) scale(0);
+      width: 8px;
+      height: 8px;
+      background: #006cfb;
+      border-radius: 50%;
+      transition: transform 0.2s;
+    }
+  }
+  
+  &.van-radio--checked {
+    .van-radio__icon {
+      border-color: #006cfb;
+      background: #fff;
+      
+      &::before {
+        transform: translate(-50%, -50%) scale(1);
+      }
+      
+      &::after {
+        content: '';
+        position: absolute;
+        top: -2px;
+        left: -2px;
+        right: -2px;
+        bottom: -2px;
+        border: 2px solid rgba(0, 108, 251, 0.2);
+        border-radius: 50%;
+        animation: ripple 0.6s ease-out;
+      }
+    }
+  }
+}
+
+@keyframes ripple {
+  0% {
+    transform: scale(0.8);
+    opacity: 1;
+  }
+  100% {
+    transform: scale(1.2);
+    opacity: 0;
+  }
+}
+</style>
\ No newline at end of file
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/detail.vue b/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
index 362b237..219b32a 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/detail.vue
@@ -1,58 +1,74 @@
 <template>
   <view class="account-detail">
-    <!-- 椤堕儴鏍囬鏍� -->
-    <view class="header">
-      <up-icon name="arrow-left" size="20" color="#333" @click="goBack" />
-      <text class="title">瀹℃壒娴佺▼</text>
-    </view>
+    <PageHeader title="瀹℃壒娴佺▼" @back="goBack" />
 
     <!-- 琛ㄥ崟鍖哄煙 -->
     <view class="form-section">
-      <van-form ref="formRef" @submit="submitForm" :rules="rules" input-align="right">
-        <van-cell-group inset style="height:auto">
+      <van-form ref="formRef" @submit="submitForm" :rules="rules" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center">
+				<van-cell-group style="margin-bottom: 16px;">
+					<van-field
+						v-model="form.approveReason"
+						name="approveReason"
+						rows="2"
+						autosize
+						label="鐢宠浜嬬敱"
+						type="textarea"
+						maxlength="200"
+						:rules="[{ required: true, message: '鐢宠浜嬬敱涓嶈兘涓虹┖' }]"
+						placeholder="璇疯緭鍏ョ敵璇蜂簨鐢�"
+						show-word-limit
+						required
+					/>
+				</van-cell-group>
+				<van-cell-group>
+					<van-field
+						v-model="form.approveDeptName"
+						readonly
+						name="picker"
+						label="鐢宠閮ㄩ棬"
+						placeholder="璇烽�夋嫨鐢宠閮ㄩ棬"
+						:rules="[{ required: true, message: '璇烽�夋嫨鐢宠閮ㄩ棬' }]"
+						@click="showPicker = true"
+						required
+					/>
           <van-field
-            v-model="taxPrice"
+            v-model="form.approveUserName"
             name="taxPrice"
-            label="濮撳悕"
-            placeholder="璇疯緭鍏ュ鍚�"
-            :rules="[{ required: true, message: '濮撳悕涓嶈兘涓虹┖' }]"
+            label="鐢宠浜�"
+            placeholder="璇疯緭鍏ョ敵璇蜂汉"
+            :rules="[{ required: true, message: '鐢宠浜轰笉鑳戒负绌�' }]"
             required
             readonly
-          />
-          <van-field
-            v-model="result"
-            readonly
-            name="picker"
-            label="鐢宠閮ㄩ棬"
-            placeholder="璇烽�夋嫨鐢宠閮ㄩ棬"
-            :rules="[{ required: true, message: '璇烽�夋嫨鐢宠閮ㄩ棬' }]"
-            @click="showPicker = true"
-            required
           />
           <van-popup
             v-model:show="showPicker"
-            destroy-on-close
             position="bottom"
           >
             <van-picker
-              :columns="columns"
+              :columns="productOptions"
               :model-value="pickerValue"
               @confirm="onConfirm"
               @cancel="showPicker = false"
             />
           </van-popup>
-          <van-field
-            v-model="message"
-            name="message"
-            rows="1"
-            autosize
-            label="鐢宠浜嬬敱"
-            type="textarea"
-            placeholder="璇疯緭鍏ョ敵璇蜂簨鐢�"
-            height="100"
-            :rules="[{ required: true, message: '鐢宠浜嬬敱涓嶈兘涓虹┖' }]"
-            required
-          />
+					<van-field
+						v-model="form.approveTime"
+						label="鐢宠鏃ユ湡"
+						placeholder="璇烽�夋嫨"
+						readonly
+						required
+						@click="showDatePicker"
+						:rules="[{ required: true, message: '璇烽�夋嫨鏉ユ鏃ユ湡' }]"
+					/>
+					<!-- 鏃ユ湡閫夋嫨鍣� -->
+					<van-popup v-model:show="showDate" position="bottom">
+						<van-date-picker
+							v-model="currentDate"
+							title="閫夋嫨鏃ユ湡"
+							@confirm="onDateConfirm"
+							@cancel="showDate = false"
+						/>
+					</van-popup>
         </van-cell-group>
       </van-form>
     </view>
@@ -60,30 +76,38 @@
     <view class="approval-process">
       <view class="approval-header">
         <text class="approval-title">瀹℃牳娴佺▼</text>
-        <text class="approval-desc">宸茬敱绠$悊鍛橀璁句笉鍙慨鏀�</text>
+        <text class="approval-desc">姣忎釜姝ラ鍙兘閫夋嫨涓�涓鎵逛汉</text>
       </view>
 
       <view class="approval-steps">
-        <view v-for="(step, stepIndex) in approvalSteps" :key="stepIndex" class="approval-step">
+        <view v-for="(step, stepIndex) in approverNodes" :key="stepIndex" class="approval-step">
+          <view class="step-dot"></view>
           <view class="step-title">
             <text>瀹℃壒浜�</text>
           </view>
-          <view class="approvers-container">
-            <view v-for="(approver, approverIndex) in step.approvers" :key="approverIndex" class="approver-item">
-              <view class="approver-avatar"></view>
-              <text class="approver-name">{{ approver.name }}</text>
-              <view class="delete-approver-btn" @click="removeApprover(stepIndex, approverIndex)">脳</view>
+          <view class="approver-container">
+            <view v-if="step.nickName" class="approver-item">
+              <view class="approver-avatar">
+                <text class="avatar-text">{{ step.nickName.charAt(0) }}</text>
+                <view class="status-dot"></view>
+              </view>
+              <view class="approver-info">
+                <text class="approver-name">{{ step.nickName }}</text>
+              </view>
+              <view class="delete-approver-btn" @click="removeApprover(stepIndex)">脳</view>
             </view>
-            <view class="add-approver-btn" @click="addApprover(stepIndex)">+
+            <view v-else class="add-approver-btn" @click="addApprover(stepIndex)">
+              <view class="add-circle">+</view>
+              <text class="add-label">閫夋嫨瀹℃壒浜�</text>
             </view>
           </view>
-          <view class="step-line" v-if="stepIndex < approvalSteps.length - 1"></view>
-          <view class="delete-step-btn" @click="removeApprovalStep(stepIndex)">鍒犻櫎鑺傜偣</view>
+          <view class="step-line" v-if="stepIndex < approverNodes.length - 1"></view>
+          <view class="delete-step-btn" v-if="approverNodes.length > 1" @click="removeApprovalStep(stepIndex)">鍒犻櫎鑺傜偣</view>
         </view>
       </view>
 
-      <view class="add-step-btn" @click="addApprovalStep">
-        <text>鏂板鑺傜偣瀹℃牳浜�</text>
+      <view class="add-step-btn">
+				<van-button icon="plus" plain type="primary" style="width: 100%" @click="addApprovalStep">鏂板鑺傜偣</van-button>
       </view>
     </view>
 
@@ -95,166 +119,238 @@
   </view>
 </template>
 
-<script>
-import { ref, onMounted } from "vue";
+<script setup>
+import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
+import PageHeader from "@/components/PageHeader.vue";
+import useUserStore from "@/store/modules/user";
+import {getDept, approveProcessGetInfo, approveProcessAdd, approveProcessUpdate} from "@/api/collaborativeApproval/approvalProcess";
+import { showToast } from 'vant'
+import {userListNoPageByTenantId} from "@/api/system/user";
 
-export default {
-  setup() {
-    const rules = ref({
-
-  taxPrice: {
-    rules: [{ required: true, errorMessage: '濮撳悕涓嶈兘涓虹┖' }]
-  },
-  result: {
-    rules: [{ required: true, errorMessage: '璇烽�夋嫨鐢宠閮ㄩ棬' }]
-  },
-  message: {
-    rules: [{ required: true, errorMessage: '鐢宠浜嬬敱涓嶈兘涓虹┖' }]
-  },
+const data = reactive({
+	form: {
+		approveTime: "",
+		approveId: "",
+		approveUser: "",
+		approveUserName: "",
+		approveDeptName: "",
+		approveDeptId: "",
+		approveReason: "",
+		checkResult: "",
+		tempFileIds: [],
+		approverList: [] // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
+	},
+	rules: {
+		approveTime: [{ required: false, message: "璇疯緭鍏�", trigger: "change" },],
+		approveId: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+		approveUser: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+		approveDeptId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+		approveReason: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+		checkResult: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+	},
 });
-    const result = ref("");
-    const pickerValue = ref([]);
-    const showPicker = ref(false);
-    const columns = ref([]);
-    onMounted(async () => {
-      try {
-        // 鏇挎崲涓哄疄闄呮帴鍙e湴鍧�
-        // const response = await axios.get('/api/getDepartments');
-        columns.value = [
-          {
-            text: "鏉窞",
-            value: "Hangzhou",
-          },
-          {
-            text: "瀹佹尝",
-            value: "Ningbo",
-          },
-          {
-            text: "娓╁窞",
-            value: "Wenzhou",
-          },
-          {
-            text: "缁嶅叴",
-            value: "Shaoxing",
-          },
-          {
-            text: "婀栧窞",
-            value: "Huzhou",
-          },
-        ];
-      } catch (error) {
-        console.error("鑾峰彇閮ㄩ棬鏁版嵁澶辫触:", error);
-      }
-    });
-    const onConfirm = ({ selectedValues, selectedOptions }) => {
-      result.value = selectedOptions[0]?.text;
-      pickerValue.value = selectedValues;
-      showPicker.value = false;
-    };
-    const taxPrice = ref("");
-    const contractAmount = ref("");
-    const approvalSteps = ref([
-      { approvers: [{ name: '鍗㈠皬鏁�' }, { name: '鍗㈠皬鏁�' }] },
-      { approvers: [{ name: '鍗㈠皬鏁�' }] },
-      { approvers: [{ name: '鍗㈠皬鏁�' }] },
-      { approvers: [{ name: '鍗㈠皬鏁�' }] }
-    ]);
+const { form, rules } = toRefs(data);
+const result = ref("");
+const pickerValue = ref([]);
+const showPicker = ref(false);
+const productOptions = ref([]);
+const operationType = ref("");
+const currentApproveStatus = ref("");
+const approverNodes = ref([]);
+const userList = ref([]);
+const formRef = ref(null);
+const message = ref("");
+const showDate = ref(false)
+const currentDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
+const userStore = useUserStore()
 
-    const goBack = () => {
-    	uni.navigateBack();
-    };
-
-    const formRef = ref(null);
-
-    const submitForm = () => {
-      formRef.value.validate().then(() => {
-        // 琛ㄥ崟鏍¢獙閫氳繃锛屽彲浠ユ彁浜ゆ暟鎹�
-        console.log("琛ㄥ崟鏁版嵁:", {
-          taxPrice: taxPrice.value,
-          department: result.value,
-          message: message.value,
-          approvalSteps: approvalSteps.value
-        });
-
-        uni.showToast({
-          title: "淇濆瓨鎴愬姛",
-          icon: "success",
-        });
-      }).catch((error) => {
-        console.error("琛ㄥ崟鏍¢獙澶辫触:", error);
-        // 鏄剧ず鍏蜂綋鐨勯敊璇俊鎭�
-        if (error.length > 0) {
-          const firstError = error[0];
-          uni.showToast({
-            title: firstError.message || '琛ㄥ崟鏍¢獙澶辫触',
-            icon: 'none'
-          });
-        } else {
-          uni.showToast({
-            title: '琛ㄥ崟鏍¢獙澶辫触锛岃妫�鏌ュ繀濉」',
-            icon: 'none'
-          });
-        }
-      });
-    };
-
-    const message = ref("");
-
-    const addApprover = (stepIndex) => {
-      // 鍦ㄦ寚瀹氬鎵规楠ゆ坊鍔犳柊鐨勫鎵逛汉
-      approvalSteps.value[stepIndex].approvers.push({ name: '鍗㈠皬鏁�' });
-    };
-
-    const addApprovalStep = () => {
-      // 娣诲姞鏂扮殑瀹℃壒姝ラ
-      approvalSteps.value.push({ approvers: [{ name: '鍗㈠皬鏁�' }] });
-    };
-
-    const removeApprover = (stepIndex, approverIndex) => {
-      // 纭繚姣忎釜姝ラ鑷冲皯淇濈暀涓�涓鎵逛汉
-      if (approvalSteps.value[stepIndex].approvers.length > 1) {
-        approvalSteps.value[stepIndex].approvers.splice(approverIndex, 1);
-      } else {
-        uni.showToast({
-          title: '姣忎釜姝ラ鑷冲皯闇�瑕佷竴涓鎵逛汉',
-          icon: 'none'
-        });
-      }
-    };
-
-    const removeApprovalStep = (stepIndex) => {
-      // 纭繚鑷冲皯淇濈暀涓�涓鎵规楠�
-      if (approvalSteps.value.length > 1) {
-        approvalSteps.value.splice(stepIndex, 1);
-      } else {
-        uni.showToast({
-          title: '鑷冲皯闇�瑕佷竴涓鎵规楠�',
-          icon: 'none'
-        });
-      }
-    };
-
-    return {
-      rules,
-      removeApprovalStep,
-    removeApprover,
-      result,
-      pickerValue,
-      columns,
-      onConfirm,
-      showPicker,
-      taxPrice,
-      contractAmount,
-      goBack,
-      submitForm,
-      approvalSteps,
-      addApprover,
-      addApprovalStep,
-      formRef,
-      message
-    };
-  },
+const getProductOptions = () => {
+	getDept().then((res) => {
+		productOptions.value = res.data.map(item => ({
+			value: item.deptId,
+			text: item.deptName
+		}))
+	});
 };
+const fileList = ref([]);
+let nextApproverId = 2;
+
+onMounted(async () => {
+  try {
+		getProductOptions()
+		userListNoPageByTenantId().then((res) => {
+			userList.value = res.data
+		})
+		form.value.approveUser = userStore.id
+		form.value.approveUserName = userStore.nickName
+		form.value.approveTime = getCurrentDate();
+		
+		// 鑾峰彇URL鍙傛暟
+		const pages = getCurrentPages();
+		const currentPage = pages[pages.length - 1];
+		operationType.value = currentPage.options.operationType || 'add';
+		
+		// 濡傛灉鏄紪杈戞ā寮忥紝浠庢湰鍦板瓨鍌ㄨ幏鍙栨暟鎹�
+		if (operationType.value === 'edit') {
+			const storedData = uni.getStorageSync('invoiceLedgerEditRow');
+			if (storedData) {
+				const row = JSON.parse(storedData);
+				fileList.value = row.commonFileList || [];
+				form.value.tempFileIds = fileList.value.map(file => file.id);
+				currentApproveStatus.value = row.approveStatus;
+				
+				approveProcessGetInfo({id: row.approveId, approveReason: '1'}).then(res => {
+					form.value = {...res.data};
+					// 鍙嶆樉瀹℃壒浜�
+					if (res.data && res.data.approveUserIds) {
+						const userIds = res.data.approveUserIds.split(',');
+						approverNodes.value = userIds.map((userId, idx) => {
+							const userIdNum = parseInt(userId.trim());
+							// 浠巙serList涓壘鍒板搴旂殑鐢ㄦ埛淇℃伅
+							const userInfo = userList.value.find(user => user.userId === userIdNum);
+							return {
+								id: idx + 1,
+								userId: userIdNum,
+								nickName: userInfo ? userInfo.nickName : null
+							};
+						});
+						nextApproverId = userIds.length + 1;
+					} else {
+						// 鏂板妯″紡锛屽垵濮嬪寲涓�涓┖鐨勫鎵硅妭鐐�
+						approverNodes.value = [{ id: 1, userId: null, nickName: null }];
+						nextApproverId = 2;
+					}
+				});
+			}
+		} else {
+			// 鏂板妯″紡锛屽垵濮嬪寲涓�涓┖鐨勫鎵硅妭鐐�
+			approverNodes.value = [{ id: 1, userId: null }];
+		}
+		
+    // 鐩戝惉鑱旂郴浜洪�夋嫨浜嬩欢
+    uni.$on('selectContact', handleSelectContact);
+  } catch (error) {
+    console.error("鑾峰彇閮ㄩ棬鏁版嵁澶辫触:", error);
+  }
+});
+
+onUnmounted(() => {
+  // 绉婚櫎浜嬩欢鐩戝惉
+  uni.$off('selectContact', handleSelectContact);
+});
+
+const onConfirm = ({ selectedValues, selectedOptions }) => {
+  form.value.approveDeptName = selectedOptions[0]?.text;
+  form.value.approveDeptId = selectedOptions[0]?.value;
+  pickerValue.value = selectedValues;
+  showPicker.value = false;
+};
+
+const goBack = () => {
+	// 娓呴櫎鏈湴瀛樺偍鐨勬暟鎹�
+	uni.removeStorageSync('invoiceLedgerEditRow');
+  uni.navigateBack();
+};
+
+const submitForm = () => {
+  // 妫�鏌ユ瘡涓鎵规楠ゆ槸鍚﹂兘鏈夊鎵逛汉
+  const hasEmptyStep = approverNodes.value.some(step => !step.nickName);
+  if (hasEmptyStep) {
+		showToast('璇蜂负姣忎釜瀹℃壒姝ラ閫夋嫨瀹℃壒浜�');
+    return;
+  }
+  
+  formRef.value.validate().then(() => {
+    // 琛ㄥ崟鏍¢獙閫氳繃锛屽彲浠ユ彁浜ゆ暟鎹�
+		// 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
+		console.log('approverNodes---', approverNodes.value)
+		form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
+		form.value.approveType = 0
+		if (operationType.value === "add" || currentApproveStatus.value == 3) {
+			approveProcessAdd(form.value).then(res => {
+				showToast("鎻愪氦鎴愬姛");
+				goBack()
+			})
+		} else {
+			approveProcessUpdate(form.value).then(res => {
+				showToast("鎻愪氦鎴愬姛");
+				goBack()
+			})
+		}
+  }).catch((error) => {
+    console.error("琛ㄥ崟鏍¢獙澶辫触:", error);
+    // 鏄剧ず鍏蜂綋鐨勯敊璇俊鎭�
+    if (error.length > 0) {
+      const firstError = error[0];
+      uni.showToast({
+        title: firstError.message || '琛ㄥ崟鏍¢獙澶辫触',
+        icon: 'none'
+      });
+    } else {
+      uni.showToast({
+        title: '琛ㄥ崟鏍¢獙澶辫触锛岃妫�鏌ュ繀濉」',
+        icon: 'none'
+      });
+    }
+  });
+};
+
+// 澶勭悊鑱旂郴浜洪�夋嫨缁撴灉
+const handleSelectContact = (data) => {
+  const { stepIndex, contact } = data;
+  // 灏嗛�変腑鐨勮仈绯讳汉璁剧疆涓哄搴斿鎵规楠ょ殑瀹℃壒浜�
+  approverNodes.value[stepIndex].userId = contact.userId;
+  approverNodes.value[stepIndex].nickName = contact.nickName;
+};
+
+const addApprover = (stepIndex) => {
+  // 璺宠浆鍒拌仈绯讳汉閫夋嫨椤甸潰
+  uni.navigateTo({
+    url: `/pages/cooperativeOffice/collaborativeApproval/contactSelect?stepIndex=${stepIndex}`
+  });
+};
+
+const addApprovalStep = () => {
+  // 娣诲姞鏂扮殑瀹℃壒姝ラ
+  approverNodes.value.push({ userId: null, nickName: null });
+};
+
+const removeApprover = (stepIndex) => {
+  // 绉婚櫎瀹℃壒浜�
+  approverNodes.value[stepIndex].userId = null;
+  approverNodes.value[stepIndex].nickName = null;
+};
+
+const removeApprovalStep = (stepIndex) => {
+  // 纭繚鑷冲皯淇濈暀涓�涓鎵规楠�
+  if (approverNodes.value.length > 1) {
+    approverNodes.value.splice(stepIndex, 1);
+  } else {
+    uni.showToast({
+      title: '鑷冲皯闇�瑕佷竴涓鎵规楠�',
+      icon: 'none'
+    });
+  }
+};
+// 鏄剧ず鏃ユ湡閫夋嫨鍣�
+const showDatePicker = () => {
+	showDate.value = true
+}
+
+// 纭鏃ユ湡閫夋嫨
+const onDateConfirm = ({ selectedValues }) => {
+	form.value.approveTime = selectedValues.join('-')
+	currentDate.value = selectedValues
+	showDate.value = false
+}
+// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
+function getCurrentDate() {
+	const today = new Date();
+	const year = today.getFullYear();
+	const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮�
+	const day = String(today.getDate()).padStart(2, "0");
+	return `${year}-${month}-${day}`;
+}
 </script>
 
 <style scoped lang="scss">
@@ -287,74 +383,6 @@
   margin-top: 16px;
 }
 
-.van-field {
-  height: 56px;
-  line-height: 36px;
-}
-
-.product-section {
-  background: #fff;
-  margin: 16px;
-  border-radius: 16px;
-  padding: 20px 16px 8px 16px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
-}
-
-.section-header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-bottom: 12px;
-}
-
-.section-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #333;
-}
-
-.add-btn {
-  background: #2979ff;
-  color: #fff;
-  border-radius: 8px;
-  padding: 4px 16px;
-  font-size: 14px;
-}
-
-.product-card {
-  background: #f8f9fa;
-  border-radius: 12px;
-  padding: 12px;
-  margin-bottom: 16px;
-  box-shadow: 0 1px 4px rgba(41, 121, 255, 0.06);
-  position: relative;
-}
-
-.product-row {
-  display: flex;
-  align-items: center;
-  margin-bottom: 8px;
-}
-
-.product-label {
-  min-width: 60px;
-  color: #888;
-  font-size: 13px;
-}
-
-.del-row {
-  justify-content: flex-end;
-}
-
-.del-btn {
-  background: #ff4d4f;
-  color: #fff;
-  border-radius: 8px;
-  padding: 4px 16px;
-  font-size: 13px;
-  margin-top: 4px;
-}
-
 .approval-process {
   background: #fff;
   margin: 16px;
@@ -380,156 +408,347 @@
   color: #999;
 }
 
+/* 鏍峰紡澧炲己涓衡�滅畝娲佸皬鍦嗗湀椋庢牸鈥� */
 .approval-steps {
-  padding-left: 16px;
+  padding-left: 22px;
   position: relative;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    left: 11px;
+    top: 40px;
+    bottom: 40px;
+    width: 2px;
+    background: linear-gradient(to bottom, #e6f7ff 0%, #bae7ff 50%, #91d5ff 100%);
+    border-radius: 1px;
+  }
 }
 
 .approval-step {
   position: relative;
-  margin-bottom: 20px;
+  margin-bottom: 24px;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    left: -18px;
+    top: 14px; // 浠� 8px 璋冩暣涓� 14px锛屼笌鏂囧瓧涓績瀵归綈
+    width: 12px;
+    height: 12px;
+    background: #fff;
+    border: 3px solid #006cfb;
+    border-radius: 50%;
+    z-index: 2;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  }
 }
 
 .step-title {
+	top: 12px;
   margin-bottom: 12px;
+  position: relative;
+	margin-left: 6px;
 }
 
 .step-title text {
   font-size: 14px;
   color: #666;
   background: #f0f0f0;
-  padding: 2px 8px;
-  border-radius: 4px;
-}
-
-.approvers-container {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 12px;
-  margin-bottom: 8px;
+  padding: 4px 12px;
+  border-radius: 12px;
+  position: relative;
+  line-height: 1.4; // 纭繚鏂囧瓧琛岄珮涓�鑷�
 }
 
 .approver-item {
   display: flex;
-  flex-direction: column;
   align-items: center;
-  width: 60px;
+  background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+  border-radius: 16px;
+  padding: 16px;
+  gap: 12px;
+  position: relative;
+  border: 1px solid #e6f7ff;
+  box-shadow: 0 4px 12px rgba(0, 108, 251, 0.08);
+  transition: all 0.3s ease;
 }
 
 .approver-avatar {
-  width: 40px;
-  height: 40px;
-  background: #e6f7ff;
+  width: 48px;
+  height: 48px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
   border-radius: 50%;
-  margin-bottom: 4px;
   display: flex;
   align-items: center;
   justify-content: center;
+  position: relative;
+  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
 }
 
-.approver-avatar::after {
-  content: '馃懁';
-  font-size: 20px;
+.avatar-text {
+  color: #fff;
+  font-size: 18px;
+  font-weight: 600;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.approver-info {
+  flex: 1;
+  position: relative;
 }
 
 .approver-name {
-  font-size: 12px;
+  display: block;
+  font-size: 16px;
   color: #333;
-  text-align: center;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  margin-bottom: 2px;
+  font-weight: 500;
+  position: relative;
+}
+
+.approver-dept {
+  font-size: 12px;
+  color: #999;
+  background: rgba(0, 108, 251, 0.05);
+  padding: 2px 8px;
+  border-radius: 8px;
+  display: inline-block;
+  position: relative;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    left: 4px;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 2px;
+    height: 2px;
+    background: #006cfb;
+    border-radius: 50%;
+  }
 }
 
 .delete-approver-btn {
-  font-size: 12px;
+  font-size: 16px;
   color: #ff4d4f;
-  background: rgba(255, 77, 79, 0.1);
-  width: 16px;
-  height: 16px;
+  background: linear-gradient(135deg, rgba(255, 77, 79, 0.1) 0%, rgba(255, 77, 79, 0.05) 100%);
+  width: 28px;
+  height: 28px;
   border-radius: 50%;
   display: flex;
   align-items: center;
   justify-content: center;
-  margin-top: 2px;
+  transition: all 0.3s ease;
+  position: relative;
+}
+
+.add-approver-btn {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%);
+  border: 2px dashed #006cfb;
+  border-radius: 16px;
+  padding: 20px;
+  color: #006cfb;
+  font-size: 14px;
+  position: relative;
+  transition: all 0.3s ease;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    width: 32px;
+    height: 32px;
+    border: 2px solid #006cfb;
+    border-radius: 50%;
+    opacity: 0;
+    transition: all 0.3s ease;
+  }
 }
 
 .delete-step-btn {
-  margin-top: 8px;
   color: #ff4d4f;
   font-size: 12px;
-  background: rgba(255, 77, 79, 0.1);
-  padding: 2px 8px;
-  border-radius: 4px;
+  background: linear-gradient(135deg, rgba(255, 77, 79, 0.1) 0%, rgba(255, 77, 79, 0.05) 100%);
+  padding: 6px 12px;
+  border-radius: 12px;
   display: inline-block;
+  position: relative;
+  transition: all 0.3s ease;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    left: 6px;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 4px;
+    height: 4px;
+    background: #ff4d4f;
+    border-radius: 50%;
   }
-
-.add-approver-btn {
-  width: 40px;
-  height: 40px;
-  border: 1px dashed #ccc;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 20px;
-  color: #999;
-  margin-top: 8px;
 }
 
 .step-line {
-  position: absolute;
-  left: 20px;
-  top: 100%;
-  width: 1px;
-  height: 30px;
-  background: #e0e0e0;
+  display: none; // 闅愯棌鍘熸潵鐨勭嚎鏉★紝浣跨敤浼厓绱犱唬鏇�
 }
 
 .add-step-btn {
   display: flex;
   align-items: center;
   justify-content: center;
-  margin-top: 16px;
-  color: #006cfb;
-  font-size: 14px;
-  padding: 8px 0;
-  border: 1px dashed #006cfb;
-  border-radius: 8px;
 }
-
 .footer-btns {
-  position: fixed;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: #fff;
-  display: flex;
-  justify-content: space-around;
-  align-items: center;
-  padding: 12px 0;
-  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
-  z-index: 1000;
+	position: fixed;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	background: #fff;
+	display: flex;
+	justify-content: space-around;
+	align-items: center;
+	padding: 0.75rem 0;
+	box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05);
+	z-index: 1000;
 }
 
 .cancel-btn {
-  font-weight: 400;
-  font-size: 16px;
-  color: #ffffff;
-  width: 102px;
-  background: #c7c9cc;
-  box-shadow: 0px 4px 10px 0px rgba(3, 88, 185, 0.2);
-  border-radius: 40px 40px 40px 40px;
+	font-weight: 400;
+	font-size: 1rem;
+	color: #FFFFFF;
+	width: 6.375rem;
+	background: #C7C9CC;
+	box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
+	border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
 }
 
 .save-btn {
-  font-weight: 400;
-  font-size: 16px;
-  color: #ffffff;
-  width: 224px;
-  background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
-  box-shadow: 0px 4px 10px 0px rgba(3, 88, 185, 0.2);
-  border-radius: 40px 40px 40px 40px;
+	font-weight: 400;
+	font-size: 1rem;
+	color: #FFFFFF;
+	width: 14rem;
+	background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%);
+	box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
+	border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
+}
+
+// 鍔ㄧ敾瀹氫箟
+@keyframes pulse {
+  0% {
+    transform: scale(1);
+    opacity: 1;
+  }
+  50% {
+    transform: scale(1.2);
+    opacity: 0.7;
+  }
+  100% {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+@keyframes rotate {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes ripple {
+  0% {
+    transform: translate(-50%, -50%) scale(0.8);
+    opacity: 1;
+  }
+  100% {
+    transform: translate(-50%, -50%) scale(1.6);
+    opacity: 0;
+  }
+}
+
+/* 濡傛灉宸叉湁 .step-line锛岃繖閲屾洿绮惧噯瀹氫綅鍒板乏渚т笌灏忓渾鐐瑰榻� */
+.step-line {
+  position: absolute;
+  left: 4px;
+  top: 48px;
+  width: 2px;
+  height: calc(100% - 48px);
+  background: #E5E7EB;
+}
+
+.approver-container {
+  display: flex;
+  align-items: center;
+  background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+  border-radius: 16px;
+  gap: 12px;
+  padding: 10px 0;
+  background: transparent;
+  border: none;
+  box-shadow: none;
+}
+
+.approver-item {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 8px 10px;
+  background: transparent;
+  border: none;
+  box-shadow: none;
+  border-radius: 0;
+}
+
+.approver-avatar {
+  position: relative;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  background: #F3F4F6;
+  border: 2px solid #E5E7EB;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  animation: none; /* 绂佺敤鏃嬭浆绛夊姩鐢伙紝鍥炲綊绠�娲� */
+}
+
+.avatar-text {
+  font-size: 14px;
+  color: #374151;
+  font-weight: 600;
+}
+
+.add-approver-btn {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  background: transparent;
+  border: none;
+  box-shadow: none;
+  padding: 0;
+}
+
+.add-approver-btn .add-circle {
+  width: 40px;
+  height: 40px;
+  border: 2px dashed #A0AEC0;
+  border-radius: 50%;
+  color: #6B7280;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 22px;
+  line-height: 1;
+}
+
+.add-approver-btn .add-label {
+  color: #3B82F6;
+  font-size: 14px;
 }
 </style>
\ No newline at end of file
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/index.vue b/src/pages/cooperativeOffice/collaborativeApproval/index.vue
index 0ae745b..a8b4654 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/index.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/index.vue
@@ -8,51 +8,87 @@
 		<view class="search-filter-section">
 			<view class="search-bar">
 				<view class="search-input">
-					<u-input placeholder="璇疯緭鍏ラ噰璐悎鍚屽彿" class="search-text" v-model="searchKeyword">
-						<template #suffix>
-							<up-icon name="search" size="24" color="#999" @click="getList"></up-icon>
-						</template>
-					</u-input>
+					<input
+						class="search-text"
+						placeholder="璇疯緭鍏ユ祦绋嬬紪鍙�"
+						v-model="searchForm.approveId"
+					/>
 				</view>
-				<view class="filter-button" @click="showFilterOptions">
-					<van-icon name="filter-o" size="24" color="#999"></van-icon>
+				<view class="search-button" @click="getList">
+					<up-icon name="search" size="24" color="#999"></up-icon>
 				</view>
 			</view>
 		</view>
 
-		<!-- 閿�鍞彴璐︾�戝竷娴� -->
-		<view class="ledger-list" v-if="total > 0">
+		<!-- 瀹℃壒鍒楄〃 -->
+		<view class="ledger-list" v-if="ledgerList.length > 0">
 			<view v-for="(item, index) in ledgerList" :key="index">
-				<view class="ledger-item" @click="handleItemClick(item)">
+				<view class="ledger-item">
 					<view class="item-header">
 						<view class="item-left">
 							<view class="document-icon">
 								<up-icon name="file-text" size="16" color="#ffffff"></up-icon>
 							</view>
-							<text class="item-id">{{ item.salesContractNo }}</text>
+							<text class="item-id">{{ item.approveId }}</text>
+						</view>
+						<view class="item-tag">
+							<van-tag :type="getTagClass(item.approveStatus)" size="medium">{{ formatReceiptType(item.approveStatus) }}</van-tag>
 						</view>
 					</view>
 					<up-divider></up-divider>
 
 					<view class="item-details">
-						<view class="detail-info">
-							<view class="detail-row">
-								<text class="detail-label">鐢宠浜�</text>
-								<text class="detail-value">{{ item.entryPersonName }}</text>
-							</view>
-							<view class="detail-row">
-								<text class="detail-label">鐢宠鏃ユ湡</text>
-								<text class="detail-value highlightBlue">{{ item.entryDate }}</text>
-							</view>
+						<view class="detail-row">
+							<text class="detail-label">鐢宠浜�</text>
+							<text class="detail-value">{{ item.approveUserName }}</text>
 						</view>
+						<view class="detail-row">
+							<text class="detail-label">鐢宠閮ㄩ棬</text>
+							<text class="detail-value">{{ item.approveDeptName }}</text>
+						</view>
+						<view class="detail-row-approveReason">
+							<text class="detail-label">瀹℃壒浜嬬敱</text>
+							<text class="detail-value highlightBlue">{{ item.approveReason }}</text>
+						</view>
+						<view class="detail-row">
+							<text class="detail-label">鐢宠鏃ユ湡</text>
+							<text class="detail-value">{{ item.approveTime }}</text>
+						</view>
+						<view class="detail-row">
+							<text class="detail-label">缁撴潫鏃ユ湡</text>
+							<text class="detail-value">{{ item.approveOverTime }}</text>
+						</view>
+						<up-divider></up-divider>
 						<view class="detail-info">
-							<view class="detail-row">
-								<text class="detail-label">鐢宠閮ㄩ棬</text>
-								<text class="detail-value">{{ item.entryPersonName }}</text>
+							<view class="detail-row-user">
+								<text class="detail-label">褰撳墠瀹℃壒浜�</text>
+								<view class="detail-value approver-value">
+									<view class="approver-chip">
+										<text class="approver-name">{{ item.approveUserCurrentName || '鏈垎閰�' }}</text>
+									</view>
+								</view>
 							</view>
 							<view class="detail-row">
-								<text class="detail-label">瀹℃壒鐘舵��</text>
-								<text class="detail-value highlightYellow">{{ item.entryDate }}</text>
+								<view class="actions">
+									<van-button
+										type="primary"
+										size="small"
+										class="action-btn edit"
+										:disabled="item.approveStatus == 2 || item.approveStatus == 1 || item.approveStatus == 4"
+										@click="handleItemClick(item)"
+									>
+										缂栬緫
+									</van-button>
+									<van-button
+										type="success"
+										size="small"
+										class="action-btn approve"
+										:disabled="item.approveUserCurrentId == null || item.approveStatus == 2 || item.approveStatus == 3 || item.approveStatus == 4 || item.approveUserCurrentId !== userStore.id"
+										@click="approve(item)"
+									>
+										瀹℃牳
+									</van-button>
+								</view>
 							</view>
 						</view>
 					</view>
@@ -62,31 +98,34 @@
 		<view v-else class="no-data">
 			<text>鏆傛棤瀹℃壒鏁版嵁</text>
 		</view>
-
+<van-floating-bubble icon="plus" @click="handleAdd"/>
 		<!-- 娴姩鎿嶄綔鎸夐挳 -->
-		<view class="fab-button" @click="handleAdd">
+		<!-- <view class="fab-button" @click="handleAdd">
 			<up-icon name="plus" size="24" color="#ffffff"></up-icon>
-		</view>
+		</view> -->
 	</view>
 </template>
 
 <script setup>
 	import {
 		ref,
-		reactive,
-		onMounted
+		toRefs,
+		reactive
 	} from "vue";
-	import {
-		ledgerListPage
-	} from "@/api/cooperativeOffice/collaborativeApproval";
 	import PageHeader from "@/components/PageHeader.vue";
-
-	// 鎼滅储鍏抽敭璇�
-	const searchKeyword = ref("");
-
-	// 閿�鍞彴璐︽暟鎹�
+	import {approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess";
+	import {onShow} from "@dcloudio/uni-app";
+	import useUserStore from "@/store/modules/user";
+	
+	const userStore = useUserStore()
+	// 鏁版嵁
 	const ledgerList = ref([]);
-	const total = ref(0);
+	const data = reactive({
+		searchForm: {
+			approveId: "",
+		},
+	});
+	const { searchForm } = toRefs(data);
 
 	// 杩斿洖涓婁竴椤�
 	const goBack = () => {
@@ -98,12 +137,11 @@
 			current: -1,
 			size: -1,
 		};
-		ledgerListPage({
-				...page
+		approveProcessListPage({
+				...page,approveType: 0,...searchForm.value
 			})
 			.then((res) => {
-				ledgerList.value = res.records;
-				total.value = res.total;
+				ledgerList.value = res.data.records;
 			})
 			.catch(() => {
 				// tableLoading.value = false;
@@ -118,23 +156,58 @@
 			},
 		});
 	};
+	// 鏍煎紡鍖栧洖娆炬柟寮�
+	const formatReceiptType = (params) => {
+		if (params == 0) {
+			return "寰呭鏍�";
+		} else if (params == 1) {
+			return "瀹℃牳涓�";
+		} else if (params == 2) {
+			return "瀹℃牳瀹屾垚";
+		} else if (params == 4) {
+			return "宸查噸鏂版彁浜�";
+		} else {
+			return '涓嶉�氳繃';
+		}
+	};
+	// 鑾峰彇鏍囩鏍峰紡绫�
+	const getTagClass = (type) => {
+		if (type == 0) {
+			return "warning";
+		} else if (type == 1) {
+			return "primary";
+		} else if (type == 2) {
+			return "success";
+		} else if (type == 4) {
+			return "primary";
+		} else {
+			return "danger";
+		}
+	};
 
 	// 鐐瑰嚮鍒楄〃椤�
 	const handleItemClick = (item) => {
-		uni.showToast({
-			title: `鏌ョ湅鍚堝悓: ${item.contractId}`,
-			icon: "none",
+		// 浣跨敤鏈湴瀛樺偍浼犻�掓暟鎹�
+		uni.setStorageSync('invoiceLedgerEditRow', JSON.stringify(item));
+		uni.navigateTo({
+			url: `/pages/cooperativeOffice/collaborativeApproval/detail?operationType=edit&approveId=${item.approveId}`,
 		});
 	};
 
 	// 娣诲姞鏂拌褰�
 	const handleAdd = () => {
 		uni.navigateTo({
-			url: "/pages/cooperativeOffice/collaborativeApproval/detail",
+			url: "/pages/cooperativeOffice/collaborativeApproval/detail?operationType=add",
 		});
 	};
+	// 鐐瑰嚮瀹℃牳
+	const approve = (item) => {
+		uni.navigateTo({
+			url: `/pages/cooperativeOffice/collaborativeApproval/approve?approveId=${item.approveId}`
+		})
+	}
 
-	onMounted(() => {
+	onShow(() => {
 		// 椤甸潰鍔犺浇瀹屾垚鍚庣殑鍒濆鍖栭�昏緫
 		getList();
 	});
@@ -150,7 +223,27 @@
 		background: #f8f9fa;
 		position: relative;
 	}
-
+	.search-input {
+		flex: 1;
+		background: #f5f5f5;
+		border-radius: 24px;
+		padding: 10px 16px;
+		display: flex;
+		align-items: center;
+		gap: 8px;
+	}
+	.search-text {
+		flex: 1;
+		font-size: 14px;
+		color: #333;
+		background: transparent;
+		border: none;
+		outline: none;
+	}
+	
+	.search-text::placeholder {
+		color: #999;
+	}
 
 
 	.search-filter-section {
@@ -163,17 +256,16 @@
 		align-items: center;
 		gap: 12px;
 	}
-
+	
 	.search-input {
 		flex: 1;
 		background: #f5f5f5;
 		border-radius: 24px;
-		padding: 4px 16px;
+		padding: 10px 16px;
 		display: flex;
 		align-items: center;
 		gap: 8px;
 	}
-
 	.search-text {
 		flex: 1;
 		font-size: 14px;
@@ -182,7 +274,7 @@
 		border: none;
 		outline: none;
 	}
-
+	
 	.search-text::placeholder {
 		color: #999;
 	}
@@ -239,7 +331,6 @@
 	}
 
 	.item-tag {
-		background: #4caf50;
 		border-radius: 4px;
 		padding: 2px 4px;
 	}
@@ -253,16 +344,27 @@
 	.item-details {
 		padding: 16px 0;
 	}
-
+	
 	.detail-row {
 		display: flex;
 		align-items: flex-end;
 		justify-content: space-between;
 		margin-bottom: 8px;
-
+		
 		&:last-child {
 			margin-bottom: 0;
 		}
+	}
+	.detail-row-user {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+	}
+	.detail-row-approveReason {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		margin-bottom: 8px;
 	}
 
 	.detail-info {
@@ -316,4 +418,50 @@
 		box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3);
 		z-index: 1000;
 	}
+
+	.approver-value {
+		display: flex;
+		justify-content: flex-end;
+	}
+	.approver-chip {
+		display: inline-flex;
+		align-items: center;
+		gap: 6px;
+		background: #f0f6ff;
+		color: #2b7cff;
+		border: 1px solid #e0efff;
+		border-radius: 999px;
+		padding: 4px 10px;
+		max-width: 100%;
+	}
+	.approver-name {
+		font-size: 12px;
+		color: #2b7cff;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+	}
+
+	.actions {
+		display: flex;
+		gap: 10px;
+		align-items: center;
+		justify-content: flex-end;
+	}
+
+	.action-btn {
+		border-radius: 16px;
+		height: 28px;
+		line-height: 28px;
+		padding: 0 12px;
+	}
+	.action-btn.edit {
+		/* primary 鏍峰紡鏉ヨ嚜缁勪欢锛岃繖閲屼繚鐣欓挬瀛愪互渚垮悗缁渶瑕佹墿灞� */
+	}
+	.action-btn.approve {
+		/* success 鏍峰紡鏉ヨ嚜缁勪欢锛岃繖閲屼繚鐣欓挬瀛愪互渚垮悗缁渶瑕佹墿灞� */
+	}
+	:deep(.van-floating-bubble) {
+		background: #ed8d05;
+	}
 </style>
\ No newline at end of file

--
Gitblit v1.9.3