| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="localVisible" |
| | | :title="dialogTitle" |
| | | width="800px" |
| | | @close="handleClose"> |
| | | <!-- æ¥éª¤æ¡ --> |
| | | <el-steps :active="activeStep" |
| | | finish-status="success"> |
| | | <el-step title="éæ©ç产订å" /> |
| | | <el-step title="å¡«ååºç¡ä¿¡æ¯" /> |
| | | <el-step title="æ¥çå·¥åºåæ°" /> |
| | | <el-step title="å¡«å产éä¿¡æ¯" /> |
| | | </el-steps> |
| | | <!-- ç¬¬ä¸æ¥ï¼éæ©ç产订å --> |
| | | <div v-if="activeStep === 0"> |
| | | <el-form :model="form" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="ç产订å" |
| | | prop="orderId" |
| | | required> |
| | | <el-select v-model="orderId" |
| | | placeholder="è¯·éæ©ç产订å" |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | :loading="orderLoading" |
| | | @change="handleOrderChange"> |
| | | <el-option v-for="order in orderList" |
| | | :key="order.id" |
| | | :label="`${order.npsNo} - ${order.productName} ${order.model}`" |
| | | :value="order.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <!-- ç¬¬äºæ¥ï¼å¡«ååºç¡ä¿¡æ¯ --> |
| | | <div v-else-if="activeStep === 1"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="ç产订åå·" |
| | | prop="npsNo"> |
| | | <el-input disabled |
| | | v-model="form.npsNo" /> |
| | | </el-form-item> |
| | | <el-form-item label="çç»" |
| | | prop="teamName" |
| | | required> |
| | | <el-select v-model="form.teamName" |
| | | placeholder="è¯·éæ©çç»" |
| | | style="width: 100%"> |
| | | <el-option label="ç½ç" |
| | | value="ç½ç" /> |
| | | <el-option label="å¤ç" |
| | | value="å¤ç" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="产åç¼ç " |
| | | prop="materialCode"> |
| | | <el-input disabled |
| | | v-model="form.materialCode" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName"> |
| | | <el-input disabled |
| | | v-model="form.productName" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼" |
| | | prop="specification"> |
| | | <el-input disabled |
| | | v-model="form.specification" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建人" |
| | | prop="createBy" |
| | | required> |
| | | <el-input v-model="form.createBy" |
| | | placeholder="请è¾å
¥å建人" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´" |
| | | prop="createTime"> |
| | | <el-date-picker disabled |
| | | v-model="form.createTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©å建æ¶é´" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <!-- ç¬¬ä¸æ¥ï¼æ¥çå·¥åºåæ° --> |
| | | <div v-else-if="activeStep === 2"> |
| | | <!-- å·¥åºTab页 --> |
| | | <el-tabs v-model="activeProcessId" |
| | | @tab-click="handleTabClick"> |
| | | <el-tab-pane v-for="process in processList" |
| | | :key="process.id" |
| | | :label="process.processName" |
| | | :name="process.id + ''"> |
| | | <div> |
| | | <!-- åæ°ç»å表 --> |
| | | <div v-for="(group, groupIndex) in form.paramGroups[process.id] || []" |
| | | :key="groupIndex" |
| | | class="param-group"> |
| | | <div class="group-header"> |
| | | <span>åæ°ç» {{ groupIndex + 1 }}</span> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="removeParamGroup(process.id, groupIndex)" |
| | | v-if="(form.paramGroups[process.id] || []).length > 1"> |
| | | å é¤ |
| | | </el-button> |
| | | </div> |
| | | <el-form :model="form" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <!-- 卿忰 --> |
| | | <el-form-item v-for="param in params" |
| | | :key="param.id" |
| | | :label="param.paramName"> |
| | | <template v-if="param.paramType == '1'"> |
| | | <!-- æ°åç±»å --> |
| | | <div style="display: flex; align-items: center; gap: 8px;"> |
| | | <el-input-number v-model="group[param.id]" |
| | | controls-position="right" |
| | | :precision="getPrecision(param.paramFormat)" |
| | | style="flex: 1" /> |
| | | <span v-if="param.unit && param.unit != '/'"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '2'"> |
| | | <!-- ææ¬ç±»å --> |
| | | <div style="display: flex; align-items: center; gap: 8px;"> |
| | | <el-input v-model="group[param.id]" |
| | | style="flex: 1" /> |
| | | <span v-if="param.unit && param.unit != '/'"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '3'"> |
| | | <!-- åå
¸ç±»å --> |
| | | <div style="display: flex; align-items: center; gap: 8px;"> |
| | | <el-select v-model="group[param.id]" |
| | | placeholder="è¯·éæ©" |
| | | style="flex: 1;width: 150px"> |
| | | <el-option v-for="option in dictOptions[param.paramFormat] || []" |
| | | :key="option.dictValue" |
| | | :label="option.dictLabel" |
| | | :value="option.dictValue" /> |
| | | </el-select> |
| | | <span v-if="param.unit && param.unit != '/'"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '4'"> |
| | | <!-- æ¥æç±»å --> |
| | | <div style="display: flex; align-items: center; gap: 8px;"> |
| | | <el-date-picker :value-format="param.paramFormat" |
| | | :format="param.paramFormat" |
| | | :type="param.paramFormat=='YYYY-MM-DD'?'daterange':'datetimerange'" |
| | | v-model="group[param.id]" |
| | | style="flex: 1" /> |
| | | <span v-if="param.unit && param.unit != '/'"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else> |
| | | <!-- å
¶ä»ç±»å --> |
| | | <div style="display: flex; align-items: center; gap: 8px;"> |
| | | <el-input v-model="group[param.id]" |
| | | style="flex: 1" /> |
| | | <span v-if="param.unit && param.unit != '/'"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <!-- æ°å¢åæ°ç»æé® --> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="addParamGroup(process.id)"> |
| | | æ°å¢åæ°ç» |
| | | </el-button> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | <!-- ç¬¬åæ¥ï¼å¡«å产éä¿¡æ¯ --> |
| | | <div v-else-if="activeStep === 3"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="äº§åºæ¹é" |
| | | prop="outputVolume" |
| | | required> |
| | | <el-input-number v-model="form.outputVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¸åæ ¼æ¹é" |
| | | prop="unqualifiedVolume" |
| | | required> |
| | | <el-input-number v-model="form.unqualifiedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="宿æ¹é" |
| | | prop="completedVolume" |
| | | required> |
| | | <el-input-number v-model="form.completedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="handleClose">å æ¶</el-button> |
| | | <el-button type="primary" |
| | | v-if="activeStep > 0" |
| | | @click="activeStep--">ä¸ä¸æ¥</el-button> |
| | | <el-button type="primary" |
| | | v-if="activeStep < 3" |
| | | @click="handleNextStep">ä¸ä¸æ¥</el-button> |
| | | <el-button type="primary" |
| | | v-if="activeStep === 3" |
| | | :loading="submitLoading" |
| | | @click="handleSubmit">确 认</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { getDicts } from "@/api/system/dict/data"; |
| | | import { productOrderListPage } from "@/api/productionManagement/productionOrder.js"; |
| | | import { |
| | | findProductProcessRouteItemList, |
| | | findProcessParamListOrder, |
| | | } from "@/api/productionManagement/productProcessRoute.js"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | data: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:visible", "completed"]); |
| | | |
| | | const dialogTitle = computed(() => (props.data.id ? "ç¼è¾æ¥å·¥" : "æ°å¢æ¥å·¥")); |
| | | |
| | | const formRef = ref(null); |
| | | const submitLoading = ref(false); |
| | | const orderLoading = ref(false); |
| | | const processLoading = ref(false); |
| | | const localVisible = ref(props.visible); |
| | | const activeStep = ref(0); |
| | | |
| | | const orderId = ref(props.data.orderId || ""); |
| | | const processId = ref(props.data.processId || ""); |
| | | const activeProcessId = ref(""); |
| | | const orderList = ref([]); |
| | | const processList = ref([]); |
| | | const params = ref([]); |
| | | const dictOptions = ref({}); |
| | | |
| | | const form = reactive({ |
| | | id: props.data.id || undefined, |
| | | orderId: props.data.orderId || "", |
| | | npsNo: props.data.npsNo || "", |
| | | teamName: props.data.teamName || "", |
| | | materialCode: props.data.materialCode || "", |
| | | productName: props.data.productName || "", |
| | | specification: props.data.specification || "", |
| | | outputVolume: props.data.outputVolume || 0, |
| | | unqualifiedVolume: props.data.unqualifiedVolume || 0, |
| | | completedVolume: props.data.completedVolume || 0, |
| | | createBy: props.data.createBy || "å½åç»å½äºº", |
| | | createTime: props.data.createTime || new Date(), |
| | | paramGroups: props.data.paramGroups || {}, // å卿¯ä¸ªå·¥åºçåæ°ç» |
| | | }); |
| | | |
| | | const rules = { |
| | | teamName: [{ required: true, message: "è¯·éæ©çç»", trigger: "blur" }], |
| | | outputVolume: [ |
| | | { required: true, message: "请è¾å
¥äº§åºæ¹é", trigger: "blur" }, |
| | | ], |
| | | unqualifiedVolume: [ |
| | | { required: true, message: "请è¾å
¥ä¸åæ ¼æ¹é", trigger: "blur" }, |
| | | ], |
| | | completedVolume: [ |
| | | { required: true, message: "请è¾å
¥å®ææ¹é", trigger: "blur" }, |
| | | ], |
| | | createBy: [{ required: true, message: "请è¾å
¥å建人", trigger: "blur" }], |
| | | }; |
| | | |
| | | // å è½½ç产订åå表 |
| | | const loadOrders = () => { |
| | | orderLoading.value = true; |
| | | productOrderListPage({ pageNum: 1, pageSize: 100 }) |
| | | .then(res => { |
| | | orderList.value = res.data.records || []; |
| | | }) |
| | | .finally(() => { |
| | | orderLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å¤çç产订åéæ© |
| | | const handleOrderChange = val => { |
| | | if (val) { |
| | | const order = orderList.value.find(item => item.id === val); |
| | | if (order) { |
| | | form.orderId = val; |
| | | form.npsNo = order.npsNo; |
| | | form.materialCode = order.materialCode; |
| | | form.productName = order.productName; |
| | | form.specification = order.model; |
| | | } |
| | | // å 载工åºå表 |
| | | loadProcesses(val); |
| | | } else { |
| | | form.orderId = ""; |
| | | form.npsNo = ""; |
| | | form.materialCode = ""; |
| | | form.productName = ""; |
| | | form.specification = ""; |
| | | processId.value = ""; |
| | | activeProcessId.value = ""; |
| | | processList.value = []; |
| | | params.value = []; |
| | | form.params = {}; |
| | | } |
| | | }; |
| | | |
| | | // å 载工åºå表 |
| | | const loadProcesses = orderId => { |
| | | processLoading.value = true; |
| | | findProductProcessRouteItemList({ orderId }) |
| | | .then(res => { |
| | | processList.value = res.data || []; |
| | | // 妿æå·¥åºï¼é»è®¤éæ©ç¬¬ä¸ä¸ª |
| | | if (processList.value.length > 0) { |
| | | const firstProcess = processList.value[0]; |
| | | activeProcessId.value = firstProcess.id + ""; |
| | | processId.value = firstProcess.id; |
| | | form.processId = firstProcess.id; |
| | | // å 载第ä¸ä¸ªå·¥åºçåæ° |
| | | loadParams(firstProcess.id, orderId); |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | processLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å¤çtab页ç¹å» |
| | | const handleTabClick = tab => { |
| | | const selectedProcessId = parseInt(tab.paneName); |
| | | processId.value = selectedProcessId; |
| | | form.processId = selectedProcessId; |
| | | // å è½½åæ°å表 |
| | | loadParams(selectedProcessId, form.orderId); |
| | | }; |
| | | |
| | | // è·ååå
¸æ°æ® |
| | | const getDictOptions = async dictType => { |
| | | if (!dictType) return []; |
| | | if (dictOptions.value[dictType]) return dictOptions.value[dictType]; |
| | | |
| | | try { |
| | | const res = await getDicts(dictType); |
| | | if (res.code === 200) { |
| | | dictOptions.value[dictType] = res.data; |
| | | return res.data; |
| | | } |
| | | return []; |
| | | } catch (error) { |
| | | console.error("è·ååå
¸æ°æ®å¤±è´¥:", error); |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | // å è½½åæ°å表 |
| | | const loadParams = (processId, orderId) => { |
| | | findProcessParamListOrder({ orderId, routeItemId: processId }).then( |
| | | async res => { |
| | | params.value = res.data || []; |
| | | // åå§ååæ°ç» |
| | | if (!form.paramGroups[processId]) { |
| | | form.paramGroups[processId] = []; |
| | | } |
| | | // å¦ææ²¡æåæ°ç»ï¼æ·»å ä¸ä¸ªé»è®¤åæ°ç» |
| | | if (form.paramGroups[processId].length === 0) { |
| | | const defaultGroup = {}; |
| | | for (const param of params.value) { |
| | | defaultGroup[param.id] = param.standardValue || ""; |
| | | // 妿æ¯åå
¸ç±»ååæ°ï¼è·ååå
¸æ°æ® |
| | | if (param.paramType == "3" && param.paramFormat) { |
| | | await getDictOptions(param.paramFormat); |
| | | } |
| | | } |
| | | form.paramGroups[processId].push(defaultGroup); |
| | | } |
| | | } |
| | | ); |
| | | }; |
| | | |
| | | // è·åå°æ°ç²¾åº¦ |
| | | const getPrecision = format => { |
| | | if (!format) return 2; |
| | | const match = format.match(/\.(\d+)/); |
| | | return match ? parseInt(match[1].length) : 2; |
| | | }; |
| | | |
| | | // å¤çä¸ä¸æ¥ |
| | | const handleNextStep = () => { |
| | | if (activeStep.value === 0) { |
| | | // ç¬¬ä¸æ¥ï¼éªè¯ç产订åéæ© |
| | | if (!orderId.value) { |
| | | ElMessage.error("è¯·éæ©ç产订å"); |
| | | return; |
| | | } |
| | | activeStep.value = 1; |
| | | } else if (activeStep.value === 1) { |
| | | // ç¬¬äºæ¥ï¼éªè¯åºç¡ä¿¡æ¯ |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | activeStep.value = 2; |
| | | } |
| | | }); |
| | | } else if (activeStep.value === 2) { |
| | | // ç¬¬ä¸æ¥ï¼ç´æ¥è¿å
¥ç¬¬åæ¥ |
| | | activeStep.value = 3; |
| | | } |
| | | }; |
| | | |
| | | // å¤çæäº¤ |
| | | const handleSubmit = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | submitLoading.value = true; |
| | | // è¿éå¯ä»¥è°ç¨APIè¿è¡æäº¤ |
| | | setTimeout(() => { |
| | | ElMessage.success(props.data.id ? "ä¿®æ¹æå" : "æ°å¢æå"); |
| | | localVisible.value = false; |
| | | emit("completed"); |
| | | submitLoading.value = false; |
| | | }, 1000); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å¤çå
³é |
| | | const handleClose = () => { |
| | | // éç½®å°åå§ç¶æ |
| | | activeStep.value = 0; |
| | | orderId.value = ""; |
| | | processId.value = ""; |
| | | activeProcessId.value = ""; |
| | | processList.value = []; |
| | | params.value = []; |
| | | Object.assign(form, { |
| | | id: undefined, |
| | | orderId: "", |
| | | npsNo: "", |
| | | teamName: "", |
| | | materialCode: "", |
| | | productName: "", |
| | | specification: "", |
| | | outputVolume: 0, |
| | | unqualifiedVolume: 0, |
| | | completedVolume: 0, |
| | | createBy: "å½åç»å½äºº", |
| | | createTime: new Date(), |
| | | params: {}, |
| | | }); |
| | | localVisible.value = false; |
| | | }; |
| | | |
| | | // æ°å¢åæ°ç» |
| | | const addParamGroup = processId => { |
| | | if (!form.paramGroups[processId]) { |
| | | form.paramGroups[processId] = []; |
| | | } |
| | | // å建ä¸ä¸ªæ°çåæ°ç»ï¼ä½¿ç¨é»è®¤å¼ |
| | | const newGroup = {}; |
| | | params.value.forEach(param => { |
| | | newGroup[param.id] = param.standardValue || ""; |
| | | }); |
| | | form.paramGroups[processId].push(newGroup); |
| | | }; |
| | | |
| | | // å é¤åæ°ç» |
| | | const removeParamGroup = (processId, index) => { |
| | | if (form.paramGroups[processId] && form.paramGroups[processId].length > 1) { |
| | | form.paramGroups[processId].splice(index, 1); |
| | | } |
| | | }; |
| | | |
| | | // åå§å |
| | | const init = () => { |
| | | // æ 论æ°å¢è¿æ¯ç¼è¾ï¼é½å 载订åå表 |
| | | loadOrders(); |
| | | |
| | | if (props.data.id) { |
| | | // ç¼è¾æ¶è®¾ç½®è¡¨åæ°æ® |
| | | Object.assign(form, props.data); |
| | | // 设置orderId |
| | | orderId.value = props.data.orderId || ""; |
| | | // 妿æè®¢åIDï¼å 载工åºååæ° |
| | | if (props.data.orderId) { |
| | | // 模æéæ©è®¢åçæä½ï¼è§¦åæ°æ®å è½½ |
| | | setTimeout(() => { |
| | | handleOrderChange(props.data.orderId); |
| | | }, 100); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¶è®¾ç½®é»è®¤å¼ |
| | | form.createBy = "å½åç»å½äºº"; |
| | | form.createTime = new Date(); |
| | | } |
| | | // å§ç»ä»ç¬¬ä¸æ¥å¼å§ |
| | | activeStep.value = 0; |
| | | }; |
| | | |
| | | // çå¬props.visibleåå |
| | | watch( |
| | | () => props.visible, |
| | | newVal => { |
| | | localVisible.value = newVal; |
| | | if (newVal) { |
| | | init(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | // çå¬localVisibleåå |
| | | watch(localVisible, newVal => { |
| | | emit("update:visible", newVal); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | |
| | | .param-group { |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 4px; |
| | | padding: 16px; |
| | | margin-bottom: 16px; |
| | | background-color: #f9f9f9; |
| | | } |
| | | |
| | | .group-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 16px; |
| | | padding-bottom: 8px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .group-header span { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | } |
| | | </style> |