| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <PageHeader content="ç产计å追踪è¿åº¦"> |
| | | </PageHeader> |
| | | <el-card style="height:82vh;overflow:auto;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>ç³è¯·åç¼å· - {{ rowData.applyNo || '' }}</span> |
| | | </div> |
| | | </template> |
| | | <!-- åºç¡ä¿¡æ¯ --> |
| | | <div class="detail-section"> |
| | | <h3 class="section-title">åºç¡ä¿¡æ¯</h3> |
| | | <el-descriptions :column="3" |
| | | border> |
| | | <el-descriptions-item label="ç³è¯·åç¼å·">{{ rowData.applyNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产ååç§°">{{ rowData.productName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产åè§æ ¼">{{ rowData.model || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç©æç¼ç ">{{ rowData.materialCode || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¸åæ°é">{{ rowData.assignedQuantity || 0 }} <span class="unit">æ¹</span></el-descriptions-item> |
| | | <el-descriptions-item label="å½åç¶æ"> |
| | | <el-tag :type="getStatusType(rowData.status)"> |
| | | {{ getStatusText(rowData.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | <div class="progress-container"> |
| | | <div class="progress-section"> |
| | | <h3 class="section-title">è¿åº¦ä¿¡æ¯</h3> |
| | | <div class="progress-item"> |
| | | <div class="progress-label">宿è¿åº¦ï¼</div> |
| | | <div class="progress-content"> |
| | | <el-progress :percentage="trackProgressForm.completionRate" |
| | | :color="customColors" |
| | | :status="trackProgressForm.completionRate === 100 ? 'success' : ''" /> |
| | | </div> |
| | | </div> |
| | | <div class="progress-item"> |
| | | <div class="progress-label">è¿åº¦è¯¦æ
ï¼</div> |
| | | <div class="progress-content"> |
| | | <el-table :data="trackProgressForm.progressDetails" |
| | | border |
| | | style="width: auto; height: 300px"> |
| | | <el-table-column prop="step" |
| | | label="æ¥éª¤ï¼ç¹å»æ¥ç详æ
ï¼" |
| | | align="center"> |
| | | <template #default="{ row, $index }"> |
| | | <el-link v-if="$index!=0" |
| | | @click="handleClickStep(row)" |
| | | type="primary">{{ row.step }}</el-link> |
| | | <span v-else |
| | | @click="handleClickStep(row)">{{ row.step }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" |
| | | label="ç¶æ" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'completed' ? 'success' : scope.row.status === 'processing' ? 'warning' : 'info'"> |
| | | {{ scope.row.status === 'completed' ? '已宿' : scope.row.status === 'processing' ? 'è¿è¡ä¸' : 'å¾
å¼å§' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="quantity" |
| | | label="æ°é" |
| | | align="center" /> |
| | | <el-table-column prop="startTime" |
| | | label="æ¶é´" |
| | | align="center" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="progress-section"> |
| | | <h3 class="section-title">订åä¿¡æ¯</h3> |
| | | <div v-for="item in rowData.orderList" |
| | | :key="item.orderNo" |
| | | class="order-item"> |
| | | <el-descriptions :column="3" |
| | | border> |
| | | <el-descriptions-item label="订åç¼å·">{{ item.orderNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="订åç¶æ"> |
| | | <el-tag :type="getStatusType(item.status)">{{ getStatusText(item.status) }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å¼å§æ¥æ">{{ item.startTime || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="éæ±æ°é">{{ item.quantity || 0 }} <span class="unit">æ¹</span></el-descriptions-item> |
| | | <el-descriptions-item label="宿æ°é">{{ item.completeQuantity || 0 }} <span class="unit">æ¹</span></el-descriptions-item> |
| | | <el-descriptions-item label="宿è¿åº¦"> |
| | | <el-progress :percentage="item.completionRate" |
| | | :color="customColors(item.completionRate)" |
| | | :status="item.completionRate === 100 ? 'success' : ''" |
| | | style="width: 120px;" /> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { useRouter, useRoute } from "vue-router"; |
| | | |
| | | const router = useRouter(); |
| | | const route = useRoute(); |
| | | |
| | | // è·¯ç±åæ°æ°æ® |
| | | const rowData = reactive({}); |
| | | |
| | | // 追踪è¿åº¦è¡¨åæ°æ® |
| | | const trackProgressForm = reactive({ |
| | | materialCode: "", |
| | | currentStatus: "", |
| | | completionRate: 0, |
| | | progressDetails: [], |
| | | remark: "", |
| | | }); |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = status => { |
| | | const typeMap = { |
| | | 0: "warning", |
| | | 1: "primary", |
| | | 2: "info", |
| | | }; |
| | | return typeMap[status] || "info"; |
| | | }; |
| | | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | 0: "å¾
ä¸å", |
| | | 1: "é¨åä¸å", |
| | | 2: "å·²ä¸å", |
| | | }; |
| | | return statusMap[status] || ""; |
| | | }; |
| | | const customColors = percentage => { |
| | | if (Number(percentage) < 10) { |
| | | return "#909399"; |
| | | } |
| | | if (Number(percentage) < 70) { |
| | | return "#e6a23c"; |
| | | } |
| | | return "#67c23a"; |
| | | }; |
| | | |
| | | // çææ¨¡æè¿åº¦è¯¦æ
æ°æ® |
| | | const generateProgressDetails = status => { |
| | | const details = [ |
| | | { |
| | | step: "计å确认", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "ç¬¬ä¸æ¬¡æ¥å·¥", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "ç¬¬äºæ¬¡æ¥å·¥", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "ç¬¬ä¸æ¬¡æ¥å·¥", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "ç¬¬åæ¬¡æ¥å·¥", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "ç¬¬äºæ¬¡æ¥å·¥", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "第å
次æ¥å·¥", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | { |
| | | step: "ç¬¬ä¸æ¬¡æ¥å·¥", |
| | | status: "completed", |
| | | quantity: 233, |
| | | startTime: "2026-03-01 09:00:00", |
| | | endTime: "2026-03-01 10:00:00", |
| | | }, |
| | | ]; |
| | | return details; |
| | | }; |
| | | |
| | | // 计ç®å®æç |
| | | const calculateCompletionRate = details => { |
| | | const completedSteps = details.filter( |
| | | step => step.status === "completed" |
| | | ).length; |
| | | return Math.round((completedSteps / details.length) * 100); |
| | | }; |
| | | |
| | | // å¤çè¿åº¦æ´æ° |
| | | const handleUpdateProgress = () => { |
| | | // è¿éå¯ä»¥æ·»å æ´æ°è¿åº¦çé»è¾ |
| | | ElMessage.success("è¿åº¦æ´æ°æå"); |
| | | }; |
| | | |
| | | // å¤çè¿å |
| | | const handleBack = () => { |
| | | router.push("/productionPlan/productionPlan"); |
| | | }; |
| | | |
| | | // çææ¨¡æè®¢åæ°æ® |
| | | const generateOrderList = () => { |
| | | return [ |
| | | { |
| | | orderNo: "ORD-2026-001", |
| | | status: 1, |
| | | quantity: 233.28, |
| | | completeQuantity: 14, |
| | | completionRate: 6, |
| | | startTime: "2026-03-25", |
| | | }, |
| | | { |
| | | orderNo: "ORD-2026-002", |
| | | status: 2, |
| | | quantity: 150.5, |
| | | completeQuantity: 100, |
| | | completionRate: 67, |
| | | startTime: "2026-03-20", |
| | | }, |
| | | { |
| | | orderNo: "ORD-2026-003", |
| | | status: 0, |
| | | quantity: 80.0, |
| | | completeQuantity: 0, |
| | | completionRate: 0, |
| | | startTime: "2026-03-30", |
| | | }, |
| | | ]; |
| | | }; |
| | | |
| | | // 页é¢å è½½æ¶è·åæ°æ® |
| | | onMounted(() => { |
| | | // ä»è·¯ç±åæ°ä¸è·åæ°æ® |
| | | const data = route.query.row |
| | | ? JSON.parse(decodeURIComponent(route.query.row)) |
| | | : null; |
| | | if (data) { |
| | | // èµå¼ç»rowData |
| | | Object.assign(rowData, data); |
| | | // èµå¼ç»è¡¨åæ°æ® |
| | | trackProgressForm.materialCode = data.materialCode; |
| | | trackProgressForm.currentStatus = data.status; |
| | | trackProgressForm.progressDetails = generateProgressDetails(data.status); |
| | | trackProgressForm.completionRate = calculateCompletionRate( |
| | | trackProgressForm.progressDetails |
| | | ); |
| | | trackProgressForm.remark = ""; |
| | | } |
| | | // çææ¨¡æè®¢åæ°æ® |
| | | rowData.orderList = generateOrderList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .action-buttons { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-top: 20px; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .detail-section { |
| | | margin-bottom: 24px; |
| | | background-color: #ffffff; |
| | | border-radius: 10px; |
| | | padding: 24px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .detail-section:hover { |
| | | box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12); |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | margin-bottom: 20px; |
| | | color: #1a1a1a; |
| | | border-bottom: 2px solid #409eff; |
| | | padding-bottom: 10px; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .section-title::before { |
| | | content: ""; |
| | | display: inline-block; |
| | | width: 4px; |
| | | height: 16px; |
| | | background-color: #409eff; |
| | | margin-right: 8px; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .unit { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | :deep(.el-descriptions) { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | :deep(.el-descriptions__row:nth-child(odd)) { |
| | | background-color: #f9f9f9; |
| | | } |
| | | |
| | | :deep(.el-descriptions__label) { |
| | | font-weight: 500; |
| | | color: #606266; |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | :deep(.el-descriptions__content) { |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .progress-container { |
| | | display: flex; |
| | | gap: 24px; |
| | | } |
| | | |
| | | .progress-section { |
| | | margin-bottom: 24px; |
| | | background-color: #ffffff; |
| | | border-radius: 10px; |
| | | padding: 24px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08); |
| | | flex: 1; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .progress-section:hover { |
| | | box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12); |
| | | } |
| | | |
| | | .progress-item { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .progress-label { |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #606266; |
| | | margin-bottom: 12px; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .progress-content { |
| | | margin-left: 0; |
| | | } |
| | | |
| | | .progress-content .el-table { |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | border-radius: 8px; |
| | | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | :deep(.el-table th) { |
| | | background-color: #f5f7fa !important; |
| | | font-weight: 600; |
| | | color: #606266; |
| | | } |
| | | |
| | | :deep(.el-table tr:hover) { |
| | | background-color: #f5f7fa !important; |
| | | } |
| | | |
| | | .order-item { |
| | | margin-bottom: 20px; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .order-item:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | :deep(.el-progress-bar__inner) { |
| | | border-radius: 10px; |
| | | } |
| | | |
| | | :deep(.el-tag) { |
| | | border-radius: 12px; |
| | | padding: 2px 10px; |
| | | } |
| | | </style> |