From acfbb4c54e74fd15a2426c7561e4ba9ba6997337 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Sat, 17 Oct 2026 08:31:27 +0800 Subject: [PATCH 1/6] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E8=BF=90?= =?UTF-8?q?=E8=BE=93=E5=AF=B9=E8=B4=A6=E5=8C=B9=E9=85=8D=E6=97=A5=E6=9C=9F?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E4=B8=8E=E5=AE=8C=E6=88=90=E5=AF=B9=E8=B4=A6?= =?UTF-8?q?=E7=96=91=E4=BC=BC=E9=87=8D=E5=A4=8D=E8=AF=AF=E5=88=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../transport-reconciliation-editor.vue | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/views/settlement/components/transport-reconciliation-editor.vue b/src/views/settlement/components/transport-reconciliation-editor.vue index bcee540..4351b68 100644 --- a/src/views/settlement/components/transport-reconciliation-editor.vue +++ b/src/views/settlement/components/transport-reconciliation-editor.vue @@ -1278,10 +1278,13 @@ export default { delete snapshot._externalKey; delete snapshot.feeItems; snapshot.feeItemsJson = row.feeItemsJson || JSON.stringify(row.feeItems || {}); + snapshot.actualDepartureTime = this.normalizeExternalDateTime(row.actualDepartureTime); + snapshot.actualCompletionTime = this.normalizeExternalDateTime(row.actualCompletionTime); return snapshot; }); }, serializeExternalRows() { + const duplicateIds = new Set(this.duplicateRows.map(row => String(row.id))); return this.externalDetails.map(row => { const snapshot = { ...row }; delete snapshot._sourceRows; @@ -1290,6 +1293,7 @@ export default { snapshot.feeItemsJson = row.feeItemsJson || JSON.stringify(row.feeItems || {}); snapshot.actualDepartureTime = this.normalizeExternalDateTime(row.actualDepartureTime); snapshot.actualCompletionTime = this.normalizeExternalDateTime(row.actualCompletionTime); + snapshot.suspectedDuplicate = duplicateIds.has(String(row.id)); return snapshot; }); }, @@ -1336,6 +1340,11 @@ export default { } }, async handleComplete() { + const unmatchedInternal = this.internalDetails.filter(item => item.matchResult !== 'matched').length; + const unmatchedExternal = this.externalDetails.filter(item => item.matchStatus !== 'matched').length; + if (this.duplicateRows.length || unmatchedInternal || unmatchedExternal) { + return this.$message.warning('所有内外部账单明细必须一一匹配且不存在疑似重复'); + } if ( Number(this.form.differenceCount || 0) !== 0 || Number(this.form.differenceQuantity || 0) !== 0 || @@ -1694,8 +1703,11 @@ export default { }, normalizeExternalDateTime(value) { if (!value) return null; - const text = String(value); - return text.length === 10 ? `${text}T00:00:00` : text; + const text = String(value).trim(); + if (!text) return null; + const date = this.$dayjs(text); + if (date.isValid()) return date.format('YYYY-MM-DD HH:mm:ss'); + return text.length === 10 ? `${text} 00:00:00` : text.replace('T', ' '); }, refreshLocalStats() { const internalQuantity = this.internalDetails.reduce( From d1e779bbccf21f75c6a1ce21a7093de9d522621d Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Sat, 17 Oct 2026 15:35:20 +0800 Subject: [PATCH 2/6] =?UTF-8?q?:zap:=20=E4=BC=98=E5=8C=96=E7=BB=84?= =?UTF-8?q?=E7=BB=87=E5=85=A8=E9=87=8F=E5=90=8C=E6=AD=A5=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=EF=BC=8C=E9=83=A8=E9=97=A8=E6=8C=89=E5=85=AC=E5=8F=B8=E8=BD=AE?= =?UTF-8?q?=E8=AF=A2=E5=B9=B6=E5=8E=BB=E6=8E=89=E7=A1=AE=E8=AE=A4=E5=BC=B9?= =?UTF-8?q?=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/api/system/dept.js | 4 +- src/views/system/dept.vue | 121 +++++++++++++++++++++++++++----------- 2 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/api/system/dept.js b/src/api/system/dept.js index 5a489e0..c7f22de 100644 --- a/src/api/system/dept.js +++ b/src/api/system/dept.js @@ -77,7 +77,8 @@ export const syncOaDepartment = ( size = 20, signal, incremental = false, - modifiedSince + modifiedSince, + companyOaId ) => { return request({ url: '/blade-system/dept/sync-oa-department', @@ -87,6 +88,7 @@ export const syncOaDepartment = ( size, incremental: !!incremental, modifiedSince: modifiedSince || undefined, + companyOaId: companyOaId || undefined, }, timeout: 60000, signal, diff --git a/src/views/system/dept.vue b/src/views/system/dept.vue index 9058af3..c37610a 100644 --- a/src/views/system/dept.vue +++ b/src/views/system/dept.vue @@ -153,7 +153,12 @@ />
当前阶段:{{ oaSyncStageLabel }}
- 当前阶段:{{ oaSyncStageLabel }},第 {{ oaSyncProgress.current || 0 }} / {{ oaSyncTotalPage }} 页 + 当前阶段:{{ oaSyncStageLabel }} + + ,第 {{ oaSyncProgress.current || 0 }} / {{ oaSyncTotalPage }} 页
@@ -257,6 +262,9 @@ export default { synced: 0, skipped: 0, dumpFilePath: '', + companyOaId: '', + companyIndex: 0, + companyTotal: 0, }, parentId: 0, page: { @@ -642,12 +650,18 @@ export default { } const totalPage = this.oaSyncTotalPage; const current = Number(this.oaSyncProgress.current) || 0; - const stageBase = this.oaSyncStage === 'department' ? 50 : 0; - if (totalPage <= 0) { - return stageBase; + if (this.oaSyncStage === 'department') { + const companyTotal = Math.max(1, Number(this.oaSyncProgress.companyTotal) || 1); + const companyIndex = Math.max(1, Number(this.oaSyncProgress.companyIndex) || 1); + const companyBase = ((companyIndex - 1) / companyTotal) * 50; + const pageRatio = totalPage > 0 ? Math.min(1, current / totalPage) : 0; + const companyPagePercent = (pageRatio * 50) / companyTotal; + return Math.min(99, Math.round(50 + companyBase + companyPagePercent)); } - const stagePercent = Math.min(50, Math.round((current / totalPage) * 50)); - return Math.min(99, stageBase + stagePercent); + if (totalPage <= 0) { + return 0; + } + return Math.min(49, Math.round((current / totalPage) * 50)); }, oaSyncProgressStatus() { if (this.oaSyncStatus === 'done') { @@ -661,25 +675,7 @@ export default { }, methods: { handleOaOrgFullSync() { - this.$confirm( - '全量同步将拉取 OA 公司/部门(仅保留二级公司 oaId=2、135 及其子公司与部门);已存在且 OA 更新时间一致的记录会跳过。是否清除后同步?选择“是”将保留顶级「外部组织」与集团锚点(id=1/GWWL),删除其余组织后再同步;选择“否”则直接全量同步。', - '全量同步', - { - confirmButtonText: '是', - cancelButtonText: '否', - distinguishCancelAndClose: true, - closeOnClickModal: false, - type: 'warning', - } - ) - .then(() => { - this.startOaOrgSync({ clearNonTop: true, incremental: false }); - }) - .catch(action => { - if (action === 'cancel') { - this.startOaOrgSync({ clearNonTop: false, incremental: false }); - } - }); + this.startOaOrgSync({ clearNonTop: false, incremental: false }); }, handleOaOrgIncrementalSync() { this.$confirm( @@ -714,6 +710,9 @@ export default { synced: 0, skipped: 0, dumpFilePath: '', + companyOaId: '', + companyIndex: 0, + companyTotal: 0, }; this.runOaOrgSyncPages(); }, @@ -725,6 +724,25 @@ export default { error?.name === 'AbortError' ); }, + applyOaOrgSyncPage(page, current, size) { + this.oaSyncProgress.current = page.current || current; + this.oaSyncProgress.size = page.size || size; + this.oaSyncProgress.total = page.total || 0; + this.oaSyncProgress.synced += page.syncedCount || 0; + this.oaSyncProgress.skipped += page.skippedCount || 0; + if (page.dumpFilePath) { + this.oaSyncProgress.dumpFilePath = page.dumpFilePath; + } + if (page.companyOaId != null) { + this.oaSyncProgress.companyOaId = page.companyOaId || ''; + } + if (page.companyIndex != null) { + this.oaSyncProgress.companyIndex = page.companyIndex || 0; + } + if (page.companyTotal != null) { + this.oaSyncProgress.companyTotal = page.companyTotal || 0; + } + }, async runOaOrgSyncStage(syncApi) { const size = 20; const incremental = this.oaSyncMode === 'incremental'; @@ -742,20 +760,52 @@ export default { if (!modifiedSince && page.modifiedSince) { modifiedSince = page.modifiedSince; } - this.oaSyncProgress.current = page.current || current; - this.oaSyncProgress.size = page.size || size; - this.oaSyncProgress.total = page.total || 0; - this.oaSyncProgress.synced += page.syncedCount || 0; - this.oaSyncProgress.skipped += page.skippedCount || 0; - if (page.dumpFilePath) { - this.oaSyncProgress.dumpFilePath = page.dumpFilePath; - } + this.applyOaOrgSyncPage(page, current, size); if (page.finished) { break; } current += 1; } }, + /** + * 部门阶段:公司同步完成后,按公司 oaId 轮询;当前公司部门拉完后切换下一公司并重置页码 + */ + async runOaOrgDepartmentSyncStage() { + const size = 20; + const incremental = this.oaSyncMode === 'incremental'; + let current = 1; + let modifiedSince = ''; + let companyOaId = ''; + while (!this.oaSyncCancelled) { + const res = await syncOaDepartment( + current, + size, + this.oaSyncController?.signal, + incremental, + modifiedSince, + companyOaId + ); + const page = res?.data?.data || {}; + if (!modifiedSince && page.modifiedSince) { + modifiedSince = page.modifiedSince; + } + if (page.companyOaId) { + companyOaId = page.companyOaId; + } + this.applyOaOrgSyncPage(page, current, size); + if (page.finished) { + break; + } + if (page.companyFinished && page.nextCompanyOaId) { + companyOaId = page.nextCompanyOaId; + current = 1; + this.oaSyncProgress.current = 0; + this.oaSyncProgress.total = 0; + continue; + } + current += 1; + } + }, async runOaOrgSyncPages() { try { if (this.oaSyncClearNonTop) { @@ -775,7 +825,10 @@ export default { this.oaSyncStage = 'department'; this.oaSyncProgress.current = 0; this.oaSyncProgress.total = 0; - await this.runOaOrgSyncStage(syncOaDepartment); + this.oaSyncProgress.companyOaId = ''; + this.oaSyncProgress.companyIndex = 0; + this.oaSyncProgress.companyTotal = 0; + await this.runOaOrgDepartmentSyncStage(); if (this.oaSyncCancelled) { this.oaSyncStatus = 'cancelled'; return; From f4542cd0b8794dec3db471f82f7ee25e45c5b092 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Sat, 17 Oct 2026 16:22:39 +0800 Subject: [PATCH 3/6] =?UTF-8?q?:zap:=20=E7=BB=84=E7=BB=87=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=8C=89=E5=85=AC=E5=8F=B8=E5=88=B7=E6=96=B0=E6=A0=91?= =?UTF-8?q?=E5=B9=B6=E5=8A=A0=E5=AE=BD=E7=BB=84=E7=BB=87=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/views/system/dept.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/views/system/dept.vue b/src/views/system/dept.vue index c37610a..5136762 100644 --- a/src/views/system/dept.vue +++ b/src/views/system/dept.vue @@ -298,7 +298,7 @@ export default { { label: '组织名称', prop: 'deptName', - minWidth: 160, + minWidth: 320, order: 80, search: true, searchOrder: 2, @@ -801,6 +801,11 @@ export default { current = 1; this.oaSyncProgress.current = 0; this.oaSyncProgress.total = 0; + // 单公司部门拉完并重建层级后刷新树,便于中途核对挂载结果 + this.parentId = 0; + this.data = []; + this.$refs.crud?.refreshTable?.(); + this.onLoad(this.page, this.query); continue; } current += 1; From 38ca25024282cc68d216e838aa28e4473aede9a6 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Sat, 17 Oct 2026 17:01:27 +0800 Subject: [PATCH 4/6] =?UTF-8?q?:sparkles:=20=E9=A1=B9=E7=9B=AE=E5=8F=98?= =?UTF-8?q?=E6=9B=B4=E5=8F=AF=E7=BC=96=E8=BE=91=E4=B8=9A=E5=8A=A1=E9=83=A8?= =?UTF-8?q?=E9=97=A8/=E5=B9=B3=E5=8F=B0=E5=85=AC=E5=8F=B8=EF=BC=8C?= =?UTF-8?q?=E7=BB=84=E7=BB=87=E5=90=8D=E7=A7=B0=E5=88=97=E5=B7=A6=E5=AF=B9?= =?UTF-8?q?=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/views/business/project-apply.vue | 7 ++++--- src/views/system/dept.vue | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue index e05c03d..e4b41bb 100644 --- a/src/views/business/project-apply.vue +++ b/src/views/business/project-apply.vue @@ -224,7 +224,7 @@ placeholder="请选择" filterable clearable - :disabled="isBasicInfoReadonly" + :disabled="dialogReadonly" /> @@ -236,7 +236,7 @@ collapse-tags-tooltip clearable filterable - :disabled="isBasicInfoReadonly" + :disabled="dialogReadonly" @change="handleUndertakeDeptChange" > { const deptTree = res.data.data || []; - const businessDeptTree = ['add', 'edit', 'majorSupplement'].includes(this.dialogType) + // 新增/编辑/重大补录/项目变更均可选择业务部门,排除外部组织 + const businessDeptTree = ['add', 'edit', 'majorSupplement', 'change'].includes(this.dialogType) ? this.excludeExternalOrganization(deptTree) : deptTree; this.deptOptions = this.flattenDept(businessDeptTree); diff --git a/src/views/system/dept.vue b/src/views/system/dept.vue index 5136762..bfac437 100644 --- a/src/views/system/dept.vue +++ b/src/views/system/dept.vue @@ -299,6 +299,8 @@ export default { label: '组织名称', prop: 'deptName', minWidth: 320, + align: 'left', + headerAlign: 'left', order: 80, search: true, searchOrder: 2, @@ -1518,6 +1520,12 @@ export default {

正式结算单

结算单号:${this.escapeHtml( + }

${this.escapeHtml( + settlementBillLabel(row.settlementType || this.activeSettlementType) + )}

结算单号:${this.escapeHtml( row.formalSettlementNo - )}
预结算单号:${this.escapeHtml( + )}
预付结算单号:${this.escapeHtml( row.preSettlementNos )}
项目:${this.escapeHtml( row.projectName @@ -582,12 +675,15 @@ export default { const params = this.buildQueryParams(); if (this.selection.length) params.ids = this.selection.map(item => item.id).join(','); const { data } = await api.exportList(params); - downloadFile(data, `正式结算单${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`); + downloadFile( + data, + `${this.billLabel}${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx` + ); }, buildQueryParams() { // 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显) const params = this.normalizeOrganizationSearch({ ...this.query }, 'deptName'); - params.settlementType = this.activeSettlementType; + params.settlementType = this.lockedSettlementType || this.activeSettlementType; const range = params.createDateRange || []; delete params.createDateRange; if (range.length === 2) { diff --git a/src/views/settlement/payment-settlement.vue b/src/views/settlement/payment-settlement.vue new file mode 100644 index 0000000..66ac565 --- /dev/null +++ b/src/views/settlement/payment-settlement.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/views/settlement/pre-settlement-form.vue b/src/views/settlement/pre-settlement-form.vue index 9bd3549..18586de 100644 --- a/src/views/settlement/pre-settlement-form.vue +++ b/src/views/settlement/pre-settlement-form.vue @@ -1,7 +1,7 @@ + + + + + +