| | |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <div style="font-weight: 600; margin-bottom: 8px;">配置</div> |
| | | <div style="font-size: 12px; margin-bottom: 8px;"> |
| | | <span v-if="boundRouteName" style="color: #67c23a;">已绑定:{{ boundRouteName }}</span> |
| | | <span v-else style="color: #e6a23c;">未绑定</span> |
| | | <div class="dialog-topbar"> |
| | | <div> |
| | | <div style="font-weight: 600; margin-bottom: 8px;">配置</div> |
| | | <div style="font-size: 12px; margin-bottom: 8px;"> |
| | | <span v-if="boundRouteName" style="color: #67c23a;">已绑定:{{ boundRouteName }}</span> |
| | | <span v-else style="color: #e6a23c;">未绑定</span> |
| | | </div> |
| | | </div> |
| | | <div class="export-toolbar"> |
| | | <el-date-picker |
| | | v-model="exportDateRange" |
| | | type="daterange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | clearable |
| | | style="width: 280px;" |
| | | /> |
| | | <el-button type="success" plain @click="exportSelectedSteps">导出已勾选</el-button> |
| | | </div> |
| | | </div> |
| | | <el-select |
| | | v-model="selectedRouteId" |
| | |
| | | class="process-diagram-segment" |
| | | > |
| | | <div class="process-diagram-node"> |
| | | <el-checkbox |
| | | v-model="step.checked" |
| | | class="process-diagram-checkbox" |
| | | @change="() => handleStepCheckedChange(step)" |
| | | /> |
| | | <div class="process-diagram-index">{{ idx + 1 }}</div> |
| | | <div class="process-diagram-name">{{ step.processName }}</div> |
| | | </div> |
| | |
| | | const routeList = ref([]); |
| | | const selectedRouteId = ref(null); |
| | | const steps = ref([]); |
| | | const exportDateRange = ref([]); |
| | | const saving = ref(false); |
| | | |
| | | const normalizeStepsFromApi = (list) => { |
| | |
| | | processId: s.processId ?? s.process_id ?? s.id ?? null, |
| | | processName: s.processName ?? s.process_name ?? s.name ?? "", |
| | | sortNo: s.sortNo ?? idx + 1, |
| | | isCompleted: Number(s.isCompleted ?? s.completed ?? 0), |
| | | checked: Boolean(s.checked ?? false), |
| | | })); |
| | | }; |
| | | |
| | |
| | | await fetchRouteSteps(selectedRouteId.value); |
| | | }; |
| | | |
| | | const handleStepCheckedChange = step => { |
| | | step.checked = Boolean(step.checked); |
| | | }; |
| | | |
| | | const handleClose = () => { |
| | | emit("update:visible", false); |
| | | saving.value = false; |
| | |
| | | } finally { |
| | | saving.value = false; |
| | | } |
| | | }; |
| | | |
| | | const exportSelectedSteps = () => { |
| | | const selectedSteps = steps.value.filter(step => step.checked); |
| | | if (selectedSteps.length === 0) { |
| | | proxy?.$modal?.msgWarning?.("请先勾选要导出的工序"); |
| | | return; |
| | | } |
| | | const payload = { |
| | | exportDateRange: Array.isArray(exportDateRange.value) ? exportDateRange.value : [], |
| | | routeId: selectedRouteId.value, |
| | | routeName: routeList.value.find(item => String(item.routeId) === String(selectedRouteId.value))?.processRouteName || "", |
| | | steps: selectedSteps.map(step => ({ |
| | | processId: step.processId, |
| | | processName: step.processName, |
| | | sortNo: step.sortNo, |
| | | isCompleted: Number(step.isCompleted ?? 0), |
| | | })), |
| | | }; |
| | | const blob = new Blob([JSON.stringify(payload, null, 2)], { |
| | | type: "application/json;charset=utf-8", |
| | | }); |
| | | const url = URL.createObjectURL(blob); |
| | | const a = document.createElement("a"); |
| | | const dateText = |
| | | Array.isArray(exportDateRange.value) && exportDateRange.value.length === 2 |
| | | ? `${exportDateRange.value[0]}_${exportDateRange.value[1]}` |
| | | : "all"; |
| | | a.href = url; |
| | | a.download = `工艺路线导出_${dateText}.json`; |
| | | a.click(); |
| | | URL.revokeObjectURL(url); |
| | | }; |
| | | </script> |
| | | |
| | |
| | | padding: 10px 12px; |
| | | margin-right: 10px; |
| | | box-sizing: border-box; |
| | | position: relative; |
| | | } |
| | | |
| | | .process-diagram-checkbox { |
| | | position: absolute; |
| | | top: 8px; |
| | | right: 8px; |
| | | } |
| | | |
| | | .process-diagram-index { |
| | |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | </style> |
| | | |
| | | .dialog-topbar { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .export-toolbar { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | flex-wrap: wrap; |
| | | } |
| | | </style> |