Compare commits

...

9 Commits

Author SHA1 Message Date
刘泉佳 698f808f0b fix: 结算行付款金额联动回填申请付款金额并校验合计一致
结算信息行改动付款比例或付款金额时,自动把行金额合计回填到上方申请付款金额并重算比例;
保存/提交前校验行金额合计与申请付款金额一致,避免推送金蝶金额与预付建档分摊不一致。
回显加载走默认不同步,避免改写历史单据。
2026-10-29 02:19:23 +08:00
刘泉佳 ea6ec13ea4 fix: 付款申请拦截已调整结算单并过滤候选列表 2026-10-29 02:15:57 +08:00
刘泉佳 6119c44394 fix: 金蝶单据状态标签移除 status-text 类恢复颜色展示 2026-10-28 19:04:09 +08:00
刘泉佳 9dab7f7495 fix: 结算列表插槽列被兜底 formatter 覆盖导致状态无颜色与悬浮 2026-10-28 17:27:10 +08:00
刘泉佳 f5697e111a feat: 结算单与客商档案金蝶状态着色并悬浮展示失败原因 2026-10-28 17:00:34 +08:00
刘泉佳 775307f5c0 feat: 付款与开票申请金蝶单据状态着色并悬浮展示失败原因 2026-10-28 17:00:34 +08:00
刘泉佳 4502881063 feat: 付款申请审批通过前增加确认弹框 2026-10-28 16:11:29 +08:00
刘泉佳 358add743e fix: 合同变更审批接口超时统一使用APPROVE_TIMEOUT 2026-10-28 14:36:03 +08:00
刘泉佳 f67a4036ed fix: 项目变更与合同管理审批接口超时统一使用APPROVE_TIMEOUT 2026-10-28 14:16:42 +08:00
13 changed files with 124 additions and 26 deletions
+2 -2
View File
@@ -1,4 +1,5 @@
import { createCrudApi } from './common';
import { APPROVE_TIMEOUT } from '@/api/common';
import request from '@/axios';
const baseUrl = '/blade-transport/contract-change';
@@ -35,8 +36,7 @@ export const approve = id =>
url: `${baseUrl}/approve`,
method: 'post',
params: { id },
// 审批通过后同步推送合规(附件转传可达 20s+),覆盖全局 10s 超时
timeout: 120000,
timeout: APPROVE_TIMEOUT,
});
export const reject = id =>
+2 -2
View File
@@ -1,4 +1,5 @@
import { createCrudApi } from './common';
import { APPROVE_TIMEOUT } from '@/api/common';
import request from '@/axios';
const baseUrl = '/blade-transport/contract-manage';
@@ -36,8 +37,7 @@ export const approve = id =>
url: `${baseUrl}/approve`,
method: 'post',
params: { id },
// 审批通过后同步推送合规(附件转传可达 20s+),覆盖全局 10s 超时
timeout: 120000,
timeout: APPROVE_TIMEOUT,
});
export const reject = id =>
+2
View File
@@ -1,4 +1,5 @@
import { createCrudApi } from './common';
import { APPROVE_TIMEOUT } from '@/api/common';
import request from '@/axios';
const baseUrl = '/blade-transport/project-change';
@@ -35,6 +36,7 @@ export const approve = id =>
url: `${baseUrl}/approve`,
method: 'post',
params: { id },
timeout: APPROVE_TIMEOUT,
});
export const reject = id =>
+1 -1
View File
@@ -169,7 +169,7 @@ export const paymentApplicationOption = ({
{ label: '当前节点', prop: 'currentNode', minWidth: 120, formatter: textFormatter },
{ label: '当前处理人', prop: 'currentProcessor', minWidth: 120, formatter: textFormatter },
{ label: '金蝶单据号', prop: 'kingdeeBillNo', minWidth: 130, formatter: textFormatter },
{ label: '金蝶单据状态', prop: 'kingdeeStatusName', minWidth: 130, formatter: textFormatter },
{ label: '金蝶单据状态', prop: 'kingdeeStatusName', slot: true, minWidth: 130 },
{ label: '金蝶锁状态', prop: 'kingdeeLockStatus', slot: true, minWidth: 150 },
{ label: '附件同步', prop: 'attachmentSyncStatusName', minWidth: 100, formatter: textFormatter },
],
+2 -1
View File
@@ -99,7 +99,8 @@ const buildTableColumns = () =>
}
// link / status 标记的列统一走单元格插槽
if (column.link || column.status) extra.slot = true;
if (!extra.formatter) extra.formatter = textFormatter;
// 插槽列不挂兜底 formatter:avue 中 formatter 会覆盖单元格插槽渲染
if (!extra.slot && !extra.formatter) extra.formatter = textFormatter;
return { ...column, ...extra };
});
+15
View File
@@ -0,0 +1,15 @@
/**
* 金蝶单据状态公共映射
*/
/** 金蝶单据状态 → el-tag 颜色类型(failed 状态列表页悬浮展示失败原因) */
export const KINGDEE_STATUS_TAG_TYPES = {
synced: 'success',
paid: 'success',
invoiced: 'success',
paying: 'warning',
failed: 'danger',
};
/** 金蝶单据状态 → el-tag 颜色类型,未映射状态返回 info */
export const kingdeeStatusType = status => KINGDEE_STATUS_TAG_TYPES[status] || 'info';
+13
View File
@@ -125,6 +125,17 @@
{{ displayValue(row[column.prop]) }}
</el-tag>
<span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span>
<template v-else-if="column.prop === 'kingdeeStatusName'">
<el-tooltip
:disabled="row.kingdeeStatus !== 'failed'"
:content="row.kingdeeFailReason || '推送金蝶失败,可点击「同步金蝶」重试'"
placement="top"
>
<el-tag :type="kingdeeStatusType(row.kingdeeStatus)">{{
row.kingdeeStatusName || '-'
}}</el-tag>
</el-tooltip>
</template>
<template v-else-if="column.prop === 'kingdeeLockStatus'">
<el-tooltip v-if="lockTag(row).show" :content="lockTag(row).tip" placement="top">
<el-tag :type="lockTag(row).type" class="status-text">{{ lockTag(row).label }}</el-tag>
@@ -238,6 +249,7 @@ import TableHeaderTools from '@/components/table-header-tools/main.vue';
import organizationSearch from '@/mixins/organization-search';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
import { kingdeeStatusType } from '@/utils/kingdee';
const emptyQuery = () => ({
applicationNo: '',
@@ -500,6 +512,7 @@ export default {
''
);
},
kingdeeStatusType,
lockTag(row) {
if (row.kingdeeLockStatus === 'locked') {
return { show: true, type: 'danger', label: '金蝶锁定中', tip: '该单据已被金蝶锁定,不可编辑/作废' };
+34 -6
View File
@@ -2702,7 +2702,8 @@ export default {
this.referenceParams('formal')
);
const data = this.unwrapData(response) || {};
const records = data.records || [];
// 已调整的结算单为调整重推后的归档原单,金额已作废,不可再发起付款
const records = (data.records || []).filter(row => row.adjustStatus !== 'adjusted');
this.formalRows = await this.fillReferencePayableAmounts(records, 'settlement_payment');
this.formalPage.total = Number(data.total || 0);
} finally {
@@ -3124,18 +3125,21 @@ export default {
if (!target) return;
target.paymentRatio = value;
this.syncRowPayment(target, 'ratio');
this.syncHeaderSettlementTotalsFromRows();
this.syncHeaderSettlementTotalsFromRows(true);
},
handleRowAppliedAmountChange(row, value) {
const target = this.findReferenceRow(row);
if (!target) return;
target.appliedAmount = value;
this.syncRowPayment(target, 'amount');
this.syncHeaderSettlementTotalsFromRows();
this.syncHeaderSettlementTotalsFromRows(true);
},
/** 多结算单时仅同步结算金额/可付款金额合计,申请付款金额与比例由上方独立维护 */
syncHeaderSettlementTotalsFromRows() {
if (this.referenceRows.length <= 1) return;
/**
* 结算信息行合计回填付款基本信息。
* @param {boolean} syncApplied 行内编辑时同步申请付款金额与比例;回显加载时保持原值,避免改写历史单据
*/
syncHeaderSettlementTotalsFromRows(syncApplied = false) {
if (!this.referenceRows.length) return;
const settlementAmount = this.referenceRows.reduce(
(total, item) => total + Number(item.settlementAmount || 0),
0
@@ -3144,9 +3148,21 @@ export default {
(total, item) => total + Number(item.payableAmount || 0),
0
);
const appliedAmount = this.referenceRows.reduce(
(total, item) => total + Number(item.appliedAmount || 0),
0
);
this.amountSyncing = true;
this.form.settlementAmount = Number(settlementAmount.toFixed(2));
this.form.payableAmount = Number(payableAmount.toFixed(2));
if (syncApplied) {
this.form.appliedAmount = Number(appliedAmount.toFixed(2));
if (this.paymentAmountBase) {
this.form.paymentRatio = Number(
((appliedAmount / this.paymentAmountBase) * 100).toFixed(2)
);
}
}
this.$nextTick(() => {
this.amountSyncing = false;
this.$refs.formRef?.validateField?.(['settlementAmount', 'appliedAmount']);
@@ -3320,6 +3336,18 @@ export default {
ElMessage.warning(`${exceededRow.settlementNo || '结算单'}付款金额不能超过可付款金额`);
return false;
}
const rowAppliedTotal = this.referenceRows.reduce(
(total, item) => total + Number(item.appliedAmount || 0),
0
);
// 金蝶按付款基本信息申请金额推单,行合计不等时预付建档分摊会与金蝶金额不一致
if (Math.abs(rowAppliedTotal - appliedAmount) > 0.005) {
this.clearMessages();
ElMessage.warning(
`结算行付款金额合计${rowAppliedTotal.toFixed(2)}与申请付款金额${appliedAmount.toFixed(2)}不一致,请调整`
);
return false;
}
}
if (this.form.paymentType === 'project_advance' && !this.form.projectId) {
this.clearMessages();
+23 -7
View File
@@ -68,6 +68,18 @@
<span v-else>-</span>
</template>
<template #kingdeeStatusName="{ row }">
<el-tooltip
:disabled="row.kingdeeStatus !== 'failed'"
:content="row.kingdeeFailReason || '推送金蝶失败,可点击「同步金蝶」重试'"
placement="top"
>
<el-tag :type="kingdeeStatusType(row.kingdeeStatus)">{{
row.kingdeeStatusName || '-'
}}</el-tag>
</el-tooltip>
</template>
<template #menu="{ row }">
<el-link
v-if="hasPermission('payment_application_view')"
@@ -151,6 +163,7 @@ import { paymentApplicationOption } from '@/option/payment/paymentApplication';
import organizationSearch from '@/mixins/organization-search';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
import { kingdeeStatusType } from '@/utils/kingdee';
const paymentApplicationListRoute = '/payment/payment-application';
const paymentApplicationListRefreshKey = 'payment-application-list-refresh';
@@ -328,15 +341,17 @@ export default {
this.refreshChange();
},
async handleApprove(row) {
// 防止重复点击:请求进行中忽略后续点击
// 防止重复点击:请求进行中忽略后续点击,避免重复审批和重复提示
if (this.approveLoading) return;
this.approveLoading = true;
const loading = this.$loading({
lock: true,
text: '审批处理中,请稍候…',
background: 'rgba(255, 255, 255, 0.6)',
});
let loading = null;
try {
await this.$confirm('确认审批通过该付款申请?', '提示', { type: 'warning' });
loading = this.$loading({
lock: true,
text: '审批处理中,请稍候…',
background: 'rgba(255, 255, 255, 0.6)',
});
const res = await api.approve({ id: row.id });
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
if (res.data.data === false) {
@@ -346,7 +361,7 @@ export default {
}
this.refreshChange();
} finally {
loading.close();
loading?.close();
this.approveLoading = false;
}
},
@@ -461,6 +476,7 @@ export default {
''
);
},
kingdeeStatusType,
lockTag(row) {
if (row.kingdeeLockStatus === 'locked') {
return { show: true, type: 'danger', label: '金蝶锁定中', tip: '该单据已被金蝶锁定,不可编辑/作废' };
@@ -60,7 +60,17 @@
}}</el-tag>
</template>
<template #kingdeeSyncStatus="{ row }">{{ kingdeeSyncName(row.kingdeeSyncStatus) }}</template>
<template #kingdeeSyncStatus="{ row }">
<el-tooltip
:disabled="row.kingdeeSyncStatus !== 'failed'"
:content="row.kingdeeFailReason || '推送金蝶失败,可点击「同步金蝶」重试'"
placement="top"
>
<el-tag :type="kingdeeStatusType(row.kingdeeSyncStatus)">{{
kingdeeSyncName(row.kingdeeSyncStatus)
}}</el-tag>
</el-tooltip>
</template>
<template #kingdeeLockStatus="{ row }">
<el-tooltip v-if="lockTag(row).show" :content="lockTag(row).tip" placement="top">
@@ -84,6 +94,7 @@
<script>
import { invoiceStatusName } from '@/option/settlement/formalSettlementSearch';
import { kingdeeSyncName, paymentStatusName } from '@/option/settlement/formalSettlement';
import { kingdeeStatusType } from '@/utils/kingdee';
export default {
name: 'FormalSettlementTablePanel',
@@ -186,6 +197,7 @@ export default {
''
);
},
kingdeeStatusType,
invoiceName(value, settlementType) {
return invoiceStatusName(value, settlementType);
},
@@ -486,6 +486,8 @@ export default {
const rows = this.selection;
if (rows.some(row => row.approvalStatus !== 'approved' || row.settlementType !== 'payable'))
return this.$message.warning('仅审批通过的付款结算单允许发起付款申请');
if (rows.some(row => row.adjustStatus === 'adjusted'))
return this.$message.warning('已调整的结算单不可再发起付款,请使用调整后的新单');
const contractIds = new Set(rows.map(row => String(row.contractId || '')));
if (contractIds.size !== 1 || contractIds.has(''))
return this.$message.warning('付款申请必须选择同一个合同的付款结算单');
@@ -93,11 +93,15 @@
><el-tag v-else-if="column.status" :type="statusType(row.approvalStatus)" class="status-text">{{
row[column.prop] || '-'
}}</el-tag
><el-tag
><el-tooltip
v-else-if="column.kingdee"
:disabled="row.kingdeeSyncStatus !== 'failed'"
:content="row.kingdeeFailReason || '推送金蝶失败,请重试'"
placement="top"
><el-tag
:type="kingdeeStatusType(row.kingdeeSyncStatus)"
class="status-text"
>{{ kingdeeSyncName(row.kingdeeSyncStatus) }}</el-tag
></el-tooltip
><span v-else-if="column.money">{{ formatMoney(row[column.prop]) }}</span
><span v-else>{{ displayValue(row[column.prop]) }}</span></template
></el-table-column
@@ -182,6 +186,7 @@ import { settlementAdjustmentSearchFields } from '@/option/settlement/settlement
import organizationSearch from '@/mixins/organization-search';
import { settlementAdjustmentTableColumns } from '@/option/settlement/settlementAdjustmentTable';
import { kingdeeSyncStatusOptions } from '@/option/settlement/formalSettlementSearch';
import { kingdeeStatusType } from '@/utils/kingdee';
import SettlementAdjustmentEditor from './components/settlement-adjustment-editor.vue';
import SettlementTableTools from './components/settlement-table-tools.vue';
import { isAdministrator } from '@/utils/auth';
@@ -365,9 +370,7 @@ export default {
'info'
);
},
kingdeeStatusType(status) {
return { synced: 'success', failed: 'danger' }[status] || 'info';
},
kingdeeStatusType,
kingdeeSyncName(value) {
return kingdeeSyncStatusOptions.find(item => item.value === value)?.label || value || '-';
},
+7 -1
View File
@@ -135,7 +135,13 @@
<template #kingdeeStatus="{ row }">
<el-tag v-if="row.kingdeeStatus === 'synced'" type="success">已推送</el-tag>
<el-tag v-else-if="row.kingdeeStatus === 'exist'" type="primary">金蝶已有</el-tag>
<el-tag v-else-if="row.kingdeeStatus === 'failed'" type="danger">推送失败</el-tag>
<el-tooltip
v-else-if="row.kingdeeStatus === 'failed'"
:content="row.kingdeeFailReason || '推送金蝶失败,请重试'"
placement="top"
>
<el-tag type="danger">推送失败</el-tag>
</el-tooltip>
<el-tag v-else type="info">未推送</el-tag>
</template>
<template #menu="{ row }">