| | |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品规格:"> |
| | | <el-input v-model="searchForm.specification" |
| | | <el-input v-model="searchForm.model" |
| | | placeholder="请输入" |
| | | clearable |
| | | style="width: 160px;" |
| | |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 240px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="下发状态:"> |
| | | <el-select v-model="searchForm.status" |
| | | placeholder="请选择状态" |
| | | clearable |
| | | filterable |
| | | style="width: 100px"> |
| | | <el-option label="待下发" |
| | | value="0" /> |
| | | <el-option label="部分下发" |
| | | value="1" /> |
| | | <el-option label="已下发" |
| | | value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="10"> |
| | | <el-form-item label="产品规格"> |
| | | <div class="info-display">{{ mergeForm.specification || '-' }}</div> |
| | | <div class="info-display">{{ mergeForm.model || '-' }}</div> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="10"> |
| | |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="强度" |
| | | v-if="mergeForm.productName === '砌块'"> |
| | | <div v-if="strengthError" |
| | | class="strength-error" |
| | | style="color: red; margin-bottom: 8px;">{{ strengthError }}</div> |
| | | <el-select v-model="mergeForm.strength" |
| | | placeholder="请选择强度" |
| | | style="width: 100%" |
| | | required> |
| | | <el-option v-for="item in block_strength" |
| | | :key="item.id" |
| | | :label="item.label" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="生产方数"> |
| | | <el-input-number v-model="mergeForm.totalAssignedQuantity" |
| | |
| | | prop="productMaterialSkuId"> |
| | | <el-select v-model="form.productMaterialSkuId" |
| | | @change="handleChangeSpecification" |
| | | filterable |
| | | placeholder="请选择"> |
| | | <el-option v-for="item in specificationOptions" |
| | | :key="item.skuId" |
| | | :label="item.specification" |
| | | :value="item.skuId" /> |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="块数" |
| | |
| | | placeholder="请选择计划结束日期" /> |
| | | </el-form-item> |
| | | <el-form-item label="强度" |
| | | prop="strength"> |
| | | <el-input v-model="form.strength" |
| | | placeholder="请输入强度" /> |
| | | prop="strength" |
| | | v-if="form.productName === '砌块'"> |
| | | <el-select v-model="form.strength" |
| | | placeholder="请选择强度" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in block_strength" |
| | | :key="item.id" |
| | | :label="item.label" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="备注 1" |
| | | prop="remarkOne"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref, reactive, getCurrentInstance } from "vue"; |
| | | import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import ImportDialog from "@/components/Dialog/ImportDialog.vue"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import { useDict } from "@/utils/dict"; |
| | | import { |
| | | productionPlanListPage, |
| | | loadProdData, |
| | |
| | | productionPlanCombine, |
| | | } from "@/api/productionPlan/productionPlan.js"; |
| | | import PIMTable from "./components/PIMTable.vue"; |
| | | import { modelListPage, productTreeList } from "@/api/basicData/newProduct.js"; |
| | | import { |
| | | modelListPage, |
| | | productTreeList, |
| | | productTreeListQuery, |
| | | } from "@/api/basicData/newProduct.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "数据来源", |
| | | width: "100px", |
| | | prop: "dataSourceType", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | 2: "warning", |
| | | 1: "primary", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | }, |
| | | formatData: cell => (cell == 1 ? "钉钉同步" : "手动新增"), |
| | | }, |
| | | { |
| | | label: "申请单编号", |
| | | prop: "applyNo", |
| | | width: "150px", |
| | | className: "code-cell", |
| | | }, |
| | | { |
| | | label: "客户名称", |
| | |
| | | { |
| | | label: "产品名称", |
| | | prop: "productName", |
| | | width: "100px", |
| | | width: "200px", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | 板材: "primary", |
| | | 砌块: "info", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | // const typeMap = { |
| | | // 板材: "primary", |
| | | // 砌块: "warning", |
| | | // }; |
| | | // return typeMap[params] || "info"; |
| | | return "primary"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "产品规格", |
| | | prop: "specification", |
| | | prop: "model", |
| | | width: "150px", |
| | | className: "spec-cell", |
| | | }, |
| | |
| | | label: "物料编码", |
| | | prop: "materialCode", |
| | | width: "150px", |
| | | className: "code-cell", |
| | | }, |
| | | { |
| | | label: "块数", |
| | | prop: "quantity", |
| | | className: "quantity-cell", |
| | | formatData: cell => (cell ? `${cell}块` : ""), |
| | | }, |
| | | { |
| | | label: "方数", |
| | | prop: "volume", |
| | | width: "150px", |
| | | className: "volume-cell", |
| | | formatData: cell => (cell ? `${cell}方` : ""), |
| | | }, |
| | | { |
| | | label: "下发状态", |
| | | prop: "status", |
| | | width: "150px", |
| | | className: "status-cell", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | 0: "warning", |
| | | 1: "primary", |
| | | 2: "info", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | }, |
| | | formatData: cell => { |
| | | const statusMap = { |
| | | 0: "待下发", |
| | | 1: "部分下发", |
| | | 2: "已下发", |
| | | }; |
| | | return statusMap[cell] || ""; |
| | | }, |
| | | }, |
| | | { |
| | | label: "已下发方数", |
| | | prop: "assignedQuantity", |
| | | width: "150px", |
| | | className: "spec-cell", |
| | | formatData: cell => (cell ? `${cell}方` : 0), |
| | | }, |
| | | { |
| | | label: "长", |
| | | prop: "length", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "宽", |
| | | prop: "width", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "高", |
| | | prop: "height", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "流水号", |
| | | prop: "serialNo", |
| | | width: "150px", |
| | | className: "code-cell", |
| | | }, |
| | | // { |
| | | // label: "流水号", |
| | | // prop: "serialNo", |
| | | // width: "150px", |
| | | // className: "code-cell", |
| | | // }, |
| | | { |
| | | label: "计划开始日期", |
| | | prop: "startDate", |
| | |
| | | { |
| | | label: "强度", |
| | | prop: "strength", |
| | | formatData: cell => { |
| | | if (!cell) return ""; |
| | | const strengthItem = block_strength.value.find(item => item.id === cell); |
| | | return strengthItem ? strengthItem.label : cell; |
| | | }, |
| | | }, |
| | | // { |
| | | // label: "数据来源", |
| | | // width: "100px", |
| | | // prop: "dataSourceType", |
| | | // formatData: cell => (cell == 1 ? "同步" : "手动"), |
| | | // }, |
| | | |
| | | { |
| | | label: "备注 1", |
| | | width: "150px", |
| | | prop: "remarkOne", |
| | | }, |
| | | { |
| | | label: "备注 2", |
| | | width: "150px", |
| | | prop: "remarkTwo", |
| | | }, |
| | | |
| | |
| | | name: "编辑", |
| | | type: "primary", |
| | | link: true, |
| | | showHide: row => { |
| | | return row.status == 0; |
| | | //status,0:待下发,1:部分下发,2:已下发 |
| | | }, |
| | | clickFun: row => { |
| | | handleEdit(row); |
| | | }, |
| | |
| | | name: "删除", |
| | | type: "danger", |
| | | link: true, |
| | | showHide: row => { |
| | | return row.status == 0; |
| | | }, |
| | | clickFun: row => { |
| | | handleDelete(row); |
| | | }, |
| | |
| | | { |
| | | name: "下发", |
| | | type: "text", |
| | | disabled: row => { |
| | | showHide: row => { |
| | | // 计算剩余方数 |
| | | const remainingVolume = |
| | | (row.volume || 0) - (row.assignedQuantity || 0); |
| | | // 如果剩余方数小于等于0,禁止选择 |
| | | return remainingVolume <= 0; |
| | | return remainingVolume > 0; |
| | | }, |
| | | clickFun: row => { |
| | | // 单独下发操作 |
| | | // 设置表单数据 |
| | | strengthError.value = ""; |
| | | mergeForm.ids = [row.id]; |
| | | mergeForm.materialCode = row.materialCode; |
| | | mergeForm.productName = row.productName || ""; |
| | | mergeForm.specification = row.specification || ""; |
| | | mergeForm.model = row.model || ""; |
| | | mergeForm.length = row.length || 0; |
| | | mergeForm.width = row.width || 0; |
| | | mergeForm.height = row.height || 0; |
| | | mergeForm.totalAssignedQuantity = |
| | | (Number(row.volume) - Number(row.assignedQuantity)).toFixed(4) || 0; |
| | | mergeForm.planCompleteTime = row.planCompleteTime || ""; |
| | | mergeForm.strength = row.strength || ""; |
| | | sumAssignedQuantity.value = mergeForm.totalAssignedQuantity; |
| | | // 打开弹窗 |
| | | isShowNewModal.value = true; |
| | |
| | | const mergeForm = reactive({ |
| | | materialCode: "", |
| | | productName: "", |
| | | specification: "", |
| | | model: "", |
| | | length: 0, |
| | | width: 0, |
| | | height: 0, |
| | | totalAssignedQuantity: 0, |
| | | planCompleteTime: "", |
| | | strength: "", |
| | | }); |
| | | |
| | | // 追踪进度弹窗控制 |
| | |
| | | const productOptions = ref([]); |
| | | const specificationOptions = ref([]); |
| | | const formRef = ref(null); |
| | | // 获取强度字典 |
| | | const { block_strength } = useDict("block_strength"); |
| | | const form = reactive({ |
| | | id: undefined, |
| | | applyNo: "", |
| | |
| | | productMaterialId: undefined, |
| | | productMaterialSkuId: undefined, |
| | | productName: "", |
| | | specification: "", |
| | | model: "", |
| | | materialCode: "", |
| | | quantity: 0, |
| | | volume: 0, |
| | |
| | | height: 0, |
| | | startDate: "", |
| | | endDate: "", |
| | | status: "", |
| | | strength: "", |
| | | remarkOne: "", |
| | | remarkTwo: "", |
| | |
| | | productMaterialSkuId: [ |
| | | { required: true, message: "请选择产品规格", trigger: "change" }, |
| | | ], |
| | | volume: [{ required: true, message: "请输入方数", trigger: "blur" }], |
| | | productMaterialId: [ |
| | | { required: true, message: "请选择产品", trigger: "change" }, |
| | | ], |
| | | strength: [ |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (form.productName === "砌块" && !value) { |
| | | callback(new Error("砌块产品的强度为必填项")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: ["blur", "change"], |
| | | required: false, |
| | | }, |
| | | ], |
| | | }); |
| | | |
| | |
| | | }; |
| | | |
| | | const fetchProductOptions = () => { |
| | | return productTreeList().then(res => { |
| | | return productTreeList({ type: 2 }).then(res => { |
| | | productOptions.value = convertIdToValue(res.data); |
| | | return res; |
| | | }); |
| | |
| | | if (item.materialList && item.materialList.length > 0) { |
| | | newItem.children = item.materialList.map(material => ({ |
| | | value: material.id, // 使用material的id作为value |
| | | label: material.materialName, // 使用materialName作为label |
| | | label: material.productName, // 使用materialName作为label |
| | | })); |
| | | } |
| | | |
| | |
| | | |
| | | const handleProductChange = value => { |
| | | form.productMaterialSkuId = undefined; |
| | | // 查找选中的产品名称 |
| | | const findProductName = (options, value) => { |
| | | for (const option of options) { |
| | | if (option.value === value) { |
| | | return option.label; |
| | | } |
| | | if (option.children) { |
| | | const found = findProductName(option.children, value); |
| | | if (found) { |
| | | return found; |
| | | } |
| | | } |
| | | } |
| | | return ""; |
| | | }; |
| | | form.productName = findProductName(productOptions.value, value); |
| | | // 触发强度字段验证 |
| | | if (formRef.value) { |
| | | formRef.value.validateField("strength"); |
| | | } |
| | | fetchSpecificationOptions(value); |
| | | }; |
| | | |
| | | const fetchSpecificationOptions = materialId => { |
| | | const fetchSpecificationOptions = productId => { |
| | | specificationOptions.value = []; |
| | | if (materialId) { |
| | | modelListPage({ materialId: materialId }).then(res => { |
| | | specificationOptions.value = res.data; |
| | | if (productId) { |
| | | modelListPage({ productId: productId, size: -1, current: -1 }).then(res => { |
| | | specificationOptions.value = res.data.records; |
| | | }); |
| | | } |
| | | }; |
| | |
| | | ); |
| | | if (selectedModel) { |
| | | form.materialCode = selectedModel.materialCode; |
| | | // 解析规格字符串获取长宽高 |
| | | const model = selectedModel.model; |
| | | if (model) { |
| | | const dimensions = model.match(/^(\d+)\*(\d+)\*(\d+)$/); |
| | | if (dimensions && dimensions.length === 4) { |
| | | form.length = parseInt(dimensions[1]); |
| | | form.width = parseInt(dimensions[2]); |
| | | form.height = parseInt(dimensions[3]); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | |
| | | searchForm: { |
| | | customerName: "", |
| | | productName: "", |
| | | specification: "", |
| | | model: "", |
| | | materialCode: "", |
| | | applyNo: "", |
| | | dateRange: [], |
| | |
| | | Object.assign(searchForm.value, { |
| | | customerName: "", |
| | | productName: "", |
| | | specification: "", |
| | | model: "", |
| | | materialCode: "", |
| | | applyNo: "", |
| | | dateRange: [], |
| | |
| | | .catch(() => {}); |
| | | }; |
| | | const sumAssignedQuantity = ref(0); |
| | | // 响应式数据 |
| | | const strengthError = ref(""); |
| | | |
| | | // 处理合并下发按钮点击 |
| | | const handleMerge = () => { |
| | | if (selectedRows.value.length === 0) { |
| | |
| | | return; |
| | | } |
| | | console.log(selectedRows.value); |
| | | // 检查强度一致性 |
| | | const firstRow = selectedRows.value[0]; |
| | | const productName = firstRow.productName || ""; |
| | | let strengthConsistent = true; |
| | | let firstStrength = firstRow.strength || ""; |
| | | strengthError.value = ""; |
| | | |
| | | if (productName === "砌块") { |
| | | for (const row of selectedRows.value) { |
| | | if (row.strength !== firstStrength) { |
| | | strengthConsistent = false; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (!strengthConsistent) { |
| | | strengthError.value = "选择的砌块强度不一致,请重新选择"; |
| | | } |
| | | } |
| | | |
| | | // 计算总制造数量 |
| | | const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => { |
| | | return ( |
| | |
| | | sumAssignedQuantity.value = totalAssignedQuantity; |
| | | console.log(totalAssignedQuantity); |
| | | // 设置表单数据 |
| | | const firstRow = selectedRows.value[0]; |
| | | mergeForm.materialCode = selectedserialNo.value; |
| | | mergeForm.productName = firstRow.productName || ""; |
| | | mergeForm.specification = firstRow.specification || ""; |
| | | mergeForm.productName = productName; |
| | | mergeForm.model = firstRow.model || ""; |
| | | mergeForm.length = firstRow.length || 0; |
| | | mergeForm.width = firstRow.width || 0; |
| | | mergeForm.height = firstRow.height || 0; |
| | | mergeForm.totalAssignedQuantity = totalAssignedQuantity; |
| | | mergeForm.planCompleteTime = firstRow.planCompleteTime || ""; |
| | | mergeForm.strength = firstStrength; |
| | | mergeForm.ids = selectedRows.value.map(row => row.id); |
| | | |
| | | // 打开弹窗 |
| | |
| | | const handleMergeSubmit = () => { |
| | | if (mergeForm.totalAssignedQuantity === 0) { |
| | | ElMessage.warning("请输入生产方数"); |
| | | return; |
| | | } |
| | | // 验证砌块产品的强度 |
| | | if (mergeForm.productName === "砌块" && !mergeForm.strength) { |
| | | ElMessage.error("砌块产品的强度为必填项"); |
| | | return; |
| | | } |
| | | console.log(sumAssignedQuantity.value, "sumAssignedQuantity"); |
| | |
| | | productName: "", |
| | | productMaterialId: undefined, |
| | | productMaterialSkuId: undefined, |
| | | specification: "", |
| | | model: "", |
| | | materialCode: "", |
| | | quantity: 0, |
| | | volume: 0, |
| | |
| | | productName: row.productName || "", |
| | | productMaterialId: row.productMaterialId || undefined, |
| | | productMaterialSkuId: row.productMaterialSkuId || undefined, |
| | | specification: row.specification || "", |
| | | model: row.model || "", |
| | | materialCode: row.materialCode || "", |
| | | quantity: row.quantity || 0, |
| | | volume: row.volume || 0, |
| | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | if (form.volume === 0) { |
| | | proxy.$modal.msgError("方数不能为0"); |
| | | return; |
| | | } |
| | | if (form.v === "add") { |
| | | payload.id = null; |
| | | } |
| | | const payload = { ...form }; |
| | | if (operationType.value === "add") { |
| | | payload.id = null; |
| | | productionPlanAdd(payload) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess( |
| | |
| | | color: #ffffff; |
| | | border-bottom: none; |
| | | padding: 16px 0; |
| | | font-size: 14px; |
| | | letter-spacing: 0.5px; |
| | | } |
| | | } |
| | |
| | | border-bottom: 1px solid #f0f0f0; |
| | | padding: 14px 0; |
| | | color: #303133; |
| | | font-size: 13px; |
| | | } |
| | | } |
| | | |
| | |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | font-family: "Courier New", monospace; |
| | | font-size: 14px; |
| | | text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2); |
| | | } |
| | | |
| | |
| | | // 日期字段样式 |
| | | .date-cell { |
| | | color: #909399; |
| | | font-size: 12px; |
| | | font-style: italic; |
| | | } |
| | | |