| | |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <OtherAmountMaintenanceButton /> |
| | | <ProcessFlowMaintenanceButton /> |
| | | </div> |
| | | <ProcessFlowConfigSelectDialog |
| | | v-model:visible="processFlowSelectDialogVisible" |
| | | @confirm="handleProcessFlowSelectConfirm" |
| | | /> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')"> |
| | | 新增台账 |
| | | </el-button> |
| | | <el-button type="primary" plain @click="openOtherAmountDialog"> |
| | | 其他金额维护 |
| | | <el-button type="primary" @click="handleBulkDelivery"> |
| | | 发货 |
| | | </el-button> |
| | | <el-button type="primary" plain @click="handleImport">导入</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | |
| | | <el-table-column align="center" label="序号" type="index"/> |
| | | <el-table-column label="产品大类" prop="productCategory" /> |
| | | <el-table-column label="规格型号" prop="specificationModel" /> |
| | | <el-table-column label="单位" prop="unit" /> |
| | | <el-table-column label="厚度" prop="thickness" min-width="90"> |
| | | <template #default="scope"> |
| | | {{ scope.row.thickness ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="产品状态" |
| | | width="100px" |
| | | align="center"> |
| | |
| | | <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <!--操作--> |
| | | <el-table-column Width="60px" label="操作" align="center"> |
| | | <!-- <el-table-column Width="60px" label="操作" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | |
| | | 发货 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> --> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="操作" width="130" align="center"> |
| | | <el-table-column fixed="right" label="操作" width="200" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">编辑</el-button> |
| | | <el-button link type="primary" @click="openProcessFlowSelect(scope.row)" :disabled="!scope.row.isEdit">工艺路线</el-button> |
| | | <el-button link type="primary" @click="downLoadFile(scope.row)">附件</el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <el-table-column align="center" label="序号" type="index" width="60" /> |
| | | <el-table-column label="产品大类" prop="productCategory" /> |
| | | <el-table-column label="规格型号" prop="specificationModel" /> |
| | | <el-table-column label="单位" prop="unit" /> |
| | | <el-table-column label="厚度" prop="thickness" min-width="90"> |
| | | <template #default="scope"> |
| | | {{ scope.row.thickness ?? "" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="数量" prop="quantity" /> |
| | | <el-table-column label="税率(%)" prop="taxRate" /> |
| | | <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> |
| | |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注:" prop="remarks"> |
| | | <el-input v-model="form.remarks" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="客户备注:" prop="customerRemarks"> |
| | | <el-input |
| | | v-model="form.customerRemarks" |
| | | placeholder="请输入" |
| | | clearable |
| | | type="textarea" |
| | | :rows="2" |
| | | :disabled="operationType === 'view'" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="单位:" prop="unit"> |
| | | <el-input v-model="productForm.unit" placeholder="请输入" clearable /> |
| | | <el-form-item label="厚度:" prop="thickness"> |
| | | <el-input-number |
| | | v-model="productForm.thickness" |
| | | :min="0" |
| | | :step="0.000000000000001" |
| | | :precision="15" |
| | | style="width: 100%;" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="楼层编号:" prop="floorCode"> |
| | | <el-input v-model="productForm.floorCode" placeholder="请输入楼层编号" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 其他金额(占满一行:等同于 3 列网格的整行) --> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | |
| | | </el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 导入弹窗 --> |
| | | <FormDialog |
| | | v-model="importUpload.open" |
| | |
| | | <tr> |
| | | <th>产品名称</th> |
| | | <th>规格型号</th> |
| | | <th>单位</th> |
| | | <th>厚度</th> |
| | | <th>单价</th> |
| | | <th>零售数量</th> |
| | | <th>零售金额</th> |
| | |
| | | <tr v-for="product in item.products" :key="product.id"> |
| | | <td>{{ product.productCategory || '' }}</td> |
| | | <td>{{ product.specificationModel || '' }}</td> |
| | | <td>{{ product.unit || '' }}</td> |
| | | <td>{{ product.thickness ?? '' }}</td> |
| | | <td>{{ product.taxInclusiveUnitPrice || '0' }}</td> |
| | | <td>{{ product.quantity || '0' }}</td> |
| | | <td>{{ product.taxInclusiveTotalPrice || '0' }}</td> |
| | |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 其他金额维护(新增/编辑/删除) --> |
| | | <el-dialog |
| | | v-model="otherAmountDialogVisible" |
| | | title="其他金额维护" |
| | | width="80%" |
| | | :close-on-click-modal="false" |
| | | @close="closeOtherAmountDialog" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="14"> |
| | | <el-table |
| | | :data="otherAmountRecords" |
| | | border |
| | | v-loading="otherAmountLoading" |
| | | height="55vh" |
| | | > |
| | | <el-table-column label="编码" prop="code" min-width="120" show-overflow-tooltip /> |
| | | <el-table-column label="项目" prop="processName" min-width="180" show-overflow-tooltip /> |
| | | <el-table-column label="数量" prop="quantity" min-width="110" :formatter="formattedNumber" /> |
| | | <el-table-column label="单价(元)" prop="unitPrice" min-width="130" :formatter="formattedNumber" /> |
| | | <el-table-column label="金额(元)" prop="amount" min-width="160" :formatter="formattedNumber" /> |
| | | <el-table-column fixed="right" label="操作" width="160" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="handleOtherEdit(scope.row)">编辑</el-button> |
| | | <el-button link type="danger" size="small" @click="handleOtherDelete(scope.row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <pagination |
| | | v-show="otherAmountTotal > 0" |
| | | :total="otherAmountTotal" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="otherAmountPage.current" |
| | | :limit="otherAmountPage.size" |
| | | @pagination="otherAmountPaginationChange" |
| | | /> |
| | | </el-col> |
| | | |
| | | <el-col :span="10"> |
| | | <div style="padding: 8px 0;"> |
| | | <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom: 10px;"> |
| | | <div style="font-weight:600;"> |
| | | {{ otherAmountOperationType === 'add' ? '新增其他金额' : '编辑其他金额' }} |
| | | </div> |
| | | <el-button |
| | | type="primary" |
| | | plain |
| | | size="small" |
| | | @click="handleOtherAdd" |
| | | :disabled="otherAmountOperationType === 'add'" |
| | | > |
| | | 新增 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <el-form |
| | | :model="otherAmountForm" |
| | | label-width="120px" |
| | | label-position="top" |
| | | :rules="otherAmountRules" |
| | | ref="otherAmountFormRef" |
| | | > |
| | | <el-form-item label="编码"> |
| | | <el-input v-model="otherAmountForm.code" placeholder="请输入编码(可选)" clearable /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="项目" prop="processName"> |
| | | <el-input v-model="otherAmountForm.processName" placeholder="请输入项目名称" clearable /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="数量" prop="quantity"> |
| | | <el-input-number |
| | | v-model="otherAmountForm.quantity" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width:100%" |
| | | placeholder="请输入数量" |
| | | clearable |
| | | @change="recalcOtherAmount" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="单价(元)" prop="unitPrice"> |
| | | <el-input-number |
| | | v-model="otherAmountForm.unitPrice" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width:100%" |
| | | placeholder="请输入单价" |
| | | clearable |
| | | @change="recalcOtherAmount" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="金额(元)"> |
| | | <el-input v-model="otherAmountForm.amount" disabled /> |
| | | </el-form-item> |
| | | |
| | | <div style="display:flex; justify-content:flex-end; gap: 10px; margin-top: 8px;"> |
| | | <el-button @click="closeOtherAmountDialog">取消</el-button> |
| | | <el-button type="primary" @click="submitOtherAmountForm">保存</el-button> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import FileListDialog from '@/components/Dialog/FileListDialog.vue'; |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import OtherAmountMaintenanceButton from "./components/OtherAmountMaintenanceButton.vue"; |
| | | import ProcessFlowMaintenanceButton from "./components/ProcessFlowMaintenanceButton.vue"; |
| | | import ProcessFlowConfigSelectDialog from "./components/ProcessFlowConfigSelectDialog.vue"; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { |
| | | ledgerListPage, |
| | |
| | | delLedgerFile, |
| | | getProductInventory, |
| | | salesLedgerProductProcessList, |
| | | salesLedgerProductProcessAdd, |
| | | salesLedgerProductProcessUpdate, |
| | | salesLedgerProductProcessDelete, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import dayjs from "dayjs"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | import { salesLedgerProductSetProcessFlowConfig } from "@/api/salesManagement/salesProcessFlowConfig.js"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | |
| | | }); |
| | | const total = ref(0); |
| | | const fileList = ref([]); |
| | | |
| | | // 工艺路线配置选择弹窗(绑定到台账产品) |
| | | const processFlowSelectDialogVisible = ref(false); |
| | | const processFlowSelectLedgerRow = ref(null); |
| | | |
| | | // 用户信息表单弹框数据 |
| | | const operationType = ref(""); |
| | |
| | | productForm: { |
| | | productCategory: "", |
| | | specificationModel: "", |
| | | unit: "", |
| | | thickness:null, |
| | | quantity: "", |
| | | taxInclusiveUnitPrice: "", |
| | | taxRate: "", |
| | |
| | | processRequirement: "", // 加工要求 |
| | | remark: "", // 备注 |
| | | salesProductProcessList: [], // 其他金额:[{id, processName, quantity}] |
| | | processFlowConfigId: null, // 工艺流程配置绑定 |
| | | floorCode: "", // 楼层编号 |
| | | }, |
| | | productRules: { |
| | | productCategory: [{ required: true, message: "请选择", trigger: "change" }], |
| | |
| | | specificationModel: [ |
| | | { required: true, message: "请选择", trigger: "change" }, |
| | | ], |
| | | unit: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | thickness: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: "请输入", trigger: "blur" }, |
| | |
| | | |
| | | // 发货相关 |
| | | const deliveryFormVisible = ref(false); |
| | | const currentDeliveryRow = ref(null); |
| | | const currentDeliveryRows = ref([]); |
| | | const deliveryFormData = reactive({ |
| | | deliveryForm: { |
| | | type: "货车", // 货车, 快递 |
| | |
| | | }, |
| | | }); |
| | | const { deliveryForm, deliveryRules } = toRefs(deliveryFormData); |
| | | |
| | | // 其他金额维护(工序/流程金额维护) |
| | | const otherAmountDialogVisible = ref(false); |
| | | const otherAmountLoading = ref(false); |
| | | const otherAmountRecords = ref([]); |
| | | const otherAmountTotal = ref(0); |
| | | const otherAmountPage = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | }); |
| | | |
| | | const otherAmountOperationType = ref("add"); // add/edit |
| | | const otherAmountFormRef = ref(null); |
| | | const otherAmountForm = reactive({ |
| | | id: null, |
| | | code: "", // 前端字段名:code;后端接口列表返回 remark,此处进行映射 |
| | | processName: "", |
| | | quantity: 0, |
| | | unitPrice: 0, |
| | | amount: "0.00", |
| | | }); |
| | | const otherAmountRules = reactive({ |
| | | processName: [{ required: true, message: "请输入项目名称", trigger: "change" }], |
| | | quantity: [{ required: true, message: "请输入数量", trigger: "blur" }], |
| | | unitPrice: [{ required: true, message: "请输入单价", trigger: "blur" }], |
| | | }); |
| | | |
| | | const recalcOtherAmount = () => { |
| | | const quantity = Number(otherAmountForm.quantity ?? 0) || 0; |
| | | const unitPrice = Number(otherAmountForm.unitPrice ?? 0) || 0; |
| | | otherAmountForm.amount = (quantity * unitPrice).toFixed(2); |
| | | }; |
| | | |
| | | const resetOtherAmountForm = (type = "add") => { |
| | | otherAmountOperationType.value = type; |
| | | otherAmountForm.id = null; |
| | | otherAmountForm.code = ""; |
| | | otherAmountForm.processName = ""; |
| | | otherAmountForm.quantity = 0; |
| | | otherAmountForm.unitPrice = 0; |
| | | otherAmountForm.amount = "0.00"; |
| | | }; |
| | | |
| | | const openOtherAmountDialog = () => { |
| | | otherAmountDialogVisible.value = true; |
| | | resetOtherAmountForm("add"); |
| | | // 打开弹框时刷新数据,避免长时间停留导致数据过期 |
| | | otherAmountPage.current = otherAmountPage.current || 1; |
| | | fetchOtherAmountList(); |
| | | }; |
| | | |
| | | const closeOtherAmountDialog = () => { |
| | | otherAmountDialogVisible.value = false; |
| | | resetOtherAmountForm("add"); |
| | | }; |
| | | |
| | | const fetchOtherAmountList = async () => { |
| | | otherAmountLoading.value = true; |
| | | try { |
| | | const params = { |
| | | current: otherAmountPage.current, |
| | | size: otherAmountPage.size, |
| | | }; |
| | | const res = await salesLedgerProductProcessList(params); |
| | | |
| | | // 兼容不同接口响应结构:可能是 res.records / res.total 或 res.data.records / res.data.total |
| | | const records = res?.records ?? res?.data?.records ?? []; |
| | | const total = res?.total ?? res?.data?.total ?? 0; |
| | | |
| | | otherAmountRecords.value = records.map((item) => { |
| | | const quantity = Number(item.quantity ?? 0) || 0; |
| | | const unitPrice = Number(item.unitPrice ?? 0) || 0; |
| | | const amount = Number(item.amount ?? quantity * unitPrice) || 0; |
| | | return { |
| | | id: item.id, |
| | | code: item.code ?? item.remark ?? "", |
| | | processName: item.processName ?? "", |
| | | quantity, |
| | | unitPrice, |
| | | amount: amount.toFixed(2), |
| | | }; |
| | | }); |
| | | |
| | | otherAmountTotal.value = total; |
| | | } finally { |
| | | otherAmountLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const otherAmountPaginationChange = (obj) => { |
| | | otherAmountPage.current = obj.page; |
| | | otherAmountPage.size = obj.limit; |
| | | fetchOtherAmountList(); |
| | | }; |
| | | |
| | | const handleOtherAdd = () => { |
| | | resetOtherAmountForm("add"); |
| | | }; |
| | | |
| | | const handleOtherEdit = (row) => { |
| | | if (!row) return; |
| | | otherAmountOperationType.value = "edit"; |
| | | otherAmountForm.id = row.id ?? null; |
| | | otherAmountForm.code = row.code ?? ""; |
| | | otherAmountForm.processName = row.processName ?? ""; |
| | | otherAmountForm.quantity = Number(row.quantity ?? 0) || 0; |
| | | otherAmountForm.unitPrice = Number(row.unitPrice ?? 0) || 0; |
| | | recalcOtherAmount(); |
| | | }; |
| | | |
| | | const submitOtherAmountForm = () => { |
| | | otherAmountFormRef.value?.validate((valid) => { |
| | | if (!valid) return; |
| | | |
| | | const payload = { |
| | | processName: otherAmountForm.processName, |
| | | quantity: Number(otherAmountForm.quantity) || 0, |
| | | unitPrice: Number(otherAmountForm.unitPrice) || 0, |
| | | amount: Number(otherAmountForm.amount) || 0, |
| | | // 列表返回字段是 remark,这里按“code=remark”做映射 |
| | | remark: otherAmountForm.code, |
| | | // 兼容后端可能直接使用 code 字段 |
| | | code: otherAmountForm.code, |
| | | }; |
| | | |
| | | if (otherAmountOperationType.value === "edit") { |
| | | payload.id = otherAmountForm.id; |
| | | salesLedgerProductProcessUpdate(payload).then(() => { |
| | | proxy.$modal.msgSuccess("保存成功"); |
| | | fetchOtherAmountList(); |
| | | resetOtherAmountForm("add"); |
| | | }); |
| | | } else { |
| | | salesLedgerProductProcessAdd(payload).then(() => { |
| | | proxy.$modal.msgSuccess("保存成功"); |
| | | fetchOtherAmountList(); |
| | | resetOtherAmountForm("add"); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleOtherDelete = (row) => { |
| | | if (!row?.id) return; |
| | | ElMessageBox.confirm("确认删除该记录?", "删除", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | return salesLedgerProductProcessDelete(row.id).then(() => { |
| | | proxy.$modal.msgSuccess("删除成功"); |
| | | fetchOtherAmountList(); |
| | | |
| | | if (otherAmountOperationType.value === "edit" && otherAmountForm.id === row.id) { |
| | | resetOtherAmountForm("add"); |
| | | } |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | }; |
| | | |
| | | // 产品弹框:其他金额多选下拉(基于“其他金额维护”查询接口) |
| | | const otherAmountSelectOptions = ref([]); // [{id, processName}] |
| | |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 打开“工艺路线配置”选择弹窗(必须显式选择) |
| | | const openProcessFlowSelect = (ledgerRow) => { |
| | | if (!ledgerRow) return; |
| | | if (!ledgerRow.isEdit) return; |
| | | processFlowSelectLedgerRow.value = ledgerRow; |
| | | processFlowSelectDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 将配置应用到台账下的所有未发货产品 |
| | | const handleProcessFlowSelectConfirm = async (configId) => { |
| | | const ledgerRow = processFlowSelectLedgerRow.value; |
| | | if (!ledgerRow?.id) return; |
| | | |
| | | const finalConfigId = configId ?? null; |
| | | if (!finalConfigId) return; |
| | | |
| | | proxy?.$modal?.loading?.("正在应用工艺路线配置,请稍候..."); |
| | | try { |
| | | const res = await productList({ salesLedgerId: ledgerRow.id, type: 1 }); |
| | | const products = res?.data ?? res?.records ?? res ?? []; |
| | | if (!Array.isArray(products) || products.length === 0) { |
| | | proxy?.$modal?.msgWarning?.("该台账下没有产品数据"); |
| | | return; |
| | | } |
| | | |
| | | for (const product of products) { |
| | | // 已发货/审核通过不可编辑,跳过 |
| | | if (isProductShipped(product)) continue; |
| | | await salesLedgerProductSetProcessFlowConfig({ |
| | | salesLedgerProductId: product.id, |
| | | processFlowConfigId: finalConfigId, |
| | | }); |
| | | } |
| | | |
| | | proxy?.$modal?.msgSuccess?.("工艺路线配置应用成功"); |
| | | |
| | | // 若当前行已展开,刷新其子产品列表 |
| | | if (expandedRowKeys.value.includes(ledgerRow.id)) { |
| | | const childRes = await productList({ salesLedgerId: ledgerRow.id, type: 1 }); |
| | | const children = childRes?.data ?? childRes?.records ?? childRes ?? []; |
| | | const index = tableData.value.findIndex((item) => item.id === ledgerRow.id); |
| | | if (index > -1) { |
| | | tableData.value[index].children = children; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | proxy?.$modal?.msgError?.("应用失败,请稍后重试"); |
| | | } finally { |
| | | proxy?.$modal?.closeLoading?.(); |
| | | } |
| | | }; |
| | | |
| | | // 获取产品大类tree数据 |
| | | const getProductOptions = () => { |
| | | // 返回 Promise,便于在编辑产品时等待加载完成 |
| | |
| | | const index = modelOptions.value.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | productForm.value.specificationModel = modelOptions.value[index].model; |
| | | productForm.value.unit = modelOptions.value[index].unit; |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.unit = null; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | |
| | | form.value.entryDate = getCurrentDate(); |
| | | // 签订日期默认为当天 |
| | | form.value.executionDate = getCurrentDate(); |
| | | form.value.customerRemarks = ""; |
| | | } else { |
| | | currentId.value = row.id; |
| | | getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => { |
| | | form.value = { ...res }; |
| | | form.value.entryPerson = Number(res.entryPerson); |
| | | // 字段名兼容:后端可能返回 customer_remarks |
| | | form.value.customerRemarks = res?.customerRemarks ?? res?.customer_remarks ?? ""; |
| | | productData.value = form.value.productData; |
| | | fileList.value = form.value.salesLedgerFiles; |
| | | }); |
| | |
| | | // 台账字段 |
| | | productCategory: p.product || p.productName || "", |
| | | specificationModel: p.specification || "", |
| | | unit: p.unit || "", |
| | | thickness: p.thickness, |
| | | quantity: quantity, |
| | | taxRate: taxRate, |
| | | taxInclusiveUnitPrice: unitPrice.toFixed(2), |
| | |
| | | settlePieceArea: 0, |
| | | settleTotalArea: 0, |
| | | processRequirement: "", |
| | | floorCode: "", |
| | | remark: "", |
| | | salesProductProcessList: [], |
| | | }; |
| | |
| | | productForm.value.processRequirement = |
| | | row?.processRequirement ?? row?.process_requirement ?? ""; |
| | | productForm.value.remark = row?.remark ?? row?.remarks ?? ""; |
| | | productForm.value.floorCode = row?.floorCode ?? row?.floor_code ?? ""; |
| | | // 工艺流程配置绑定字段(后续由后端确认字段名) |
| | | productForm.value.processFlowConfigId = |
| | | row?.processFlowConfigId ?? row?.process_flow_config_id ?? null; |
| | | |
| | | // 周长回显(如后端返回;最终仍以公式计算为准) |
| | | productForm.value.perimeter = |
| | | row?.perimeter ?? row?.heavyBoxPerimeter ?? row?.heavyboxPerimeter ?? 0; |
| | | |
| | | // 后端直接返回 thickness |
| | | productForm.value.thickness = row?.thickness; |
| | | |
| | | productForm.value.salesProductProcessList = normalizeOtherAmountsFromRow(row); |
| | | productIndex.value = index; |
| | |
| | | const submitProduct = () => { |
| | | proxy.$refs["productFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // 厚度保留 15 位小数,避免由于浮点计算/输入导致精度偏差 |
| | | if (productForm.value.thickness !== null && productForm.value.thickness !== undefined) { |
| | | productForm.value.thickness = Number(Number(productForm.value.thickness).toFixed(15)); |
| | | } |
| | | |
| | | // 面积/总计字段在提交前兜底计算一次 |
| | | recalcAreaTotals(); |
| | | // 其他金额只提交 {id, processName, quantity}(后端字段:salesProductProcessList) |
| | |
| | | <tr> |
| | | <th>产品名称</th> |
| | | <th>规格型号</th> |
| | | <th>单位</th> |
| | | <th>厚度</th> |
| | | <th>单价</th> |
| | | <th>零售数量</th> |
| | | <th>零售金额</th> |
| | |
| | | <tr> |
| | | <td>${product.productCategory || ''}</td> |
| | | <td>${product.specificationModel || ''}</td> |
| | | <td>${product.unit || ''}</td> |
| | | <td>${product.thickness ?? ''}</td> |
| | | <td>${product.taxInclusiveUnitPrice || '0'}</td> |
| | | <td>${product.quantity || '0'}</td> |
| | | <td>${product.taxInclusiveTotalPrice || '0'}</td> |
| | |
| | | return statusStr === '待发货' || statusStr === '审核拒绝'; |
| | | }; |
| | | |
| | | const handleBulkDelivery = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | | return; |
| | | } |
| | | |
| | | const customerNames = selectedRows.value.map((r) => String(r.customerName || "").trim()); |
| | | const uniqueCustomers = Array.from(new Set(customerNames)); |
| | | |
| | | // 客户名称不一致不允许发货 |
| | | if (uniqueCustomers.length > 1) { |
| | | proxy.$modal.msgWarning("客户名称不一致,不允许发货"); |
| | | return; |
| | | } |
| | | |
| | | // 多条且客户一致:二次确认 |
| | | if (selectedRows.value.length > 1) { |
| | | try { |
| | | await ElMessageBox.confirm("是否确认合并发货?", "合并发货", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }); |
| | | } catch (e) { |
| | | proxy.$modal.msg("已取消"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | proxy.$modal.loading("正在获取产品数据,请稍候..."); |
| | | try { |
| | | const targets = []; |
| | | for (const ledger of selectedRows.value) { |
| | | let products = []; |
| | | try { |
| | | const res = await productList({ salesLedgerId: ledger.id, type: 1 }); |
| | | products = res?.data || []; |
| | | } catch { |
| | | products = []; |
| | | } |
| | | |
| | | for (const product of products) { |
| | | if (!canShip(product)) continue; |
| | | targets.push({ |
| | | ...product, |
| | | salesLedgerId: product.salesLedgerId || ledger.id, |
| | | }); |
| | | } |
| | | } |
| | | |
| | | if (targets.length === 0) { |
| | | proxy.$modal.msgWarning("没有可发货的数据"); |
| | | return; |
| | | } |
| | | |
| | | currentDeliveryRows.value = targets; |
| | | deliveryForm.value = { type: "货车" }; |
| | | // 重置审批人节点(默认一个空节点) |
| | | approverNodes.value = [{ id: 1, userId: null }]; |
| | | nextApproverId = 2; |
| | | deliveryFormVisible.value = true; |
| | | } finally { |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 下载文件 |
| | | * |
| | |
| | | return; |
| | | } |
| | | |
| | | currentDeliveryRow.value = row; |
| | | currentDeliveryRows.value = [row]; |
| | | deliveryForm.value = { |
| | | type: "货车", |
| | | }; |
| | |
| | | const approveUserIds = approverNodes.value.map(node => node.userId).join(","); |
| | | // 保存当前展开的行ID,以便发货后重新加载子表格数据 |
| | | const currentExpandedKeys = [...expandedRowKeys.value]; |
| | | const salesLedgerId = currentDeliveryRow.value.salesLedgerId; |
| | | addShippingInfo({ |
| | | salesLedgerId: salesLedgerId, |
| | | salesLedgerProductId: currentDeliveryRow.value.id, |
| | | type: deliveryForm.value.type, |
| | | approveUserIds, |
| | | }) |
| | | |
| | | const targets = currentDeliveryRows.value || []; |
| | | if (targets.length === 0) { |
| | | proxy.$modal.msgWarning("未选择可发货的数据"); |
| | | return; |
| | | } |
| | | |
| | | // 依次发货(避免并发下库存扣减/状态更新互相影响) |
| | | const run = async () => { |
| | | for (const item of targets) { |
| | | const salesLedgerId = item.salesLedgerId; |
| | | if (!salesLedgerId) continue; |
| | | await addShippingInfo({ |
| | | salesLedgerId, |
| | | salesLedgerProductId: item.id, |
| | | type: deliveryForm.value.type, |
| | | approveUserIds, |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | run() |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("发货成功"); |
| | | closeDeliveryDia(); |
| | |
| | | getList().then(() => { |
| | | // 如果之前有展开的行,重新加载这些行的子表格数据 |
| | | if (currentExpandedKeys.length > 0) { |
| | | // 使用 Promise.all 并行加载所有展开行的子表格数据 |
| | | const loadPromises = currentExpandedKeys.map(ledgerId => { |
| | | const loadPromises = currentExpandedKeys.map((ledgerId) => { |
| | | return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === ledgerId); |
| | | if (index > -1) { |
| | |
| | | }); |
| | | }); |
| | | Promise.all(loadPromises).then(() => { |
| | | // 恢复展开状态 |
| | | expandedRowKeys.value = currentExpandedKeys; |
| | | }); |
| | | } |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError("发货失败,请稍后重试"); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | const closeDeliveryDia = () => { |
| | | proxy.resetForm("deliveryFormRef"); |
| | | deliveryFormVisible.value = false; |
| | | currentDeliveryRow.value = null; |
| | | currentDeliveryRows.value = []; |
| | | }; |
| | | const currentFactoryName = ref(""); |
| | | const getCurrentFactoryName = async () => { |