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