| | |
| | | <div class="app-container"> |
| | | <template v-if="isFormPageMode"> |
| | | <div class="sales-ledger-page-header"> |
| | | <div> |
| | | <el-button class="sales-ledger-page-back" |
| | | @click="exitFormPage()">返回台账</el-button> |
| | | <div class="sales-ledger-page-header-content"> |
| | | <div class="sales-ledger-page-title">{{ pageFormTitle }}</div> |
| | | <div class="sales-ledger-page-subtitle">{{ pageFormSubtitle }}</div> |
| | | </div> |
| | | <el-button class="sales-ledger-page-back" |
| | | @click="exitFormPage()">返回台账</el-button> |
| | | </div> |
| | | <div class="sales-ledger-page-form"> |
| | | <el-form :model="form" |
| | |
| | | <el-row> |
| | | <el-form-item label="产品信息:" |
| | | prop="entryDate"> |
| | | <el-button type="primary" |
| | | :disabled="hasEditingProductRow() || isReviewedEdit" |
| | | @click="addProductInline"> |
| | | 添加 |
| | | </el-button> |
| | | <el-button plain |
| | | type="danger" |
| | | :disabled="isReviewedEdit" |
| | | @click="deleteProduct">删除</el-button> |
| | | <el-button plain |
| | | type="primary" |
| | | :disabled="isReviewedEdit" |
| | | @click="pinSelectedProductRow">固定</el-button> |
| | | </el-form-item> |
| | | </el-row> |
| | | <el-table :data="productData" |
| | | border |
| | | class="compact-product-table" |
| | | @selection-change="productSelected" |
| | | show-summary |
| | | :summary-method="summarizeProductTable"> |
| | |
| | | width="60" /> |
| | | <el-table-column label="产品大类" |
| | | prop="productCategory" |
| | | min-width="160"> |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-tree-select v-if="scope.row.__editing" |
| | | v-model="scope.row.__productCategoryId" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="规格型号" |
| | | prop="specificationModel" |
| | | min-width="200"> |
| | | min-width="140"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.productModelId" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="厚度(mm)" |
| | | prop="thickness" |
| | | min-width="160"> |
| | | min-width="95"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="楼层编号" |
| | | prop="floorCode" |
| | | min-width="250" |
| | | min-width="120" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="含税单价(元)" |
| | | prop="taxInclusiveUnitPrice" |
| | | min-width="160"> |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | | :step="0.01" |
| | | :min="0" |
| | | :precision="2" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="宽(mm)" |
| | | prop="width" |
| | | min-width="160"> |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="高(mm)" |
| | | prop="height" |
| | | min-width="160"> |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="数量" |
| | | prop="quantity" |
| | | min-width="150"> |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="结算单片面积(㎡)" |
| | | prop="settlePieceArea" |
| | | min-width="200"> |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="面积(m²)" |
| | | prop="actualTotalArea" |
| | | min-width="200"> |
| | | min-width="110"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="税率(%)" |
| | | prop="taxRate" |
| | | min-width="120"> |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.taxRate" |
| | |
| | | <el-table-column label="含税总价(元)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="120" /> |
| | | min-width="100" /> |
| | | <el-table-column label="不含税总价(元)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="120" /> |
| | | min-width="100" /> |
| | | <el-table-column label="加工要求" |
| | | prop="processRequirement" |
| | | min-width="160" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="发票类型" |
| | | prop="invoiceType" |
| | | min-width="120"> |
| | | min-width="90"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.invoiceType" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="备注" |
| | | prop="remark" |
| | | min-width="140" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="重箱" |
| | | prop="heavyBox" |
| | | min-width="100"> |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.heavyBox" |
| | |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | min-width="220" |
| | | min-width="150" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <template v-if="scope.row.__editing"> |
| | |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="copyProductInline(scope.row, scope.$index)"> |
| | | 复制新建 |
| | | 复制 |
| | | </el-button> |
| | | </template> |
| | | <el-popover :width="560" |
| | |
| | | <el-form-item label="产品信息:" |
| | | prop="entryDate"> |
| | | <el-button v-if="operationType !== 'view'" |
| | | type="primary" |
| | | :disabled="hasEditingProductRow() || isReviewedEdit" |
| | | @click="addProductInline"> |
| | | 添加 |
| | | </el-button> |
| | | <el-button v-if="operationType !== 'view'" |
| | | plain |
| | | type="danger" |
| | | :disabled="isReviewedEdit" |
| | | @click="deleteProduct">删除</el-button> |
| | | <el-button v-if="operationType !== 'view'" |
| | | plain |
| | | type="primary" |
| | | :disabled="isReviewedEdit" |
| | | @click="pinSelectedProductRow">固定</el-button> |
| | | </el-form-item> |
| | | </el-row> |
| | | <el-table :data="productData" |
| | | border |
| | | class="compact-product-table" |
| | | @selection-change="productSelected" |
| | | show-summary |
| | | :summary-method="summarizeProductTable"> |
| | |
| | | width="60" /> |
| | | <el-table-column label="产品大类" |
| | | prop="productCategory" |
| | | min-width="160"> |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-tree-select v-if="scope.row.__editing" |
| | | v-model="scope.row.__productCategoryId" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="规格型号" |
| | | prop="specificationModel" |
| | | min-width="200"> |
| | | min-width="140"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.productModelId" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="厚度(mm)" |
| | | prop="thickness" |
| | | min-width="160"> |
| | | min-width="95"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="楼层编号" |
| | | prop="floorCode" |
| | | min-width="250" |
| | | min-width="120" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="含税单价(元)" |
| | | prop="taxInclusiveUnitPrice" |
| | | min-width="160"> |
| | | min-width="105"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | :step="0.01" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="宽(mm)" |
| | | prop="width" |
| | | min-width="160"> |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="高(mm)" |
| | | prop="height" |
| | | min-width="160"> |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="数量" |
| | | prop="quantity" |
| | | min-width="150"> |
| | | min-width="85"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="结算单片面积(㎡)" |
| | | prop="settlePieceArea" |
| | | min-width="200"> |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="面积(m²)" |
| | | prop="actualTotalArea" |
| | | min-width="200"> |
| | | min-width="110"> |
| | | <template #default="scope"> |
| | | <el-input-number v-if="scope.row.__editing" |
| | | controls-position="right" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="税率(%)" |
| | | prop="taxRate" |
| | | min-width="120"> |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.taxRate" |
| | |
| | | <el-table-column label="含税总价(元)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="120" /> |
| | | min-width="100" /> |
| | | <el-table-column label="不含税总价(元)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | min-width="120" /> |
| | | min-width="100" /> |
| | | <el-table-column label="加工要求" |
| | | prop="processRequirement" |
| | | min-width="160" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="发票类型" |
| | | prop="invoiceType" |
| | | min-width="120"> |
| | | min-width="90"> |
| | | <template #default="scope"> |
| | | <el-select v-if="scope.row.__editing" |
| | | v-model="scope.row.invoiceType" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="备注" |
| | | prop="remark" |
| | | min-width="140" |
| | | min-width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | |
| | | </el-table-column> |
| | | <el-table-column label="重箱" |
| | | prop="heavyBox" |
| | | min-width="100"> |
| | | min-width="80"> |
| | | <template #default="scope"> |
| | | <el-input v-if="scope.row.__editing" |
| | | v-model="scope.row.heavyBox" |
| | |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | min-width="220" |
| | | min-width="150" |
| | | align="center" |
| | | v-if="operationType !== 'view'"> |
| | | <template #default="scope"> |
| | |
| | | size="small" |
| | | :disabled="isProductShipped(scope.row)" |
| | | @click="copyProductInline(scope.row, scope.$index)"> |
| | | 复制新建 |
| | | 复制 |
| | | </el-button> |
| | | </template> |
| | | <el-popover :width="560" |
| | |
| | | const productData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const productSelectedRows = ref([]); |
| | | const pinnedProductTemplate = ref(null); |
| | | const userList = ref([]); |
| | | const userListApprove = ref([]); |
| | | const customerOption = ref([]); |
| | |
| | | return (productData.value || []).some(r => r && r.__editing); |
| | | }; |
| | | |
| | | const buildEmptyInlineProductRow = () => ({ |
| | | const isPlaceholderRowDirty = row => { |
| | | if (!row || !row.__placeholder) return false; |
| | | const fieldsToCheck = [ |
| | | "width", |
| | | "height", |
| | | "quantity", |
| | | "settlePieceArea", |
| | | "taxInclusiveUnitPrice", |
| | | "taxRate", |
| | | "invoiceType", |
| | | "floorCode", |
| | | "processRequirement", |
| | | "remark", |
| | | "heavyBox", |
| | | ]; |
| | | return fieldsToCheck.some(key => { |
| | | const value = row[key]; |
| | | return value !== null && value !== undefined && value !== ""; |
| | | }); |
| | | }; |
| | | |
| | | const getPlaceholderRowIndex = () => |
| | | (productData.value || []).findIndex(r => r && r.__placeholder); |
| | | |
| | | const discardPlaceholderRowIfPristine = () => { |
| | | const index = getPlaceholderRowIndex(); |
| | | if (index === -1) return true; |
| | | const row = productData.value[index]; |
| | | if (isPlaceholderRowDirty(row)) { |
| | | proxy.$modal.msgWarning("请先保存或取消当前待录入行"); |
| | | return false; |
| | | } |
| | | productData.value.splice(index, 1); |
| | | if (editingProductRow.value === row) { |
| | | editingProductRow.value = null; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | const buildEmptyInlineProductRow = (prefill = {}) => ({ |
| | | id: null, |
| | | __tempKey: `__temp_${Date.now()}_${Math.random().toString(16).slice(2)}`, |
| | | __editing: true, |
| | | __isNew: true, |
| | | __productCategoryId: null, |
| | | productCategory: "", |
| | | productModelId: null, |
| | | specificationModel: "", |
| | | thickness: null, |
| | | __placeholder: true, |
| | | __productCategoryId: prefill.__productCategoryId ?? null, |
| | | productCategory: prefill.productCategory ?? "", |
| | | productModelId: prefill.productModelId ?? null, |
| | | specificationModel: prefill.specificationModel ?? "", |
| | | thickness: prefill.thickness ?? null, |
| | | quantity: null, |
| | | taxInclusiveUnitPrice: null, |
| | | taxRate: "", |
| | |
| | | heavyBox: "", |
| | | }); |
| | | |
| | | const getPinnedProductPrefill = async () => { |
| | | if (!pinnedProductTemplate.value) return {}; |
| | | await getProductOptions(); |
| | | if (pinnedProductTemplate.value.__productCategoryId) { |
| | | const models = await modelList({ |
| | | id: pinnedProductTemplate.value.__productCategoryId, |
| | | }); |
| | | modelOptions.value = models || []; |
| | | } |
| | | return { ...pinnedProductTemplate.value }; |
| | | }; |
| | | |
| | | const appendEditablePlaceholderRow = async () => { |
| | | if (operationType.value === "view" || isReviewedEdit.value) return; |
| | | const existingPlaceholder = (productData.value || []).find( |
| | | r => r && r.__placeholder |
| | | ); |
| | | if (existingPlaceholder) { |
| | | if (!existingPlaceholder.__editing) existingPlaceholder.__editing = true; |
| | | editingProductRow.value = existingPlaceholder; |
| | | productForm.value = existingPlaceholder; |
| | | return; |
| | | } |
| | | if (hasEditingProductRow()) return; |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | | const prefill = await getPinnedProductPrefill(); |
| | | const row = buildEmptyInlineProductRow(prefill); |
| | | productData.value.push(row); |
| | | editingProductRow.value = row; |
| | | productForm.value = row; |
| | | }; |
| | | |
| | | const pinSelectedProductRow = async () => { |
| | | if (operationType.value === "view" || isReviewedEdit.value) return; |
| | | if (!productSelectedRows.value || productSelectedRows.value.length !== 1) { |
| | | proxy.$modal.msgWarning("请选择一条产品数据进行固定"); |
| | | return; |
| | | } |
| | | const selectedRow = productSelectedRows.value[0]; |
| | | const categoryId = |
| | | selectedRow.__productCategoryId ?? |
| | | findNodeIdByLabel(productOptions.value, selectedRow.productCategory) ?? |
| | | null; |
| | | if (!selectedRow.productCategory || !selectedRow.specificationModel) { |
| | | proxy.$modal.msgWarning("请先选择完整的产品大类和规格型号再固定"); |
| | | return; |
| | | } |
| | | if (!categoryId || !selectedRow.productModelId) { |
| | | proxy.$modal.msgWarning("请先保存当前产品后再固定"); |
| | | return; |
| | | } |
| | | pinnedProductTemplate.value = { |
| | | __productCategoryId: categoryId, |
| | | productCategory: selectedRow.productCategory ?? "", |
| | | productModelId: selectedRow.productModelId ?? null, |
| | | specificationModel: selectedRow.specificationModel ?? "", |
| | | thickness: |
| | | selectedRow.thickness !== null && |
| | | selectedRow.thickness !== undefined && |
| | | selectedRow.thickness !== "" |
| | | ? Number(selectedRow.thickness) |
| | | : null, |
| | | }; |
| | | proxy.$modal.msgSuccess("固定成功,后续会自动带出该产品和规格型号"); |
| | | const editingRow = (productData.value || []).find(r => r?.__editing); |
| | | if ( |
| | | editingRow && |
| | | !editingRow.productCategory && |
| | | !editingRow.productModelId && |
| | | !editingRow.specificationModel |
| | | ) { |
| | | Object.assign(editingRow, await getPinnedProductPrefill()); |
| | | } |
| | | }; |
| | | |
| | | const addProductInline = async () => { |
| | | if (operationType.value === "view") return; |
| | | if (hasEditingProductRow()) { |
| | | proxy.$modal.msgWarning("请先保存或取消当前编辑行"); |
| | | return; |
| | | } |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | | const row = buildEmptyInlineProductRow(); |
| | | productData.value.push(row); |
| | | editingProductRow.value = row; |
| | | // 让现有的计算/其他金额逻辑复用当前行 |
| | | productForm.value = row; |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const copyProductInline = async row => { |
| | |
| | | proxy.$modal.msgWarning("已发货或审核通过的产品不能复制"); |
| | | return; |
| | | } |
| | | if (hasEditingProductRow()) { |
| | | const hasBlockingEditingRow = (productData.value || []).some( |
| | | item => item && item.__editing && !item.__placeholder |
| | | ); |
| | | if (hasBlockingEditingRow) { |
| | | proxy.$modal.msgWarning("请先保存或取消当前编辑行"); |
| | | return; |
| | | } |
| | | if (!discardPlaceholderRowIfPristine()) return; |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | | |
| | | const copied = buildEmptyInlineProductRow(); |
| | | copied.__placeholder = false; |
| | | copied.__productCategoryId = |
| | | row.__productCategoryId ?? |
| | | findNodeIdByLabel(productOptions.value, row.productCategory) ?? |
| | |
| | | proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑"); |
| | | return; |
| | | } |
| | | if (!discardPlaceholderRowIfPristine()) return; |
| | | stopOtherEditingRows(); |
| | | await getProductOptions(); |
| | | await fetchOtherAmountSelectOptions(true); |
| | |
| | | if (model?.model) row.specificationModel = model.model; |
| | | |
| | | if (operationType.value === "edit") { |
| | | row.__placeholder = false; |
| | | // 台账已存在:走原接口保存到后端,再回拉刷新 |
| | | const payload = { ...row, salesLedgerId: currentId.value, type: 1 }; |
| | | delete payload.__backup; |
| | | delete payload.__editing; |
| | | delete payload.__isNew; |
| | | delete payload.__placeholder; |
| | | delete payload.__productCategoryId; |
| | | delete payload.__tempKey; |
| | | await addOrUpdateSalesLedgerProduct(payload); |
| | |
| | | productData.value = res.productData; |
| | | } |
| | | ); |
| | | stopOtherEditingRows(); |
| | | await appendEditablePlaceholderRow(); |
| | | } else { |
| | | // 新增台账:仅在本地 productData 生效,最终随台账一起提交 |
| | | row.__isNew = false; |
| | | row.__placeholder = false; |
| | | row.__editing = false; |
| | | delete row.__backup; |
| | | stopOtherEditingRows(); |
| | | await appendEditablePlaceholderRow(); |
| | | } |
| | | |
| | | stopOtherEditingRows(); |
| | | }; |
| | | |
| | | const cancelProductInline = (row, index) => { |
| | | const cancelProductInline = async (row, index) => { |
| | | if (!row) return; |
| | | if (row.__isNew) { |
| | | if (row.__placeholder) { |
| | | productData.value.splice(index, 1); |
| | | } else if (row.__backup) { |
| | | const restored = JSON.parse(JSON.stringify(row.__backup)); |
| | |
| | | Object.assign(row, restored); |
| | | row.id = keepId; |
| | | row.__editing = false; |
| | | row.__placeholder = false; |
| | | delete row.__backup; |
| | | } |
| | | stopOtherEditingRows(); |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const openOtherAmountInline = async row => { |
| | |
| | | productData.value = []; |
| | | selectedQuotation.value = null; |
| | | fileList.value = []; |
| | | pinnedProductTemplate.value = null; |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | customerList().then(res => { |
| | |
| | | form.value.customerRemarks = detail.customerRemarks ?? detail.customer_remarks ?? ""; |
| | | productData.value = detail.productData || []; |
| | | form.value.deliveryDate = dayjs(form.value.entryDate).add(7, "day").format("YYYY-MM-DD"); |
| | | await appendEditablePlaceholderRow(); |
| | | dialogFormVisible.value = !keepPageMode; |
| | | }; |
| | | |
| | |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | selectedQuotation.value = null; |
| | | pinnedProductTemplate.value = null; |
| | | |
| | | const userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | |
| | | form.value.deliveryDate = dayjs(form.value.entryDate) |
| | | .add(7, "day") |
| | | .format("YYYY-MM-DD"); |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const initEditFormState = async rowId => { |
| | |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | selectedQuotation.value = null; |
| | | pinnedProductTemplate.value = null; |
| | | |
| | | const userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | |
| | | productData.value = form.value.productData || []; |
| | | fileList.value = form.value.salesLedgerFiles || []; |
| | | isReviewedEdit.value = Number(res?.reviewStatus) === 1; |
| | | await appendEditablePlaceholderRow(); |
| | | }; |
| | | |
| | | const enterAddPage = async detail => { |
| | |
| | | console.log("productData.value--", productData.value); |
| | | // 行内编辑未保存时不允许提交,避免脏数据/临时字段进入后端 |
| | | const hasEditingRow = (productData.value || []).some( |
| | | r => r && r.__editing |
| | | r => r && r.__editing && !r.__placeholder |
| | | ); |
| | | if (hasEditingRow) { |
| | | proxy.$modal.msgWarning("产品信息存在未保存的编辑行,请先保存或取消"); |
| | | return; |
| | | } |
| | | if (productData.value !== null && productData.value.length > 0) { |
| | | const cleanedProducts = (productData.value || []).map(p => { |
| | | const cleanedProducts = (productData.value || []) |
| | | .filter(p => p && !p.__placeholder) |
| | | .map(p => { |
| | | if (!p || typeof p !== "object") return p; |
| | | const { |
| | | __editing, |
| | | __isNew, |
| | | __placeholder, |
| | | __backup, |
| | | __productCategoryId, |
| | | __tempKey, |
| | |
| | | rest.quantity = Number(rest.quantity ?? 0) || 0; |
| | | return rest; |
| | | }); |
| | | if (cleanedProducts.length === 0) { |
| | | proxy.$modal.msgWarning("请添加产品信息"); |
| | | return; |
| | | } |
| | | form.value.productData = proxy.HaveJson(cleanedProducts); |
| | | } else { |
| | | proxy.$modal.msgWarning("请添加产品信息"); |
| | |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | appendEditablePlaceholderRow(); |
| | | } else { |
| | | const placeholderRows = productSelectedRows.value.filter( |
| | | item => item?.__placeholder |
| | | ); |
| | | if (placeholderRows.length > 0) { |
| | | placeholderRows.forEach(selectedRow => { |
| | | const index = productData.value.findIndex(product => { |
| | | if (!product || !selectedRow) return false; |
| | | return ( |
| | | product.__tempKey && |
| | | selectedRow.__tempKey && |
| | | String(product.__tempKey) === String(selectedRow.__tempKey) |
| | | ); |
| | | }); |
| | | if (index !== -1) { |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | appendEditablePlaceholderRow(); |
| | | } |
| | | let ids = []; |
| | | if (productSelectedRows.value.length > 0) { |
| | | ids = productSelectedRows.value.map(item => item.id); |
| | | const persistedRows = productSelectedRows.value.filter( |
| | | item => !item?.__placeholder && item?.id !== null && item?.id !== undefined |
| | | ); |
| | | if (persistedRows.length > 0) { |
| | | ids = persistedRows.map(item => item.id); |
| | | } else { |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", { |
| | | confirmButtonText: "确认", |
| | |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .sales-ledger-page-header-content { |
| | | flex: 1; |
| | | } |
| | | |
| | | .sales-ledger-page-title { |
| | | font-size: 22px; |
| | | font-weight: 600; |
| | |
| | | } |
| | | |
| | | .sales-ledger-page-back { |
| | | margin-bottom: 12px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .sales-ledger-page-subtitle { |
| | |
| | | justify-content: center; |
| | | gap: 16px; |
| | | margin-top: 24px; |
| | | } |
| | | |
| | | .compact-product-table { |
| | | :deep(.el-table__cell) { |
| | | padding: 4px 0; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | :deep(.cell) { |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | :deep(.el-input__wrapper) { |
| | | padding: 0 8px; |
| | | } |
| | | |
| | | :deep(.el-input-number .el-input__wrapper) { |
| | | padding-left: 8px; |
| | | padding-right: 34px; |
| | | } |
| | | |
| | | :deep(.el-input-number), |
| | | :deep(.el-select), |
| | | :deep(.el-input), |
| | | :deep(.el-tree-select) { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | :deep(.el-input-number .el-input__inner) { |
| | | height: 28px; |
| | | font-size: 12px; |
| | | text-align: left; |
| | | } |
| | | |
| | | :deep(.el-select .el-input__inner), |
| | | :deep(.el-input .el-input__inner) { |
| | | height: 28px; |
| | | } |
| | | |
| | | :deep(.el-button--small), |
| | | :deep(.el-button.is-link) { |
| | | font-size: 12px; |
| | | padding: 2px 4px; |
| | | } |
| | | } |
| | | |
| | | .ledger-qr-dialog { |
| | |
| | | margin-right: 0; |
| | | margin-bottom: 8px; |
| | | } |
| | | </style> |
| | | </style> |