| | |
| | | <text class="progress-text">{{ item.completeQuantity || 0 }} / {{ item.quantity || 0 }}</text> |
| | | </view> |
| | | </view> |
| | | <!-- 工序生产进度展示 --> |
| | | <view class="detail-row process-row"> |
| | | <text class="detail-label">工序进度</text> |
| | | <scroll-view scroll-x |
| | | class="process-scroll"> |
| | | <view class="process-container"> |
| | | <view v-for="(process, pIdx) in item.processRouteStatus" |
| | | :key="pIdx" |
| | | class="process-item"> |
| | | <view class="process-node"> |
| | | <view class="node-circle" |
| | | :class="{ 'is-complete': process.percentage >= 100 }"> |
| | | <text class="node-percentage" |
| | | :style="{ color: process.percentage >= 100 ? '#52c41a' : (process.percentage >= 70 ? '#f56c6c' : '#3c9cff') }">{{ process.percentage }}%</text> |
| | | </view> |
| | | <text class="node-name">{{ process.name }}</text> |
| | | </view> |
| | | <view v-if="pIdx < item.processRouteStatus.length - 1" |
| | | class="node-line"></view> |
| | | </view> |
| | | <view v-if="!item.processRouteStatus || !item.processRouteStatus.length" |
| | | class="no-process">-</view> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">计划完成</text> |
| | | <text class="detail-value">{{ formatDate(item.planCompleteTime) }}</text> |
| | |
| | | productOrderListPage, |
| | | getOrderProcessRouteMain, |
| | | } from "@/api/productionManagement/productionOrder.js"; |
| | | import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | |
| | | 2: "warning", |
| | | 3: "success", |
| | | 4: "info", |
| | | 5: "danger", |
| | | 5: "error", |
| | | }; |
| | | return typeMap[status] || "info"; |
| | | }; |
| | |
| | | }; |
| | | |
| | | productOrderListPage(params) |
| | | .then(res => { |
| | | loading.value = false; |
| | | .then(async res => { |
| | | const records = res.data.records || []; |
| | | |
| | | // 为每个订单并行查询工序进度 |
| | | const processPromises = records.map(async item => { |
| | | if (item.npsNo) { |
| | | try { |
| | | const workOrderRes = await productWorkOrderPage({ |
| | | npsNo: item.npsNo, |
| | | size: 100, |
| | | }); |
| | | const workOrders = workOrderRes.data.records || []; |
| | | const processRouteStatus = workOrders.map(wo => ({ |
| | | name: wo.operationName || "未知工序", |
| | | percentage: |
| | | Number(wo.completionStatus) > 100 |
| | | ? 100 |
| | | : Number(wo.completionStatus || 0), |
| | | })); |
| | | return { ...item, processRouteStatus }; |
| | | } catch (error) { |
| | | console.error(`获取工单 ${item.npsNo} 进度失败:`, error); |
| | | return { ...item, processRouteStatus: [] }; |
| | | } |
| | | } |
| | | return { ...item, processRouteStatus: [] }; |
| | | }); |
| | | |
| | | const updatedRecords = await Promise.all(processPromises); |
| | | |
| | | loading.value = false; |
| | | if (page.current === 1) { |
| | | tableData.value = records; |
| | | tableData.value = updatedRecords; |
| | | } else { |
| | | tableData.value = [...tableData.value, ...records]; |
| | | tableData.value = [...tableData.value, ...updatedRecords]; |
| | | } |
| | | |
| | | if (records.length < page.size) { |
| | | if (updatedRecords.length < page.size) { |
| | | loadStatus.value = "nomore"; |
| | | } else { |
| | | loadStatus.value = "loadmore"; |
| | |
| | | text-align: right; |
| | | } |
| | | } |
| | | |
| | | &.process-row { |
| | | flex-direction: column; |
| | | margin: 20rpx 0; |
| | | |
| | | .process-scroll { |
| | | width: 100%; |
| | | margin-top: 16rpx; |
| | | |
| | | .process-container { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | padding: 10rpx 0; |
| | | min-height: 120rpx; |
| | | |
| | | .process-item { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .process-node { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | width: 100rpx; |
| | | |
| | | .node-circle { |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | border-radius: 50%; |
| | | border: 2rpx solid #3c9cff; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: #fff; |
| | | margin-bottom: 8rpx; |
| | | |
| | | .node-percentage { |
| | | font-size: 18rpx; |
| | | color: #3c9cff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | &.is-complete { |
| | | border-color: #52c41a; |
| | | background: #f6ffed; |
| | | |
| | | .node-percentage { |
| | | color: #52c41a; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .node-name { |
| | | font-size: 20rpx; |
| | | color: #666; |
| | | text-align: center; |
| | | width: 120rpx; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | } |
| | | |
| | | .node-line { |
| | | width: 40rpx; |
| | | height: 2rpx; |
| | | background: #e8e8e8; |
| | | margin: -30rpx 0 0 0; |
| | | } |
| | | } |
| | | |
| | | .no-process { |
| | | font-size: 24rpx; |
| | | color: #ccc; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |