新疆-融邦环保
1.油品出库表单改「多台账分摊绑定」:各台账数量之和必须等于出库吨位,产品取各台账明细交集,代储不锁行可自由增删
2.新增到货记录模块:销售台账页入口,车辆随出库单联动,到货上限=该台账分摊量,附件走永久链接
3.发货台账改层级父行:油品出库为父行,children 为到货记录,来源列区分发货单/油品出库/到货
4.开票/收款候选按「出库单×客户」拆行,bindingId 与出库单 id 分两串回传,防重复开票/收款
5.销售台账下拉补产品/规格型号
6.代储入库绑定销售订单:油品入库表单加销售订单下拉,入库台账列表加「销售订单」列
7.采购申请补齐台账字段(油库、采购合同号、提货方式、物流方式、最佳到站日期、采购类别、付款方式、预警数量、附件等)
8.修复采购申请「转订单后跳转无反应」
9.采购台账删掉「销售合同号」的搜索条件、列表列和表单字段
10.公海客户新增「信托基金(元)」,代储金额合计超限标红提示
| | |
| | | }); |
| | | }; |
| | | |
| | | // 代å¨å
¥åºå¯ç»å®çéå®è®¢åï¼å端åªåº ledgerType='代å¨' çå°è´¦ï¼ |
| | | export const bindableSalesLedger = (params) => { |
| | | return request({ |
| | | url: "/stockInRecord/bindableSalesLedger", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | |
| | | export const updateStockInRecord = (id, data) => { |
| | | return request({ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // å°è´§è®°å½æ¥å£ï¼æå¨ãåºåºå à éå®å°è´¦ãä¸ï¼åæ¶æ¯åè´§å°è´¦æ²¹ååºåºç¶è¡çåè¡ |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ï¼æ¯æ salesLedgerId / stockOutRecordId / truckPlateNo / salesContractNo / customerName / arrivalDateï¼ |
| | | export function salesLedgerArrivalListPage(query) { |
| | | return request({ |
| | | url: "/salesLedgerArrival/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // å¯å°è´§è½¦è¾ä¸æï¼åªåºãç»å®äºè¯¥å°è´¦ãä¸å®¡æ¹éè¿çåºåºè®°å½éç¨è¿ç车ã |
| | | export function bindableArrivalVehicles(query) { |
| | | return request({ |
| | | url: "/salesLedgerArrival/bindableVehicles", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function addSalesLedgerArrival(data) { |
| | | return request({ |
| | | url: "/salesLedgerArrival/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | export function updateSalesLedgerArrival(data) { |
| | | return request({ |
| | | url: "/salesLedgerArrival/update", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // æ¹éå é¤ï¼Body æ¯ [id, id] |
| | | export function delSalesLedgerArrival(ids) { |
| | | return request({ |
| | | url: "/salesLedgerArrival/delete", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| | |
| | | function getItemUrl(item) { |
| | | if (!item) return '' |
| | | if (typeof item === 'string') return item |
| | | return item.url || item.previewURL || '' |
| | | // æ°¸ä¹
模å¼ä¼å
previewURLï¼url å¯è½æ¯ 2 å°æ¶å¤±æçç¾åå°å |
| | | return props.permanent |
| | | ? item.previewURL || item.previewUrl || item.url || '' |
| | | : item.url || item.previewURL || '' |
| | | } |
| | | |
| | | function getItemName(item, index = 0) { |
| | |
| | | url: item, |
| | | } |
| | | } |
| | | const url = props.permanent |
| | | ? item.previewURL || item.previewUrl || item.url || '' |
| | | : item.url || item.previewURL || item.previewUrl || '' |
| | | return Object.assign({}, item, { |
| | | url: item.url || item.previewURL || item.previewUrl || '', |
| | | url, |
| | | name: item.name || item.originalFilename || item.fileName || `image-${currentList.value.length + index + 1}`, |
| | | }) |
| | | } |
| | |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination"></PIMTable> |
| | | @pagination="pagination"> |
| | | <template #depositAmountSlot="{ row }"> |
| | | <el-tooltip v-if="row.exceedsTrustFund" |
| | | :content="`代å¨éé¢å计已è¶
åºè¯¥å®¢æ·ä¿¡æåºéï¼ä¿¡æåºéï¼${row.trustFund}ï¼`" |
| | | placement="top"> |
| | | <el-text type="danger">{{ formatAmount(row.depositTotalAmount) }}</el-text> |
| | | </el-tooltip> |
| | | <span v-else>{{ formatAmount(row.depositTotalAmount) }}</span> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢å®¢æ·ä¿¡æ¯' : 'ç¼è¾å®¢æ·ä¿¡æ¯'" |
| | |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¿¡æåºé(å
)ï¼" |
| | | prop="trustFund"> |
| | | <el-input-number v-model="form.trustFund" |
| | | :min="0" |
| | | :precision="2" |
| | | :controls="false" |
| | | placeholder="请è¾å
¥ï¼å¯ä¸ºç©º" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "ä¿¡æåºé(å
)", |
| | | prop: "trustFund", |
| | | width: 140, |
| | | formatData: value => |
| | | value === null || value === undefined || value === "" |
| | | ? "-" |
| | | : Number(value).toFixed(2), |
| | | }, |
| | | { |
| | | label: "代å¨éé¢(å
)", |
| | | prop: "depositTotalAmount", |
| | | dataType: "slot", |
| | | slot: "depositAmountSlot", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "弿·è¡å·", |
| | | prop: "bankCode", |
| | | width: 220, |
| | |
| | | bankAccount: "", |
| | | bankCode: "", |
| | | customerType: "", |
| | | // éé¢åæ®µç» null èé ""ï¼é¿å
el-input-number æç©ºä¸²æç®æ 0 |
| | | trustFund: null, |
| | | type: 1 |
| | | }, |
| | | rules: { |
| | |
| | | return true; |
| | | }; |
| | | |
| | | // éé¢å±ç¤ºï¼ç©ºå¼æ¾ç¤º -ï¼å¦åä¿ç两ä½å°æ° |
| | | const formatAmount = value => { |
| | | return value === null || value === undefined || value === "" |
| | | ? "-" |
| | | : Number(value).toFixed(2); |
| | | }; |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | const formatFileSize = size => { |
| | | if (size < 1024) { |
| | |
| | | ref="outboundTableRef" |
| | | v-loading="outboundBatchLoading" |
| | | :data="outboundBatchList" |
| | | row-key="id" |
| | | :row-key="getOutboundOptionValue" |
| | | border |
| | | stripe |
| | | max-height="480" |
| | |
| | | const outboundSelectVisible = ref(false); |
| | | const outboundTableRef = ref(null); |
| | | const dialogOutboundSelection = ref([]); |
| | | // 已确认éä¸çåºåºåéæ´è¡ï¼å« bindingIdï¼ï¼æäº¤æ¶æå®æå id ç©ºé´ |
| | | const selectedOutboundRows = ref([]); |
| | | |
| | | const getCustomerList = () => { |
| | | listCustomer({ current: -1, size: -1, type: 0 }).then((res) => { |
| | |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // ä¸ä¸ª CK å·å¯è½æå®¢æ·ææå¤è¡ï¼æ²¹ååºåºæãåºåºå à 客æ·ãåºåï¼ï¼ |
| | | // æä»¥ä¸è½ç¨åºåºå id å½å¯ä¸é®ï¼æ bindingId çè¡ç¨ bindingIdï¼å
¶ä½ç¨åºåºå id |
| | | const getOutboundOptionValue = (row) => { |
| | | const bindingId = row?.bindingId; |
| | | if (bindingId !== undefined && bindingId !== null && bindingId !== "") { |
| | | return `binding-${bindingId}`; |
| | | } |
| | | const id = row?.id ?? row?.stockOutRecordId; |
| | | return id === undefined || id === null ? undefined : `record-${id}`; |
| | | }; |
| | | |
| | | // 两个 id 空é´é½æ¯å°æ´æ°ï¼æ··çå伿å·ï¼FIND_IN_SET åä¸åºæ¥ï¼å¯¼è´éå¤å¼ç¥¨ï¼ |
| | | // æä»¥å¿
é¡»æ bindingId æ¯å¦ä¸ºç©ºææä¸¤ä¸²åå¼åä¼ |
| | | const splitOutboundIds = (rows) => { |
| | | const recordIds = []; |
| | | const bindingIds = []; |
| | | rows.forEach((row) => { |
| | | const bindingId = row?.bindingId; |
| | | if (bindingId !== undefined && bindingId !== null && bindingId !== "") { |
| | | bindingIds.push(bindingId); |
| | | return; |
| | | } |
| | | const id = row?.id ?? row?.stockOutRecordId; |
| | | if (id !== undefined && id !== null) { |
| | | recordIds.push(id); |
| | | } |
| | | }); |
| | | return { recordIds, bindingIds }; |
| | | }; |
| | | |
| | | // ç¼è¾åæ¾æ¶è¡¨åéåçæ¯å¸¦åç¼çé项å¼ï¼åä¼ åè¦è¿åæè£¸ id |
| | | const stripOutboundPrefix = (value) => String(value).replace(/^(record|binding)-/, ""); |
| | | |
| | | const normalizeOutboundBatchOptions = (data) => { |
| | | const list = Array.isArray(data) ? data : []; |
| | |
| | | item.outboundNo ?? |
| | | item.label ?? |
| | | `åºåºå${index + 1}`; |
| | | const value = item.id ?? item.stockOutRecordId ?? item.stockOutRecordIds ?? label; |
| | | const value = getOutboundOptionValue(item) ?? label; |
| | | const outboundAmount = Number(item.outboundAmount) || 0; |
| | | const taxRate = |
| | | item.taxRate !== undefined && item.taxRate !== null && item.taxRate !== "" |
| | |
| | | form.amount = sum > 0 ? Number(sum.toFixed(2)) : 0; |
| | | }; |
| | | |
| | | const getOutboundRowId = (row) => row?.id ?? row?.stockOutRecordId; |
| | | |
| | | const outboundBatchDisplayText = computed(() => { |
| | | if (isEdit.value || isView.value) { |
| | | return form.outboundBatches || ""; |
| | |
| | | table.clearSelection(); |
| | | const selectedIds = new Set((form.outboundBatchNos || []).map((id) => String(id))); |
| | | outboundBatchList.value.forEach((row) => { |
| | | const rowId = getOutboundRowId(row); |
| | | const rowId = getOutboundOptionValue(row); |
| | | if (rowId !== undefined && rowId !== null && selectedIds.has(String(rowId))) { |
| | | table.toggleRowSelection(row, true); |
| | | } |
| | |
| | | const prevIds = [...(form.outboundBatchNos || [])]; |
| | | const prevBatches = form.outboundBatches; |
| | | form.outboundBatchNos = dialogOutboundSelection.value |
| | | .map((row) => getOutboundRowId(row)) |
| | | .map((row) => getOutboundOptionValue(row)) |
| | | .filter((id) => id !== undefined && id !== null); |
| | | form.outboundBatches = dialogOutboundSelection.value |
| | | .map((row) => row.outboundBatches ?? row.batchNo ?? row.shippingNo ?? "") |
| | |
| | | form.outboundBatches = prevBatches; |
| | | return; |
| | | } |
| | | // ä¿çæ´è¡ï¼æäº¤æ¶æç¥éæ¯è¡æ¯èµ° stockOutRecordIds è¿æ¯ stockOutBindingIds |
| | | selectedOutboundRows.value = [...dialogOutboundSelection.value]; |
| | | outboundSelectVisible.value = false; |
| | | syncInvoiceAmount(); |
| | | formRef.value?.validateField("outboundBatchNos"); |
| | |
| | | outboundBatchOptions.value = []; |
| | | if (!keepSelected) { |
| | | form.outboundBatchNos = []; |
| | | form.stockOutBindingIds = []; |
| | | selectedOutboundRows.value = []; |
| | | form.amount = 0; |
| | | } |
| | | return Promise.resolve(); |
| | |
| | | |
| | | const handleCustomerChange = (customerId) => { |
| | | form.outboundBatchNos = []; |
| | | form.stockOutBindingIds = []; |
| | | selectedOutboundRows.value = []; |
| | | form.outboundBatches = ""; |
| | | form.amount = 0; |
| | | loadOutboundBatches(customerId); |
| | |
| | | applyCode: "", |
| | | customerId: "", |
| | | outboundBatchNos: [], |
| | | // æ²¹ååºåºçç»å®è¡ idï¼ä¸åºåºå id åå¼åï¼æ··ç¨ä¼æå·ï¼ |
| | | stockOutBindingIds: [], |
| | | outboundBatches: "", |
| | | amount: 0, |
| | | taxRate: 13, |
| | |
| | | }; |
| | | |
| | | const fillFormFromRow = (row) => { |
| | | const outboundBatchNos = Array.isArray(row.outboundBatchNos) |
| | | ? row.outboundBatchNos |
| | | : parseStockOutRecordIds(row.stockOutRecordIds ?? row.stockOutRecordId); |
| | | // 带åç¼åæ¾æè½ååéé项çå¼å¯¹ä¸ï¼ç»å®è¡ä¸åºåºåæ¯ä¸¤ä¸ª id 空é´ï¼ |
| | | const outboundBatchNos = ( |
| | | Array.isArray(row.outboundBatchNos) |
| | | ? row.outboundBatchNos |
| | | : parseStockOutRecordIds(row.stockOutRecordIds ?? row.stockOutRecordId) |
| | | ).map((id) => `record-${id}`); |
| | | Object.assign(form, { |
| | | ...row, |
| | | applyCode: row.applyCode ?? row.invoiceApplicationNo ?? "", |
| | |
| | | content: row.content ?? row.invoiceContent, |
| | | status: normalizeStatus(row.status ?? row.auditStatus), |
| | | outboundBatchNos, |
| | | // ç¼è¾æ¶ä¸éå¼éæ©å¼¹æ¡ï¼åæ ·å¸¦å廿è½ä¿ä½ç»å®è¡å
³è |
| | | stockOutBindingIds: parseStockOutRecordIds(row.stockOutBindingIds).map( |
| | | (id) => `binding-${id}` |
| | | ), |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | createTime: row.createTime ?? "", |
| | | }); |
| | |
| | | applyCode: "", |
| | | customerId: "", |
| | | outboundBatchNos: [], |
| | | stockOutBindingIds: [], |
| | | outboundBatches: "", |
| | | amount: 0, |
| | | taxRate: 13, |
| | |
| | | }); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | selectedOutboundRows.value = []; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | }; |
| | | |
| | | const buildSubmitPayload = (forUpdate = false) => { |
| | | // æ°éçè¡æ bindingId ææä¸¤ä¸²ï¼ç¼è¾æ¶éæ©å¼¹æ¡æ²¡éå¼ï¼æ²¿ç¨è®°å½é另忥ç两串 |
| | | const { recordIds, bindingIds } = selectedOutboundRows.value.length |
| | | ? splitOutboundIds(selectedOutboundRows.value) |
| | | : { |
| | | recordIds: (form.outboundBatchNos || []).map(stripOutboundPrefix), |
| | | bindingIds: (form.stockOutBindingIds || []).map(stripOutboundPrefix), |
| | | }; |
| | | const payload = { |
| | | customerId: form.customerId, |
| | | stockOutRecordIds: (form.outboundBatchNos || []).join(","), |
| | | stockOutRecordIds: recordIds.join(","), |
| | | stockOutBindingIds: bindingIds.join(","), |
| | | invoiceApplicationNo: form.applyCode || "", |
| | | invoiceType: form.invoiceType, |
| | | applyDate: form.applyDate, |
| | |
| | | <el-table ref="outboundTableRef" |
| | | v-loading="outboundBatchLoading" |
| | | :data="outboundBatchList" |
| | | row-key="id" |
| | | :row-key="getOutboundOptionValue" |
| | | border |
| | | stripe |
| | | max-height="480" |
| | |
| | | const outboundSelectVisible = ref(false); |
| | | const outboundTableRef = ref(null); |
| | | const dialogOutboundSelection = ref([]); |
| | | // å¾éè¡ç宿´æç»ï¼æäº¤æ¶æ bindingId æ¯å¦ä¸ºç©ºææä¸¤ä¸ª id åæ®µ |
| | | const selectedOutboundRows = ref([]); |
| | | |
| | | const getReceiptMethodLabel = value => { |
| | | if (value === undefined || value === null || value === "") return "-"; |
| | |
| | | amount: 0, |
| | | receiptMethod: "", |
| | | stockOutRecordIds: [], |
| | | // æ²¹ååºåºçç»å®è¡ idï¼ä¸åºåºå id åå¼åï¼æ··ç¨ä¼æå·ï¼ |
| | | stockOutBindingIds: [], |
| | | outboundBatches: "", |
| | | remark: "", |
| | | createTime: "", |
| | |
| | | item.outboundNo ?? |
| | | item.label ?? |
| | | `åºåºå${index + 1}`; |
| | | const value = item.id ?? item.stockOutRecordId ?? label; |
| | | const value = getOutboundOptionValue(item) ?? label; |
| | | const outboundAmount = Number(item.outboundAmount) || 0; |
| | | const amountReceived = Number(item.amountReceived) || 0; |
| | | const availableAmount = outboundAmount - amountReceived; |
| | |
| | | |
| | | const isSameOutboundId = (a, b) => String(a) === String(b); |
| | | |
| | | const getOutboundRowId = row => row?.id ?? row?.stockOutRecordId; |
| | | // ä¸ä¸ª CK å·å¯è½æå®¢æ·ææå¤è¡ï¼æ²¹ååºåºæãåºåºå à 客æ·ãåºåï¼ï¼ |
| | | // æä»¥ä¸è½ç¨åºåºå id å½å¯ä¸é®ï¼æ bindingId çè¡ç¨ bindingIdï¼å
¶ä½ç¨åºåºå id |
| | | const getOutboundOptionValue = row => { |
| | | const bindingId = row?.bindingId; |
| | | if (bindingId !== undefined && bindingId !== null && bindingId !== "") { |
| | | return `binding-${bindingId}`; |
| | | } |
| | | const id = row?.id ?? row?.stockOutRecordId; |
| | | return id === undefined || id === null ? undefined : `record-${id}`; |
| | | }; |
| | | |
| | | // 两个 id 空é´é½æ¯å°æ´æ°ï¼æ··çå伿å·ï¼FIND_IN_SET åä¸åºæ¥ï¼å¯¼è´å䏿¹è´§é夿¶æ¬¾ï¼ |
| | | // æä»¥å¿
é¡»æ bindingId æ¯å¦ä¸ºç©ºææä¸¤ä¸²åå¼åä¼ |
| | | const splitOutboundIds = rows => { |
| | | const recordIds = []; |
| | | const bindingIds = []; |
| | | rows.forEach(row => { |
| | | const bindingId = row?.bindingId; |
| | | if (bindingId !== undefined && bindingId !== null && bindingId !== "") { |
| | | bindingIds.push(bindingId); |
| | | return; |
| | | } |
| | | const id = row?.id ?? row?.stockOutRecordId; |
| | | if (id !== undefined && id !== null) { |
| | | recordIds.push(id); |
| | | } |
| | | }); |
| | | return { recordIds, bindingIds }; |
| | | }; |
| | | |
| | | // ç¼è¾åæ¾æ¶è¡¨åéåçæ¯å¸¦åç¼çé项å¼ï¼åä¼ åè¦è¿åæè£¸ id |
| | | const stripOutboundPrefix = value => String(value).replace(/^(record|binding)-/, ""); |
| | | |
| | | const outboundBatchDisplayText = computed(() => { |
| | | if (isEdit.value || isView.value) { |
| | |
| | | ); |
| | | if (exists) return; |
| | | const fromList = outboundBatchList.value.find(row => |
| | | isSameOutboundId(getOutboundRowId(row), id) |
| | | isSameOutboundId(getOutboundOptionValue(row), id) |
| | | ); |
| | | if (fromList) { |
| | | const [option] = normalizeOutboundBatchOptions([fromList]); |
| | |
| | | (form.stockOutRecordIds || []).map(id => String(id)) |
| | | ); |
| | | outboundBatchList.value.forEach(row => { |
| | | const rowId = getOutboundRowId(row); |
| | | const rowId = getOutboundOptionValue(row); |
| | | if ( |
| | | rowId !== undefined && |
| | | rowId !== null && |
| | |
| | | outboundBatchOptions.value = []; |
| | | if (!keepSelected) { |
| | | form.stockOutRecordIds = []; |
| | | form.stockOutBindingIds = []; |
| | | selectedOutboundRows.value = []; |
| | | form.amount = 0; |
| | | } |
| | | return Promise.resolve(); |
| | |
| | | |
| | | const handleCustomerChange = customerId => { |
| | | form.stockOutRecordIds = []; |
| | | form.stockOutBindingIds = []; |
| | | selectedOutboundRows.value = []; |
| | | form.outboundBatches = ""; |
| | | form.amount = 0; |
| | | loadOutboundBatches(customerId); |
| | |
| | | return; |
| | | } |
| | | form.stockOutRecordIds = dialogOutboundSelection.value |
| | | .map(row => getOutboundRowId(row)) |
| | | .map(row => getOutboundOptionValue(row)) |
| | | .filter(id => id !== undefined && id !== null); |
| | | form.outboundBatches = dialogOutboundSelection.value |
| | | .map(row => row.outboundBatches ?? row.batchNo ?? row.shippingNo ?? "") |
| | | .filter(Boolean) |
| | | .join("ã"); |
| | | // ä¿çæ´è¡ï¼æäº¤æ¶æç¥éæ¯è¡æ¯èµ° stockOutRecordIds è¿æ¯ stockOutBindingIds |
| | | selectedOutboundRows.value = [...dialogOutboundSelection.value]; |
| | | outboundSelectVisible.value = false; |
| | | syncCollectionAmount(); |
| | | formRef.value?.validateField("stockOutRecordIds"); |
| | |
| | | const buildExportParams = () => appendFilterParams({}); |
| | | |
| | | const buildSubmitPayload = (forUpdate = false) => { |
| | | // æ°éçè¡æ bindingId ææä¸¤ä¸²ï¼ç¼è¾æ¶éæ©å¼¹æ¡æ²¡éå¼ï¼æ²¿ç¨è®°å½é另忥ç两串 |
| | | const { recordIds, bindingIds } = selectedOutboundRows.value.length |
| | | ? splitOutboundIds(selectedOutboundRows.value) |
| | | : { |
| | | recordIds: (form.stockOutRecordIds || []).map(stripOutboundPrefix), |
| | | bindingIds: (form.stockOutBindingIds || []).map(stripOutboundPrefix), |
| | | }; |
| | | const payload = { |
| | | customerId: form.customerId, |
| | | collectionDate: form.receiptDate, |
| | |
| | | collectionMethod: form.receiptMethod, |
| | | collectionNumber: form.receiptCode || "", |
| | | remark: form.remark || "", |
| | | stockOutRecordIds: (form.stockOutRecordIds || []).join(","), |
| | | stockOutRecordIds: recordIds.join(","), |
| | | stockOutBindingIds: bindingIds.join(","), |
| | | createTime: form.createTime, |
| | | }; |
| | | if (forUpdate) { |
| | |
| | | }; |
| | | |
| | | const fillFormFromRow = row => { |
| | | // 带åç¼åæ¾æè½ååéé项çå¼å¯¹ä¸ï¼ç»å®è¡ä¸åºåºåæ¯ä¸¤ä¸ª id 空é´ï¼ |
| | | const stockOutRecordIds = parseStockOutRecordIds( |
| | | row.stockOutRecordIds ?? row.stockOutRecordId |
| | | ); |
| | | ).map(id => `record-${id}`); |
| | | Object.assign(form, { |
| | | receiptCode: row.receiptCode ?? row.collectionNumber ?? "", |
| | | customerId: row.customerId, |
| | |
| | | amount: Number(row.amount ?? row.collectionAmount ?? 0), |
| | | receiptMethod: row.receiptMethod ?? row.collectionMethod ?? "", |
| | | stockOutRecordIds, |
| | | // ç¼è¾æ¶ä¸éå¼éæ©å¼¹æ¡ï¼åæ ·å¸¦å廿è½ä¿ä½ç»å®è¡å
³è |
| | | stockOutBindingIds: parseStockOutRecordIds(row.stockOutBindingIds).map( |
| | | id => `binding-${id}` |
| | | ), |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | remark: row.remark ?? "", |
| | | createTime: row.createTime ?? "", |
| | |
| | | amount: 0, |
| | | receiptMethod: getDefaultReceiptMethod(), |
| | | stockOutRecordIds: [], |
| | | stockOutBindingIds: [], |
| | | outboundBatches: "", |
| | | remark: "", |
| | | createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | }); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | selectedOutboundRows.value = []; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | prop="inboundCategory" |
| | | min-width="100" |
| | | show-overflow-tooltip /> |
| | | <!-- 代å¨å
¥åºç»å®çéå®è®¢åï¼èæ°æ®æªç»å®ï¼æ¾ç¤º - --> |
| | | <el-table-column label="éå®è®¢å" |
| | | prop="salesContractNo" |
| | | min-width="150" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.salesContractNo || "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ²¹åº" |
| | | prop="oilDepotName" |
| | | min-width="130" |
| | |
| | | label-position="top" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产ååç§°" |
| | | <el-col :span="12"> |
| | | <el-form-item label="产å大类" |
| | | prop="productId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©äº§å大类', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-tree-select v-model="formState.productId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable |
| | | check-strictly |
| | | @change="handleProductCategoryChange" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | prop="productModelId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©äº§å', |
| | | message: 'è¯·éæ©äº§åè§æ ¼', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-button type="primary" |
| | | @click="showProductSelectDialog = true"> |
| | | {{ formState.productName ? formState.productName : 'éæ©äº§å' }} |
| | | </el-button> |
| | | <el-select v-model="formState.productModelId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | @change="handleProductModelChange"> |
| | | <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-form-item label="è§æ ¼" |
| | | prop="productModelName"> |
| | | <el-input v-model="formState.productModelName" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½" |
| | | prop="unit"> |
| | |
| | | <el-select v-model="formState.inboundCategory" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 100%"> |
| | | style="width: 100%" |
| | | @change="handleInboundCategoryChange"> |
| | | <el-option label="èªè´å
¥åº" |
| | | value="èªè´å
¥åº" /> |
| | | <el-option label="代å¨å
¥åº" |
| | | value="代å¨å
¥åº" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-if="isDepositInbound" |
| | | :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®è®¢å" |
| | | prop="salesLedgerId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©éå®è®¢å', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-select v-model="formState.salesLedgerId" |
| | | placeholder="请è¾å
¥ååå·/客æ·å/项ç®åæç´¢" |
| | | clearable |
| | | filterable |
| | | remote |
| | | :remote-method="searchSalesLedger" |
| | | :loading="salesLedgerLoading" |
| | | style="width: 100%" |
| | | @change="handleSalesLedgerChange"> |
| | | <el-option v-for="item in salesLedgerOptions" |
| | | :key="item.id" |
| | | :label="salesLedgerLabel(item)" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | <div v-if="boundLedgerRow" |
| | | class="bound-ledger-tip"> |
| | | å·²ç»å®ï¼{{ salesLedgerLabel(boundLedgerRow) }}ï¼å®¢æ·ãæ²¹åºãå¨ç½ä»ä»¥æ¬è¡¨åå¡«åç为å |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <!-- 产åéæ©å¼¹çª --> |
| | | <ProductSelectDialog v-model="showProductSelectDialog" |
| | | @confirm="handleProductSelect" |
| | | single /> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, getCurrentInstance, onMounted } from "vue"; |
| | | import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; |
| | | import { ref, computed, watch, getCurrentInstance, onMounted } from "vue"; |
| | | import ImageUpload from "@/components/AttachmentUpload/image/index.vue"; |
| | | import { addStockInRecordOnly } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { bindableSalesLedger } from "@/api/inventoryManagement/stockInRecord.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { allOilDepot } from "@/api/basicData/oilDepot.js"; |
| | | import { allTankInfo } from "@/api/basicData/tankInfo.js"; |
| | | import { allVehicle } from "@/api/basicData/vehicle.js"; |
| | |
| | | unit: "", |
| | | oilDepotId: undefined, |
| | | inboundCategory: undefined, |
| | | // 代å¨å
¥åºå¿
å¡«ï¼ç»å®çéå®å°è´¦ï¼éå®è®¢åï¼id |
| | | salesLedgerId: undefined, |
| | | tankId: undefined, |
| | | inboundTime: "", |
| | | oilProduct: undefined, |
| | |
| | | const tankList = ref([]); |
| | | const customerList = ref([]); |
| | | const vehicleList = ref([]); |
| | | |
| | | // 代å¨å
¥åºæéè¦ç»å®éå®è®¢åï¼åç«¯ä¸æåªåº ledgerType='代å¨' çå°è´¦ |
| | | const isDepositInbound = computed( |
| | | () => formState.value.inboundCategory === "代å¨å
¥åº" |
| | | ); |
| | | const salesLedgerOptions = ref([]); |
| | | const salesLedgerLoading = ref(false); |
| | | const boundLedgerRow = ref(null); |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | |
| | | return Number((netWeight.value * Number(d)).toFixed(2)); |
| | | }); |
| | | |
| | | const showProductSelectDialog = ref(false); |
| | | // å
¥åºç±»å«æ¹åï¼ååèªè´å
¥åºæ¶éå®è®¢åä½åºï¼é¿å
æå°è´¦ id 带è¿ä¸ç»å®çåå |
| | | const handleInboundCategoryChange = value => { |
| | | if (value !== "代å¨å
¥åº") { |
| | | formState.value.salesLedgerId = undefined; |
| | | boundLedgerRow.value = null; |
| | | return; |
| | | } |
| | | fetchSalesLedgerOptions(""); |
| | | }; |
| | | |
| | | const salesLedgerLabel = item => |
| | | [item.salesContractNo, item.customerName, item.projectName] |
| | | .filter(Boolean) |
| | | .join(" / "); |
| | | |
| | | // æç´¢æ¡åªæä¸ä¸ªï¼èå端ä¸ä¸ªåæ°æ¯ AND å
³ç³»ï¼æä»¥ä¾æ¬¡æ ååå· â 客æ·å â 项ç®å è¯ï¼å½ä¸å³æ¢ |
| | | const LEDGER_SEARCH_FIELDS = ["salesContractNo", "customerName", "projectName"]; |
| | | |
| | | const queryLedger = params => |
| | | bindableSalesLedger({ current: 1, size: 50, ...params }).then( |
| | | res => res.data?.records || [] |
| | | ); |
| | | |
| | | const fetchSalesLedgerOptions = async keyword => { |
| | | salesLedgerLoading.value = true; |
| | | try { |
| | | let records = []; |
| | | if (keyword) { |
| | | for (const field of LEDGER_SEARCH_FIELDS) { |
| | | records = await queryLedger({ [field]: keyword }); |
| | | if (records.length) { |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | records = await queryLedger({}); |
| | | } |
| | | // æç´¢åå·²éä¸çå°è´¦å¯è½ä¸å¨ç»æéï¼è¡¥ä¸é¿å
䏿æ¡åªæ¾ç¤ºä¸ä¸ª id |
| | | if ( |
| | | boundLedgerRow.value && |
| | | !records.some(item => item.id === boundLedgerRow.value.id) |
| | | ) { |
| | | records.unshift(boundLedgerRow.value); |
| | | } |
| | | salesLedgerOptions.value = records; |
| | | } catch (e) { |
| | | salesLedgerOptions.value = []; |
| | | } finally { |
| | | salesLedgerLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const searchSalesLedger = keyword => |
| | | fetchSalesLedgerOptions(String(keyword || "").trim()); |
| | | |
| | | const handleSalesLedgerChange = id => { |
| | | const row = salesLedgerOptions.value.find(item => item.id === id); |
| | | boundLedgerRow.value = row || null; |
| | | }; |
| | | |
| | | // 产åéæ©ï¼äº§å大类æ + 该大类ä¸çè§æ ¼åå·ï¼ä¸éå®å°è´¦é产åçæ¹å¼ä¸è´ |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]); |
| | | |
| | | let { proxy } = getCurrentInstance(); |
| | | |
| | | const closeModal = () => { |
| | | proxy.resetForm("formRef"); |
| | | formState.value = buildEmptyForm(); |
| | | boundLedgerRow.value = null; |
| | | salesLedgerOptions.value = []; |
| | | isShow.value = false; |
| | | }; |
| | | |
| | |
| | | } |
| | | }; |
| | | |
| | | // 产åéæ©å¤ç |
| | | const handleProductSelect = async products => { |
| | | if (products && products.length > 0) { |
| | | const product = products[0]; |
| | | formState.value.productId = product.productId; |
| | | formState.value.productName = product.productName; |
| | | formState.value.productModelName = product.model; |
| | | formState.value.productModelId = product.id; |
| | | formState.value.unit = product.unit; |
| | | // æ¬ç§æ·ãæ²¹åãå°±æ¯äº§ååç§°ï¼åºåæç»/åºåºå°è´¦çæ²¹ååç»ç齿¯ productNameï¼ï¼ |
| | | // ç´æ¥åæé产ååï¼ä¸åå»äº§åæ 鿝坹ï¼é¿å
æ¯å¯¹ä¸ä¸æ¶æ²¹åæ ç©ºç½ |
| | | formState.value.oilProduct = product.productName || undefined; |
| | | showProductSelectDialog.value = false; |
| | | // 触å表åéªè¯æ´æ° |
| | | proxy.$refs["formRef"]?.validateField("productModelId"); |
| | | const getProductOptions = () => |
| | | productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res || []); |
| | | return productOptions.value; |
| | | }); |
| | | |
| | | function convertIdToValue(data) { |
| | | return (data || []).map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { ...rest, value: id }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | return newItem; |
| | | }); |
| | | } |
| | | |
| | | // 产å大类æ¹åï¼è§æ ¼å鿢䏿¹ï¼å·²éè§æ ¼ä½åº |
| | | const handleProductCategoryChange = value => { |
| | | formState.value.productModelId = undefined; |
| | | formState.value.productName = ""; |
| | | formState.value.productModelName = ""; |
| | | formState.value.unit = ""; |
| | | formState.value.oilProduct = undefined; |
| | | modelOptions.value = []; |
| | | if (!value) { |
| | | return; |
| | | } |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res || []; |
| | | }); |
| | | }; |
| | | |
| | | // è§æ ¼åå·æ¹åï¼äº§ååç§°/è§æ ¼/åä½ç±è§æ ¼ä¸»æ°æ®å¸¦åºï¼æ²¹åå产ååç§° |
| | | const handleProductModelChange = value => { |
| | | const item = modelOptions.value.find(row => row.id === value); |
| | | formState.value.productName = item?.productName || ""; |
| | | formState.value.productModelName = item?.model || ""; |
| | | formState.value.unit = item?.unit || ""; |
| | | // æ¬ç§æ·ãæ²¹åãå°±æ¯äº§ååç§°ï¼åºåæç»/åºåºå°è´¦çæ²¹ååç»ç齿¯ productNameï¼ |
| | | formState.value.oilProduct = item?.productName || undefined; |
| | | proxy.$refs["formRef"]?.validateField("productModelId"); |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | |
| | | if (!formState.value.productModelId) { |
| | | proxy.$modal.msgError("è¯·éæ©äº§å"); |
| | | return; |
| | | } |
| | | // èªè´å
¥åºä¸ç»éå®è®¢åï¼å段ä¸å¹¶ä¼ å端çåæ³æ¯ä¸¢å¼ï¼è¿éç´æ¥ä¸ä¼ æ´å¹²å |
| | | if (!isDepositInbound.value) { |
| | | delete formState.value.salesLedgerId; |
| | | } |
| | | // 计ç®ç»æé表åæäº¤ï¼æ²¹åå
¥åºåºå®ä¸ºåæ ¼åºåï¼åºåæ°éååé |
| | | formState.value.type = "qualified"; |
| | |
| | | }); |
| | | }; |
| | | |
| | | // æ¯æ¬¡æå¼éæ°æäº§å大类ï¼é¿å
馿¬¡å 载失败æå«å¤æ°å¢äº§å忬页çä¸å°å
¨éæ°æ® |
| | | watch( |
| | | () => props.visible, |
| | | visible => { |
| | | if (visible) { |
| | | getProductOptions(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | onMounted(() => { |
| | | getProductOptions(); |
| | | allOilDepot().then(res => { |
| | | oilDepotList.value = res?.data || []; |
| | | }); |
| | |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | // å·²ç»å®éå®è®¢åæç¤ºæ¡ |
| | | .bound-ledger-tip { |
| | | margin-top: 6px; |
| | | padding: 6px 14px; |
| | | font-size: 13px; |
| | | color: #303133; |
| | | background: rgba(64, 158, 255, 0.08); |
| | | border-left: 3px solid #409eff; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | // å
¥åºåç§°ä¸æé项ï¼å°å¡çæ ·å¼ |
| | | .oil-depot-option { |
| | | padding: 8px 10px; |
| | |
| | | :model="formState" |
| | | label-position="top" |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="ç»å®éå®å°è´¦"> |
| | | <el-select v-model="formState.salesLedgerId" |
| | | placeholder="ä¸ç»å®åæåæåºåºæµç¨" |
| | | clearable |
| | | filterable |
| | | remote |
| | | :remote-method="searchSalesLedger" |
| | | :loading="salesLedgerLoading" |
| | | style="width: 100%" |
| | | @change="handleSalesLedgerChange"> |
| | | <el-option v-for="item in salesLedgerOptions" |
| | | :key="item.id" |
| | | :label="salesLedgerLabel(item)" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | <div v-if="boundLedgerRow" |
| | | class="bound-ledger-tip"> |
| | | å·²ç»å®ï¼{{ salesLedgerLabel(boundLedgerRow) }}ï¼åºåºç±»å«ã客æ·{{ isStorageBound ? 'ãæ²¹åº/å¨ç½' : '' }}以å°è´¦ä¸ºå |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productModelId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©äº§åè§æ ¼', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <!-- ç»å®åæ²¹å°è´¦å产ååªè½ä»è¯¥å°è´¦æç»ééï¼ä»£å¨å°è´¦æ²¡ææç»ï¼ä»èµ°äº§åéæ©å¼¹çª --> |
| | | <el-select v-if="useLedgerProducts" |
| | | v-model="formState.productModelId" |
| | | placeholder="è¯·éæ©å°è´¦äº§å" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | @change="handleLedgerProductChange"> |
| | | <el-option v-for="(item, index) in ledgerProductOptions" |
| | | :key="`${item.productModelId}-${index}`" |
| | | :label="ledgerProductLabel(item)" |
| | | :value="item.productModelId" /> |
| | | </el-select> |
| | | <el-button v-else |
| | | type="primary" |
| | | @click="showProductSelectDialog = true"> |
| | | {{ formState.productName ? formState.productName : 'éæ©äº§å' }} |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼" |
| | | prop="model"> |
| | | <el-input v-model="formState.model" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½" |
| | | prop="unit"> |
| | | <el-input v-model="formState.unit" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <div class="form-section-title">åºåºä¿¡æ¯</div> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºç±»å«" |
| | | prop="outCategory" |
| | | :rules="[{ required: true, message: 'è¯·éæ©åºåºç±»å«', trigger: 'change' }]"> |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="åºåºæ¹æ¬¡" |
| | | prop="batchNo" |
| | | :rules="[{ required: true, message: 'æ²¹ååºåºå¿
须鿩åºåºæ¹æ¬¡', trigger: 'change' }]"> |
| | | <el-select v-model="formState.batchNo" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable |
| | | style="width: 100%"> |
| | | <el-option v-for="item in batchNoOptions" |
| | | :key="item" |
| | | :label="item" |
| | | :value="item" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¨åºåç§°" |
| | | prop="oilDepotId" |
| | | :rules="[{ required: true, message: 'è¯·éæ©å¨åºåç§°', trigger: 'change' }]"> |
| | |
| | | maxlength="11" |
| | | @input="phoneInput('contactPhone')" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 䏿¬¡åºåºå¯åæç»å¤ä¸ªå®¢æ·ï¼åå°è´¦æç»é½è¦å«æ¬æ¬¡åºåºè§æ ¼ï¼åææ°éä¹å = åºåºå¨ä½ --> |
| | | <div class="form-section-title">éå®å°è´¦ç»å®</div> |
| | | <el-row v-for="(ledger, index) in ledgerRows" |
| | | :key="ledger.key" |
| | | :gutter="30"> |
| | | <el-col :span="14"> |
| | | <el-form-item :label="`éå®å°è´¦${index + 1}`"> |
| | | <el-select v-model="ledger.salesLedgerId" |
| | | placeholder="ä¸ç»å®åæåæåºåºæµç¨" |
| | | clearable |
| | | filterable |
| | | remote |
| | | popper-class="ledger-select-popper" |
| | | :remote-method="keyword => searchSalesLedger(keyword, index)" |
| | | :loading="salesLedgerLoading" |
| | | style="width: 100%" |
| | | @change="handleSalesLedgerChange(index)"> |
| | | <el-option v-for="item in visibleLedgerOptions(ledger)" |
| | | :key="item.id" |
| | | :label="salesLedgerLabel(item)" |
| | | :value="item.id"> |
| | | <div class="ledger-option"> |
| | | <div class="ledger-option-title">{{ salesLedgerLabel(item) }}</div> |
| | | <div class="ledger-option-line">产å/è§æ ¼ï¼{{ ledgerSpecsText(item) || "--" }}</div> |
| | | </div> |
| | | </el-option> |
| | | <el-option v-if="!visibleLedgerOptions(ledger).length" |
| | | disabled |
| | | value="__empty__" |
| | | :label="emptyLedgerOptionTip" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="æ¬æ¬¡åææ°é(å¨)"> |
| | | <el-input-number v-model="ledger.quantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :controls="false" |
| | | placeholder="请è¾å
¥æ°åï¼æå¤ä¿ç两ä½å°æ°" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="2"> |
| | | <div class="ledger-row-remove"> |
| | | <el-button link |
| | | type="danger" |
| | | @click="removeLedgerRow(index)">å é¤</el-button> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <div class="ledger-actions"> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="addLedgerRow">æ·»å éå®å°è´¦</el-button> |
| | | <span class="ledger-sum-tip" |
| | | :class="{ 'is-mismatch': isLedgerSumMismatch }"> |
| | | å·²åæ {{ ledgerSum }} å¨ / åºåºå¨ä½ {{ ledgerOutTonnage }} å¨ï¼ä¸¤è
å¿
é¡»ä¸è´ |
| | | </span> |
| | | </div> |
| | | <div v-if="boundLedgerTip" |
| | | class="bound-ledger-tip"> |
| | | {{ boundLedgerTip }} |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- ç»å®åæ²¹å°è´¦å产ååªè½ä»åå°è´¦æç»ç交éééï¼ä»£å¨å°è´¦æ²¡ææç»ï¼èµ°äº§å大类+è§æ ¼åå· --> |
| | | <el-row v-if="useLedgerProducts" |
| | | :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productModelId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©äº§åè§æ ¼', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-select v-model="formState.productModelId" |
| | | placeholder="è¯·éæ©å°è´¦äº§å" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | @change="handleLedgerProductChange"> |
| | | <el-option v-for="(item, index) in ledgerProductOptions" |
| | | :key="`${item.productModelId}-${index}`" |
| | | :label="ledgerProductLabel(item)" |
| | | :value="item.productModelId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-else |
| | | :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产å大类" |
| | | prop="productId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©äº§å大类', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-tree-select v-model="formState.productId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable |
| | | check-strictly |
| | | @change="handleProductCategoryChange" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | prop="productModelId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©äº§åè§æ ¼', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-select v-model="formState.productModelId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | @change="handleProductModelChange"> |
| | | <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-form-item label="åä½" |
| | | prop="unit"> |
| | | <el-input v-model="formState.unit" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºæ¹æ¬¡" |
| | | prop="batchNo" |
| | | :rules="[{ required: true, message: 'æ²¹ååºåºå¿
须鿩åºåºæ¹æ¬¡', trigger: 'change' }]"> |
| | | <el-select v-model="formState.batchNo" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable |
| | | style="width: 100%"> |
| | | <el-option v-for="item in batchNoOptions" |
| | | :key="item" |
| | | :label="item" |
| | | :value="item" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 产åéæ©å¼¹çª --> |
| | | <ProductSelectDialog v-model="showProductSelectDialog" |
| | | @confirm="handleProductSelect" |
| | | single /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, watch, getCurrentInstance, onMounted } from "vue"; |
| | | import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; |
| | | import { |
| | | ref, |
| | | reactive, |
| | | computed, |
| | | watch, |
| | | getCurrentInstance, |
| | | onMounted, |
| | | } from "vue"; |
| | | import ImageUpload from "@/components/AttachmentUpload/image/index.vue"; |
| | | import { |
| | | addOilOutRecordOnly, |
| | |
| | | bindableSalesLedger, |
| | | } from "@/api/inventoryManagement/stockOut.js"; |
| | | import { productList } from "@/api/salesManagement/salesLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { productModelList } from "@/api/basicData/productModel"; |
| | | import { allOilDepot } from "@/api/basicData/oilDepot.js"; |
| | | import { allTankInfo } from "@/api/basicData/tankInfo.js"; |
| | |
| | | const emit = defineEmits(["update:visible", "completed"]); |
| | | |
| | | const buildEmptyForm = () => ({ |
| | | // ç»å®çéå®å°è´¦ idï¼ä¸ºç©ºè¡¨ç¤ºä¸ç»å®ï¼èµ°åæåºåºæµç¨ |
| | | salesLedgerId: undefined, |
| | | productId: undefined, |
| | | productModelId: undefined, |
| | | productName: "", |
| | |
| | | const tankList = ref([]); |
| | | const customerList = ref([]); |
| | | const vehicleList = ref([]); |
| | | const showProductSelectDialog = ref(false); |
| | | // æªç»å®åæ²¹å°è´¦æ¶èµ°ã产å大类æ + è§æ ¼åå·ãï¼ä¸éå®å°è´¦é产åçæ¹å¼ä¸è´ |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]); |
| | | // åºåºæ¹æ¬¡åéåè¯¥è§æ ¼çå
¥åºæ¹æ¬¡ |
| | | const batchNoOptions = ref([]); |
| | | // éå®ç±»åºåºçå¯ç¨éæç¤ºï¼ä¸æ¬¡å©ä½ + å½ååºåï¼ |
| | | const availableQty = ref(null); |
| | | // ç»å®éå®å°è´¦ï¼ä¸æåéãå½åç»å®è¡ãå°è´¦æç»éå®åºç产å |
| | | const salesLedgerOptions = ref([]); |
| | | const salesLedgerLoading = ref(false); |
| | | const boundLedgerRow = ref(null); |
| | | const ledgerProductOptions = ref([]); |
| | | // å°è´¦æç»è¡åªæ productModelIdï¼äº§ååç§°/åå·/åä½è¦å产åè§æ ¼è¡¨å |
| | | const productModelCache = ref(null); |
| | | |
| | | const hasBoundLedger = computed(() => !!formState.value.salesLedgerId); |
| | | // å¤å°è´¦ç»å®ï¼ä¸æ¬¡åºåºå¯åæç»å¤ä¸ªå®¢æ·ï¼åå°è´¦æç»é¡»é½å«æ¬æ¬¡åºåºè§æ ¼ |
| | | let ledgerRowSeed = 0; |
| | | const ledgerRows = ref([]); |
| | | |
| | | const buildEmptyLedgerRow = () => ({ |
| | | // è¡çç¨³å®æ è¯ï¼å 餿¶ä¸è½é ç´¢å¼å®ä½ |
| | | key: ++ledgerRowSeed, |
| | | salesLedgerId: undefined, |
| | | quantity: null, |
| | | ledgerType: "", |
| | | // 该è¡ç䏿åéä¸éä¸è¡ï¼ä¸åè¡å¯ä»¥æä¸åå
³é®è¯ |
| | | options: [], |
| | | raw: null, |
| | | // 该å°è´¦ type=1 æç»ç productModelId éåï¼å符串ï¼ï¼ç¨äºç®å¤å°è´¦äº¤é |
| | | detailModelIds: null, |
| | | }); |
| | | |
| | | const boundLedgerRows = computed(() => |
| | | ledgerRows.value.filter(item => item.salesLedgerId) |
| | | ); |
| | | const hasBoundLedger = computed(() => boundLedgerRows.value.length > 0); |
| | | // 代å¨å°è´¦çæ²¹æ¯å®¢æ·èªå·±çï¼æ¢ä¸äº§ç»å®è¡ï¼ä¹åªè½åç¬ç»ï¼æ··ç»ç±æäº¤æ¶æ ¡éªæ¦ï¼ |
| | | const depositRow = computed(() => |
| | | boundLedgerRows.value.find(item => item.ledgerType === "代å¨") |
| | | ); |
| | | // ç»ä»£å¨å°è´¦ï¼æ²¹åº/å¨ç½ç±å°è´¦å¸¦å
¥ï¼ä½äº§åä»è¦éï¼ä»£å¨åºåºä¹è¦æ£åºåï¼ |
| | | const isStorageBound = computed(() => boundLedgerRow.value?.ledgerType === "代å¨"); |
| | | // åªæåæ²¹å°è´¦æäº§åæç»å¯æï¼ä»£å¨å°è´¦æ²¡æç»ï¼èµ°äº§åéæ©å¼¹çª |
| | | const useLedgerProducts = computed(() => hasBoundLedger.value && !isStorageBound.value); |
| | | const isStorageBound = computed(() => !!depositRow.value); |
| | | // åªæåæ²¹å°è´¦æäº§åæç»å¯æï¼çº¯ä»£å¨æ²¡æç»ï¼èµ°äº§å大类+è§æ ¼åå· |
| | | const hasOilLedger = computed(() => |
| | | boundLedgerRows.value.some(item => item.ledgerType !== "代å¨") |
| | | ); |
| | | const useLedgerProducts = computed(() => hasOilLedger.value); |
| | | const ledgerSum = computed(() => |
| | | Number( |
| | | boundLedgerRows.value |
| | | .reduce((acc, item) => acc + (Number(item.quantity) || 0), 0) |
| | | .toFixed(2) |
| | | ) |
| | | ); |
| | | const ledgerOutTonnage = computed( |
| | | () => Number(formState.value.outTonnage) || 0 |
| | | ); |
| | | // åºåºå¨ä½æ¯åæç便®ï¼ä¸¤è¾¹ä¸ä¸è´å端ä¼ç´æ¥æ¥éï¼è¿éå
æç¤º |
| | | const isLedgerSumMismatch = computed( |
| | | () => hasBoundLedger.value && ledgerSum.value !== ledgerOutTonnage.value |
| | | ); |
| | | const boundLedgerTip = computed(() => { |
| | | const rows = boundLedgerRows.value; |
| | | if (!rows.length) return ""; |
| | | const names = rows.map(item => salesLedgerLabel(item.raw)).join("ï¼"); |
| | | return `å·²ç»å®ï¼${names}ï¼åºåºç±»å«ã客æ·${ |
| | | isStorageBound.value ? "ãæ²¹åº/å¨ç½" : "" |
| | | }以å°è´¦ä¸ºå`; |
| | | }); |
| | | |
| | | const fetchBatchNoOptions = () => { |
| | | batchNoOptions.value = []; |
| | |
| | | const closeModal = () => { |
| | | proxy.resetForm("formRef"); |
| | | formState.value = buildEmptyForm(); |
| | | boundLedgerRow.value = null; |
| | | ledgerRows.value = [buildEmptyLedgerRow()]; |
| | | ledgerProductOptions.value = []; |
| | | isShow.value = false; |
| | | }; |
| | |
| | | .slice(0, 11); |
| | | }; |
| | | |
| | | // 产åéæ©å¤ç |
| | | const handleProductSelect = async products => { |
| | | if (products && products.length > 0) { |
| | | const product = products[0]; |
| | | formState.value.productId = product.productId; |
| | | formState.value.productName = product.productName; |
| | | formState.value.model = product.model; |
| | | formState.value.productModelId = product.id; |
| | | formState.value.unit = product.unit; |
| | | // æ¬ç§æ·ãæ²¹åãå°±æ¯äº§ååç§°ï¼åºåæç»/åºåºå°è´¦çæ²¹ååç»ç齿¯ productNameï¼ï¼ |
| | | // ç´æ¥åæé产ååï¼ä¸åå»äº§åæ 鿝坹ï¼é¿å
æ¯å¯¹ä¸ä¸æ¶æ²¹åæ ç©ºç½ |
| | | formState.value.oilProduct = product.productName || undefined; |
| | | formState.value.batchNo = undefined; |
| | | fetchBatchNoOptions(); |
| | | showProductSelectDialog.value = false; |
| | | proxy.$refs["formRef"]?.validateField("productModelId"); |
| | | } |
| | | }; |
| | | const getProductOptions = () => |
| | | productTreeList().then(res => { |
| | | productOptions.value = convertIdToValue(res || []); |
| | | return productOptions.value; |
| | | }); |
| | | |
| | | function convertIdToValue(data) { |
| | | return (data || []).map(item => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { ...rest, value: id }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | return newItem; |
| | | }); |
| | | } |
| | | |
| | | // å¨åºéæ©æ¹åï¼è®°å½åºå-å
¬å¸å |
| | | const depotChange = id => { |
| | |
| | | .filter(Boolean) |
| | | .join(" / "); |
| | | |
| | | // åºåºç±»å«å³å®è½ç»åªç±»å°è´¦ï¼éå® â åæ²¹å°è´¦ï¼ä»£å¨ â 代å¨å°è´¦ï¼å®¢å没æå¯¹åºå°è´¦ |
| | | const visibleLedgerOptions = row => { |
| | | const category = formState.value.outCategory; |
| | | if (!category) return row.options; |
| | | if (category === "客å") return []; |
| | | return row.options.filter(item => |
| | | category === "代å¨" |
| | | ? item.ledgerType === "代å¨" |
| | | : item.ledgerType !== "代å¨" |
| | | ); |
| | | }; |
| | | |
| | | const emptyLedgerOptionTip = computed(() => { |
| | | const category = formState.value.outCategory; |
| | | if (category === "客å") return "客ååºåºä¸éè¦ç»å®éå®å°è´¦"; |
| | | return category === "代å¨" ? "æ²¡ææ¥å°ä»£å¨å°è´¦" : "æ²¡ææ¥å°éå®å°è´¦"; |
| | | }); |
| | | |
| | | // æç´¢æ¡åªæä¸ä¸ªï¼èå端ä¸ä¸ªåæ°æ¯ AND å
³ç³»ï¼æä»¥ä¾æ¬¡æ ååå· â 客æ·å â 项ç®å è¯ï¼å½ä¸å³æ¢ |
| | | const LEDGER_SEARCH_FIELDS = ["salesContractNo", "customerName", "projectName"]; |
| | | |
| | |
| | | ); |
| | | |
| | | // ç»å®ä¸æï¼åæ²¹å代å¨é½ä¼è¿åï¼ä¸åç±»åè¿æ»¤ï¼ç±ç¨æ·èªå·±é |
| | | const fetchSalesLedgerOptions = async keyword => { |
| | | const fetchSalesLedgerOptions = async (keyword, row) => { |
| | | salesLedgerLoading.value = true; |
| | | try { |
| | | let records = []; |
| | |
| | | records = await queryLedger({}); |
| | | } |
| | | // æç´¢åå·²éä¸çå°è´¦å¯è½ä¸å¨ç»æéï¼è¡¥ä¸é¿å
䏿æ¡åªæ¾ç¤ºä¸ä¸ª id |
| | | if ( |
| | | boundLedgerRow.value && |
| | | !records.some(item => item.id === boundLedgerRow.value.id) |
| | | ) { |
| | | records.unshift(boundLedgerRow.value); |
| | | if (row.raw && !records.some(item => item.id === row.raw.id)) { |
| | | records.unshift(row.raw); |
| | | } |
| | | salesLedgerOptions.value = records; |
| | | row.options = records; |
| | | enrichLedgerSpecs(records); |
| | | } catch (e) { |
| | | salesLedgerOptions.value = []; |
| | | row.options = row.raw ? [row.raw] : []; |
| | | } finally { |
| | | salesLedgerLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const searchSalesLedger = keyword => |
| | | fetchSalesLedgerOptions(String(keyword || "").trim()); |
| | | const searchSalesLedger = (keyword, index) => { |
| | | const row = ledgerRows.value[index]; |
| | | if (!row) return; |
| | | return fetchSalesLedgerOptions(String(keyword || "").trim(), row); |
| | | }; |
| | | |
| | | const addLedgerRow = () => { |
| | | const row = buildEmptyLedgerRow(); |
| | | ledgerRows.value.push(row); |
| | | fetchSalesLedgerOptions("", row); |
| | | }; |
| | | |
| | | const removeLedgerRow = index => { |
| | | ledgerRows.value.splice(index, 1); |
| | | syncLedgerDerived(); |
| | | }; |
| | | |
| | | // 产åè§æ ¼è¡¨å
¨éå䏿¬¡å¹¶ç¼åï¼å°è´¦æç»åªæ productModelIdï¼åç§°/åå·/åä½å¾ä»è¿éè¡¥ |
| | | const getProductModels = () => { |
| | |
| | | .catch(() => []); |
| | | }; |
| | | |
| | | // å°è´¦æç» â 产å䏿åéï¼ä»£å¨å°è´¦æ²¡ææç»ï¼ä¸ä¼è°å°è¿é |
| | | const fetchLedgerProducts = ledgerId => { |
| | | ledgerProductOptions.value = []; |
| | | return Promise.all([ |
| | | productList({ salesLedgerId: ledgerId, type: 1 }), |
| | | getProductModels(), |
| | | ]).then(([ledgerRes, models]) => { |
| | | const rows = Array.isArray(ledgerRes?.data) ? ledgerRes.data : []; |
| | | const modelMap = new Map((models || []).map(item => [String(item.id), item])); |
| | | ledgerProductOptions.value = rows.map(row => { |
| | | const model = modelMap.get(String(row.productModelId)) || {}; |
| | | return { |
| | | productId: row.productId ?? model.productId, |
| | | productModelId: row.productModelId ?? model.id, |
| | | productName: |
| | | model.productName || row.productCategory || row.specificationModel || "", |
| | | model: model.model || row.specificationModel || "", |
| | | unit: model.unit || row.unit || "", |
| | | }; |
| | | }); |
| | | }); |
| | | const toLedgerProduct = (row, modelMap) => { |
| | | const model = modelMap.get(String(row.productModelId)) || {}; |
| | | return { |
| | | productId: row.productId ?? model.productId, |
| | | productModelId: row.productModelId ?? model.id, |
| | | productName: |
| | | model.productName || row.productCategory || row.specificationModel || "", |
| | | model: model.model || row.specificationModel || "", |
| | | unit: model.unit || row.unit || "", |
| | | }; |
| | | }; |
| | | |
| | | // å°è´¦æç»ç¼åï¼ä¸æè¦æ¾ç¤ºäº§å/è§æ ¼ã产å交éä¹è¦ç¨ï¼åä¸ä¸ªå°è´¦åªæä¸æ¬¡ |
| | | const ledgerDetailCache = reactive(new Map()); |
| | | const loadLedgerDetails = ledgerId => { |
| | | const key = String(ledgerId); |
| | | if (ledgerDetailCache.has(key)) { |
| | | return Promise.resolve(ledgerDetailCache.get(key)); |
| | | } |
| | | return productList({ salesLedgerId: ledgerId, type: 1 }) |
| | | .then(res => { |
| | | const list = Array.isArray(res?.data) ? res.data : []; |
| | | ledgerDetailCache.set(key, list); |
| | | return list; |
| | | }) |
| | | .catch(() => null); |
| | | }; |
| | | |
| | | // 䏿éå±ç¤ºã产å大类 è§æ ¼åå·ãï¼å¤ä¸ªååå·ä¸è³äºé¿å¾ä¸æ¨¡ä¸æ ·æé |
| | | const ledgerSpecsText = item => { |
| | | const list = ledgerDetailCache.get(String(item.id)); |
| | | if (!list || !list.length) return ""; |
| | | const texts = [ |
| | | ...new Set( |
| | | list |
| | | .map(row => |
| | | [row.productCategory, row.specificationModel].filter(Boolean).join(" ") |
| | | ) |
| | | .filter(Boolean) |
| | | ), |
| | | ]; |
| | | if (!texts.length) return ""; |
| | | return texts.length > 3 |
| | | ? `${texts.slice(0, 3).join("ã")} ç${texts.length}项` |
| | | : texts.join("ã"); |
| | | }; |
| | | |
| | | // åéå°è´¦å¯è½ä¸æ¬¡å å个ï¼åæ¹ææç»ï¼å«æåç«¯ææ»¡ |
| | | const enrichLedgerSpecs = async records => { |
| | | const pending = records.filter(item => !ledgerDetailCache.has(String(item.id))); |
| | | const BATCH = 6; |
| | | for (let i = 0; i < pending.length; i += BATCH) { |
| | | await Promise.all( |
| | | pending.slice(i, i + BATCH).map(item => loadLedgerDetails(item.id)) |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | // ææè¢«ç»åæ²¹å°è´¦çæç»é½å¿
须嫿¬æ¬¡åºåºè§æ ¼ï¼æä»¥äº§ååéååå°è´¦æç»ç交é |
| | | const refreshLedgerProducts = async () => { |
| | | const sellRows = boundLedgerRows.value.filter( |
| | | item => item.ledgerType !== "代å¨" |
| | | ); |
| | | if (!sellRows.length) { |
| | | ledgerProductOptions.value = []; |
| | | clearProduct(); |
| | | return; |
| | | } |
| | | const models = await getProductModels(); |
| | | const modelMap = new Map((models || []).map(item => [String(item.id), item])); |
| | | let options = null; |
| | | for (const item of sellRows) { |
| | | const detailRows = (await loadLedgerDetails(item.salesLedgerId)) || []; |
| | | const rowOptions = detailRows.map(detail => toLedgerProduct(detail, modelMap)); |
| | | const ids = new Set(rowOptions.map(opt => String(opt.productModelId))); |
| | | item.detailModelIds = [...ids]; |
| | | options = options === null |
| | | ? rowOptions |
| | | : options.filter(opt => ids.has(String(opt.productModelId))); |
| | | } |
| | | ledgerProductOptions.value = options || []; |
| | | // æ¢è¿å°è´¦åå·²éè§æ ¼å¯è½ä¸å¨äº¤ééï¼ä½åºæï¼é¿å
æäº¤æ¶å端æ¥ãæé产åä¸å¨è¯¥å°è´¦æç»ä¸ã |
| | | if ( |
| | | formState.value.productModelId && |
| | | !ledgerProductOptions.value.some( |
| | | opt => |
| | | String(opt.productModelId) === String(formState.value.productModelId) |
| | | ) |
| | | ) { |
| | | clearProduct(); |
| | | } |
| | | }; |
| | | |
| | | // å°è´¦éæ©åååéç®æ´¾çç¶æï¼å®¢æ·åé¦ä¸ªå°è´¦ï¼äº§ååéååå°è´¦æç»äº¤é |
| | | const syncLedgerDerived = () => { |
| | | const rows = boundLedgerRows.value; |
| | | if (!rows.length) return refreshLedgerProducts(); |
| | | applyLedgerCustomer(rows[0].raw); |
| | | // 代å¨è¡çæ²¹åº/å¨ç½ç±å°è´¦å¸¦å
¥ï¼ä»£å¨ä¸å¨ç¬¬ä¸è¡æ¶ä¹è¦å¡«ï¼å¦åå¨åºæ§ä»¶è¢«éä½åæ²¡å¼ |
| | | const storage = rows.find(item => item.ledgerType === "代å¨"); |
| | | formState.value.outCategory = storage ? "代å¨" : "éå®"; |
| | | if (storage) { |
| | | formState.value.oilDepotId = storage.raw.oilDepotId; |
| | | formState.value.oilDepotName = storage.raw.oilDepotName || ""; |
| | | formState.value.tankId = storage.raw.tankId; |
| | | formState.value.tankNo = storage.raw.tankNo || ""; |
| | | } |
| | | return refreshLedgerProducts(); |
| | | }; |
| | | |
| | | const ledgerProductLabel = item => |
| | |
| | | proxy.$refs["formRef"]?.validateField("productModelId"); |
| | | }; |
| | | |
| | | const handleSalesLedgerChange = id => { |
| | | const row = salesLedgerOptions.value.find(item => item.id === id); |
| | | // æ¸
ç©ºéæ©æ¶ el-select ä¼ç»ç©ºä¸²ï¼å½ä¸åæ undefinedï¼é¿å
æç©ºä¸²å½å°è´¦ id ä¼ å端 |
| | | if (!row) { |
| | | formState.value.salesLedgerId = undefined; |
| | | } |
| | | boundLedgerRow.value = row || null; |
| | | ledgerProductOptions.value = []; |
| | | // 产å大类æ¹åï¼è§æ ¼å鿢䏿¹ï¼å·²éè§æ ¼ä½åº |
| | | const handleProductCategoryChange = value => { |
| | | clearProduct(); |
| | | if (!row) { |
| | | // åæ¶ç»å®ï¼åºåºç±»å«ãå®¢æ·æ¢å¤ä¸ºæé |
| | | formState.value.productId = value || undefined; |
| | | modelOptions.value = []; |
| | | if (!value) { |
| | | return; |
| | | } |
| | | // åºåºç±»å«ä¸å®¢æ·ä»¥å°è´¦ä¸ºåï¼å端ä¹ä¼å¼ºå¶è¦çï¼ï¼åç«¯åæ¥å±ç¤ºå¹¶ç½®ç° |
| | | formState.value.outCategory = row.ledgerType === "代å¨" ? "代å¨" : "éå®"; |
| | | applyLedgerCustomer(row); |
| | | if (row.ledgerType === "代å¨") { |
| | | formState.value.oilDepotId = row.oilDepotId; |
| | | formState.value.oilDepotName = row.oilDepotName || ""; |
| | | formState.value.tankId = row.tankId; |
| | | formState.value.tankNo = row.tankNo || ""; |
| | | return; |
| | | modelList({ id: value }).then(res => { |
| | | modelOptions.value = res || []; |
| | | }); |
| | | }; |
| | | |
| | | // è§æ ¼åå·æ¹åï¼äº§ååç§°/è§æ ¼/åä½ç±è§æ ¼ä¸»æ°æ®å¸¦åºï¼æ²¹åå产ååç§° |
| | | const handleProductModelChange = value => { |
| | | const item = modelOptions.value.find(row => row.id === value); |
| | | formState.value.productName = item?.productName || ""; |
| | | formState.value.model = item?.model || ""; |
| | | formState.value.unit = item?.unit || ""; |
| | | formState.value.oilProduct = item?.productName || undefined; |
| | | formState.value.batchNo = undefined; |
| | | fetchBatchNoOptions(); |
| | | proxy.$refs["formRef"]?.validateField("productModelId"); |
| | | }; |
| | | |
| | | const handleSalesLedgerChange = async index => { |
| | | const row = ledgerRows.value[index]; |
| | | if (!row) return; |
| | | // æ¸
ç©ºéæ©æ¶ el-select ä¼ç»ç©ºä¸²ï¼å½ä¸åæ undefinedï¼é¿å
æç©ºä¸²å½å°è´¦ id ä¼ å端 |
| | | const picked = row.options.find(item => item.id === row.salesLedgerId) || null; |
| | | row.raw = picked; |
| | | row.salesLedgerId = picked?.id; |
| | | row.ledgerType = picked?.ledgerType || ""; |
| | | row.detailModelIds = null; |
| | | clearProduct(); |
| | | await syncLedgerDerived(); |
| | | }; |
| | | |
| | | // å°è´¦ç»å®æ ¡éªï¼ææ¡ä¸åç«¯ä¿æä¸è´ï¼è½å¨æäº¤åæ¦ä½çå°±å
æ¦ä½ |
| | | const validateLedgerBinding = () => { |
| | | const rows = boundLedgerRows.value; |
| | | if (!rows.length) return true; |
| | | if (depositRow.value && rows.length > 1) { |
| | | proxy.$modal.msgError( |
| | | "代å¨å°è´¦ä¸è½ä¸å
¶ä»éå®å°è´¦ä¸èµ·ç»å®,䏿¬¡åºåºåªè½ç»ä¸ä¸ªä»£å¨å°è´¦" |
| | | ); |
| | | return false; |
| | | } |
| | | fetchLedgerProducts(row.id); |
| | | const zeroRow = rows.find(item => !(Number(item.quantity) > 0)); |
| | | if (zeroRow) { |
| | | proxy.$modal.msgError( |
| | | `æ¯ä¸ªéå®å°è´¦çç»å®æ°éå¿
须大äº0,éå®ååå·:${ |
| | | zeroRow.raw?.salesContractNo || "" |
| | | }` |
| | | ); |
| | | return false; |
| | | } |
| | | if (ledgerSum.value !== ledgerOutTonnage.value) { |
| | | proxy.$modal.msgError( |
| | | `åéå®å°è´¦ç»å®æ°éä¹åå¿
é¡»çäºæ¬æ¬¡åºåºæ°é,åºåºæ°é:${ledgerOutTonnage.value},ç»å®æ°éä¹å:${ledgerSum.value}` |
| | | ); |
| | | return false; |
| | | } |
| | | // éå®/代å¨/客åé½è¦æ£åºåï¼ç»äºå°è´¦å°±å¿
é¡»éè§æ ¼ï¼ä»£å¨å°è´¦æ²¡æç»ï¼ä»äº§åå¼¹çªéï¼ |
| | | if (!formState.value.productModelId) { |
| | | proxy.$modal.msgError( |
| | | depositRow.value |
| | | ? `代å¨åºåºéè¦æ£ååºå,å¿
é¡»éæ©äº§åè§æ ¼,éå®ååå·:${ |
| | | depositRow.value.raw?.salesContractNo || "" |
| | | }` |
| | | : `ç»å®éå®å°è´¦æ¶å¿
é¡»éæ©äº§åè§æ ¼,éå®ååå·:${ |
| | | rows[0].raw?.salesContractNo || "" |
| | | }` |
| | | ); |
| | | return false; |
| | | } |
| | | const missingRow = rows.find( |
| | | item => |
| | | item.ledgerType !== "代å¨" && |
| | | item.detailModelIds && |
| | | !item.detailModelIds.includes(String(formState.value.productModelId)) |
| | | ); |
| | | if (missingRow) { |
| | | proxy.$modal.msgError( |
| | | `æé产åä¸å¨è¯¥éå®å°è´¦æç»ä¸,éå®ååå·:${ |
| | | missingRow.raw?.salesContractNo || "" |
| | | }` |
| | | ); |
| | | return false; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | // éå®/代å¨/客åé½è¦æ£åºåï¼ä¸å¾å¿
é¡»éè§æ ¼ï¼ä»£å¨å°è´¦æ²¡æç»ï¼ä»äº§åå¼¹çªéï¼ |
| | | if (!formState.value.productModelId) { |
| | | proxy.$modal.msgError("è¯·éæ©äº§åè§æ ¼"); |
| | | return; |
| | | } |
| | | if (!validateLedgerBinding()) return; |
| | | // 计ç®ç»æé表åæäº¤ï¼åºåºå¨ä½æ¯æå¡«çï¼ä¸åè¦çï¼ |
| | | formState.value.netWeight = netWeight.value; |
| | | formState.value.lossQuantity = lossQuantity.value; |
| | |
| | | ...formState.value, |
| | | loadingPhotos: JSON.stringify(formState.value.loadingPhotos || []), |
| | | poundPhotos: JSON.stringify(formState.value.poundPhotos || []), |
| | | // åæ²¹å°è´¦å¯å¤ç»ï¼ç©ºæ°ç»æ¯ãä¸ç»å°è´¦ãçæç¡®è¯ä¹ï¼ä¸è½çç¥ |
| | | ledgerList: boundLedgerRows.value.map(item => ({ |
| | | salesLedgerId: item.salesLedgerId, |
| | | quantity: Number(item.quantity), |
| | | })), |
| | | }; |
| | | |
| | | addOilOutRecordOnly(payload).then(res => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | // æ¯æ¬¡æå¼éæ°æå°è´¦åéï¼é¿å
å¨å«å¤æ°å»ºå°è´¦åæ¬é¡µçä¸å° |
| | | // æ¯æ¬¡æå¼éæ°æå°è´¦åéä¸äº§å大类æ ï¼é¿å
å«å¤æ°å»ºåæ¬é¡µçä¸å° |
| | | watch( |
| | | () => props.visible, |
| | | visible => { |
| | | if (visible) { |
| | | fetchSalesLedgerOptions(""); |
| | | getProductOptions(); |
| | | const row = ledgerRows.value[0]; |
| | | if (row) { |
| | | fetchSalesLedgerOptions("", row); |
| | | } |
| | | } |
| | | } |
| | | ); |
| | | |
| | | onMounted(() => { |
| | | fetchSalesLedgerOptions(""); |
| | | getProductOptions(); |
| | | ledgerRows.value = [buildEmptyLedgerRow()]; |
| | | fetchSalesLedgerOptions("", ledgerRows.value[0]); |
| | | allOilDepot().then(res => { |
| | | oilDepotList.value = res?.data || []; |
| | | }); |
| | |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | // å°è´¦è¡å°¾çãå é¤ãï¼ä¸è¾å
¥æ¡å¯¹é½ï¼label-position=topï¼æ ç¾å ä¸è¡ï¼ |
| | | .ledger-row-remove { |
| | | padding-top: 30px; |
| | | } |
| | | |
| | | // ãæ·»å éå®å°è´¦ãæé® + åæå计æç¤º |
| | | .ledger-actions { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | margin-bottom: 10px; |
| | | |
| | | .ledger-sum-tip { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | |
| | | &.is-mismatch { |
| | | color: #f56c6c; |
| | | font-weight: 600; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ç»å®éå®å°è´¦åçæç¤ºæ¡ |
| | | .bound-ledger-tip { |
| | | margin-top: 6px; |
| | | margin-bottom: 10px; |
| | | padding: 6px 14px; |
| | | font-size: 13px; |
| | | color: #303133; |
| | |
| | | line-height: 1.8; |
| | | } |
| | | } |
| | | |
| | | // éå®å°è´¦ä¸æé项ï¼å°å¡çæ ·å¼ |
| | | .ledger-option { |
| | | padding: 8px 10px; |
| | | |
| | | .ledger-option-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .ledger-option-line { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | line-height: 1.8; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | <style lang="scss"> |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | // éå®å°è´¦ä¸æé¢æ¿ï¼body ä¸ï¼éå
¨å±æ ·å¼ï¼ |
| | | .ledger-select-popper { |
| | | .el-select-dropdown__item { |
| | | height: auto; |
| | | line-height: normal; |
| | | padding: 4px 0; |
| | | } |
| | | |
| | | .ledger-option { |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 6px; |
| | | background-color: #fafbfc; |
| | | |
| | | &:hover { |
| | | border-color: #409eff; |
| | | background-color: #f0f7ff; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | </el-form-item> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <el-input v-model="searchForm.salesContractNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="项ç®åç§°ï¼"> |
| | | <el-input v-model="searchForm.projectName" |
| | | placeholder="请è¾å
¥" |
| | |
| | | width="60" /> |
| | | <el-table-column label="éè´ååå·" |
| | | prop="purchaseContractNumber" |
| | | width="160" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | width="160" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ä¾åºååç§°" |
| | |
| | | <el-input v-model="form.purchaseContractNumber" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" |
| | | prop="salesLedgerId"> |
| | | <el-select v-model="form.salesLedgerId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | @change="salesLedgerChange"> |
| | | <el-option v-for="item in salesContractList" |
| | | :key="item.id" |
| | | :label="item.salesContractNo" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | addOrUpdateSalesLedgerProduct, |
| | | delProduct, |
| | | delLedgerFile, |
| | | getProductInfoByContractNo, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { |
| | | addOrEditPurchase, |
| | |
| | | updatePurchaseTemplate, |
| | | createPurchaseNo, |
| | | delPurchase, |
| | | getSalesNo, |
| | | purchaseListPage, |
| | | productList, |
| | | getPurchaseById, |
| | |
| | | const modelOptions = ref([]); |
| | | const userList = ref([]); |
| | | const productOptions = ref([]); |
| | | const salesContractList = ref([]); |
| | | const supplierList = ref([]); |
| | | const oilDepotList = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | searchForm: { |
| | | supplierName: "", // ä¾åºååç§° |
| | | purchaseContractNumber: "", // éè´ååç¼å· |
| | | salesContractNo: "", // éå®ååç¼å· |
| | | projectName: "", // 项ç®åç§° |
| | | entryDate: null, // å½å
¥æ¥æ |
| | | entryDateStart: undefined, |
| | |
| | | }, |
| | | form: { |
| | | purchaseContractNumber: "", |
| | | salesLedgerId: "", |
| | | projectName: "", |
| | | recorderId: "", |
| | | entryDate: "", |
| | |
| | | isTemplateNameDuplicate.value = false; |
| | | try { |
| | | // å¹¶è¡å è½½åºç¡æ°æ® |
| | | const [salesRes, supplierRes, oilDepotRes] = await Promise.all([ |
| | | getSalesNo(), |
| | | const [supplierRes, oilDepotRes] = await Promise.all([ |
| | | getOptions(), |
| | | allOilDepot(), |
| | | ]); |
| | | |
| | | salesContractList.value = salesRes || []; |
| | | // ä¾åºåè¿æ»¤åºisWhite=0 çæ°æ® |
| | | supplierList.value = (supplierRes.data || []).filter( |
| | | item => item.isWhite === 0 |
| | |
| | | } |
| | | form.value.storageBlobDTOS = fileList.value; |
| | | form.value.type = 2; |
| | | |
| | | // 妿salesLedgerId为空ï¼åä¸ä¼ ésalesContractNo |
| | | if (!form.value.salesLedgerId) { |
| | | form.value.salesContractNo = ""; |
| | | } |
| | | |
| | | // æ°å¢æ¶ä¸ä¼ éid |
| | | const submitData = { ...form.value }; |
| | |
| | | const purchaseCategoryChange = val => { |
| | | if (!["代å¨", "éè´ç´é"].includes(val)) { |
| | | form.value.customerName = ""; |
| | | } |
| | | }; |
| | | |
| | | // éå®ååéæ©æ¹åæ¹æ³ |
| | | const salesLedgerChange = async row => { |
| | | console.log("row", row); |
| | | var index = salesContractList.value.findIndex(item => item.id == row); |
| | | console.log("index", index); |
| | | if (index > -1) { |
| | | await querygProductInfoByContractNo(); |
| | | } |
| | | }; |
| | | |
| | | const querygProductInfoByContractNo = async () => { |
| | | const { code, data } = await getProductInfoByContractNo({ |
| | | contractNo: form.value.salesLedgerId, |
| | | }); |
| | | if (code == 200) { |
| | | productData.value = data || []; |
| | | } |
| | | }; |
| | | |
| | |
| | | prop="projectName" |
| | | width="280" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | width="170" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ç³è¯·äºº" |
| | | prop="applicantName" |
| | | width="100" |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ååå·ï¼" |
| | | prop="purchaseContractNumber"> |
| | | <el-input v-model="form.purchaseContractNumber" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºååç§°ï¼" |
| | | prop="supplierId"> |
| | | <el-select v-model="form.supplierId" |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" |
| | | prop="salesLedgerId"> |
| | | <el-select v-model="form.salesLedgerId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | @change="salesLedgerChange"> |
| | | <el-option v-for="item in salesContractList" |
| | | :key="item.id" |
| | | :label="item.salesContractNo" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·äººï¼" |
| | | prop="applicantId"> |
| | | <el-select v-model="form.applicantId" |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="8"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææå°è´§æ¥æï¼" |
| | | prop="expectedArrivalDate"> |
| | | <el-date-picker style="width: 100%" |
| | |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æä½³å°ç«æ¥æï¼" |
| | | prop="bestArrivalDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.bestArrivalDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼ï¼" |
| | | prop="paymentMethod"> |
| | | <el-input v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="å½å
¥äººï¼" |
| | | prop="recorderId"> |
| | | <el-select v-model="form.recorderId" |
| | | placeholder="è¯·éæ©" |
| | | filterable |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="ç¾è®¢æ¥æï¼" |
| | | prop="executionDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.executionDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="å½å
¥æ¥æï¼" |
| | | prop="entryDate"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <div class="form-section-title">éè´å
¥åºæäº¤</div> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç³è¯·æäº¤æ¶é´ï¼" |
| | | prop="applicationSubmitTime"> |
| | | <el-date-picker style="width: 100%" |
| | | v-model="form.applicationSubmitTime" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ç±»å«ï¼" |
| | | prop="purchaseCategory"> |
| | | <el-radio-group v-model="form.purchaseCategory" |
| | | @change="purchaseCategoryChange"> |
| | | <el-radio value="客å">客å</el-radio> |
| | | <el-radio value="代å¨">代å¨</el-radio> |
| | | <el-radio value="éè´å
¥åº">éè´å
¥åº</el-radio> |
| | | <el-radio value="éè´ç´é">éè´ç´é</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç©æµæ¹å¼ï¼" |
| | | prop="logisticsMethod"> |
| | | <el-select v-model="form.logisticsMethod" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option label="åå®¶é
é" |
| | | value="åå®¶é
é" /> |
| | | <el-option label="å§å¤ç©æµ" |
| | | value="å§å¤ç©æµ" /> |
| | | <el-option label="客æ·èªæ" |
| | | value="客æ·èªæ" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æè´§æ¹å¼ï¼" |
| | | prop="pickupMethod"> |
| | | <el-radio-group v-model="form.pickupMethod"> |
| | | <el-radio value="æä¸æè´§">æä¸æè´§</el-radio> |
| | | <el-radio value="æè´§">æè´§</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´æ²¹åºï¼" |
| | | prop="oilDepotId"> |
| | | <el-select v-model="form.oilDepotId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | popper-class="oil-depot-select-popper" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in oilDepotList" |
| | | :key="item.id" |
| | | :label="item.depotName" |
| | | :value="item.id"> |
| | | <div class="oil-depot-option"> |
| | | <div class="oil-depot-option-title">{{ item.depotName }}</div> |
| | | <div class="oil-depot-option-line">è´è´£äººï¼{{ item.chargePerson }}</div> |
| | | <div class="oil-depot-option-line">èç³»çµè¯ï¼{{ item.phone }}</div> |
| | | <div class="oil-depot-option-line">æå±å
¬å¸ï¼{{ item.company }}</div> |
| | | <div class="oil-depot-option-line">å¯ç¨æ¥æï¼{{ item.enableDate }}</div> |
| | | </div> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·åç§°ï¼" |
| | | prop="customerName"> |
| | | <el-input v-model="form.customerName" |
| | | placeholder="ä»
éè´ç±»å«ä¸ºä»£å¨/éè´ç´éæ¶å¯å¡«å" |
| | | clearable |
| | | :disabled="!['代å¨', 'éè´ç´é'].includes(form.purchaseCategory)" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ååéé¢(å
)ï¼" |
| | | prop="contractAmount"> |
| | | <el-input-number v-model="form.contractAmount" |
| | |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="仿¬¾æ¹å¼ï¼" |
| | | prop="paymentMethod"> |
| | | <el-input v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´æ°éï¼å¨ï¼ï¼" |
| | | prop="purchaseQuantity"> |
| | | <el-input-number v-model="form.purchaseQuantity" |
| | | :precision="0" |
| | | :step="1" |
| | | :min="0" |
| | | placeholder="请è¾å
¥" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <div class="form-section-title">ç³»ç»ä¿¡æ¯</div> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ç³è¯·è®¢åç¶æï¼" |
| | | prop="purchaseOrderStatus"> |
| | | <el-select v-model="form.purchaseOrderStatus" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 100%"> |
| | | <el-option label="å·²ç³è¯·ï¼æªè£
车" |
| | | value="å·²ç³è¯·ï¼æªè£
车" /> |
| | | <el-option label="以åºåï¼æªå
¥åº" |
| | | value="以åºåï¼æªå
¥åº" /> |
| | | <el-option label="å·²å
¥åº" |
| | | value="å·²å
¥åº" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | <el-table-column label="æ°é" |
| | | prop="quantity" |
| | | width="80" /> |
| | | <el-table-column label="åºåé¢è¦æ°é" |
| | | prop="warnNum" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | width="80" /> |
| | |
| | | clearable |
| | | type="textarea" |
| | | :rows="2" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶ææï¼" |
| | | prop="storageBlobDTOS"> |
| | | <FileUpload v-model:file-list="fileList" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåé¢è¦æ°éï¼" |
| | | prop="warnNum"> |
| | | <el-input-number v-model="productForm.warnNum" |
| | | :precision="2" |
| | | :step="0.1" |
| | | :min="0" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¯å¦è´¨æ£ï¼" |
| | | prop="isChecked"> |
| | | <el-radio-group v-model="productForm.isChecked"> |
| | |
| | | delApplication, |
| | | convertToOrder, |
| | | } from "@/api/procurementManagement/purchaseApplication.js"; |
| | | import { |
| | | getSalesNo, |
| | | getOptions, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { allOilDepot } from "@/api/basicData/oilDepot.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import FileUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { tax_rate } = proxy.useDict("tax_rate"); |
| | |
| | | const productSelectedRows = ref([]); |
| | | const supplierOptions = ref([]); |
| | | const supplierList = ref([]); |
| | | const salesContractList = ref([]); |
| | | const userList = ref([]); |
| | | const oilDepotList = ref([]); |
| | | const fileList = ref([]); |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | form: { |
| | | id: "", |
| | | applicationNo: "", |
| | | // 以ä¸å段ä¸ãæ°å¢éè´å°è´¦ãä¿æä¸è´ |
| | | purchaseContractNumber: "", |
| | | supplierId: "", |
| | | projectName: "", |
| | | salesContractNo: "", |
| | | salesLedgerId: "", |
| | | applicantId: "", |
| | | applyDate: "", |
| | | expectedArrivalDate: "", |
| | | contractAmount: "", |
| | | bestArrivalDate: "", |
| | | paymentMethod: "", |
| | | recorderId: "", |
| | | executionDate: "", |
| | | entryDate: "", |
| | | applicationSubmitTime: "", |
| | | purchaseCategory: "", |
| | | logisticsMethod: "", |
| | | pickupMethod: "", |
| | | oilDepotId: "", |
| | | customerName: "", |
| | | purchaseQuantity: "", |
| | | purchaseOrderStatus: "", |
| | | contractAmount: "", |
| | | remarks: "", |
| | | }, |
| | | rules: { |
| | | applyDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | projectName: [ |
| | | { required: true, message: "请è¾å
¥é¡¹ç®åç§°", trigger: "blur" }, |
| | | ], |
| | | supplierId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | entryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | executionDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | purchaseCategory: [ |
| | | { required: true, message: "è¯·éæ©éè´ç±»å«", trigger: "change" }, |
| | | ], |
| | | oilDepotId: [ |
| | | { required: true, message: "è¯·éæ©éè´æ²¹åº", trigger: "change" }, |
| | | ], |
| | | purchaseQuantity: [ |
| | | { required: true, message: "请è¾å
¥éè´æ°é", trigger: "change" }, |
| | | ], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | warnNum: "", |
| | | isChecked: false, |
| | | remark: "", |
| | | }, |
| | |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | warnNum: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { productForm, productRules } = toRefs(productFormData); |
| | |
| | | supplierList.value = list.filter(item => item.isWhite === 0); |
| | | }; |
| | | |
| | | const getSalesContractList = async () => { |
| | | const res = await getSalesNo(); |
| | | salesContractList.value = Array.isArray(res) ? res : res?.data || []; |
| | | }; |
| | | |
| | | const getUserList = async () => { |
| | | const res = await userListNoPage(); |
| | | userList.value = res.data || []; |
| | | }; |
| | | |
| | | const getOilDepotList = async () => { |
| | | const res = await allOilDepot(); |
| | | oilDepotList.value = res?.data || []; |
| | | }; |
| | | |
| | | const getProductOptions = async () => { |
| | |
| | | productFormVisible.value = false; |
| | | }; |
| | | |
| | | const salesLedgerChange = value => { |
| | | const matched = salesContractList.value.find(item => item.id === value); |
| | | form.value.salesContractNo = matched ? matched.salesContractNo : ""; |
| | | // éè´ç±»å«æ¹åï¼ä»
代å¨/éè´ç´éæ¶å®¢æ·åç§°å¯ç¨ï¼åæ¢åæ¸
空 |
| | | const purchaseCategoryChange = val => { |
| | | if (!["代å¨", "éè´ç´é"].includes(val)) { |
| | | form.value.customerName = ""; |
| | | } |
| | | }; |
| | | |
| | | const openForm = async (type, row) => { |
| | |
| | | form.value = { |
| | | id: "", |
| | | applicationNo: "", |
| | | purchaseContractNumber: "", |
| | | supplierId: "", |
| | | projectName: "", |
| | | salesContractNo: "", |
| | | salesLedgerId: "", |
| | | applicantId: "", |
| | | applyDate: dayjs().format("YYYY-MM-DD"), |
| | | expectedArrivalDate: "", |
| | | contractAmount: "", |
| | | bestArrivalDate: "", |
| | | paymentMethod: "", |
| | | recorderId: "", |
| | | executionDate: "", |
| | | entryDate: dayjs().format("YYYY-MM-DD"), |
| | | applicationSubmitTime: "", |
| | | purchaseCategory: "", |
| | | logisticsMethod: "", |
| | | pickupMethod: "", |
| | | oilDepotId: "", |
| | | customerName: "", |
| | | purchaseQuantity: "", |
| | | purchaseOrderStatus: "", |
| | | contractAmount: "", |
| | | remarks: "", |
| | | }; |
| | | productData.value = []; |
| | | productSelectedRows.value = []; |
| | | fileList.value = []; |
| | | |
| | | try { |
| | | await Promise.all([ |
| | | getSupplierOptions(), |
| | | getSalesContractList(), |
| | | getUserList(), |
| | | getProductOptions(), |
| | | getOilDepotList(), |
| | | ]); |
| | | } catch (error) { |
| | | proxy.$modal.msgError("å è½½åºç¡æ°æ®å¤±è´¥"); |
| | |
| | | const res = await getApplicationById({ id: row.id }); |
| | | form.value = { ...(res.data || {}), id: row.id }; |
| | | productData.value = res.data?.productData || []; |
| | | fileList.value = res.data?.storageBlobVOS || []; |
| | | } catch (error) { |
| | | proxy.$modal.msgError("å è½½éè´ç³è¯·è¯¦æ
失败"); |
| | | return; |
| | |
| | | const payload = { |
| | | supplierId: form.value.supplierId || null, |
| | | projectName: form.value.projectName, |
| | | salesContractNo: form.value.salesContractNo, |
| | | salesLedgerId: form.value.salesLedgerId || null, |
| | | applyDate: form.value.applyDate, |
| | | expectedArrivalDate: form.value.expectedArrivalDate || null, |
| | | contractAmount: |
| | | form.value.contractAmount === "" ? null : form.value.contractAmount, |
| | | paymentMethod: form.value.paymentMethod, |
| | | remarks: form.value.remarks, |
| | | // ä¸ãæ°å¢éè´å°è´¦ã对é½çåæ®µ |
| | | purchaseContractNumber: form.value.purchaseContractNumber, |
| | | bestArrivalDate: form.value.bestArrivalDate || null, |
| | | recorderId: form.value.recorderId || null, |
| | | executionDate: form.value.executionDate || null, |
| | | entryDate: form.value.entryDate || null, |
| | | applicationSubmitTime: form.value.applicationSubmitTime || null, |
| | | purchaseCategory: form.value.purchaseCategory, |
| | | logisticsMethod: form.value.logisticsMethod, |
| | | pickupMethod: form.value.pickupMethod, |
| | | oilDepotId: form.value.oilDepotId || null, |
| | | customerName: form.value.customerName, |
| | | purchaseQuantity: |
| | | form.value.purchaseQuantity === "" ? null : form.value.purchaseQuantity, |
| | | purchaseOrderStatus: form.value.purchaseOrderStatus, |
| | | storageBlobDTOS: fileList.value, |
| | | productData: productData.value.map( |
| | | ({ id, purchaseApplicationId, ...rest }) => rest |
| | | ), |
| | |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | const handleConvert = row => { |
| | | ElMessageBox.confirm( |
| | | `确认å°ç³è¯·åã${row.applicationNo}ã转为éè´è®¢ååï¼è½¬è®¢ååå°èªå¨æäº¤éè´å®¡æ¹ã`, |
| | | "转订å", |
| | | { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(async () => { |
| | | const res = await convertToOrder(row.id); |
| | | getList(); |
| | | ElMessageBox.confirm( |
| | | `转订åæåï¼å·²çæéè´å°è´¦ï¼idï¼${res.data}ï¼ãæ¯å¦åå¾éè´å°è´¦æ¥çï¼`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "å徿¥ç", |
| | | cancelButtonText: "ç卿¬é¡µ", |
| | | type: "success", |
| | | } |
| | | ) |
| | | .then(() => { |
| | | const path = findLedgerPath(permissionStore.sidebarRouters); |
| | | if (path) { |
| | | router.push(path); |
| | | } else { |
| | | ElMessage.info("请åå¾ãéè´å°è´¦ãæ¥ççæç订å"); |
| | | } |
| | | }) |
| | | .catch(() => {}); |
| | | }) |
| | | .catch(() => {}); |
| | | const handleConvert = async row => { |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | `确认å°ç³è¯·åã${row.applicationNo}ã转为éè´è®¢ååï¼è½¬è®¢ååå°èªå¨æäº¤éè´å®¡æ¹ã`, |
| | | "转订å", |
| | | { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ); |
| | | } catch { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | const res = await convertToOrder(row.id); |
| | | getList(); |
| | | await ElMessageBox.confirm( |
| | | `转订åæåï¼å·²çæéè´å°è´¦ï¼idï¼${res?.data}ï¼ãæ¯å¦åå¾éè´å°è´¦æ¥çï¼`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "å徿¥ç", |
| | | cancelButtonText: "ç卿¬é¡µ", |
| | | type: "success", |
| | | } |
| | | ); |
| | | await gotoLedger(); |
| | | } catch { |
| | | // 转订å失败ç±è¯·æ±æ¦æªå¨æç¤ºï¼ç¹ãç卿¬é¡µãä¹èµ°è¿éï¼ä¸é¢å¤å¤ç |
| | | } |
| | | }; |
| | | |
| | | // ä»ä¾§è¾¹æ èåä¸å®ä½éè´å°è´¦è·¯ç±ï¼æ¾ä¸å°æ¶ä¸è·³è½¬ä»
æç¤º |
| | | // 跳转éè´å°è´¦ãè·¯ç±å¹é
ä¸ä¸æ push è¢«æ¦æªé½ç»åºå¯è§æç¤ºï¼ä¸éé» |
| | | const gotoLedger = async () => { |
| | | const path = resolveLedgerPath(); |
| | | try { |
| | | await router.push(path); |
| | | } catch { |
| | | ElMessage.warning(`跳转éè´å°è´¦å¤±è´¥ï¼${path}ï¼ï¼è¯·ä»ä¾§è¾¹æ è¿å
¥æ¥ç`); |
| | | } |
| | | }; |
| | | |
| | | // èåæ é¢å¯è½ä¸é¡µé¢æ é¢æç©ºæ ¼å·®å¼ï¼å½ä¸åå忝 |
| | | const normalizeMenuTitle = title => String(title ?? "").replace(/\s+/g, "").trim(); |
| | | |
| | | const resolveLedgerPath = () => { |
| | | const path = |
| | | findLedgerPath(permissionStore.defaultRoutes) || |
| | | findLedgerPath(permissionStore.sidebarRouters); |
| | | // æ¾ä¸å°èåæ¶å
åºå°ä»åºéå¤å¤ä½¿ç¨çåºå®å°åï¼éè´ç®¡çé¦é¡µå¡çãé¦é¡µå¿«æ·å
¥å£ï¼ |
| | | return path || "/procurementManagement/procurementLedger"; |
| | | }; |
| | | |
| | | // ä»ä¾§è¾¹æ èåä¸å®ä½éè´å°è´¦è·¯ç± |
| | | const findLedgerPath = (routes, prefix = "") => { |
| | | for (const route of routes || []) { |
| | | if (!route) continue; |
| | | const fullPath = String(route.path || "").startsWith("/") |
| | | ? route.path |
| | | : `${prefix}/${route.path || ""}`; |
| | | if (route.meta?.title === "éè´å°è´¦") { |
| | | return fullPath; |
| | | if (normalizeMenuTitle(route.meta?.title ?? route.title) === "éè´å°è´¦") { |
| | | return fullPath.replace(/\/{2,}/g, "/").replace(/\/$/, ""); |
| | | } |
| | | const found = findLedgerPath(route.children, fullPath); |
| | | if (found) return found; |
| | |
| | | getSupplierOptions(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | // 表ååæ æ é¢ï¼èè²ç«æ¡ + æµ
èåºè²ï¼çªåºååº |
| | | .form-section-title { |
| | | margin: 18px 0 14px; |
| | | padding: 8px 14px; |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | color: #1d6fd8; |
| | | background: linear-gradient(90deg, rgba(64, 158, 255, 0.14), rgba(64, 158, 255, 0.02)); |
| | | border-left: 4px solid #409eff; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | // éè´æ²¹åºä¸æé项ï¼å°å¡çæ ·å¼ |
| | | .oil-depot-option { |
| | | padding: 8px 10px; |
| | | |
| | | .oil-depot-option-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .oil-depot-option-line { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | line-height: 1.8; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | <style lang="scss"> |
| | | // 䏿颿¿æ¸²æå¨ body ä¸ï¼éå
¨å±æ ·å¼ |
| | | .oil-depot-select-popper { |
| | | .el-select-dropdown__item { |
| | | height: auto; |
| | | line-height: normal; |
| | | padding: 4px 0; |
| | | } |
| | | |
| | | .oil-depot-option { |
| | | border: 1px solid #e4e7ed; |
| | | border-radius: 6px; |
| | | background-color: #fafbfc; |
| | | |
| | | &:hover { |
| | | border-color: #409eff; |
| | | background-color: #f0f7ff; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <!-- æ²¹ååºåºç¶è¡ç children æ¯å°è´§è®°å½ï¼é row-key + children 渲ææåè¡ --> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :row-key="(row) => `${row.source || 'shipping'}-${row.stockOutRecordId ?? row.id}`" |
| | | :row-key=" |
| | | (row) => |
| | | row.source |
| | | ? `${row.source}-${row.stockOutRecordId ?? row.id}` |
| | | : `arrival-${row.id}` |
| | | " |
| | | style="width: 100%" |
| | | height="calc(100vh - 21.5em)" |
| | | > |
| | |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="æ¥æº" prop="source" align="center" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag |
| | | :type="isOilOutRow(scope.row) ? 'success' : 'info'" |
| | | size="small" |
| | | > |
| | | {{ isOilOutRow(scope.row) ? "æ²¹ååºåº" : "åè´§å" }} |
| | | <el-tag :type="sourceTagType(scope.row)" size="small"> |
| | | {{ sourceLabel(scope.row) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | prop="oilDepotName" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åè´§æ¶é´" |
| | | prop="shippingDate" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åè´§æ°é" |
| | | prop="totalQuantity" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å货车çå·" |
| | | prop="shippingCarNumber" |
| | | show-overflow-tooltip |
| | | /> |
| | | <!-- å°è´§åè¡ç对åºåè½å¨å°è´§è®°å½å段ä¸ï¼è·ç¶è¡å
±ç¨è¡¨å¤´ --> |
| | | <el-table-column label="åè´§æ¶é´" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.shippingDate || scope.row.arrivalDate || "--" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§æ°é" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.totalQuantity ?? scope.row.arrivalQuantity ?? "--" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å货车çå·" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.shippingCarNumber || scope.row.truckPlateNo || "--" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.remark || "--" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éä»¶" align="center" width="90"> |
| | | <template #default="scope"> |
| | | <el-image |
| | | v-if="toAttachmentList(scope.row.attachments).length" |
| | | :src="toAttachmentList(scope.row.attachments)[0]" |
| | | :preview-src-list="toAttachmentList(scope.row.attachments)" |
| | | preview-teleported |
| | | fit="cover" |
| | | style="width: 40px; height: 40px; border-radius: 4px" |
| | | /> |
| | | <span v-else>--</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="叿ºå§å" |
| | | prop="driverName" |
| | |
| | | <!-- @click="openForm('edit', scope.row)">åè´§--> |
| | | <!-- </el-button>--> |
| | | <el-button |
| | | v-if="!isArrivalRow(scope.row)" |
| | | link |
| | | type="primary" |
| | | style="color: #67c23a" |
| | |
| | | align="center" |
| | | /> |
| | | </el-table> |
| | | <!-- æ²¹ååºåºç¶è¡çå°è´§è®°å½ï¼åè¡ï¼ --> |
| | | <template v-if="detailArrivalList.length"> |
| | | <div class="detail-arrival-title">å°è´§è®°å½</div> |
| | | <el-table |
| | | :data="detailArrivalList" |
| | | border |
| | | size="small" |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column label="车çå·" prop="truckPlateNo" min-width="120" /> |
| | | <el-table-column label="客æ·åç§°" prop="customerName" min-width="160" show-overflow-tooltip /> |
| | | <el-table-column label="å°è´§æ°é(å¨)" prop="arrivalQuantity" min-width="110" align="center" /> |
| | | <el-table-column label="å°è´§æ¥æ" prop="arrivalDate" min-width="120" /> |
| | | <el-table-column label="éä»¶" align="center" min-width="90"> |
| | | <template #default="scope"> |
| | | <el-image |
| | | v-if="toAttachmentList(scope.row.attachments).length" |
| | | :src="toAttachmentList(scope.row.attachments)[0]" |
| | | :preview-src-list="toAttachmentList(scope.row.attachments)" |
| | | preview-teleported |
| | | fit="cover" |
| | | style="width: 40px; height: 40px; border-radius: 4px" |
| | | /> |
| | | <span v-else>--</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" prop="remark" min-width="140" show-overflow-tooltip /> |
| | | </el-table> |
| | | </template> |
| | | <ImagePreview :file-list="detailRow.storageBlobVOs || []" /> |
| | | </div> |
| | | <template #footer> |
| | |
| | | const detailDialogVisible = ref(false); |
| | | const detailRow = ref(null); |
| | | const detailProductList = ref([]); |
| | | // æ²¹ååºåºç¶è¡çå°è´§è®°å½ï¼åè¡ï¼ |
| | | const detailArrivalList = ref([]); |
| | | |
| | | // å°è´§éä»¶å端å JSON å符串ï¼åºåå¯è½æ¯æ°ç»ä¹å¯è½æ¯ä¸² |
| | | const toAttachmentList = (value) => { |
| | | if (!value) return []; |
| | | const list = Array.isArray(value) |
| | | ? value |
| | | : typeof value === "string" |
| | | ? (() => { |
| | | try { |
| | | const parsed = JSON.parse(value); |
| | | return Array.isArray(parsed) ? parsed : []; |
| | | } catch (e) { |
| | | return []; |
| | | } |
| | | })() |
| | | : []; |
| | | return list |
| | | .map((item) => |
| | | typeof item === "string" ? item : item?.previewURL || item?.url || "" |
| | | ) |
| | | .filter(Boolean); |
| | | }; |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | |
| | | // æ²¹ååºåºè¡æ¥èª stock_out_recordï¼id ä¸ shipping_info 伿å·ï¼ |
| | | // åè´§/å é¤/详æ
çæ¥å£èå齿¯ shipping_infoï¼ä¸è½ç¨äºè¿ç±»è¡ |
| | | const isOilOutRow = (row) => row?.source === "oilOut"; |
| | | const isRowSelectable = (row) => !isOilOutRow(row); |
| | | // å±çº§å±ç¤ºæ¶å°è´§è®°å½æå¨ç¶è¡ç children ä¸ï¼åè¡æ²¡æ source |
| | | const isArrivalRow = (row) => !row?.source; |
| | | const sourceLabel = (row) => { |
| | | if (isArrivalRow(row)) return "å°è´§"; |
| | | return isOilOutRow(row) ? "æ²¹ååºåº" : "åè´§å"; |
| | | }; |
| | | const sourceTagType = (row) => { |
| | | if (isArrivalRow(row)) return "warning"; |
| | | return isOilOutRow(row) ? "success" : "info"; |
| | | }; |
| | | const isRowSelectable = (row) => !isOilOutRow(row) && !isArrivalRow(row); |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection.filter((item) => !isOilOutRow(item)); |
| | | selectedRows.value = selection.filter(item => isRowSelectable(item)); |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | |
| | | const openDetail = async (row) => { |
| | | detailRow.value = row || null; |
| | | detailProductList.value = []; |
| | | detailArrivalList.value = Array.isArray(row?.children) ? row.children : []; |
| | | detailDialogVisible.value = true; |
| | | // æ²¹ååºåºè¡ä¸èµ°åè´§å详æ
æ¥å£ï¼id 伿å·ï¼ï¼ç´æ¥ç¨åè¡¨è¡æ°æ®å±ç¤º |
| | | if (!row?.id || isOilOutRow(row)) return; |
| | |
| | | detailDialogVisible.value = false; |
| | | detailRow.value = null; |
| | | detailProductList.value = []; |
| | | detailArrivalList.value = []; |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | .detail-arrival-title { |
| | | margin: 16px 0 10px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .detail-images { |
| | | margin-top: 16px; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="isShow" |
| | | title="å°è´§è®°å½" |
| | | width="70%" |
| | | @close="closeModal"> |
| | | <div v-if="ledgerLabel" |
| | | class="ledger-tip"> |
| | | éå®å°è´¦ï¼{{ ledgerLabel }} |
| | | </div> |
| | | <div class="actions"> |
| | | <el-button type="primary" |
| | | @click="openForm()">æ°å¢å°è´§</el-button> |
| | | </div> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | style="width: 100%"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="åºåºåå·" |
| | | prop="outboundBatches" |
| | | min-width="150" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="车çå·" |
| | | prop="truckPlateNo" |
| | | min-width="110" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="å°è´§æ°é(å¨)" |
| | | prop="arrivalQuantity" |
| | | min-width="110" |
| | | align="center" /> |
| | | <el-table-column label="å°è´§æ¥æ" |
| | | prop="arrivalDate" |
| | | min-width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="éä»¶" |
| | | align="center" |
| | | min-width="120"> |
| | | <template #default="scope"> |
| | | <el-image v-if="toPhotoArray(scope.row.attachments).length" |
| | | :src="toPhotoArray(scope.row.attachments)[0]" |
| | | :preview-src-list="toPhotoArray(scope.row.attachments)" |
| | | preview-teleported |
| | | fit="cover" |
| | | style="width: 40px; height: 40px; border-radius: 4px" /> |
| | | <span v-else>--</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" |
| | | prop="remark" |
| | | min-width="140" |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | width="140" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openForm(scope.row)">ç¼è¾ |
| | | </el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="handleDelete(scope.row)">å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | |
| | | <!-- æ°å¢ / ç¼è¾å°è´§ --> |
| | | <el-dialog v-model="formVisible" |
| | | :title="form.id ? 'ç¼è¾å°è´§è®°å½' : 'æ°å¢å°è´§è®°å½'" |
| | | width="55%" |
| | | append-to-body |
| | | @close="closeForm"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | label-width="140px" |
| | | label-position="top"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºåå·" |
| | | prop="stockOutRecordId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©åºåºå', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <!-- åºåºå + å°è´¦æ¯è¿æ¡è®°å½ç身份ï¼ç¼è¾æ¶ä¸å
è®¸æ¹ --> |
| | | <el-select v-model="form.stockOutRecordId" |
| | | placeholder="è¯·éæ©åºåºå" |
| | | clearable |
| | | filterable |
| | | :disabled="!!form.id" |
| | | style="width: 100%" |
| | | @change="handleOutboundChange"> |
| | | <el-option v-for="item in outboundOptions" |
| | | :key="item.stockOutRecordId" |
| | | :label="outboundLabel(item)" |
| | | :value="item.stockOutRecordId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="车è¾" |
| | | prop="vehicleId" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©è½¦è¾', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-select v-model="form.vehicleId" |
| | | placeholder="请å
éæ©åºåºå" |
| | | clearable |
| | | filterable |
| | | :disabled="!form.stockOutRecordId" |
| | | style="width: 100%" |
| | | @change="handleVehicleChange"> |
| | | <el-option v-for="item in vehicleOptions" |
| | | :key="item.vehicleId" |
| | | :label="item.truckPlateNo" |
| | | :value="item.vehicleId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å°è´§æ°é(å¨)" |
| | | prop="arrivalQuantity" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: '请è¾å
¥å°è´§æ°é', |
| | | trigger: 'blur', |
| | | } |
| | | ]"> |
| | | <el-input-number v-model="form.arrivalQuantity" |
| | | :min="0" |
| | | :max="arrivalMax" |
| | | :precision="2" |
| | | :controls="false" |
| | | placeholder="请è¾å
¥æ°åï¼æå¤ä¿ç两ä½å°æ°" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å°è´§æ¥æ" |
| | | prop="arrivalDate"> |
| | | <el-date-picker v-model="form.arrivalDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶"> |
| | | <!-- å°è´§éä»¶è¦æ°¸ä¹
å¯çï¼å¿
须走 /common/public/uploadï¼previewURLï¼ --> |
| | | <ImageUpload v-model:file-list="form.attachmentList" |
| | | :permanent="true" |
| | | :limit="9" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | maxlength="500" |
| | | show-word-limit |
| | | placeholder="请è¾å
¥" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <div v-if="arrivalTip" |
| | | class="arrival-tip"> |
| | | {{ arrivalTip }} |
| | | </div> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeForm">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, watch, getCurrentInstance, toRefs } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import ImageUpload from "@/components/AttachmentUpload/image/index.vue"; |
| | | import { |
| | | salesLedgerArrivalListPage, |
| | | bindableArrivalVehicles, |
| | | addSalesLedgerArrival, |
| | | updateSalesLedgerArrival, |
| | | delSalesLedgerArrival, |
| | | } from "@/api/salesManagement/salesLedgerArrival.js"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | required: true, |
| | | }, |
| | | // å½åéå®å°è´¦ idï¼å°è´§è®°å½æå¨ãåºåºå à å°è´¦ãä¸ï¼å°è´¦ç±å¤é¢å¸¦è¿æ¥ |
| | | ledgerId: { |
| | | type: [String, Number], |
| | | default: undefined, |
| | | }, |
| | | ledgerLabel: { |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:visible", "changed"]); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | | }, |
| | | set(val) { |
| | | emit("update:visible", val); |
| | | }, |
| | | }); |
| | | |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const total = ref(0); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | }); |
| | | |
| | | // å¯å°è´§çãåºåºå + 车è¾ãåéï¼ä¸æ¬¡æåæ¥ï¼åºåºåå»éãè½¦è¾æåºåºåèå¨ |
| | | const bindableRows = ref([]); |
| | | const formVisible = ref(false); |
| | | const formRef = ref(null); |
| | | |
| | | const buildEmptyForm = () => ({ |
| | | id: null, |
| | | stockOutRecordId: undefined, |
| | | vehicleId: undefined, |
| | | truckPlateNo: "", |
| | | arrivalQuantity: null, |
| | | arrivalDate: "", |
| | | attachmentList: [], |
| | | remark: "", |
| | | }); |
| | | |
| | | const form = ref(buildEmptyForm()); |
| | | |
| | | // åºåºååéï¼åä¸ä¸ª CK å·åªåºä¸ä¸ªé项 |
| | | const outboundOptions = computed(() => { |
| | | const map = new Map(); |
| | | bindableRows.value.forEach(item => { |
| | | if (!item.stockOutRecordId || map.has(item.stockOutRecordId)) { |
| | | return; |
| | | } |
| | | map.set(item.stockOutRecordId, item); |
| | | }); |
| | | return [...map.values()]; |
| | | }); |
| | | |
| | | const vehicleOptions = computed(() => |
| | | bindableRows.value.filter( |
| | | item => item.stockOutRecordId === form.value.stockOutRecordId |
| | | ) |
| | | ); |
| | | |
| | | const selectedOutbound = computed(() => |
| | | outboundOptions.value.find( |
| | | item => item.stockOutRecordId === form.value.stockOutRecordId |
| | | ) |
| | | ); |
| | | |
| | | const outboundLabel = item => |
| | | [item.outboundBatches, `å·²å°è´§ ${item.arrivedQuantity ?? 0} / ${item.outQuantity ?? 0} å¨`] |
| | | .filter(Boolean) |
| | | .join("ï½"); |
| | | |
| | | // ç¼è¾æ¶æ¬æ¡å·²å çéè¦ä»ãå·²å°è´§ãéå 忥ï¼å¦åæ°éæ¹ä¸å¤§äºåå¼ |
| | | const originalArrivalQuantity = ref(0); |
| | | |
| | | // å°è´§ä¸é = 该å°è´¦å¨è¿å¼ åºåºåä¸åæå°çæ°é |
| | | const arrivalMax = computed(() => { |
| | | const base = selectedOutbound.value; |
| | | if (!base) return undefined; |
| | | const remain = |
| | | (Number(base.outQuantity) || 0) - (Number(base.arrivedQuantity) || 0); |
| | | return Number((remain + originalArrivalQuantity.value).toFixed(2)); |
| | | }); |
| | | |
| | | const arrivalTip = computed(() => { |
| | | const base = selectedOutbound.value; |
| | | if (!base) return ""; |
| | | return `该å°è´¦å¨è¿å¼ åºåºåä¸åæ ${base.outQuantity ?? 0} å¨ï¼å·²å°è´§ ${ |
| | | base.arrivedQuantity ?? 0 |
| | | } å¨ï¼æ¬æ¬¡æå¤å¯ç»è®° ${arrivalMax.value} å¨`; |
| | | }); |
| | | |
| | | const getList = () => { |
| | | if (!props.ledgerId) { |
| | | tableData.value = []; |
| | | total.value = 0; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | salesLedgerArrivalListPage({ |
| | | salesLedgerId: props.ledgerId, |
| | | ...page, |
| | | }) |
| | | .then(res => { |
| | | tableData.value = res.data?.records || []; |
| | | total.value = res.data?.total || 0; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const paginationChange = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | |
| | | const loadBindableRows = () => { |
| | | if (!props.ledgerId) { |
| | | bindableRows.value = []; |
| | | return Promise.resolve([]); |
| | | } |
| | | return bindableArrivalVehicles({ salesLedgerId: props.ledgerId }) |
| | | .then(res => { |
| | | bindableRows.value = Array.isArray(res?.data) ? res.data : []; |
| | | return bindableRows.value; |
| | | }) |
| | | .catch(() => { |
| | | bindableRows.value = []; |
| | | return []; |
| | | }); |
| | | }; |
| | | |
| | | // éä»¶å端å JSON å符串ï¼åºåå¯è½æ¯æ°ç»ä¹å¯è½æ¯ä¸² |
| | | const toPhotoArray = value => { |
| | | if (!value) return []; |
| | | if (Array.isArray(value)) { |
| | | return value.map(getPhotoUrl).filter(Boolean); |
| | | } |
| | | if (typeof value === "string") { |
| | | try { |
| | | const parsed = JSON.parse(value); |
| | | return Array.isArray(parsed) |
| | | ? parsed.map(getPhotoUrl).filter(Boolean) |
| | | : []; |
| | | } catch (e) { |
| | | return []; |
| | | } |
| | | } |
| | | return []; |
| | | }; |
| | | |
| | | const getPhotoUrl = item => |
| | | typeof item === "string" ? item : item?.previewURL || item?.url || ""; |
| | | |
| | | const openForm = row => { |
| | | form.value = buildEmptyForm(); |
| | | originalArrivalQuantity.value = 0; |
| | | loadBindableRows().then(() => { |
| | | if (row) { |
| | | form.value.id = row.id ?? null; |
| | | form.value.stockOutRecordId = row.stockOutRecordId; |
| | | // 车è¾/车ç以åºåºåç»è®°ä¸ºåï¼ä½å表è¡é带åçæ¯å½æ¶çå¼ï¼ç´æ¥åæ¾ |
| | | form.value.vehicleId = row.vehicleId; |
| | | form.value.truckPlateNo = row.truckPlateNo || ""; |
| | | form.value.arrivalQuantity = row.arrivalQuantity ?? null; |
| | | form.value.arrivalDate = row.arrivalDate || ""; |
| | | form.value.remark = row.remark || ""; |
| | | form.value.attachmentList = toPhotoArray(row.attachments).map(url => ({ |
| | | url, |
| | | })); |
| | | originalArrivalQuantity.value = Number(row.arrivalQuantity) || 0; |
| | | } |
| | | formVisible.value = true; |
| | | }); |
| | | }; |
| | | |
| | | const handleOutboundChange = () => { |
| | | // 车è¾éåºåºåèå¨ï¼æ¢äºåºåºåï¼åæ¥éç车è¾å¯è½ä¸å¨åéé |
| | | form.value.vehicleId = undefined; |
| | | form.value.truckPlateNo = ""; |
| | | }; |
| | | |
| | | const handleVehicleChange = id => { |
| | | const vehicle = vehicleOptions.value.find(item => item.vehicleId === id); |
| | | form.value.truckPlateNo = vehicle?.truckPlateNo || ""; |
| | | }; |
| | | |
| | | const closeForm = () => { |
| | | proxy.resetForm("formRef"); |
| | | form.value = buildEmptyForm(); |
| | | originalArrivalQuantity.value = 0; |
| | | formVisible.value = false; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (!valid) return; |
| | | const payload = { |
| | | stockOutRecordId: form.value.stockOutRecordId, |
| | | salesLedgerId: props.ledgerId, |
| | | vehicleId: form.value.vehicleId, |
| | | truckPlateNo: form.value.truckPlateNo, |
| | | arrivalQuantity: form.value.arrivalQuantity, |
| | | arrivalDate: form.value.arrivalDate, |
| | | // åªåæ°¸ä¹
龿¥ï¼2 å°æ¶å¤±æçç¾å URL è¿æåé件伿ä¸å¼ |
| | | attachments: form.value.attachmentList.map(getPhotoUrl).filter(Boolean), |
| | | remark: form.value.remark, |
| | | }; |
| | | const submit = form.value.id |
| | | ? updateSalesLedgerArrival({ ...payload, id: form.value.id }) |
| | | : addSalesLedgerArrival(payload); |
| | | submit.then(() => { |
| | | proxy.$modal.msgSuccess("æä½æå"); |
| | | closeForm(); |
| | | getList(); |
| | | emit("changed"); |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("æ¤æä½å°å é¤è¯¥å°è´§è®°å½ï¼æ¯å¦ç¡®è®¤ï¼", "å é¤", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delSalesLedgerArrival([row.id]).then(() => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | emit("changed"); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | const closeModal = () => { |
| | | isShow.value = false; |
| | | }; |
| | | |
| | | watch( |
| | | () => props.visible, |
| | | visible => { |
| | | if (visible) { |
| | | page.current = 1; |
| | | getList(); |
| | | } |
| | | } |
| | | ); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .ledger-tip, |
| | | .arrival-tip { |
| | | margin-bottom: 10px; |
| | | padding: 6px 14px; |
| | | font-size: 13px; |
| | | color: #303133; |
| | | background: rgba(64, 158, 255, 0.08); |
| | | border-left: 3px solid #409eff; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | .arrival-tip { |
| | | margin-bottom: 0; |
| | | } |
| | | </style> |
| | |
| | | </div> |
| | | <!-- åæ²¹ / 代卿æä¸¤ä¸ªé¡µç¾ï¼åèªåºå®å¸¦ ledgerType è°åä¸ä¸ªå表æ¥å£ --> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="ledger-tabs"> |
| | | <el-tab-pane label="éå®å°è´¦ï¼åæ²¹ï¼" name="åæ²¹" /> |
| | | <el-tab-pane label="éå®å°è´¦" name="åæ²¹" /> |
| | | <el-tab-pane label="代å¨å°è´¦" name="代å¨" /> |
| | | </el-tabs> |
| | | <div class="table_list"> |
| | |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" /> |
| | | <el-table-column label="产åç¶æ" width="100px" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.approveStatus === 1" type="success" |
| | | >å
è¶³ |
| | | </el-tag> |
| | | <el-tag |
| | | v-else-if=" |
| | | scope.row.approveStatus === 0 && |
| | | scope.row.noQuantity === 0 |
| | | " |
| | | type="success" |
| | | >å·²åºåº |
| | | </el-tag> |
| | | <el-tag v-else type="danger">ä¸è¶³ </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè´§ç¶æ" width="140" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getShippingStatusType(scope.row)" size="small"> |
| | | {{ getShippingStatusText(scope.row) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="审æ¹ä¸æ°é" width="120" align="center"> |
| | | <template #default="scope"> |
| | | <span>{{ scope.row.pendingApprovalQuantity || 0 }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å¿«éå
¬å¸" |
| | | prop="expressCompany" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å¿«éåå·" |
| | | prop="expressNumber" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="å货车ç" minWidth="100px" align="center"> |
| | | <template #default="scope"> |
| | | <div> |
| | |
| | | </el-table-column> |
| | | <el-table-column label="æ°é" prop="quantity" /> |
| | | <el-table-column label="éè´§æ°é" prop="returnQuantity" /> |
| | | <el-table-column label="å¾
åè´§æ°é" prop="noQuantity" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" /> |
| | | <el-table-column |
| | | label="å«ç¨åä»·(å
)" |
| | |
| | | label="仿¬¾æ¹å¼" |
| | | prop="paymentMethod" |
| | | show-overflow-tooltip |
| | | :formatter="paymentMethodFormatter" |
| | | /> |
| | | <el-table-column |
| | | label="ååéé¢(å
)" |
| | |
| | | prop="ledgerType" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | :formatter="ledgerTypeFormatter" |
| | | /> |
| | | <el-table-column |
| | | v-if="activeTab === '代å¨'" |
| | |
| | | width="140" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | v-if="activeTab === '代å¨'" |
| | | label="å¨ä½(å¨)" |
| | | prop="tonnage" |
| | | width="120" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="审æ¹ç¶æ" width="110" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getApprovalStatusType(row.approvalStatus)" size="small"> |
| | |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column fixed="right" label="æä½" width="220" align="center"> |
| | | <el-table-column fixed="right" label="æä½" width="300" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="openForm('view', scope.row)" |
| | | >详æ
|
| | |
| | | </el-button> |
| | | <el-button link type="primary" @click="openFileDialog(scope.row)" |
| | | >éä»¶ |
| | | </el-button> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | @click="openArrivalDialog(scope.row)" |
| | | >å°è´§è®°å½ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | v-model="form.ledgerType" |
| | | :disabled="operationType === 'view'" |
| | | > |
| | | <el-radio value="åæ²¹">åæ²¹</el-radio> |
| | | <el-radio value="代å¨">ååå¨ä½ç½®ï¼ä»£å¨ï¼</el-radio> |
| | | <el-radio value="åæ²¹">éå®</el-radio> |
| | | <el-radio value="代å¨">代å¨è´¹</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼"> |
| | | <el-input |
| | | <el-form-item label="仿¬¾æ¹å¼" prop="paymentMethod"> |
| | | <el-select |
| | | v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | /> |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in payment_methods" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 代å¨ï¼ååå¨ä½ç½®ï¼åªéæ²¹åº+å¨ç½å¹¶æå·¥å¡«ååéé¢ --> |
| | | <!-- 代å¨è´¹ï¼åªéæ²¹åº+å¨ç½å¹¶æå·¥å¡«ååéé¢ãå¨ä½ --> |
| | | <el-row v-if="form.ledgerType === '代å¨'" :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ²¹åºï¼" prop="oilDepotId"> |
| | |
| | | :closable="false" |
| | | class="trust-fund-alert" |
| | | :title="`ååéé¢å·²è¶
åºè¯¥å®¢æ·ä¿¡æåºéï¼ä¿¡æåºéï¼${selectedCustomerTrustFund}ï¼`" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¨ä½(å¨)ï¼" prop="tonnage"> |
| | | <el-input-number |
| | | v-model="form.tonnage" |
| | | :min="0" |
| | | :precision="2" |
| | | :controls="false" |
| | | placeholder="请è¾å
¥" |
| | | :disabled="operationType === 'view'" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- å°è´§è®°å½ï¼æå¨ãåºåºå à æ¬å°è´¦ãä¸ï¼åæ¶æ¯åè´§å°è´¦æ²¹ååºåºç¶è¡çåè¡ --> |
| | | <ArrivalDialog |
| | | v-model:visible="arrivalDialogVisible" |
| | | :ledger-id="arrivalLedgerId" |
| | | :ledger-label="arrivalLedgerLabel" |
| | | @changed="getList" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { getCustomer, listCustomer } from "@/api/basicData/customer.js"; |
| | | import { allOilDepot } from "@/api/basicData/oilDepot.js"; |
| | | import { allTankInfo } from "@/api/basicData/tankInfo.js"; |
| | | import ArrivalDialog from "./components/ArrivalDialog.vue"; |
| | | |
| | | const FileList = defineAsyncComponent(() => |
| | | import("@/components/Dialog/FileList.vue") |
| | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const { tax_rate } = proxy.useDict("tax_rate"); |
| | | const { payment_methods } = proxy.useDict("payment_methods"); |
| | | // 仿¬¾æ¹å¼åçæ¯åå
¸ valueï¼åæ¾è¦è½¬æ labelï¼æ¹é åçèæ°æ®æ¯èªç±ææ¬ï¼åä¸å°å°±åæ ·æ¾ç¤º |
| | | const getPaymentMethodLabel = (value) => { |
| | | if (value === undefined || value === null || value === "") return "-"; |
| | | const item = payment_methods.value?.find( |
| | | (m) => String(m.value) === String(value) |
| | | ); |
| | | return item?.label ?? value; |
| | | }; |
| | | const paymentMethodFormatter = (row) => getPaymentMethodLabel(row.paymentMethod); |
| | | // åæ²¹ / 代å¨ä¸¤ä¸ªé¡µç¾ï¼åºå®å¸¦ ledgerType è°åä¸ä¸ªå表æ¥å£ |
| | | const activeTab = ref("åæ²¹"); |
| | | // çé¢å£å¾ï¼ledgerType=åæ²¹ æ¾ç¤ºãéå®ãï¼=ä»£å¨ æ¾ç¤ºã代å¨è´¹ãï¼æäº¤ç»åç«¯ä»æ¯åæ²¹ / ä»£å¨ |
| | | const ledgerTypeLabel = (value) => |
| | | value === "åæ²¹" ? "éå®" : value === "代å¨" ? "代å¨è´¹" : value; |
| | | const ledgerTypeFormatter = (row) => ledgerTypeLabel(row.ledgerType); |
| | | const approvalStatusOptions = ["å¾
审æ¹", "å®¡æ ¸ä¸", "éè¿", "æç»"]; |
| | | // 代å¨è¡¨åï¼æ²¹åºãå¨ç½ä¸æï¼ä¸¤è
äºç¸ç¬ç«ï¼å¨ç½ä¸æå¨æ²¹åºä¸ï¼ |
| | | const oilDepotOptions = ref([]); |
| | |
| | | oilDepotId: "", // 代å¨ï¼æ²¹åº |
| | | tankId: "", // 代å¨ï¼å¨ç½ |
| | | contractAmount: null, // 代å¨ï¼ååéé¢ç±å端æå·¥å¡« |
| | | tonnage: null, // 代å¨è´¹ï¼å¨ä½ï¼éå¡« |
| | | }, |
| | | rules: { |
| | | ledgerType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | |
| | | form.value.type = 1; |
| | | form.value.ledgerType = form.value.ledgerType || "åæ²¹"; |
| | | if (form.value.ledgerType === "代å¨") { |
| | | // 代å¨ï¼ååå¨ä½ç½®ï¼æ²¡æäº§åæç»ï¼ååé颿工填 |
| | | // 代å¨è´¹ï¼æ²¡æäº§åæç»ï¼ååé颿工填 |
| | | if (!form.value.oilDepotId) { |
| | | proxy.$modal.msgWarning("代å¨å¿
é¡»éæ©æ²¹åº"); |
| | | return; |
| | |
| | | if (!(Number(form.value.contractAmount) > 0)) { |
| | | proxy.$modal.msgWarning("代å¨å¿
须填åååéé¢"); |
| | | return; |
| | | } |
| | | // å¨ä½éå¡«ï¼æ¸
空è¾å
¥æ¡æ¶ el-input-number ä¼ç» undefinedï¼ç»ä¸æ null åæäº¤ |
| | | if (form.value.tonnage === "" || form.value.tonnage === undefined) { |
| | | form.value.tonnage = null; |
| | | } |
| | | delete form.value.productData; |
| | | } else { |
| | |
| | | form.value.oilDepotId = ""; |
| | | form.value.tankId = ""; |
| | | form.value.contractAmount = null; |
| | | form.value.tonnage = null; |
| | | } |
| | | form.value.storageBlobDTOs = fileList; |
| | | if (form.value.autoGenerateContractNo) { |
| | |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | /** |
| | | * è·ååè´§ç¶æææ¬ |
| | | * @param row è¡æ°æ® |
| | | */ |
| | | const getShippingStatusText = (row) => { |
| | | // 妿已åè´§ï¼æåè´§æ¥ææè½¦çå·ï¼ï¼æ¾ç¤º"å·²åè´§" |
| | | // if (row.shippingDate || row.shippingCarNumber) { |
| | | // return "å·²åè´§"; |
| | | // } |
| | | |
| | | // è·ååè´§ç¶æåæ®µ |
| | | const status = row.shippingStatus; |
| | | |
| | | // å¦æç¶æä¸ºç©ºææªå®ä¹ï¼é»è®¤ä¸º"å¾
åè´§" |
| | | if (status === null || status === undefined || status === "") { |
| | | return "å¾
åè´§"; |
| | | } |
| | | |
| | | // ç¶ææ¯å符串 |
| | | const statusStr = String(status).trim(); |
| | | const statusTextMap = { |
| | | å¾
åè´§: "å¾
åè´§", |
| | | å¾
å®¡æ ¸: "å¾
å®¡æ ¸", |
| | | å®¡æ ¸ä¸: "å®¡æ ¸ä¸", |
| | | å®¡æ ¸æç»: "å®¡æ ¸æç»", |
| | | å®¡æ ¸éè¿: "å®¡æ ¸éè¿", |
| | | å·²åè´§: "å·²åè´§", |
| | | é¨ååè´§: "é¨ååè´§", |
| | | }; |
| | | return statusTextMap[statusStr] || "å¾
åè´§"; |
| | | }; |
| | | |
| | | /** |
| | | * è·ååè´§ç¶ææ ç¾ç±»åï¼é¢è²ï¼ |
| | | * @param row è¡æ°æ® |
| | | */ |
| | | const getShippingStatusType = (row) => { |
| | | // 妿已åè´§ï¼æåè´§æ¥ææè½¦çå·ï¼ï¼æ¾ç¤ºç»¿è² |
| | | if (row.shippingStatus === "å·²åè´§") { |
| | | return "success"; |
| | | } |
| | | |
| | | // è·ååè´§ç¶æåæ®µ |
| | | const status = row.shippingStatus; |
| | | |
| | | // å¦æç¶æä¸ºç©ºææªå®ä¹ï¼é»è®¤ä¸ºç°è²ï¼å¾
åè´§ï¼ |
| | | if (status === null || status === undefined || status === "") { |
| | | return "info"; |
| | | } |
| | | |
| | | // ç¶ææ¯å符串 |
| | | const statusStr = String(status).trim(); |
| | | const typeTextMap = { |
| | | å¾
åè´§: "info", |
| | | å¾
å®¡æ ¸: "info", |
| | | å®¡æ ¸ä¸: "warning", |
| | | å®¡æ ¸æç»: "danger", |
| | | å®¡æ ¸éè¿: "success", |
| | | å·²åè´§: "success", |
| | | é¨ååè´§: "warning", |
| | | }; |
| | | return typeTextMap[statusStr] || "info"; |
| | | }; |
| | | |
| | | /** |
| | | * 夿æ¯å¦å¯ä»¥åè´§ |
| | |
| | | fileDialogVisible.value = true; |
| | | }; |
| | | |
| | | // å°è´§è®°å½å¼¹æ¡ï¼æå½åå°è´¦çéï¼å°è´¦ id/åç±å表è¡å¸¦å
¥ |
| | | const arrivalDialogVisible = ref(false); |
| | | const arrivalLedgerId = ref(undefined); |
| | | const arrivalLedgerLabel = ref(""); |
| | | const openArrivalDialog = (row) => { |
| | | arrivalLedgerId.value = row.id; |
| | | arrivalLedgerLabel.value = [row.salesContractNo, row.customerName] |
| | | .filter(Boolean) |
| | | .join(" / "); |
| | | arrivalDialogVisible.value = true; |
| | | }; |
| | | |
| | | // æå¼åè´§å¼¹æ¡ |
| | | const openDeliveryForm = async (row) => { |
| | | // æ£æ¥æ¯å¦å¯ä»¥åè´§ |