diff --git a/src/api/payment/paymentApplication.js b/src/api/payment/paymentApplication.js index 3e4c802..59c9aa1 100644 --- a/src/api/payment/paymentApplication.js +++ b/src/api/payment/paymentApplication.js @@ -10,6 +10,13 @@ export const getReferenceAmount = (paymentType, referenceId, excludeId) => method: 'get', params: { paymentType, referenceId, excludeId: excludeId || undefined }, }); +/** 按合同查询可选项目预付核销列表(审批通过且仍有可核销余额) */ +export const getProjectAdvanceWriteOffOptions = params => + request({ + url: `${baseUrl}/project-advance-write-off-options`, + method: 'get', + params, + }); export const save = data => request({ url: `${baseUrl}/save`, method: 'post', data }); export const remove = id => request({ url: `${baseUrl}/remove`, method: 'post', params: { id } }); export const submit = data => request({ url: `${baseUrl}/submit`, method: 'post', data }); diff --git a/src/views/business/common-route.vue b/src/views/business/common-route.vue index 99095ac..15b8cba 100644 --- a/src/views/business/common-route.vue +++ b/src/views/business/common-route.vue @@ -396,8 +396,11 @@ export default { return this.selectionList.map(item => item.id).join(','); }, }, - created() { - this.applyDefaultDeptSearch(); + created() { + // 超管默认查全部组织,不预填组织筛选;普通用户预填当前组织。 + if (!this.isAdmin) { + this.applyDefaultDeptSearch(); + } this.initDeptTree(); }, methods: { @@ -455,10 +458,23 @@ export default { normalizeListQuery(params = {}) { // 搜索条件优先于缓存的 query,避免默认组织 ID 覆盖用户新选择的组织。 const query = { ...this.query, ...params }; - const rawDeptId = query.deptId ?? query.searchDeptId; + // 本次搜索显式带了组织字段时,以本次为准(含清空);否则沿用缓存。 + const hasDeptInParams = + Object.prototype.hasOwnProperty.call(params, 'deptId') || + Object.prototype.hasOwnProperty.call(params, 'searchDeptId'); + const rawDeptId = hasDeptInParams + ? params.deptId ?? params.searchDeptId + : query.deptId ?? query.searchDeptId; delete query.searchDeptId; if (rawDeptId !== undefined && rawDeptId !== null && String(rawDeptId).trim() !== '') { - query.deptId = String(rawDeptId).split(',')[0].trim(); + // 级联可能回传路径数组,统一取末级并保持字符串,避免雪花 ID 精度丢失。 + const deptValue = Array.isArray(rawDeptId) ? rawDeptId.at(-1) : rawDeptId; + query.deptId = String(deptValue ?? '') + .split(',')[0] + .trim(); + if (!query.deptId) { + delete query.deptId; + } } else { delete query.deptId; } @@ -466,6 +482,23 @@ export default { return query; }, applyDefaultDeptSearch() { + // 超管默认不限组织,避免预填组织后新增落在其他组织时列表看不见。 + if (this.isAdmin) { + const deptColumn = this.findColumn(this.option.column, 'searchDeptId'); + if (deptColumn) { + deptColumn.searchValue = undefined; + } + const nextQuery = { ...this.query }; + delete nextQuery.deptId; + delete nextQuery.searchDeptId; + this.query = nextQuery; + this.$nextTick(() => { + if (this.$refs.crud?.searchForm) { + this.$refs.crud.searchForm.searchDeptId = undefined; + } + }); + return; + } const deptId = this.defaultDeptId(); const deptColumn = this.findColumn(this.option.column, 'searchDeptId'); if (!deptId || !deptColumn) return; @@ -477,6 +510,11 @@ export default { } }); }, + resolveDeptName(deptId) { + if (!deptId) return ''; + const matched = this.deptOptions.find(item => String(item.value) === String(deptId)); + return matched ? String(matched.label || '').trim() : ''; + }, resetAddressQuery() { this.addressQuery = { addressName: '', @@ -933,7 +971,8 @@ export default { } api.submit(submitRow).then( () => { - this.onLoad(this.page); + this.page.currentPage = 1; + this.onLoad(this.page, this.query); this.$message({ type: 'success', message: '操作成功!' }); done(); }, @@ -951,7 +990,7 @@ export default { } api.submit(submitRow).then( () => { - this.onLoad(this.page); + this.onLoad(this.page, this.query); this.$message({ type: 'success', message: '操作成功!' }); done(); }, @@ -1006,7 +1045,13 @@ export default { beforeOpen(done, type) { this.dialogReadonly = type === 'view'; if (type === 'add') { - this.form = {}; + const form = {}; + // 超管若当前按某组织筛选,新增默认归属该组织,避免保存后被筛选条件滤掉。 + if (this.isAdmin && this.query.deptId) { + form.deptId = String(this.query.deptId); + form.deptName = this.resolveDeptName(form.deptId); + } + this.form = form; done(); return; } diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue index 6ae6c21..e05c03d 100644 --- a/src/views/business/project-apply.vue +++ b/src/views/business/project-apply.vue @@ -2506,8 +2506,9 @@ export default { const list = res.data.data || []; this.platformCompanyOptions = list.map(item => ({ label: item.deptName || item.fullName || item.title || item.name, - value: item.id, - })); + // 雪花 ID 必须以字符串绑定,避免 JS Number 精度丢失导致多选提交异常 + value: item.id == null || item.id === '' ? '' : String(item.id), + })).filter(item => item.value); this.ensureCurrentPlatformOption(); }); }, diff --git a/src/views/payment/payment-application-form.vue b/src/views/payment/payment-application-form.vue index 140937f..9dd62e0 100644 --- a/src/views/payment/payment-application-form.vue +++ b/src/views/payment/payment-application-form.vue @@ -688,6 +688,9 @@ const attachmentViewerPlugins = [ fallbackPlugin(), ]; +const paymentApplicationListRoute = '/payment/payment-application'; +const paymentApplicationListRefreshKey = 'payment-application-list-refresh'; + const ATTACHMENT_MATERIALS = { weighingSlip: { key: 'weighingSlip', label: '磅单', keywords: ['磅单'] }, entrustOrder: { key: 'entrustOrder', label: '委托单', keywords: ['委托单'] }, @@ -2712,12 +2715,14 @@ export default { mkEnabled: await loadMkEnabled().catch(() => false), }); this.$message.success('提交成功'); + // 通知列表页(keep-alive)重新拉取数据 + sessionStorage.setItem(paymentApplicationListRefreshKey, '1'); this.goBack(); }, goBack() { removeSettlementTransfer(this.$route.query.transferToken); this.$router.$avueRouter?.closeTag?.(); - this.$router.push('/payment/payment-application'); + this.$router.push(paymentApplicationListRoute); }, formatMoney(value) { const amount = Number(value); diff --git a/src/views/payment/payment-application.vue b/src/views/payment/payment-application.vue index b3e63d3..44cde10 100644 --- a/src/views/payment/payment-application.vue +++ b/src/views/payment/payment-application.vue @@ -152,6 +152,9 @@ import organizationSearch from '@/mixins/organization-search'; import { getMKApprovalUrl } from '@/api/system/business-process'; import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval'; +const paymentApplicationListRoute = '/payment/payment-application'; +const paymentApplicationListRefreshKey = 'payment-application-list-refresh'; + export default { name: 'PaymentApplication', mixins: [organizationSearch], @@ -193,7 +196,28 @@ export default { this.mkEnabled = false; }); }, + activated() { + this.consumeListRefreshFlag(); + }, + watch: { + $route(to, from) { + if ( + to.path === paymentApplicationListRoute && + from?.path?.includes('/payment-application/form') + ) { + this.consumeListRefreshFlag(); + } + }, + }, methods: { + /** 表单提交成功后带标记返回时,重新拉取列表 */ + consumeListRefreshFlag() { + if (!sessionStorage.getItem(paymentApplicationListRefreshKey)) { + return; + } + sessionStorage.removeItem(paymentApplicationListRefreshKey); + this.onLoad(this.page); + }, hasPermission(code) { return this.permission?.[code] !== false; }, diff --git a/src/views/settlement/components/formal-settlement-editor.vue b/src/views/settlement/components/formal-settlement-editor.vue index 5da1c16..b5dce08 100644 --- a/src/views/settlement/components/formal-settlement-editor.vue +++ b/src/views/settlement/components/formal-settlement-editor.vue @@ -378,6 +378,16 @@ + +
@@ -1011,12 +1021,13 @@ import { viewKingdeeInvoiceFile, } from '@/utils/kingdee-attachment'; import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/main.vue'; +import ProjectAdvanceWriteOff from './project-advance-write-off.vue'; import * as XLSX from 'xlsx'; import { isAdministrator } from '@/utils/auth'; export default { name: 'FormalSettlementEditor', - components: { InfoFilled, KingdeeInvoicePoolDialog }, + components: { InfoFilled, KingdeeInvoicePoolDialog, ProjectAdvanceWriteOff }, props: { modelValue: Boolean, recordId: [String, Number], @@ -1045,6 +1056,7 @@ export default { receiptClaims: [], adjustments: [], changeRecords: [], + projectAdvanceWriteOffs: [], attachments: [], selectedAttachmentRows: [], invoices: [], @@ -1275,6 +1287,13 @@ export default { } this.adjustments = data.adjustments || []; this.changeRecords = data.changeRecords || []; + this.projectAdvanceWriteOffs = (data.projectAdvanceWriteOffs || []).map(row => ({ + ...row, + writeOffAmount: Number(row.writeOffAmount || 0), + projectAdvanceAmount: Number(row.projectAdvanceAmount || 0), + availableWriteOffAmount: Number(row.availableWriteOffAmount || 0), + createTime: row.paymentCreateTime || row.createTime, + })); this.attachments = this.parseAttachments(data.attachmentsJson); this.selectedAttachmentRows = []; this.invoices = data.invoices || []; @@ -1305,6 +1324,7 @@ export default { this.receiptClaims = []; this.adjustments = []; this.changeRecords = []; + this.projectAdvanceWriteOffs = []; this.attachments = []; this.selectedAttachmentRows = []; this.invoices = []; @@ -2549,6 +2569,7 @@ export default { matchedAmount: Number(row.matchedAmount || 0), attachmentJson: row.attachmentJson || '', })), + projectAdvanceWriteOffs: this.$refs.projectAdvanceWriteOffRef?.toSavePayload?.() || [], remark: this.form.remark, }; }, @@ -2557,6 +2578,9 @@ export default { }, async handleDraftSave() { // 保存草稿:不做表单校验,直接落草稿并返回列表刷新 + if (this.$refs.projectAdvanceWriteOffRef?.validateBeforeSave?.() === false) { + return; + } this.draftSaving = true; try { await save({ @@ -2584,6 +2608,9 @@ export default { return this.$message.warning('请完善手工费用的费用类型和费用项'); } if (!this.validateInvoices()) return; + if (this.$refs.projectAdvanceWriteOffRef?.validateBeforeSave?.() === false) { + return; + } // 附件类型仅做提示,不阻断正式结算单新增/保存提交。 // this.validateAttachments(); this.saving = true; diff --git a/src/views/settlement/components/pre-settlement-editor.vue b/src/views/settlement/components/pre-settlement-editor.vue index b46cec5..e60bedc 100644 --- a/src/views/settlement/components/pre-settlement-editor.vue +++ b/src/views/settlement/components/pre-settlement-editor.vue @@ -421,6 +421,16 @@
+ + @@ -939,6 +949,7 @@ import { h } from 'vue'; import { mapGetters } from 'vuex'; import { InfoFilled } from '@element-plus/icons-vue'; import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue'; +import ProjectAdvanceWriteOff from './project-advance-write-off.vue'; import { getMkPublicDetail } from '@/api/mk-process'; import { adjustDetail, @@ -981,7 +992,7 @@ import { isAdministrator } from '@/utils/auth'; export default { name: 'PreSettlementEditor', - components: { InfoFilled, ChangeRecordDetailDialog }, + components: { InfoFilled, ChangeRecordDetailDialog, ProjectAdvanceWriteOff }, props: { modelValue: { type: Boolean, @@ -1084,6 +1095,7 @@ export default { size: 10, }, advances: [], + projectAdvanceWriteOffs: [], changeRecords: [], attachments: [], attachmentTypeOptions: [], @@ -1396,6 +1408,7 @@ export default { this.appliedDetailQuery = { ...this.detailQuery }; this.detailPage = { current: 1, size: 10 }; this.advances = []; + this.projectAdvanceWriteOffs = []; this.changeRecords = []; this.attachments = []; this.selectedAttachmentRows = []; @@ -1435,6 +1448,13 @@ export default { createUserName: row.createUserName || detail.createUserName, createTime: row.createTime || detail.createTime, })); + this.projectAdvanceWriteOffs = (detail.projectAdvanceWriteOffs || []).map(row => ({ + ...row, + writeOffAmount: Number(row.writeOffAmount || 0), + projectAdvanceAmount: Number(row.projectAdvanceAmount || 0), + availableWriteOffAmount: Number(row.availableWriteOffAmount || 0), + createTime: row.paymentCreateTime || row.createTime, + })); this.changeRecords = detail.changeRecords || []; this.attachments = this.parseAttachments(detail.attachmentsJson); this.sortAttachments(); @@ -1579,6 +1599,7 @@ export default { this.form.payerName = ''; this.form.payeeName = ''; this.summaryFees = []; + this.projectAdvanceWriteOffs = []; return; } this.form.contractId = contract.id; @@ -1593,6 +1614,7 @@ export default { this.form.payeeName = contract.partyB; this.form.currency = contract.settlementCurrency || 'RMB'; this.summaryFees = []; + this.projectAdvanceWriteOffs = []; if (this.form.currency !== 'RMB') { this.form.exchangeRateDate = this.$dayjs().format('YYYY-MM-DD'); this.fetchBfiExchangeRate(); @@ -1613,6 +1635,9 @@ export default { this.$message.warning(`请完善结算合计第${invalidManualFeeIndex + 1}行的费用类型和费用项`); return; } + if (this.$refs.projectAdvanceWriteOffRef?.validateBeforeSave?.() === false) { + return; + } const stateKey = shouldSubmit ? 'submitting' : 'saving'; this[stateKey] = true; try { @@ -1660,6 +1685,7 @@ export default { remark: row.remark, manualFlag: row.manualFlag || 0, })), + projectAdvanceWriteOffs: this.$refs.projectAdvanceWriteOffRef?.toSavePayload?.() || [], }; }, addSummaryFee() { diff --git a/src/views/settlement/components/project-advance-write-off.vue b/src/views/settlement/components/project-advance-write-off.vue new file mode 100644 index 0000000..a273121 --- /dev/null +++ b/src/views/settlement/components/project-advance-write-off.vue @@ -0,0 +1,300 @@ + + + + +