| | |
| | | <FormDialog |
| | | v-model="productFormVisible" |
| | | :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" |
| | | :width="'40%'" |
| | | :width="'60%'" |
| | | :operation-type="productOperationType" |
| | | @close="closeProductDia" |
| | | @confirm="submitProduct" |
| | | @cancel="closeProductDia"> |
| | | <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef"> |
| | | <!-- 每行三个:产品大类/规格型号/单位 --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="产品大类:" prop="productCategory"> |
| | | <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> |
| | | </el-select> --> |
| | | <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly |
| | | @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" /> |
| | | <el-tree-select |
| | | v-model="productForm.productCategory" |
| | | placeholder="请选择" |
| | | clearable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="规格型号:" prop="productModelId"> |
| | | <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel" filterable> |
| | | <el-select |
| | | v-model="productForm.productModelId" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="getProductModel" |
| | | filterable |
| | | style="width: 100%" |
| | | > |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="单位:" prop="unit"> |
| | | <el-input v-model="productForm.unit" placeholder="请输入" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | </el-row> |
| | | |
| | | <!-- 每行三个:税率/含税单价/数量 --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="税率(%):" prop="taxRate"> |
| | | <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate"> |
| | | <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate" style="width: 100%"> |
| | | <el-option label="1" value="1" /> |
| | | <el-option label="6" value="6" /> |
| | | <el-option label="13" value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice"> |
| | | <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%" |
| | | :precision="2" |
| | | placeholder="请输入" clearable @change="calculateFromUnitPrice" /> |
| | | <el-input-number |
| | | :step="0.01" |
| | | :min="0" |
| | | v-model="productForm.taxInclusiveUnitPrice" |
| | | style="width: 100%" |
| | | :precision="2" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="calculateFromUnitPrice" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input-number :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable |
| | | :precision="2" |
| | | @change="calculateFromQuantity" style="width: 100%" /> |
| | | <el-input-number |
| | | :step="0.1" |
| | | :min="0" |
| | | v-model="productForm.quantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | :precision="2" |
| | | @change="() => { calculateFromQuantity(); recalcAreaTotals(); }" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 每行三个:含税总价/不含税总价/发票类型 --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice"> |
| | | <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice"> |
| | | <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="发票类型:" prop="invoiceType"> |
| | | <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable> |
| | | <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable style="width: 100%"> |
| | | <el-option label="增普票" value="增普票" /> |
| | | <el-option label="增专票" value="增专票" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 每行三个:宽/高/实际单片面积 --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="宽(mm):" prop="width"> |
| | | <el-input-number |
| | | v-model="productForm.width" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入宽(mm)" |
| | | clearable |
| | | @change="recalcAreaFromWidthHeight" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="高(mm):" prop="height"> |
| | | <el-input-number |
| | | v-model="productForm.height" |
| | | :min="0" |
| | | :step="1" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入高(mm)" |
| | | clearable |
| | | @change="recalcAreaFromWidthHeight" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="周长(cm):" prop="perimeter"> |
| | | <el-input-number |
| | | v-model="productForm.perimeter" |
| | | :min="0" |
| | | :step="0.01" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | :disabled="true" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 每行三个:实际单片面积/实际总面积/结算单片面积 --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="实际单片面积(㎡):" prop="actualPieceArea"> |
| | | <el-input-number |
| | | v-model="productForm.actualPieceArea" |
| | | :min="0" |
| | | :step="0.00001" |
| | | :precision="5" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="recalcAreaTotals" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="实际总面积(㎡):" prop="actualTotalArea"> |
| | | <el-input-number |
| | | v-model="productForm.actualTotalArea" |
| | | :min="0" |
| | | :step="0.00001" |
| | | :precision="5" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="结算单片面积(㎡):" prop="settlePieceArea"> |
| | | <el-input-number |
| | | v-model="productForm.settlePieceArea" |
| | | :min="0" |
| | | :step="0.00001" |
| | | :precision="5" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="recalcAreaTotals" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="结算总面积(㎡):" prop="settleTotalArea"> |
| | | <el-input-number |
| | | v-model="productForm.settleTotalArea" |
| | | :min="0" |
| | | :step="0.00001" |
| | | :precision="5" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="结算总面积(㎡):" prop="settleTotalArea"> |
| | | <el-input-number |
| | | v-model="productForm.settleTotalArea" |
| | | :min="0" |
| | | :step="0.00001" |
| | | :precision="5" |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="重箱:" prop="heavyBox"> |
| | | <el-input v-model="productForm.heavyBox" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 其他金额(占满一行:等同于 3 列网格的整行) --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="加工要求:" prop="processRequirement"> |
| | | <el-input v-model="productForm.processRequirement" placeholder="请输入加工要求" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="备注:" prop="remark"> |
| | | <el-input v-model="productForm.remark" placeholder="请输入备注" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item> |
| | | <template #label> |
| | | <div style="display:flex; align-items:center; gap: 12px; width: 100%;"> |
| | | <div style="font-weight: 600; color: #303133;">其他金额:</div> |
| | | <div style="color:#909399; font-size: 13px; flex: 1;"> |
| | | 已选择 {{ productForm?.salesProductProcessList?.length || 0 }} 项 |
| | | </div> |
| | | <el-button |
| | | v-if="operationType !== 'view'" |
| | | type="primary" |
| | | plain |
| | | size="small" |
| | | @click="startAddOtherAmount" |
| | | > |
| | | 新增 |
| | | </el-button> |
| | | </div> |
| | | </template> |
| | | |
| | | <div style="display:flex; flex-direction:column; gap: 12px;"> |
| | | <div v-if="Array.isArray(productForm?.salesProductProcessList) && productForm.salesProductProcessList.length > 0" |
| | | style="display:flex; flex-wrap:wrap; gap: 12px; align-items:flex-start;" |
| | | > |
| | | <div |
| | | v-for="(item, index) in productForm.salesProductProcessList" |
| | | :key="String(item.id) + '_' + index" |
| | | style="display:flex; gap: 10px; align-items:center; padding: 10px 12px; border: 1px solid #ebeef5; border-radius: 8px; box-sizing:border-box; min-width: 0;" |
| | | :style="getOtherAmountCardFlexStyle()" |
| | | > |
| | | <div style="flex: 1; min-width: 0;"> |
| | | <el-tag type="info" style="width: 100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"> |
| | | {{ item.processName }} |
| | | </el-tag> |
| | | </div> |
| | | <div style="flex: 1;"> |
| | | <el-input-number |
| | | v-model="item.quantity" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | style="width: 100%;" |
| | | placeholder="请输入数量" |
| | | :disabled="operationType === 'view'" |
| | | /> |
| | | </div> |
| | | <el-button |
| | | v-if="operationType !== 'view'" |
| | | type="danger" |
| | | link |
| | | size="small" |
| | | @click="removeOtherAmountAt(index)" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-else style="color:#909399; font-size: 13px;"> |
| | | 暂无其他金额 |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </FormDialog> |
| | | <!-- 其他金额:新增弹框 --> |
| | | <el-dialog |
| | | v-model="otherAmountAddDialogVisible" |
| | | title="新增其他金额" |
| | | width="520px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div style="padding: 4px 0 10px;"> |
| | | <div style="font-size: 14px; color: #606266; margin-bottom: 10px;"> |
| | | 请选择要新增的其他金额项目 |
| | | </div> |
| | | <el-select |
| | | v-model="otherAmountAddId" |
| | | filterable |
| | | clearable |
| | | placeholder="请选择其他金额项目" |
| | | style="width: 100%;" |
| | | :disabled="operationType === 'view'" |
| | | > |
| | | <el-option |
| | | v-for="item in otherAmountSelectOptions" |
| | | :key="item.id" |
| | | :label="item.processName" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </div> |
| | | <template #footer> |
| | | <el-button @click="cancelAddOtherAmount">取消</el-button> |
| | | <el-button |
| | | type="primary" |
| | | @click="confirmAddOtherAmount" |
| | | :disabled="operationType === 'view' || otherAmountAddId === null || otherAmountAddId === undefined || otherAmountAddId === ''" |
| | | > |
| | | 确认添加 |
| | | </el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 导入弹窗 --> |
| | | <FormDialog |
| | | v-model="importUpload.open" |
| | |
| | | :rules="otherAmountRules" |
| | | ref="otherAmountFormRef" |
| | | > |
| | | <el-form-item label="编码code"> |
| | | <el-form-item label="编码"> |
| | | <el-input v-model="otherAmountForm.code" placeholder="请输入编码(可选)" clearable /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="项目processName" prop="processName"> |
| | | <el-form-item label="项目" prop="processName"> |
| | | <el-input v-model="otherAmountForm.processName" placeholder="请输入项目名称" clearable /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="数量quantity" prop="quantity"> |
| | | <el-form-item label="数量" prop="quantity"> |
| | | <el-input-number |
| | | v-model="otherAmountForm.quantity" |
| | | :min="0" |
| | |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="单价unitPrice(元)" prop="unitPrice"> |
| | | <el-form-item label="单价(元)" prop="unitPrice"> |
| | | <el-input-number |
| | | v-model="otherAmountForm.unitPrice" |
| | | :min="0" |
| | |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="金额amount(元)"> |
| | | <el-form-item label="金额(元)"> |
| | | <el-input v-model="otherAmountForm.amount" disabled /> |
| | | </el-form-item> |
| | | |
| | |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | // 新增:尺寸/面积/加工与其他金额 |
| | | width: 0, // 宽(mm) |
| | | height: 0, // 高(mm) |
| | | perimeter: 0, // 周长(cm) = (宽+高)*2,宽高为 mm |
| | | // 面积字段(㎡) |
| | | actualPieceArea: 0, // 实际单片面积(㎡) |
| | | actualTotalArea: 0, // 实际总面积(㎡) |
| | | settlePieceArea: 0, // 结算单片面积(㎡) |
| | | settleTotalArea: 0, // 结算总面积(㎡) |
| | | processRequirement: "", // 加工要求 |
| | | remark: "", // 备注 |
| | | salesProductProcessList: [], // 其他金额:[{id, processName, quantity}] |
| | | }, |
| | | productRules: { |
| | | productCategory: [{ required: true, message: "请选择", trigger: "change" }], |
| | |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | }; |
| | | |
| | | // 产品弹框:其他金额多选下拉(基于“其他金额维护”查询接口) |
| | | const otherAmountSelectOptions = ref([]); // [{id, processName}] |
| | | const otherAmountSelectOptionsLoading = ref(false); |
| | | |
| | | const fetchOtherAmountSelectOptions = async (force = false) => { |
| | | if (!force && otherAmountSelectOptions.value.length > 0) return; |
| | | otherAmountSelectOptionsLoading.value = true; |
| | | try { |
| | | const params = { |
| | | current: 1, |
| | | // 下拉框尽量一次性拉全,避免多次分页影响选择体验 |
| | | size: 1000, |
| | | }; |
| | | const res = await salesLedgerProductProcessList(params); |
| | | const records = res?.records ?? res?.data?.records ?? []; |
| | | |
| | | otherAmountSelectOptions.value = records.map((item) => ({ |
| | | id: item.id, |
| | | processName: item.processName ?? "", |
| | | })); |
| | | } finally { |
| | | otherAmountSelectOptionsLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const normalizeOtherAmountsFromRow = (row) => { |
| | | if (!row) return []; |
| | | const raw = |
| | | row.other_amounts ?? |
| | | row.otherAmounts ?? |
| | | row.otherAmountProjects ?? |
| | | row.otherAmountList ?? |
| | | row.salesProductProcessList ?? |
| | | []; |
| | | |
| | | if (!Array.isArray(raw)) return []; |
| | | if (raw.length === 0) return []; |
| | | |
| | | // 情况1:后端直接返回 [{id, processName}...] |
| | | if (typeof raw[0] === "object") { |
| | | return raw |
| | | .map((it) => { |
| | | const id = it?.id ?? it?.processId ?? it?.otherAmountId ?? null; |
| | | const quantity = |
| | | Number( |
| | | it?.quantity ?? |
| | | it?.processQuantity ?? |
| | | it?.otherQuantity ?? |
| | | it?.process_quantity ?? |
| | | it?.other_amount_quantity |
| | | ) || 0; |
| | | return { |
| | | id, |
| | | processName: it?.processName ?? "", |
| | | quantity, |
| | | }; |
| | | }) |
| | | .filter((it) => it.id !== null && it.id !== undefined && it.id !== ""); |
| | | } |
| | | |
| | | // 情况2:后端只返回 ids: [1,2,3] |
| | | return raw |
| | | .map((id) => ({ |
| | | id, |
| | | processName: "", |
| | | quantity: 0, |
| | | })) |
| | | .filter((it) => it.id !== null && it.id !== undefined && it.id !== ""); |
| | | }; |
| | | |
| | | const mergeOtherAmountOptionsBySelection = (selected) => { |
| | | const list = Array.isArray(selected) ? selected : []; |
| | | for (const s of list) { |
| | | const exists = otherAmountSelectOptions.value.some((o) => String(o.id) === String(s.id)); |
| | | if (!exists) { |
| | | otherAmountSelectOptions.value.push({ |
| | | id: s.id, |
| | | processName: s.processName ?? "", |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const fillOtherAmountProcessName = (selected) => { |
| | | const list = Array.isArray(selected) ? selected : []; |
| | | return list.map((s) => { |
| | | const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(s.id)); |
| | | return { |
| | | id: s.id, |
| | | processName: opt?.processName ?? s.processName ?? "", |
| | | quantity: Number(s.quantity ?? 0) || 0, |
| | | }; |
| | | }); |
| | | }; |
| | | |
| | | // “其他金额”卡片布局:只有一条时占满整行;>=2时两列排布 |
| | | const getOtherAmountCardFlexStyle = () => { |
| | | const list = productForm.value?.salesProductProcessList; |
| | | const len = Array.isArray(list) ? list.length : 0; |
| | | if (len === 1) { |
| | | return { flex: "0 0 100%", maxWidth: "100%", width: "100%" }; |
| | | } |
| | | return { |
| | | flex: "0 0 calc(50% - 6px)", |
| | | maxWidth: "calc(50% - 6px)", |
| | | width: "calc(50% - 6px)", |
| | | }; |
| | | }; |
| | | |
| | | // 其他金额:点击“新增”后在弹窗里选择一个项目 |
| | | const otherAmountAddDialogVisible = ref(false); |
| | | const otherAmountAddId = ref(null); |
| | | |
| | | const startAddOtherAmount = () => { |
| | | if (operationType.value === "view") return; |
| | | otherAmountAddDialogVisible.value = true; |
| | | otherAmountAddId.value = null; |
| | | // 通常 openProductForm 已经拉过 options,这里兜底 |
| | | if (otherAmountSelectOptions.value.length === 0) { |
| | | fetchOtherAmountSelectOptions(true); |
| | | } |
| | | }; |
| | | |
| | | const cancelAddOtherAmount = () => { |
| | | otherAmountAddDialogVisible.value = false; |
| | | otherAmountAddId.value = null; |
| | | }; |
| | | |
| | | const handleOtherAmountSelected = (id) => { |
| | | const selectedId = id ?? otherAmountAddId.value; |
| | | if (selectedId === null || selectedId === undefined || selectedId === "") return; |
| | | const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(selectedId)); |
| | | if (!opt) return; |
| | | |
| | | const exists = (productForm.value?.salesProductProcessList ?? []).some( |
| | | (it) => String(it?.id) === String(opt.id) |
| | | ); |
| | | if (exists) { |
| | | proxy.$modal.msgWarning("该其他金额项目已添加"); |
| | | return; |
| | | } |
| | | |
| | | productForm.value.salesProductProcessList.push({ |
| | | id: opt.id, |
| | | processName: opt.processName, |
| | | quantity: 0, |
| | | }); |
| | | |
| | | // 选择完成后关闭弹窗,下一次可再次点击“新增”继续添加 |
| | | otherAmountAddDialogVisible.value = false; |
| | | otherAmountAddId.value = null; |
| | | }; |
| | | |
| | | const confirmAddOtherAmount = () => { |
| | | handleOtherAmountSelected(otherAmountAddId.value); |
| | | }; |
| | | |
| | | const removeOtherAmountAt = (index) => { |
| | | if (operationType.value === "view") return; |
| | | if (!Array.isArray(productForm.value?.salesProductProcessList)) return; |
| | | productForm.value.salesProductProcessList.splice(index, 1); |
| | | }; |
| | | |
| | | // 发货审批人节点(仿协同审批 infoFormDia.vue) |
| | |
| | | taxInclusiveTotalPrice: taxInclusiveTotalPrice, |
| | | taxExclusiveTotalPrice: taxExclusiveTotalPrice, |
| | | invoiceType: "增普票", |
| | | // 新增:默认值(避免后续提交时字段缺失) |
| | | width: 0, |
| | | height: 0, |
| | | actualPieceArea: 0, |
| | | actualTotalArea: 0, |
| | | settlePieceArea: 0, |
| | | settleTotalArea: 0, |
| | | processRequirement: "", |
| | | remark: "", |
| | | salesProductProcessList: [], |
| | | }; |
| | | }); |
| | | |
| | |
| | | productOperationType.value = type; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | | // 确保多选项默认是数组,避免 el-select multiple 报错 |
| | | productForm.value.salesProductProcessList = []; |
| | | otherAmountAddDialogVisible.value = false; |
| | | otherAmountAddId.value = null; |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | |
| | | // 字段命名兼容:优先驼峰(如 actualPieceArea),兼容后端可能返回下划线(如 actual_piece_area) |
| | | productForm.value.actualPieceArea = row?.actualPieceArea ?? row?.actual_piece_area ?? 0; |
| | | productForm.value.actualTotalArea = row?.actualTotalArea ?? row?.actual_total_area ?? 0; |
| | | productForm.value.settlePieceArea = row?.settlePieceArea ?? row?.settle_piece_area ?? 0; |
| | | productForm.value.settleTotalArea = row?.settleTotalArea ?? row?.settle_total_area ?? 0; |
| | | |
| | | // 加工要求/备注兼容:后端可能使用其它命名 |
| | | productForm.value.processRequirement = |
| | | row?.processRequirement ?? row?.process_requirement ?? ""; |
| | | productForm.value.remark = row?.remark ?? row?.remarks ?? ""; |
| | | |
| | | // 周长回显(如后端返回;最终仍以公式计算为准) |
| | | productForm.value.perimeter = |
| | | row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0; |
| | | |
| | | productForm.value.salesProductProcessList = normalizeOtherAmountsFromRow(row); |
| | | productIndex.value = index; |
| | | // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表 |
| | | try { |
| | |
| | | // 加载失败时保持可编辑,不中断弹窗 |
| | | console.error("加载产品规格型号失败", e); |
| | | } |
| | | |
| | | // 根据当前宽高重新计算周长与面积 |
| | | recalcPerimeterFromWidthHeight(); |
| | | recalcAreaFromWidthHeight(); |
| | | |
| | | // 回显“其他金额”多选:先拉取下拉选项,再补齐 processName |
| | | await fetchOtherAmountSelectOptions(true); |
| | | mergeOtherAmountOptionsBySelection(productForm.value.salesProductProcessList); |
| | | productForm.value.salesProductProcessList = fillOtherAmountProcessName(productForm.value.salesProductProcessList); |
| | | } else { |
| | | getProductOptions() |
| | | // 新增时下拉选项加载一次即可 |
| | | fetchOtherAmountSelectOptions(true); |
| | | } |
| | | productFormVisible.value = true; |
| | | }; |
| | |
| | | const submitProduct = () => { |
| | | proxy.$refs["productFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // 面积/总计字段在提交前兜底计算一次 |
| | | recalcAreaTotals(); |
| | | // 其他金额只提交 {id, processName, quantity}(后端字段:salesProductProcessList) |
| | | productForm.value.salesProductProcessList = (Array.isArray(productForm.value.salesProductProcessList) |
| | | ? productForm.value.salesProductProcessList |
| | | : [] |
| | | ) |
| | | .map((it) => ({ |
| | | id: it?.id, |
| | | processName: it?.processName ?? "", |
| | | quantity: Number(it?.quantity ?? 0) || 0, |
| | | })) |
| | | .filter((it) => it.id !== null && it.id !== undefined && it.id !== ""); |
| | | |
| | | if (operationType.value === "edit") { |
| | | submitProductEdit(); |
| | | } else { |
| | |
| | | const closeProductDia = () => { |
| | | proxy.resetForm("productFormRef"); |
| | | productFormVisible.value = false; |
| | | otherAmountAddDialogVisible.value = false; |
| | | otherAmountAddId.value = null; |
| | | }; |
| | | // 导入 |
| | | const handleImport = () => { |
| | |
| | | } |
| | | }; |
| | | |
| | | // 新增:尺寸(宽高)与面积(单片/总计)联动 |
| | | const recalcAreaTotals = () => { |
| | | const qty = Number(productForm.value.quantity ?? 0) || 0; |
| | | const actualPiece = Number(productForm.value.actualPieceArea ?? 0) || 0; |
| | | const settlePiece = Number(productForm.value.settlePieceArea ?? 0) || 0; |
| | | |
| | | productForm.value.actualTotalArea = Number((actualPiece * qty).toFixed(5)); |
| | | productForm.value.settleTotalArea = Number((settlePiece * qty).toFixed(5)); |
| | | }; |
| | | |
| | | // 新增:周长(cm)(重箱heavyBox周长) |
| | | // width/height 单位为 mm,因此周长(cm)需要除以 10 |
| | | const recalcPerimeterFromWidthHeight = () => { |
| | | const width = Number(productForm.value.width ?? 0) || 0; |
| | | const height = Number(productForm.value.height ?? 0) || 0; |
| | | |
| | | if (width <= 0 || height <= 0) { |
| | | productForm.value.perimeter = 0; |
| | | return; |
| | | } |
| | | |
| | | // 周长 = (宽 + 高) * 2,单位从 mm 转为 cm:/10 |
| | | productForm.value.perimeter = Number((((width + height) * 2) / 10).toFixed(2)); |
| | | }; |
| | | |
| | | const recalcAreaFromWidthHeight = () => { |
| | | const width = Number(productForm.value.width ?? 0) || 0; |
| | | const height = Number(productForm.value.height ?? 0) || 0; |
| | | |
| | | if (width <= 0 || height <= 0) { |
| | | // 宽高为空/为0时,把单片面积与总面积置为0 |
| | | productForm.value.actualPieceArea = 0; |
| | | productForm.value.actualTotalArea = 0; |
| | | productForm.value.perimeter = 0; |
| | | |
| | | // 只有在结算单片面积也为空/为0时,才同步置0,避免覆盖用户手动填写 |
| | | const settlePiece = Number(productForm.value.settlePieceArea ?? 0) || 0; |
| | | if (!settlePiece) { |
| | | productForm.value.settlePieceArea = 0; |
| | | } |
| | | productForm.value.settleTotalArea = Number( |
| | | ((Number(productForm.value.settlePieceArea ?? 0) || 0) * (Number(productForm.value.quantity ?? 0) || 0)).toFixed(5) |
| | | ); |
| | | return; |
| | | } |
| | | |
| | | const computedPieceArea = (width * height) / 1e6; // mm*mm -> ㎡ |
| | | const computed = Number(computedPieceArea.toFixed(5)); |
| | | |
| | | productForm.value.actualPieceArea = computed; |
| | | |
| | | // settlePieceArea:若用户未填写/为0,则默认使用宽高计算值 |
| | | const settlePieceRaw = Number(productForm.value.settlePieceArea ?? 0) || 0; |
| | | if (!settlePieceRaw) { |
| | | productForm.value.settlePieceArea = computed; |
| | | } |
| | | |
| | | recalcPerimeterFromWidthHeight(); |
| | | recalcAreaTotals(); |
| | | }; |
| | | |
| | | // 根据含税总价计算含税单价和数量 |
| | | const calculateFromTotalPrice = () => { |
| | | if (isCalculating.value) return; |
| | |
| | | background-color: #F4DEFA; |
| | | } |
| | | |
| | | .other-amount-select { |
| | | /* 多选标签区域强制单行,不让输入框随标签换行而拉高高度 */ |
| | | :deep .el-select__tags { |
| | | display: flex; |
| | | flex-wrap: nowrap !important; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | max-height: 32px; |
| | | } |
| | | } |
| | | |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |