| src/api/productionManagement/productionOrder.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/processRoute/processRouteItem/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productStructure/Detail/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionOrder/New.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionOrder/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionReporting/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/workOrder/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/productionManagement/productionOrder.js
@@ -36,6 +36,14 @@ }); } export function startOrPause(data) { return request({ url: "/productOrder/startOrPause", method: "post", data, }); } // 生产订单-新增 export function addProductOrder(data) { return request({ src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -63,11 +63,9 @@ <el-button icon="Grid" @click="toggleView" style="margin-right: 10px;" > 卡片视图 </el-button> <el-button type="primary" @click="handleAdd">新增</el-button> </div> </div> <el-table @@ -87,10 +85,6 @@ {{ getProcessName(scope.row.processId) || '-' }} </template> </el-table-column> <el-table-column label="产品名称" prop="productName" min-width="160" /> <el-table-column label="图纸编号" prop="model" min-width="140" /> <el-table-column label="规格型号" prop="drawingNumber" min-width="160" /> <el-table-column label="单位" prop="unit" width="100" /> <el-table-column label="是否质检" prop="isQuality" width="100"> <template #default="scope"> {{scope.row.isQuality ? "是" : "否"}} @@ -117,11 +111,9 @@ <el-button icon="Menu" @click="toggleView" style="margin-right: 10px;" > 表格视图 </el-button> <el-button type="primary" @click="handleAdd">新增</el-button> </div> </div> <div v-loading="tableLoading" class="card-container"> @@ -141,18 +133,8 @@ <div class="card-process-name">{{ getProcessName(item.processId) || '-' }}</div> </div> <!-- 产品信息 --> <div class="card-content"> <div v-if="item.productName" class="product-info"> <div class="product-name">{{ item.productName }}</div> <div class="product-model">{{ item.drawingNumber || '-' }}</div> <div v-if="item.model" class="product-model"> {{ item.model }} <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> --> </div> <el-tag type="primary" class="product-tag" v-if="item.isQuality">质检</el-tag> </div> <div v-else class="product-info empty">暂无产品信息</div> </div> <!-- 操作按钮 --> @@ -194,23 +176,6 @@ </el-select> </el-form-item> <el-form-item label="产品名称" prop="productModelId"> <el-button type="primary" @click="showProductSelectDialog = true"> {{ form.productName && form.model ? `${form.productName} - ${form.model}` : '选择产品' }} </el-button> </el-form-item> <el-form-item label="单位" prop="unit"> <el-input v-model="form.unit" :placeholder="form.productModelId ? '根据选择的产品自动带出' : '请先选择产品'" clearable :disabled="true" /> </el-form-item> <el-form-item label="是否质检" prop="isQuality"> <el-switch v-model="form.isQuality" :active-value="true" :inactive-value="false"/> </el-form-item> @@ -237,18 +202,11 @@ </template> </el-dialog> <!-- 产品选择对话框 --> <ProductSelectDialog v-model="showProductSelectDialog" @confirm="handleProductSelect" single /> </div> </template> <script setup> import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue"; import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; import { findProcessRouteItemList, addOrUpdateProcessRouteItem, sortProcessRouteItem, batchDeleteProcessRouteItem } from "@/api/productionManagement/processRouteItem.js"; import { findProductProcessRouteItemList, deleteRouteItem, addRouteItem, addOrUpdateProductProcessRouteItem, sortRouteItem } from "@/api/productionManagement/productProcessRoute.js"; import { processList } from "@/api/productionManagement/productionProcess.js"; @@ -283,7 +241,6 @@ }); const processOptions = ref([]); const showProductSelectDialog = ref(false); const staffList = ref([]); const treeProps = { @@ -307,17 +264,12 @@ id: undefined, routeId: routeId.value, processId: undefined, productModelId: undefined, productName: "", model: "", unit: "", isQuality: false, userPower: [], }); const rules = { processId: [{ required: true, message: '请选择工序', trigger: 'change' }], productModelId: [{ required: true, message: '请选择产品', trigger: 'change' }], }; // 根据工序ID获取工序名称 @@ -395,10 +347,6 @@ id: row.id, routeId: routeId.value, processId: row.processId, productModelId: row.productModelId, productName: row.productName || "", model: row.model || "", unit: row.unit || "", isQuality: row.isQuality, userPower: userPowerIds, }; @@ -447,20 +395,6 @@ .catch(() => {}); }; // 产品选择 const handleProductSelect = (products) => { if (products && products.length > 0) { const product = products[0]; form.value.productModelId = product.id; form.value.productName = product.productName; form.value.model = product.model; form.value.unit = product.unit || ""; showProductSelectDialog.value = false; // 触发表单验证 formRef.value?.validateField('productModelId'); } }; // 提交 const handleSubmit = () => { formRef.value.validate((valid) => { @@ -480,7 +414,6 @@ productOrderId: orderId.value, productRouteId: routeId.value, processId: form.value.processId, productModelId: form.value.productModelId, isQuality: form.value.isQuality, userPower: userPowerNames.join(','), dragSort, @@ -488,7 +421,6 @@ : addOrUpdateProcessRouteItem({ routeId: routeId.value, processId: form.value.processId, productModelId: form.value.productModelId, isQuality: form.value.isQuality, userPower: userPowerNames.join(','), dragSort, @@ -514,14 +446,12 @@ ? addOrUpdateProductProcessRouteItem({ id: form.value.id, processId: form.value.processId, productModelId: form.value.productModelId, isQuality: form.value.isQuality, userPower: userPowerNames.join(','), }) : addOrUpdateProcessRouteItem({ routeId: routeId.value, processId: form.value.processId, productModelId: form.value.productModelId, id: form.value.id, isQuality: form.value.isQuality, userPower: userPowerNames.join(','), @@ -550,10 +480,8 @@ id: undefined, routeId: routeId.value, processId: undefined, productModelId: undefined, productName: "", model: "", unit: "", isQuality: false, userPower: [], }; formRef.value?.resetFields(); }; @@ -800,20 +728,20 @@ .process-card { flex-shrink: 0; width: 220px; width: 160px; background: #fff; border: 1px solid #e4e7ed; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); padding: 16px; display: flex; flex-direction: column; cursor: move; transition: all 0.3s; transition: all 0.2s; } .process-card:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); transform: translateY(-2px); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); } .card-header { @@ -822,20 +750,20 @@ } .card-number { width: 36px; height: 36px; line-height: 36px; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; background: #409eff; color: #fff; font-weight: bold; font-size: 16px; font-size: 13px; margin: 0 auto 8px; } .card-process-name { font-size: 14px; color: #333; color: #303133; font-weight: 500; word-break: break-all; } @@ -843,7 +771,7 @@ .card-content { flex: 1; margin-bottom: 12px; min-height: 60px; min-height: 40px; display: flex; align-items: center; justify-content: center; src/views/productionManagement/productStructure/Detail/index.vue
@@ -144,7 +144,8 @@ </el-form> </template> </el-table-column> <el-table-column label="BOM编号" <el-table-column v-if="!isOrderPage" label="BOM编号" prop="bomNo" /> <el-table-column label="产品名称" prop="productName" /> src/views/productionManagement/productionOrder/New.vue
@@ -28,8 +28,8 @@ </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="图纸编号" prop="productModelName"> <el-input v-model="formState.productModelName" disabled /> <el-form-item label="图纸编号" prop="model"> <el-input v-model="formState.model" disabled /> </el-form-item> </el-col> </el-row> @@ -61,18 +61,22 @@ <el-col :span="12"> <el-form-item label="图纸上传"> <el-upload action="#" :auto-upload="false" :on-change="handleFileChange" :file-list="fileList" v-model:file-list="fileList" :action="upload.url" :headers="upload.headers" :data="upload.data" :on-success="handleDrawingUploadSuccess" :on-remove="handleDrawingRemove" :before-upload="handleDrawingBeforeUpload" :limit="5" class="upload-inline" accept=".pdf,.jpg,.jpeg,.png,.dwg" list-type="picture-card" > <el-button type="primary" plain size="small"> <el-icon><Upload /></el-icon> 选择文件 </el-button> <el-icon class="avatar-uploader-icon"><Plus /></el-icon> <template #tip> <div class="el-upload__tip">支持jpg/png/pdf,单个不超过10MB</div> <div class="el-upload__tip"> 支持 pdf、jpg、jpeg、png、dwg 格式,大小不超过 10MB </div> </template> </el-upload> </el-form-item> @@ -94,11 +98,26 @@ @confirm="handleMaterialProductSelect" /> <!-- 生产任务 --> <!-- BOM选择弹窗 --> <el-dialog v-model="showBomDialog" title="选择BOM" width="500" append-to-body > <el-table :data="bomList" border size="small" @row-click="handleBomSelect" highlight-current-row style="margin-bottom: 10px;"> <el-table-column type="index" label="序号" width="60" /> <el-table-column label="BOM编号" prop="bomNo" min-width="120" /> <el-table-column label="版本" prop="version" min-width="80" /> <el-table-column label="产品名称" prop="productName" min-width="100" /> </el-table> </el-dialog> <!-- 工序 --> <div class="section-card"> <div class="section-header"> <span class="section-icon">🔧</span> <span class="section-title-text">生产任务</span> <span class="section-title-text">工序</span> <el-button type="primary" size="small" @click="addProductionTask" class="add-btn"> <el-icon><Plus /></el-icon> 添加任务 </el-button> @@ -115,10 +134,10 @@ @change="(val) => handleProcessChange(val, row)" > <el-option v-for="item in processOptions" v-for="item in processRouteItemsOptions" :key="item.id" :label="item.name" :value="item.id" :label="item.processName" :value="item.processId" /> </el-select> </template> @@ -185,7 +204,7 @@ </el-table-column> </el-table> <div v-if="processRouteItems.length === 0" class="empty-tip"> <el-empty description="暂无生产任务,点击上方按钮添加" :image-size="60" /> <el-empty description="暂无工序,点击上方按钮添加" :image-size="60" /> </div> </div> </div> @@ -202,6 +221,8 @@ <div class="table-container"> <el-table :data="productStructureRecords" border size="small" class="compact-table"> <el-table-column type="index" label="序号" width="50" align="center" /> <el-table-column label="图纸编号" prop="model" min-width="120" /> <el-table-column label="产品名称" prop="productName" min-width="120" /> <el-table-column label="单位产出需要数量" min-width="140"> <template #default="{ row }"> <el-input-number v-model="row.unitQuantity" :min="0" :precision="2" size="small" style="width: 100%" /> @@ -215,11 +236,6 @@ <el-table-column label="单位" min-width="80"> <template #default="{ row }"> <el-input v-model="row.unit" placeholder="请输入" size="small" /> </template> </el-table-column> <el-table-column label="bomId" min-width="100"> <template #default="{ row }"> <el-input-number v-model="row.bomId" :min="0" size="small" style="width: 100%" /> </template> </el-table-column> <el-table-column label="操作" width="80" fixed="right" align="center"> @@ -247,12 +263,15 @@ </template> <script setup> import {ref, computed, getCurrentInstance} from "vue"; import {ref, computed, getCurrentInstance, watch} from "vue"; import { Plus, Delete, Upload } from '@element-plus/icons-vue'; import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js"; import {list as listProcess} from "@/api/productionManagement/productionProcess.js"; import {addProductOrder} from "@/api/productionManagement/productionOrder.js"; import {listDeptUserTree} from "@/api/basicData/productProcess.js"; import {findProcessRouteItemList} from "@/api/productionManagement/processRouteItem.js"; import {listPage as listProductBom} from "@/api/productionManagement/productBom.js"; import {listByBomIdIsParent} from "@/api/productionManagement/productStructure.js"; import { getToken } from "@/utils/auth.js"; const props = defineProps({ visible: { @@ -275,11 +294,13 @@ productModelId: undefined, routeId: undefined, productName: "", productModelName: "", model: "", unit: "", drawingNumber: "", quantity: 0, deliveryDate: "", tempFileIds: [], salesLedgerFiles: [], }); // 工序路线明细列表 @@ -288,14 +309,17 @@ // 物料清单列表 const productStructureRecords = ref([]); // 工序列表 const processOptions = ref([]); // 用户列表 const userOptions = ref([]); // 文件列表 const fileList = ref([]); const upload = reactive({ url: import.meta.env.VITE_APP_BASE_API + "/file/upload", headers: { Authorization: "Bearer " + getToken() }, data: { type: 14 }, }); const isShow = computed({ get() { @@ -308,13 +332,6 @@ const showProductSelectDialog = ref(false); const showMaterialProductDialog = ref(false); // 获取工序列表 const fetchProcessOptions = () => { listProcess().then(res => { processOptions.value = res.data || []; }); }; // 获取用户列表 const fetchUserOptions = () => { @@ -341,7 +358,6 @@ }; // 组件挂载时获取数据 fetchProcessOptions(); fetchUserOptions(); let { proxy } = getCurrentInstance() @@ -354,10 +370,12 @@ routeId: undefined, productName: "", drawingNumber: "", productModelName: "", model: "", unit: "", quantity: 0, deliveryDate: "", tempFileIds: [], salesLedgerFiles: [], }; // 重置工序路线明细和物料清单 processRouteItems.value = []; @@ -373,35 +391,108 @@ formState.value.productId = product.productId; formState.value.productName = product.productName; formState.value.drawingNumber = product.drawingNumber; formState.value.productModelName = product.model; formState.value.model = product.model; formState.value.productModelId = product.id; formState.value.unit = product.unit; formState.value.routeId = product.routeId; showProductSelectDialog.value = false; fetchRouteOptions( product.id); // 1. 通过产品自带的routeId获取工序列表 if (product.routeId) { fetchProcessRouteItems(product.routeId); } // 2. 通过产品id查询BOM列表 fetchBomList(product.id); // 触发表单验证更新 proxy.$refs["formRef"]?.validateField('productModelId'); } }; const routeOptions = ref([]); const bindRouteLoading = ref(false); const fetchRouteOptions = (productModelId) => { formState.value.routeId = undefined; routeOptions.value = [] bindRouteLoading.value = true; listProcessRoute({ productModelId: productModelId }).then(res => { routeOptions.value = res.data || []; }).finally(() => { bindRouteLoading.value = false; }) // 工艺路线工序列表 const processRouteItemsOptions = ref([]); const fetchProcessRouteItems = (routeId) => { processRouteItemsOptions.value = []; findProcessRouteItemList({ routeId: routeId }).then(res => { const items = res.data || []; processRouteItemsOptions.value = items; // 自动添加工序 processRouteItems.value = items.map(item => ({ processId: item.processId, processName: item.processName, productModelId: item.productModelId, userPower: item.userPower ? item.userPower.split(',') : [], planStartTime: "", planEndTime: "", planNum: 1, isQuality: item.isQuality || false, })); }); }; // BOM列表弹窗相关 const showBomDialog = ref(false); const bomList = ref([]); const currentProductId = ref(null); const fetchBomList = (productModelId) => { currentProductId.value = productModelId; listProductBom({ productModelId: productModelId }).then(res => { const bomData = res.data?.records || []; if (bomData.length === 1) { // 只有一个BOM,直接查询物料清单 fetchMaterialList(bomData[0].id); } else if (bomData.length > 1) { // 多个BOM,弹出选择框 bomList.value = bomData; showBomDialog.value = true; } }); }; const handleBomSelect = (bom) => { showBomDialog.value = false; fetchMaterialList(bom.id); }; const fetchMaterialList = (bomId) => { listByBomIdIsParent(bomId).then(res => { const materials = res.data || []; const demandQty = formState.value.quantity || 1; productStructureRecords.value = materials.map(item => ({ parentId: item.parentId, productModelId: item.productModelId, model: item.model || '', productName: item.productName || '', productOrderId: undefined, processId: undefined, unitQuantity: item.unitQuantity || 1, demandedQuantity: (item.unitQuantity || 1) * demandQty, unit: item.unit || '', bomId: item.bomId, })); }); }; // 监听需求数量变化,重新计算物料需求数量 watch(() => formState.value.quantity, (newQty) => { if (productStructureRecords.value.length > 0 && newQty) { productStructureRecords.value = productStructureRecords.value.map(item => ({ ...item, demandedQuantity: (item.unitQuantity || 1) * newQty })); } }); // 工序选择变化处理 const handleProcessChange = (processId, row) => { const selectedProcess = processOptions.value.find(item => item.id === processId); const selectedProcess = processRouteItemsOptions.value.find(item => item.processId === processId); if (selectedProcess) { row.processName = selectedProcess.name; row.processNo = selectedProcess.no; row.processName = selectedProcess.processName; row.productModelId = selectedProcess.productModelId; row.isQuality = selectedProcess.isQuality || false; // userPower是逗号分隔的用户名,转换为数组 if (selectedProcess.userPower) { @@ -412,8 +503,16 @@ } }; // 添加生产任务 // 添加工序 const addProductionTask = () => { if (!formState.value.productModelId) { proxy.$modal.msgWarning("请先选择产品"); return; } if (processRouteItemsOptions.value.length === 0) { proxy.$modal.msgWarning("请先选择产品以获取工序列表"); return; } processRouteItems.value.push({ processId: undefined, processName: "", @@ -446,6 +545,8 @@ parentId: undefined, productOrderId: undefined, processId: undefined, model: product.model || '', productName: product.productName || '', unitQuantity: 1, demandedQuantity: 1, unit: product.unit, @@ -461,9 +562,47 @@ productStructureRecords.value.splice(index, 1); }; // 文件上传变更 const handleFileChange = (file, files) => { fileList.value = files; const handleDrawingBeforeUpload = (file) => { const isAllowed = [ 'application/pdf', 'image/jpeg', 'image/jpg', 'image/png', 'application/dwg' ].includes(file.type) || file.name.endsWith('.dwg'); const isLt10M = file.size / 1024 / 1024 < 10; if (!isAllowed) { proxy.$modal.msgError("只能上传 pdf、jpg、jpeg、png、dwg 格式的文件!"); return false; } if (!isLt10M) { proxy.$modal.msgError("上传文件大小不能超过 10MB!"); return false; } return true; }; const handleDrawingUploadSuccess = (response, file, fileList) => { console.log('上传成功响应', response); console.log('response.data', response.data); if (response.code === 200) { formState.value.tempFileIds = [response.data?.tempId]; formState.value.salesLedgerFiles = [{ tempId: response.data?.tempId, originalName: response.data?.originalName || file.name, tempPath: response.data?.tempPath, type: response.data?.type || 14 }]; proxy.$modal.msgSuccess("上传成功"); } else { proxy.$modal.msgError(response.msg || "上传失败"); } }; const handleDrawingRemove = (file) => { formState.value.tempFileIds = []; formState.value.salesLedgerFiles = []; }; const handleSubmit = () => { @@ -490,7 +629,7 @@ ...formState.value, processRouteItems: processedProcessRouteItems, productStructureRecords: productStructureRecords.value, files: fileList.value, files: formState.value.salesLedgerFiles, }; addProductOrder(submitData).then(res => { @@ -583,6 +722,31 @@ padding: 20px 0; } .avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 148px; height: 148px; text-align: center; line-height: 148px; } :deep(.el-upload--picture-card) { width: 148px; height: 148px; } :deep(.el-upload-list__item) { width: 148px; height: 148px; } :deep(.el-upload__tip) { font-size: 12px; color: #909399; margin-top: 8px; } .upload-inline :deep(.el-upload) { display: inline-flex; } src/views/productionManagement/productionOrder/index.vue
@@ -83,7 +83,7 @@ productOrderListPage, listProcessRoute, bindingRoute, listProcessBom, delProductOrder, listProcessBom, delProductOrder, startOrPause, } from "@/api/productionManagement/productionOrder.js"; import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js"; import {fileDel} from "@/api/financialManagement/revenueManagement.js"; @@ -158,8 +158,24 @@ label: "操作", align: "center", fixed: "right", width: 200, width: 240, operation: [ { name: "开始", type: "text", showHide: row => row.status === '待生产', clickFun: row => { handleStartOrPause(row); }, }, { name: "暂停", type: "text", showHide: row => row.status === '生产中', clickFun: row => { handleStartOrPause(row); }, }, { name: "工艺路线", type: "text", @@ -176,7 +192,7 @@ }, }, { name: "产品结构", name: "物料清单", type: "text", clickFun: row => { showProductStructure(row); @@ -225,6 +241,8 @@ if (row.isFh) return ''; const diff = row.deliveryDaysDiff; if (diff === undefined || diff === null || diff === '' || diff < 0) return ''; if (diff === 15) { return 'yellow'; } else if (diff === 10) { @@ -331,11 +349,12 @@ const orderId = row.id; try { const res = await getOrderProcessRouteMain(orderId); const data = res.data || {}; if (!data || !data.id) { const dataList = res.data || []; if (!dataList || dataList.length === 0 || !dataList[0].id) { proxy.$modal.msgWarning("未找到关联的工艺路线"); return; } const data = dataList[0]; router.push({ path: "/productionManagement/processRouteItem", query: { @@ -356,12 +375,37 @@ } }; const showProductStructure = row => { const handleStartOrPause = async (row) => { const operation = row.status === '待生产' ? 1 : 2; const operationText = operation === 1 ? "开始" : "暂停"; try { await startOrPause({ id: row.id, operation }); proxy.$modal.msgSuccess(`${operationText}成功`); getList(); } catch (e) { console.error(`${operationText}失败:`, e); proxy.$modal.msgError(`${operationText}失败`); } }; const showProductStructure = async row => { let bomNo = row.bomNo || ""; if (!bomNo && row.id) { try { const res = await getOrderProcessRouteMain(row.id); const dataList = res.data || []; if (dataList && dataList.length > 0 && dataList[0].bomNo) { bomNo = dataList[0].bomNo; } } catch (e) { console.error("获取BOM编号失败:", e); } } router.push({ path: "/productionManagement/productStructureDetail", query: { id: row.id, bomNo: row.bomNo || "", bomNo: bomNo, drawingNumber: row.drawingNumber || "", productName: row.productCategory || "", productModelName: row.specificationModel || "", @@ -434,7 +478,7 @@ } :deep(.red) { background-color: #f80202; background-color: #FFCCCC; } :deep(.purple){ src/views/productionManagement/productionReporting/index.vue
@@ -168,11 +168,6 @@ width: 120, }, { label: "销售合同号", prop: "salesContractNo", width: 120, }, { label: "产品名称", prop: "productName", width: 120, @@ -181,11 +176,6 @@ label: "产品图纸编号", prop: "productModelName", width: 160, }, { label: "产品规格型号", prop: "drawingNumber", width: 120, }, { label: "产出数量", src/views/productionManagement/workOrder/index.vue
@@ -255,10 +255,6 @@ prop: "model" }, { label: "规格型号", prop: "drawingNumber", }, { label: "单位", prop: "unit", }, @@ -333,7 +329,7 @@ clickFun: row => { showReportDialog(row); }, disabled: row => row.planQuantity <= 0, disabled: row => row.status === 1 || row.status === 3 || row.planQuantity <= 0, }, ], },