| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <PageHeader content="生产计划追踪进度"> |
| | | <PageHeader v-if="applyNo" |
| | | content="生产计划追踪进度"> |
| | | </PageHeader> |
| | | <el-card style="height:82vh;overflow:auto;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>申请单编号 - {{ rowData.applyNo || '' }}</span> |
| | | <el-form :inline="true" |
| | | :model="searchForm" |
| | | class="search-form"> |
| | | <el-form-item label="申请单编号"> |
| | | <el-select v-model="selectedApplyNo" |
| | | filterable |
| | | remote |
| | | reserve-keyword |
| | | placeholder="请输入申请单编号" |
| | | :loading="applyNoLoading" |
| | | :remote-method="handleApplyNoSearch" |
| | | @change="handleSearch" |
| | | style="width: 400px;"> |
| | | <el-option v-for="option in applyNoOptions" |
| | | :key="option.id" |
| | | :label="option.applyNo+'-'+option.productName+'-'+option.model" |
| | | :value="option.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | </template> |
| | | <!-- 基础信息 --> |
| | | <div class="detail-section"> |
| | | <div v-if="rowData.productionPlanDto" |
| | | 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> |
| | | <el-skeleton :loading="loading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 80%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 60%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 40%" /> |
| | | </template> |
| | | <el-descriptions :column="3" |
| | | border> |
| | | <el-descriptions-item label="申请单编号">{{ rowData.productionPlanDto?.applyNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产品名称">{{ rowData.productionPlanDto?.productName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产品规格">{{ rowData.productionPlanDto?.model || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="物料编码">{{ rowData.productionPlanDto?.materialCode || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="下发数量">{{ rowData.productionPlanDto?.assignedQuantity || 0 }} <span class="unit">方</span></el-descriptions-item> |
| | | <el-descriptions-item label="当前状态"> |
| | | <el-tag :type="getStatusType(rowData.productionPlanDto?.status)"> |
| | | {{ getStatusText(rowData.productionPlanDto?.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-skeleton> |
| | | </div> |
| | | <div class="progress-container"> |
| | | <el-empty v-else |
| | | description="请搜索申请单编号" /> |
| | | <div v-if="rowData.orderDtoList" |
| | | class="progress-container"> |
| | | <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> |
| | | <el-table :data="trackProgressForm.progressDetails" |
| | | border |
| | | style="width: auto; height: 200px"> |
| | | <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-columnstep prop="startTime" |
| | | label="时间" |
| | | align="center" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | <!-- <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" |
| | | <el-skeleton :loading="loading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 80%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 60%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 40%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <div v-for="(item, index) in rowData.orderDtoList" |
| | | :key="item.productOrderDto?.npsNo || index" |
| | | class="order-item"> |
| | | <el-descriptions :column="3" |
| | | border> |
| | | <el-descriptions-item label="订单编号">{{ item.productOrderDto?.npsNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="开始日期">{{ item.productOrderDto?.startTime || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="完成进度"> |
| | | <el-progress :percentage="item.productOrderDto?.completionStatus" |
| | | :color="customColors(item.productOrderDto?.completionStatus)" |
| | | :status="item.productOrderDto?.completionStatus === 100 ? 'success' : ''" |
| | | style="width: 120px;" /> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="需求数量">{{ item.productOrderDto?.quantity || 0 }} <span class="unit">方</span></el-descriptions-item> |
| | | <el-descriptions-item label="完成数量">{{ item.productOrderDto?.completeQuantity || 0 }} <span class="unit">方</span></el-descriptions-item> |
| | | <el-descriptions-item label="剩余数量">{{ (item.productOrderDto?.quantity - item.productOrderDto?.completeQuantity) || 0 }} <span class="unit">方</span></el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-table :data="item.productionProductMainDtos" |
| | | border |
| | | style="width: auto; height: 300px"> |
| | | style="width: auto; max-height: 200px"> |
| | | <el-table-column prop="step" |
| | | label="步骤(点击查看详情)" |
| | | 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 #default="{ row }"> |
| | | <el-link @click="handleClickStep(row)" |
| | | type="primary">{{ row.productNo }}</el-link> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="quantity" |
| | | label="数量" |
| | | label="数量(方)" |
| | | align="center" /> |
| | | <el-table-column prop="startTime" |
| | | <el-table-column prop="reportingTime" |
| | | label="时间" |
| | | align="center" /> |
| | | <el-table-column prop="schedule" |
| | | label="班次" |
| | | align="center" /> |
| | | <el-table-column prop="postName" |
| | | label="岗位人员" |
| | | align="center" /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | </el-skeleton> |
| | | </div> |
| | | </div> |
| | | <el-empty v-else-if="rowData.productionPlanDto" |
| | | description="暂无进度" /> |
| | | </el-card> |
| | | <!-- 生产报工详情弹窗 --> |
| | | <el-dialog v-model="detailDialogVisible" |
| | | :title="'生产报工详情'" |
| | | width="1000px" |
| | | :close-on-click-modal="false" |
| | | custom-class="custom-dialog"> |
| | | <div class="detail-container"> |
| | | <el-skeleton :loading="dialogLoading" |
| | | animated> |
| | | <template #template> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 80%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 60%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 40%" /> |
| | | <el-skeleton-item variant="h3" |
| | | style="width: 50%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 100%" /> |
| | | <el-skeleton-item variant="p" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <!-- 基础信息 --> |
| | | <div class="detail-section"> |
| | | <h3 class="section-title">基础信息</h3> |
| | | <el-descriptions :column="3" |
| | | border> |
| | | <el-descriptions-item label="生产订单号">{{ detailData.npsNo || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="班组"><el-tag :type="detailData.schedule == '白班' ? 'primary' : 'warning'">{{ detailData.schedule || '-' }}</el-tag></el-descriptions-item> |
| | | <el-descriptions-item label="岗位人员">{{ detailData.postName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产品编码">{{ detailData.materialCode || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="产品名称">{{ detailData.productName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="规格">{{ detailData.model || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="合格数量"><span class="num2">{{ detailData.qualifiedQuantity || 0 }}</span> <span class="unit">方</span></el-descriptions-item> |
| | | <el-descriptions-item label="不合格数量"><span class="num3">{{ detailData.unqualifiedQuantity || 0 }}</span> <span class="unit">方</span></el-descriptions-item> |
| | | <el-descriptions-item label="总数量"><span class="num1">{{ detailData.quantity || 0 }}</span> <span class="unit">方</span></el-descriptions-item> |
| | | <el-descriptions-item label="报工时间">{{ formatTime(detailData.reportingTime) }}</el-descriptions-item> |
| | | <el-descriptions-item label="创建时间">{{ formatTime(detailData.createTime) }}</el-descriptions-item> |
| | | <el-descriptions-item label="更新时间">{{ formatTime(detailData.updateTime) }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | <!-- 工序信息 --> |
| | | <div class="detail-section" |
| | | v-if="detailData.productionProductRouteItemDtoList && detailData.productionProductRouteItemDtoList.length > 0"> |
| | | <h3 class="section-title">工序信息</h3> |
| | | <div v-for="(process) in detailData.productionProductRouteItemDtoList" |
| | | :key="process.id" |
| | | class="process-item"> |
| | | <div class="process-header"> |
| | | <h4 class="process-title">{{ process.processName || '-' }}</h4> |
| | | <div class="process-info"> |
| | | <span class="process-label">岗位人员:{{ process.postName || '-' }}</span> |
| | | <span class="process-label">工序ID:{{ process.processNo || '-' }}</span> |
| | | </div> |
| | | </div> |
| | | <!-- 工序基本信息 --> |
| | | <div class="process-details"> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="设备异常情况">{{ process.equipmentMalfunction || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="当班设备处置">{{ process.equipmentDisposal || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="工艺人员交待" |
| | | :span="2">{{ process.processExplained || '-' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | <!-- 工序参数 --> |
| | | <div v-if="process.productionProductRouteItemParamDtoList && process.productionProductRouteItemParamDtoList.length > 0"> |
| | | <!-- BOM信息 --> |
| | | <div class="param-section" |
| | | v-if="getBomList(process.productionProductRouteItemParamDtoList).length > 0"> |
| | | <h5 class="param-title">投入品信息</h5> |
| | | <el-table :data="getBomList(process.productionProductRouteItemParamDtoList)" |
| | | style="width: 100%" |
| | | size="small"> |
| | | <el-table-column prop="paramName" |
| | | label="产品名称" |
| | | min-width="120"></el-table-column> |
| | | <el-table-column prop="model" |
| | | label="规格型号" |
| | | min-width="120"></el-table-column> |
| | | <el-table-column prop="productValue" |
| | | label="投入量" |
| | | min-width="100"></el-table-column> |
| | | <el-table-column prop="unit" |
| | | label="单位" |
| | | width="80"></el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <!-- 参数信息 --> |
| | | <div class="param-section" |
| | | v-if="getParamList(process.productionProductRouteItemParamDtoList).length > 0"> |
| | | <h5 class="param-title">生产记录</h5> |
| | | <el-card v-for="group in getParamGroups(process.productionProductRouteItemParamDtoList)" |
| | | :key="group.sourceSort" |
| | | class="detail-card" |
| | | style="margin-top: 10px;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span v-if="Object.keys(getParamGroups(process.productionProductRouteItemParamDtoList)).length > 1">生产记录组 - {{ group.sourceSort }}</span> |
| | | <span v-else>生产记录</span> |
| | | </div> |
| | | </template> |
| | | <el-table :data="group.items" |
| | | style="width: 100%" |
| | | :row-class-name="rowClassName"> |
| | | <el-table-column prop="paramName" |
| | | label="指标" /> |
| | | <el-table-column prop="unit" |
| | | label="单位" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.unit || "/" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="standardText" |
| | | label="标准值" /> |
| | | <el-table-column prop="paramValue" |
| | | label="实际值" /> |
| | | <el-table-column prop="result" |
| | | label="结果" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.result === '合格' ? 'success' : 'danger'"> |
| | | {{ scope.row.result }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | <!-- 上传文件 --> |
| | | <div class="file-section" |
| | | v-if="process.fileList && process.fileList.length > 0"> |
| | | <h5 class="file-title">上传文件</h5> |
| | | <div class="file-grid"> |
| | | <div v-for="file in process.fileList" |
| | | :key="file.id" |
| | | class="file-item"> |
| | | <el-image style="width: 100px; height: 100px" |
| | | v-if="file.fileUrl" |
| | | :src="baseUrl + file.fileUrl" |
| | | :zoom-rate="1.2" |
| | | :max-scale="7" |
| | | :alt="file.fileName" |
| | | :min-scale="0.2" |
| | | :preview-src-list="formatFileList(process.fileList)" |
| | | show-progress |
| | | :initial-index="4" |
| | | fit="cover" /> |
| | | <div class="file-info"> |
| | | <span class="file-name">{{ file.fileName || '-' }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-skeleton> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { useRouter, useRoute } from "vue-router"; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | trackProgressByNo, |
| | | productionPlanListPage, |
| | | } from "@/api/productionPlan/productionPlan"; |
| | | import { productionReportDetail } from "@/api/productionManagement/productionReporting.js"; |
| | | |
| | | const router = useRouter(); |
| | | const route = useRoute(); |
| | |
| | | progressDetails: [], |
| | | remark: "", |
| | | }); |
| | | |
| | | // 生产报工详情弹窗 |
| | | const detailDialogVisible = ref(false); |
| | | const detailData = ref({}); |
| | | const baseUrl = import.meta.env.VITE_APP_BASE_API; |
| | | |
| | | // 加载状态 |
| | | const loading = ref(false); |
| | | // 弹窗加载状态 |
| | | const dialogLoading = ref(false); |
| | | |
| | | // 申请单下拉框数据 |
| | | const applyNoOptions = ref([]); |
| | | const applyNoLoading = ref(false); |
| | | const applyNoQuery = ref(""); |
| | | const selectedApplyNo = ref(null); |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = status => { |
| | |
| | | router.push("/productionPlan/productionPlan"); |
| | | }; |
| | | |
| | | // 处理申请单编号搜索 |
| | | const handleApplyNoSearch = query => { |
| | | if (query) { |
| | | applyNoLoading.value = true; |
| | | productionPlanListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | applyNo: query, |
| | | }) |
| | | .then(res => { |
| | | // 转换数据格式为下拉框所需的格式 |
| | | applyNoOptions.value = res.data.records; |
| | | }) |
| | | .catch(error => { |
| | | console.error(error); |
| | | }) |
| | | .finally(() => { |
| | | applyNoLoading.value = false; |
| | | }); |
| | | } else { |
| | | applyNoOptions.value = []; |
| | | } |
| | | }; |
| | | |
| | | // 处理搜索 |
| | | const handleSearch = () => { |
| | | if (!selectedApplyNo.value) { |
| | | ElMessage.warning("请选择申请单编号"); |
| | | return; |
| | | } |
| | | // 调用API获取数据 |
| | | loading.value = true; |
| | | trackProgressByNo({ productionPlanId: selectedApplyNo.value }) |
| | | .then(res => { |
| | | console.log(res, "搜索结果"); |
| | | // 合并数据到rowData |
| | | Object.assign(rowData, res.data); |
| | | ElMessage.success("搜索成功"); |
| | | }) |
| | | .catch(error => { |
| | | ElMessage.error("搜索失败,请稍后重试"); |
| | | console.error(error); |
| | | }) |
| | | .finally(() => { |
| | | loading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 生成模拟订单数据 |
| | | const generateOrderList = () => { |
| | | return [ |
| | |
| | | status: 1, |
| | | quantity: 233.28, |
| | | completeQuantity: 14, |
| | | remainingQuantity: 149.28, |
| | | completionRate: 6, |
| | | startTime: "2026-03-25", |
| | | }, |
| | |
| | | status: 2, |
| | | quantity: 150.5, |
| | | completeQuantity: 100, |
| | | remainingQuantity: 50.5, |
| | | completionRate: 67, |
| | | startTime: "2026-03-20", |
| | | }, |
| | |
| | | status: 0, |
| | | quantity: 80.0, |
| | | completeQuantity: 0, |
| | | remainingQuantity: 80.0, |
| | | completionRate: 0, |
| | | startTime: "2026-03-30", |
| | | }, |
| | | ]; |
| | | }; |
| | | |
| | | // 处理点击步骤查看详情 |
| | | const handleClickStep = row => { |
| | | // 获取报工详情数据 |
| | | dialogLoading.value = true; |
| | | productionReportDetail(row.id) |
| | | .then(res => { |
| | | console.log(res, "报工详情"); |
| | | // 将API返回的数据赋值给detailData |
| | | detailData.value = res.data; |
| | | // 打开弹窗 |
| | | detailDialogVisible.value = true; |
| | | }) |
| | | .catch(error => { |
| | | ElMessage.error("获取报工详情失败,请稍后重试"); |
| | | console.error(error); |
| | | }) |
| | | .finally(() => { |
| | | dialogLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 格式化时间 |
| | | const formatTime = time => { |
| | | return time ? dayjs(time).format("YYYY-MM-DD HH:mm:ss") : "-"; |
| | | }; |
| | | |
| | | // 格式化文件列表 |
| | | const formatFileList = fileList => { |
| | | return fileList.map(file => ({ |
| | | name: file.fileName, |
| | | url: baseUrl + file.fileUrl, |
| | | size: file.fileSize, |
| | | })); |
| | | }; |
| | | |
| | | // 获取BOM列表 |
| | | const getBomList = paramList => { |
| | | return paramList.filter(item => item.bomId); |
| | | }; |
| | | |
| | | // 获取参数列表 |
| | | const getParamList = paramList => { |
| | | return paramList.filter(item => !item.bomId); |
| | | }; |
| | | |
| | | // 按sourceSort分组参数 |
| | | const getParamGroups = paramList => { |
| | | const params = getParamList(paramList); |
| | | const groups = {}; |
| | | |
| | | params.forEach(param => { |
| | | const sort = param.sourceSort || 1; |
| | | if (!groups[sort]) { |
| | | groups[sort] = []; |
| | | } |
| | | // 计算结果 |
| | | let result = "合格"; |
| | | let standardText = ""; |
| | | if (param.valueMode === 1) { |
| | | // 单值比较 |
| | | if (param.standardValue !== null && param.standardValue !== undefined) { |
| | | standardText = param.standardValue; |
| | | if (param.paramValue !== param.standardValue) { |
| | | result = "不合格"; |
| | | } |
| | | } else { |
| | | standardText = "-"; |
| | | result = "合格"; |
| | | } |
| | | } else if (param.valueMode === 2) { |
| | | // 区间比较 |
| | | if (param.minValue !== null || param.maxValue !== null) { |
| | | standardText = |
| | | (param.minValue ? param.minValue : "-∞") + |
| | | "~" + |
| | | (param.maxValue ? param.maxValue : "+∞"); |
| | | if ( |
| | | param.paramValue < param.minValue || |
| | | param.paramValue > param.maxValue |
| | | ) { |
| | | result = "不合格"; |
| | | } |
| | | } else { |
| | | standardText = "-"; |
| | | result = "合格"; |
| | | } |
| | | } else { |
| | | // 默认情况 |
| | | standardText = "-"; |
| | | result = "合格"; |
| | | } |
| | | groups[sort].push({ |
| | | ...param, |
| | | standardText, |
| | | result, |
| | | }); |
| | | }); |
| | | |
| | | // 转换为数组格式 |
| | | return Object.entries(groups).map(([key, items]) => ({ |
| | | sourceSort: key, |
| | | items, |
| | | })); |
| | | }; |
| | | |
| | | // 为不合格的行添加样式 |
| | | const rowClassName = ({ row }) => { |
| | | return row.result === "不合格" ? "warning-row" : ""; |
| | | }; |
| | | |
| | | const applyNo = ref(null); |
| | | // 页面加载时获取数据 |
| | | onMounted(() => { |
| | | // 从路由参数中获取数据 |
| | | const data = route.query.row |
| | | ? JSON.parse(decodeURIComponent(route.query.row)) |
| | | selectedApplyNo.value = route.query.applyNo |
| | | ? route.query.applyNo + |
| | | "-" + |
| | | route.query.productName + |
| | | "-" + |
| | | route.query.model |
| | | : 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 = ""; |
| | | |
| | | if (route.query.id) { |
| | | loading.value = true; |
| | | trackProgressByNo({ productionPlanId: route.query.id }) |
| | | .then(res => { |
| | | console.log(res, "追踪进度"); |
| | | // 合并数据到rowData |
| | | Object.assign(rowData, res.data); |
| | | }) |
| | | .finally(() => { |
| | | loading.value = false; |
| | | }); |
| | | } |
| | | // 生成模拟订单数据 |
| | | rowData.orderList = generateOrderList(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .search-form { |
| | | width: 100%; |
| | | } |
| | | |
| | | .search-form .el-form-item { |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .action-buttons { |
| | |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08); |
| | | flex: 1; |
| | | transition: all 0.3s ease; |
| | | width: 100%; |
| | | } |
| | | |
| | | .progress-section:hover { |
| | |
| | | border-radius: 12px; |
| | | padding: 2px 10px; |
| | | } |
| | | |
| | | /* 弹窗样式 */ |
| | | .detail-container { |
| | | max-height: 600px; |
| | | overflow-y: auto; |
| | | padding: 0 16px; |
| | | } |
| | | |
| | | .process-item { |
| | | margin-bottom: 24px; |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-radius: 8px; |
| | | border: 1px solid #ebeef5; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .process-header { |
| | | margin-bottom: 20px; |
| | | padding-bottom: 12px; |
| | | border-bottom: 1px solid #f0f2f5; |
| | | } |
| | | |
| | | .process-title { |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | margin-bottom: 12px; |
| | | color: #1a1a1a; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .process-title::before { |
| | | content: ""; |
| | | display: inline-block; |
| | | width: 4px; |
| | | height: 16px; |
| | | background-color: #409eff; |
| | | margin-right: 8px; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .process-info { |
| | | display: flex; |
| | | gap: 20px; |
| | | font-size: 13px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .process-label { |
| | | padding: 4px 12px; |
| | | background-color: #ecf5ff; |
| | | border-radius: 4px; |
| | | color: #409eff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .process-details { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .param-section { |
| | | margin-bottom: 20px; |
| | | background-color: #f9f9f9; |
| | | border-radius: 6px; |
| | | padding: 16px; |
| | | border: 1px solid #f0f2f5; |
| | | } |
| | | |
| | | .param-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | margin-bottom: 14px; |
| | | color: #1a1a1a; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .file-section { |
| | | margin-top: 20px; |
| | | background-color: #f9f9f9; |
| | | border-radius: 6px; |
| | | padding: 16px; |
| | | border: 1px solid #f0f2f5; |
| | | } |
| | | |
| | | .file-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | margin-bottom: 14px; |
| | | color: #1a1a1a; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .file-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); |
| | | gap: 16px; |
| | | } |
| | | |
| | | .file-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | background-color: #ffffff; |
| | | border: 1px solid #e8e8e8; |
| | | border-radius: 6px; |
| | | padding: 10px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .file-item:hover { |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); |
| | | border-color: #409eff; |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | .file-info { |
| | | width: 100%; |
| | | text-align: center; |
| | | } |
| | | |
| | | .file-name { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | word-break: break-all; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | .param-group { |
| | | margin-bottom: 16px; |
| | | padding: 14px; |
| | | background-color: #ffffff; |
| | | border-radius: 6px; |
| | | border: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .group-header { |
| | | margin-bottom: 12px; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #f0f2f5; |
| | | } |
| | | |
| | | .num1 { |
| | | color: #1107cc; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .num2 { |
| | | color: #0fcf25; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .num3 { |
| | | color: #d31818; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .group-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .param-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| | | gap: 16px; |
| | | } |
| | | |
| | | .param-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 8px 0; |
| | | border-bottom: 1px solid #f5f7fa; |
| | | } |
| | | |
| | | .param-item:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .param-label { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | min-width: 100px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .param-value { |
| | | font-size: 13px; |
| | | color: #1a1a1a; |
| | | font-weight: 600; |
| | | flex: 1; |
| | | } |
| | | |
| | | .param-unit { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | background-color: #f0f2f5; |
| | | padding: 2px 6px; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: center; |
| | | padding: 20px; |
| | | border-top: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .dialog-footer .el-button { |
| | | min-width: 100px; |
| | | padding: 8px 20px; |
| | | } |
| | | |
| | | /* 自定义对话框样式 */ |
| | | :deep(.custom-dialog) { |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | :deep(.custom-dialog .el-dialog__header) { |
| | | background-color: #f5f7fa; |
| | | padding: 20px; |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | :deep(.custom-dialog .el-dialog__title) { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #1a1a1a; |
| | | } |
| | | |
| | | :deep(.custom-dialog .el-dialog__body) { |
| | | padding: 20px; |
| | | } |
| | | |
| | | /* 表格样式优化 */ |
| | | :deep(.el-table) { |
| | | border-radius: 6px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | :deep(.el-table th) { |
| | | background-color: #f5f7fa; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | :deep(.el-table tr:hover > td) { |
| | | background-color: #ecf5ff !important; |
| | | } |
| | | |
| | | /* 描述列表样式优化 */ |
| | | :deep(.el-descriptions) { |
| | | border-radius: 6px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | :deep(.el-descriptions__label) { |
| | | font-weight: 500; |
| | | color: #606266; |
| | | } |
| | | |
| | | :deep(.el-descriptions__content) { |
| | | color: #1a1a1a; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | /* 不合格行样式 */ |
| | | :deep(.el-table .warning-row) { |
| | | background-color: #fef0f0 !important; |
| | | } |
| | | |
| | | .detail-card { |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | .card-header { |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | </style> |