From c58665039ce8b7c895ed4f1000ff4cf525a92085 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 27 八月 2025 16:28:21 +0800
Subject: [PATCH] 1.设备保养开发联调

---
 src/pages/cooperativeOffice/collaborativeApproval/approve.vue |  519 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 519 insertions(+), 0 deletions(-)

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

--
Gitblit v1.9.3