|
|
|
|
@ -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;
|
|
|
|
|
try {
|
|
|
|
|
await confirmCollectionStage(confirmForm);
|
|
|
|
|
confirmLoading.value = false;
|
|
|
|
|
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>
|
|
|
|
|
|