From a6e09eeeb5bb9694a79276a71bc92b21778a65e1 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 24 九月 2025 15:50:49 +0800
Subject: [PATCH] 过程追踪

---
 src/views/collaborativeApproval/processTracking/index.vue |  498 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 498 insertions(+), 0 deletions(-)

diff --git a/src/views/collaborativeApproval/processTracking/index.vue b/src/views/collaborativeApproval/processTracking/index.vue
new file mode 100644
index 0000000..3d8f1ad
--- /dev/null
+++ b/src/views/collaborativeApproval/processTracking/index.vue
@@ -0,0 +1,498 @@
+<template>
+  <div class="process-tracking">
+    <div class="header">
+      <h2>杩囩▼杩借釜</h2>
+      <el-button type="primary" @click="refreshData">鍒锋柊鏁版嵁</el-button>
+    </div>
+
+    <!-- 椤圭洰鐘舵�佺粺璁� -->
+    <div class="status-cards">
+      <el-row :gutter="20">
+        <el-col :span="6" v-for="(item, index) in statusStats" :key="index">
+          <el-card class="status-card" :class="item.type">
+            <div class="card-content">
+              <div class="card-icon">
+                <el-icon :size="24">
+                  <component :is="item.icon" />
+                </el-icon>
+              </div>
+              <div class="card-info">
+                <div class="card-title">{{ item.label }}</div>
+                <div class="card-count">{{ item.count }}</div>
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 椤圭洰鍒楄〃 -->
+    <el-card class="project-list">
+      <template #header>
+        <div class="card-header">
+          <span>椤圭洰鍒楄〃</span>
+          <el-button type="text" @click="toggleView">
+            {{ viewMode === 'table' ? '鍒囨崲鍒扮敇鐗瑰浘' : '鍒囨崲鍒板垪琛�' }}
+          </el-button>
+        </div>
+      </template>
+
+      <!-- 琛ㄦ牸瑙嗗浘 -->
+      <div v-if="viewMode === 'table'">
+        <el-table :data="projectList" style="width: 100%">
+          <el-table-column prop="name" label="椤圭洰鍚嶇О"  />
+          <el-table-column prop="manager" label="璐熻矗浜�"/>
+          <el-table-column label="鐘舵��" >
+            <template #default="{ row }">
+              <el-tag :type="getStatusType(row.status)">
+                {{ getStatusText(row.status) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="杩涘害" width="150">
+            <template #default="{ row }">
+              <el-progress :percentage="row.progress" :status="getProgressStatus(row.status)" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="startDate" label="寮�濮嬫椂闂�" width="120" />
+          <el-table-column prop="endDate" label="缁撴潫鏃堕棿" width="120" />
+          <el-table-column label="鎿嶄綔" width="150">
+            <template #default="{ row }">
+              <el-button type="text" @click="updateStatus(row)">鏇存柊鐘舵��</el-button>
+              <el-button type="text" @click="viewDetails(row)">璇︽儏</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+
+      <!-- 鐢樼壒鍥捐鍥� -->
+      <div v-else class="gantt-container">
+        <div ref="ganttChart" style="width: 100%; height: 400px;"></div>
+      </div>
+    </el-card>
+
+    <!-- 鐘舵�佹洿鏂板璇濇 -->
+    <el-dialog v-model="statusDialogVisible" title="鏇存柊椤圭洰鐘舵��" width="400px">
+      <el-form :model="statusForm" label-width="80px">
+        <el-form-item label="椤圭洰鍚嶇О">
+          <el-input v-model="statusForm.name" disabled />
+        </el-form-item>
+        <el-form-item label="褰撳墠鐘舵��">
+          <el-select v-model="statusForm.status" placeholder="璇烽�夋嫨鐘舵��">
+            <el-option label="鏈紑濮�" value="not_started" />
+            <el-option label="杩涜涓�" value="in_progress" />
+            <el-option label="宸插畬鎴�" value="completed" />
+            <el-option label="寤舵湡" value="delayed" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="杩涘害">
+          <el-slider v-model="statusForm.progress" :max="100" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="statusDialogVisible = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="confirmStatusUpdate">纭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, nextTick } from 'vue'
+import { ElMessage } from 'element-plus'
+import { Clock, Loading, Check, Warning } from '@element-plus/icons-vue'
+import * as echarts from 'echarts'
+
+// 鍝嶅簲寮忔暟鎹�
+const viewMode = ref('table')
+const statusDialogVisible = ref(false)
+const ganttChart = ref(null)
+let chartInstance = null
+
+// 鐘舵�佺粺璁℃暟鎹�
+const statusStats = reactive([
+  { label: '鏈紑濮�', count: 3, type: 'not-started', icon: Clock },
+  { label: '杩涜涓�', count: 5, type: 'in-progress', icon: Loading },
+  { label: '宸插畬鎴�', count: 8, type: 'completed', icon: Check },
+  { label: '寤舵湡', count: 2, type: 'delayed', icon: Warning }
+])
+
+// 椤圭洰鍒楄〃鏁版嵁
+const projectList = reactive([
+  {
+    id: 1,
+    name: '浜у搧闇�姹傚垎鏋�',
+    manager: '寮犱笁',
+    status: 'completed',
+    progress: 100,
+    startDate: '2024-01-01',
+    endDate: '2024-01-15'
+  },
+  {
+    id: 2,
+    name: '绯荤粺鏋舵瀯璁捐',
+    manager: '鏉庡洓',
+    status: 'in_progress',
+    progress: 75,
+    startDate: '2024-01-10',
+    endDate: '2024-01-25'
+  },
+  {
+    id: 3,
+    name: '鍓嶇寮�鍙�',
+    manager: '鐜嬩簲',
+    status: 'in_progress',
+    progress: 60,
+    startDate: '2024-01-20',
+    endDate: '2024-02-10'
+  },
+  {
+    id: 4,
+    name: '鍚庣寮�鍙�',
+    manager: '璧靛叚',
+    status: 'in_progress',
+    progress: 45,
+    startDate: '2024-01-25',
+    endDate: '2024-02-15'
+  },
+  {
+    id: 5,
+    name: '鏁版嵁搴撹璁�',
+    manager: '瀛欎竷',
+    status: 'delayed',
+    progress: 30,
+    startDate: '2024-01-15',
+    endDate: '2024-01-30'
+  },
+  {
+    id: 6,
+    name: '娴嬭瘯鐢ㄤ緥缂栧啓',
+    manager: '鍛ㄥ叓',
+    status: 'not_started',
+    progress: 0,
+    startDate: '2024-02-01',
+    endDate: '2024-02-20'
+  }
+])
+
+// 鐘舵�佹洿鏂拌〃鍗�
+const statusForm = reactive({
+  id: null,
+  name: '',
+  status: '',
+  progress: 0
+})
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+  const typeMap = {
+    not_started: 'info',
+    in_progress: 'warning',
+    completed: 'success',
+    delayed: 'danger'
+  }
+  return typeMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+  const textMap = {
+    not_started: '鏈紑濮�',
+    in_progress: '杩涜涓�',
+    completed: '宸插畬鎴�',
+    delayed: '寤舵湡'
+  }
+  return textMap[status] || '鏈煡'
+}
+
+// 鑾峰彇杩涘害鐘舵��
+const getProgressStatus = (status) => {
+  if (status === 'completed') return 'success'
+  if (status === 'delayed') return 'exception'
+  return null
+}
+
+// 鍒囨崲瑙嗗浘妯″紡
+const toggleView = () => {
+  viewMode.value = viewMode.value === 'table' ? 'gantt' : 'table'
+  if (viewMode.value === 'gantt') {
+    nextTick(() => {
+      initGanttChart()
+    })
+  }
+}
+
+// 鍒濆鍖栫敇鐗瑰浘
+const initGanttChart = () => {
+  if (!ganttChart.value) return
+  
+  if (chartInstance) {
+    chartInstance.dispose()
+  }
+  
+  chartInstance = echarts.init(ganttChart.value)
+  
+  // 鍑嗗鐢樼壒鍥炬暟鎹�
+  const data = projectList.map((project, index) => ({
+    name: project.name,
+    value: [
+      index,
+      new Date(project.startDate).getTime(),
+      new Date(project.endDate).getTime(),
+      project.progress
+    ],
+    itemStyle: {
+      color: getGanttColor(project.status)
+    }
+  }))
+  
+  const option = {
+    title: {
+      text: '椤圭洰鐢樼壒鍥�',
+      left: 'center'
+    },
+    tooltip: {
+      formatter: (params) => {
+        const project = projectList[params.value[0]]
+        return `
+          <div>
+            <strong>${project.name}</strong><br/>
+            璐熻矗浜�: ${project.manager}<br/>
+            鐘舵��: ${getStatusText(project.status)}<br/>
+            杩涘害: ${project.progress}%<br/>
+            寮�濮嬫椂闂�: ${project.startDate}<br/>
+            缁撴潫鏃堕棿: ${project.endDate}
+          </div>
+        `
+      }
+    },
+    grid: {
+      left: '15%',
+      right: '10%',
+      top: '15%',
+      bottom: '15%'
+    },
+    xAxis: {
+      type: 'time',
+      axisLabel: {
+        formatter: (value) => {
+          return echarts.format.formatTime('MM-dd', value)
+        }
+      }
+    },
+    yAxis: {
+      type: 'category',
+      data: projectList.map(p => p.name),
+      inverse: true
+    },
+    series: [{
+      type: 'custom',
+      renderItem: (params, api) => {
+        const categoryIndex = api.value(0)
+        const start = api.coord([api.value(1), categoryIndex])
+        const end = api.coord([api.value(2), categoryIndex])
+        const height = api.size([0, 1])[1] * 0.6
+        
+        return {
+          type: 'rect',
+          shape: {
+            x: start[0],
+            y: start[1] - height / 2,
+            width: end[0] - start[0],
+            height: height
+          },
+          style: api.style()
+        }
+      },
+      data: data
+    }]
+  }
+  
+  chartInstance.setOption(option)
+}
+
+// 鑾峰彇鐢樼壒鍥鹃鑹�
+const getGanttColor = (status) => {
+  const colorMap = {
+    not_started: '#909399',
+    in_progress: '#E6A23C',
+    completed: '#67C23A',
+    delayed: '#F56C6C'
+  }
+  return colorMap[status] || '#909399'
+}
+
+// 鏇存柊鐘舵��
+const updateStatus = (project) => {
+  statusForm.id = project.id
+  statusForm.name = project.name
+  statusForm.status = project.status
+  statusForm.progress = project.progress
+  statusDialogVisible.value = true
+}
+
+// 纭鐘舵�佹洿鏂�
+const confirmStatusUpdate = () => {
+  const project = projectList.find(p => p.id === statusForm.id)
+  if (project) {
+    project.status = statusForm.status
+    project.progress = statusForm.progress
+    
+    // 鏇存柊缁熻鏁版嵁
+    updateStatusStats()
+    
+    // 濡傛灉鏄敇鐗瑰浘瑙嗗浘锛岄噸鏂版覆鏌�
+    if (viewMode.value === 'gantt') {
+      nextTick(() => {
+        initGanttChart()
+      })
+    }
+    
+    ElMessage.success('鐘舵�佹洿鏂版垚鍔�')
+  }
+  statusDialogVisible.value = false
+}
+
+// 鏇存柊鐘舵�佺粺璁�
+const updateStatusStats = () => {
+  const stats = {
+    not_started: 0,
+    in_progress: 0,
+    completed: 0,
+    delayed: 0
+  }
+  
+  projectList.forEach(project => {
+    stats[project.status]++
+  })
+  
+  statusStats[0].count = stats.not_started
+  statusStats[1].count = stats.in_progress
+  statusStats[2].count = stats.completed
+  statusStats[3].count = stats.delayed
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetails = (project) => {
+  ElMessage.info(`鏌ョ湅椤圭洰璇︽儏: ${project.name}`)
+}
+
+// 鍒锋柊鏁版嵁
+const refreshData = () => {
+  // 妯℃嫙瀹炴椂鏇存柊
+  const randomProject = projectList[Math.floor(Math.random() * projectList.length)]
+  if (randomProject.status === 'in_progress') {
+    randomProject.progress = Math.min(100, randomProject.progress + Math.floor(Math.random() * 10))
+    if (randomProject.progress === 100) {
+      randomProject.status = 'completed'
+    }
+  }
+  
+  updateStatusStats()
+  
+  if (viewMode.value === 'gantt') {
+    nextTick(() => {
+      initGanttChart()
+    })
+  }
+  
+  ElMessage.success('鏁版嵁宸插埛鏂�')
+}
+
+onMounted(() => {
+  updateStatusStats()
+})
+</script>
+
+<style scoped>
+.process-tracking {
+  padding: 20px;
+}
+
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.header h2 {
+  margin: 0;
+  color: #303133;
+}
+
+.status-cards {
+  margin-bottom: 20px;
+}
+
+.status-card {
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.status-card:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.card-content {
+  display: flex;
+  align-items: center;
+}
+
+.card-icon {
+  margin-right: 15px;
+  padding: 10px;
+  border-radius: 8px;
+}
+
+.status-card.not-started .card-icon {
+  background-color: #f4f4f5;
+  color: #909399;
+}
+
+.status-card.in-progress .card-icon {
+  background-color: #fdf6ec;
+  color: #E6A23C;
+}
+
+.status-card.completed .card-icon {
+  background-color: #f0f9ff;
+  color: #67C23A;
+}
+
+.status-card.delayed .card-icon {
+  background-color: #fef0f0;
+  color: #F56C6C;
+}
+
+.card-info {
+  flex: 1;
+}
+
+.card-title {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 5px;
+}
+
+.card-count {
+  font-size: 24px;
+  font-weight: bold;
+  color: #303133;
+}
+
+.project-list {
+  margin-top: 20px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.gantt-container {
+  min-height: 400px;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.9.3