1.1.17 回款确认前端页面显示列优化

dev
yinq 2 weeks ago
parent afbe755d60
commit a450c8112d

@ -323,7 +323,6 @@ export interface ContractCollectionStageDetailVO {
contractId: string | number;
paymentStageId: string | number;
stageName: string;
/** base_payment_stage.collection_stage可选 */
collectionStage?: string;
repaymentRate: number;
paymentPercentage: number;
@ -332,10 +331,13 @@ export interface ContractCollectionStageDetailVO {
paymentDescription: string;
planStageId: string | number;
repaymentAmount?: number;
/** 合同订单金额(erp_project_info.amount),实际回款比例分母 */
contractTotalPrice?: number;
receivableDate: string;
actualRepaymentAmount: number;
actualRepaymentRate?: number;
collectionConfirmUserId: string | number;
collectionConfirmNickName?: string;
collectionConfirmTime: string;
collectionConfirmStatus: string;
collectionConfirmRemark: string;

@ -45,7 +45,7 @@
</template>
</el-table-column>
<el-table-column v-if="columns[5].visible" label="部门" prop="deptName" min-width="140" show-overflow-tooltip />
<el-table-column v-if="columns[6].visible" label="合同金额" prop="amount" min-width="120" />
<el-table-column v-if="columns[6].visible" label="合同金额(元)" prop="amount" min-width="120" />
<el-table-column v-if="columns[7].visible" label="回款比例(%)" prop="paymentRate" min-width="100">
<template #default="scope">
{{ scope.row.paymentRate ?? 0 }}
@ -53,7 +53,7 @@
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="openStageDialog(scope.row)"></el-button>
<el-button link type="primary" @click="openStageDialog(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
@ -67,22 +67,40 @@
/>
<el-dialog v-model="stageDialogVisible" title="合同回款阶段确认" width="1200px">
<el-table v-loading="stageLoading" :data="stageList" border>
<el-table-column label="合同订单阶段" prop="stageName" min-width="130" />
<el-table-column label="回款阶段标识" prop="collectionStage" min-width="120" />
<el-table-column label="预计回款比例(%)" prop="repaymentRate" width="130" />
<div class="stage-dialog-contract mb8">
<span>合同编号{{ stageDialogContractNo || '—' }}</span>
<span class="stage-dialog-contract-gap">合同名称{{ stageDialogContractName || '—' }}</span>
<span class="stage-dialog-contract-gap">合同金额(){{ stageDialogContractAmount != null ? stageDialogContractAmount : '—' }}</span>
</div>
<el-table
v-loading="stageLoading"
:data="stageList"
border
:header-cell-style="{ textAlign: 'center' }"
:cell-style="{ textAlign: 'center' }"
>
<el-table-column label="回款阶段" prop="stageName" min-width="130" />
<el-table-column label="合同阶段比例(%)" prop="paymentPercentage" width="130" />
<el-table-column label="合同阶段金额" prop="paymentAmount" width="130" />
<el-table-column label="实际回款金额" prop="actualRepaymentAmount" width="130" />
<el-table-column label="合同阶段金额(元)" prop="repaymentAmount" width="130" />
<el-table-column label="实际回款金额(元)" prop="actualRepaymentAmount" width="130" />
<el-table-column label="实际回款比例(%)" width="130">
<template #default="scope">
{{ displayActualRepaymentRate(scope.row) }}
</template>
</el-table-column>
<el-table-column label="确认状态" width="110">
<template #default="scope">
<dict-tag :options="collection_confirm_status" :value="scope.row.collectionConfirmStatus || '0'" />
</template>
</el-table-column>
<el-table-column label="确认人" prop="collectionConfirmUserId" width="100" />
<el-table-column label="确认时间" width="160">
<el-table-column label="确认人" prop="collectionConfirmNickName" min-width="100" show-overflow-tooltip>
<template #default="scope">
{{ formatDate(scope.row.collectionConfirmTime) }}
{{ scope.row.collectionConfirmNickName || scope.row.collectionConfirmUserId || '—' }}
</template>
</el-table-column>
<el-table-column label="确认日期" width="120">
<template #default="scope">
{{ parseTime(scope.row.collectionConfirmTime, '{y}-{m}-{d}') || '—' }}
</template>
</el-table-column>
<el-table-column label="确认备注" prop="collectionConfirmRemark" min-width="160" show-overflow-tooltip />
@ -95,17 +113,33 @@
</el-dialog>
<el-dialog v-model="confirmDialogVisible" title="阶段回款确认" width="500px">
<el-form :model="confirmForm" label-width="120px">
<el-form :model="confirmForm" label-width="150px">
<el-form-item label="回款阶段">
<span class="confirm-readonly-text">{{ confirmDialogStageName || '—' }}</span>
</el-form-item>
<el-form-item label="合同阶段比例(%)">
<span class="confirm-readonly-text">{{ confirmDialogPaymentPercentage != null ? confirmDialogPaymentPercentage : '—' }}</span>
</el-form-item>
<el-form-item label="合同阶段金额(元)">
<span class="confirm-readonly-text">{{
confirmDialogRepaymentAmount != null
? confirmDialogRepaymentAmount
: confirmStageAmountMax != null
? confirmStageAmountMax
: '—'
}}</span>
</el-form-item>
<el-form-item label="实际回款日期" required>
<el-date-picker v-model="confirmForm.receivableDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
<el-form-item label="实际回款金额" required>
<el-input-number v-model="confirmForm.actualRepaymentAmount" :precision="2" :min="0" style="width: 100%" />
</el-form-item>
<el-form-item label="确认状态">
<el-select v-model="confirmForm.collectionConfirmStatus" clearable style="width: 100%">
<el-option v-for="item in collection_confirm_status" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-input-number
v-model="confirmForm.actualRepaymentAmount"
:precision="2"
:min="0"
:max="confirmStageAmountMax != null ? confirmStageAmountMax : undefined"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="确认备注">
<el-input v-model="confirmForm.collectionConfirmRemark" type="textarea" :rows="3" maxlength="500" show-word-limit />
@ -127,6 +161,7 @@ import {
contractCollectionPage,
contractCollectionStageDetail
} from '@/api/oa/erp/erpProjectPlan';
import { parseTime } from '@/utils/ruoyi';
import { ContractCollectionPageVO, ContractCollectionStageDetailVO } from '@/api/oa/erp/erpProjectPlan/types';
import { ErpProjectPlanStageForm } from '@/api/oa/erp/erpProjectPlanStage/types';
@ -162,14 +197,21 @@ const queryParams = reactive({
const stageDialogVisible = ref(false);
const stageLoading = ref(false);
const stageList = ref<ContractCollectionStageDetailVO[]>([]);
const stageDialogContractNo = ref('');
const stageDialogContractName = ref('');
const stageDialogContractAmount = ref<number | undefined>(undefined);
const confirmDialogVisible = ref(false);
const confirmLoading = ref(false);
const confirmDialogStageName = ref('');
const confirmDialogPaymentPercentage = ref<number | undefined>(undefined);
const confirmDialogRepaymentAmount = ref<number | undefined>(undefined);
const confirmStageAmountMax = ref<number | undefined>(undefined);
const confirmForm = reactive<ErpProjectPlanStageForm>({
planStageId: undefined,
receivableDate: '',
actualRepaymentAmount: 0,
collectionConfirmStatus: '',
collectionConfirmStatus: '3',
collectionConfirmRemark: ''
});
const currentPlanId = ref<string | number>();
@ -202,6 +244,9 @@ const openStageDialog = async (row: ContractCollectionPageVO) => {
return;
}
currentPlanId.value = row.projectPlanId;
stageDialogContractNo.value = row.contractNo ?? '';
stageDialogContractName.value = row.contractName ?? '';
stageDialogContractAmount.value = row.amount != null ? Number(row.amount) : undefined;
stageDialogVisible.value = true;
stageLoading.value = true;
const res = await contractCollectionStageDetail(row.projectPlanId);
@ -209,11 +254,31 @@ const openStageDialog = async (row: ContractCollectionPageVO) => {
stageLoading.value = false;
};
const toFiniteNumber = (v: unknown): number | undefined => {
if (v == null || v === '') return undefined;
const n = Number(v);
return Number.isFinite(n) ? n : undefined;
};
/** 行数据可能为 camelCase 或下划线字段 */
const pickStageAmountFields = (row: ContractCollectionStageDetailVO) => {
const r = row as unknown as Record<string, unknown>;
const repayment = toFiniteNumber(row.repaymentAmount ?? r['repayment_amount']);
const payment = toFiniteNumber(row.paymentAmount ?? r['payment_amount']);
return { repayment, payment };
};
const openConfirmDialog = (row: ContractCollectionStageDetailVO) => {
confirmDialogStageName.value = row.stageName ?? '';
const pct = row.paymentPercentage != null ? Number(row.paymentPercentage) : NaN;
confirmDialogPaymentPercentage.value = Number.isFinite(pct) ? pct : undefined;
const { repayment, payment } = pickStageAmountFields(row);
confirmDialogRepaymentAmount.value = repayment;
confirmStageAmountMax.value = repayment ?? payment;
confirmForm.planStageId = row.planStageId;
confirmForm.receivableDate = '';
confirmForm.actualRepaymentAmount = row.actualRepaymentAmount || 0;
confirmForm.collectionConfirmStatus = row.collectionConfirmStatus || '';
confirmForm.collectionConfirmStatus = row.collectionConfirmStatus || '3';
confirmForm.collectionConfirmRemark = row.collectionConfirmRemark || '';
confirmDialogVisible.value = true;
};
@ -228,20 +293,54 @@ const submitConfirm = async () => {
return;
}
confirmLoading.value = true;
await confirmCollectionStage(confirmForm);
confirmLoading.value = false;
confirmDialogVisible.value = false;
ElMessage.success('确认成功');
if (currentPlanId.value) {
const res = await contractCollectionStageDetail(currentPlanId.value);
stageList.value = res.data || [];
try {
await confirmCollectionStage(confirmForm);
confirmDialogVisible.value = false;
ElMessage.success('确认成功');
if (currentPlanId.value) {
const res = await contractCollectionStageDetail(currentPlanId.value);
stageList.value = res.data || [];
}
getList();
} finally {
confirmLoading.value = false;
}
};
const formatDate = (val?: string) => {
if (!val) return '';
return String(val).replace('T', ' ').slice(0, 19);
/** 无库中比例时的推算:本阶段实际回款 / 合同订单金额 * 100与后端一致 */
const formatActualRepaymentRate = (row: ContractCollectionStageDetailVO) => {
const actual = Number(row.actualRepaymentAmount ?? 0);
const total = Number(row.contractTotalPrice ?? 0);
if (!Number.isFinite(total) || total <= 0) {
return 0;
}
const pct = (actual / total) * 100;
return Math.round(pct * 100) / 100;
};
/** 优先展示后端持久化字段,缺省再按金额推算 */
const displayActualRepaymentRate = (row: ContractCollectionStageDetailVO) => {
const stored = row.actualRepaymentRate;
if (stored != null && Number.isFinite(Number(stored))) {
return Number(stored);
}
return formatActualRepaymentRate(row);
};
getList();
</script>
<style scoped lang="scss">
.stage-dialog-contract {
font-size: 14px;
color: var(--el-text-color-regular);
line-height: 1.6;
}
.stage-dialog-contract-gap {
margin-left: 24px;
}
.confirm-readonly-text {
color: var(--el-text-color-regular);
line-height: 32px;
}
</style>

@ -1063,7 +1063,7 @@ const loadContractInfo = async () => {
repaymentRate: pm.paymentPercentage ?? undefined,
repaymentAmount: undefined,
repaymentTime: getDefaultRepaymentDate(pm),
delayDay: undefined,
delayDay: pm.paymentDeadline,
receivableDate: undefined,
reasonsExplanation: '',
scheduleRemark: '',

Loading…
Cancel
Save