feat(web-admin): wire fq last-period and dj deadline autofill in SamplePaymentPanel

Auto-recalculate the final installment due amount when earlier periods
change (with manual override), and sync lump-sum deadline to due date.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-13 14:53:11 +08:00
1 parent 6ec4ec6730
commit 618d8fe21c
1 file changed
+65 -1
@@ -59,7 +59,7 @@
placeholder="选择截止时间"
clearable
style="width: 100%"
@update:model-value="(v: string) => patchPay(pIdx, { lumpSumDeadline: v || '' })"
@update:model-value="(v: string) => onLumpSumDeadlineChange(pIdx, v || '')"
/>
</el-form-item>
</template>
@@ -364,12 +364,15 @@
import {
applyConsolidateRemainingToPayment,
applyExpandRemainingToInstallment,
applyLumpSumDeadlineToLastDueDate,
availableSamplePayModes,
calcLastPeriodDueAmount,
parseInstallmentRatios,
parsePreferentialPrice,
resolvePerSamplePaymentDefaults,
samplePaymentHasConfirmedTransfer,
schedulePaidArrears,
shouldAutoFillLastPeriodDue,
shouldReplaceContractWithPreferentialPrice
} from '@/utils/protocol-sample-payment'
import { isMultiSchedulePayMode } from '@/utils/protocol-payment-presets'
@@ -430,6 +433,8 @@
const transferTypeOptions = ref<Api.Dict.DictDataOption[]>([])
const submittingId = ref('')
const savingKey = ref('')
/** 按样本 key:用户手改过最后一期应缴金额则 true,前几期再变时清除 */
const lastPeriodDueOverrideByKey = ref<Record<string, boolean>>({})
async function ensureTransferTypes() {
await loadTransferTypeDict()
@@ -442,6 +447,18 @@
return '¥' + formatMoneyDisplay(v)
}
function samplePayKey(pay: Record<string, any>, pIdx: number): string {
return String(pay.sampleCode || pay.protocolSampleId || pIdx)
}
function activeScheduleIndices(schedules: Record<string, any>[]): number[] {
const idx: number[] = []
schedules.forEach((s, i) => {
if (String(s.scheduleStatus || 'open') !== 'void' && !s._deleted) idx.push(i)
})
return idx
}
/** 仅财务已确认(yjf)锁定;付款确认中仍可改,直至财务确认 */
function isTransferLocked(tr: Record<string, any>) {
return tr.payStatus === 'yjf'
@@ -458,6 +475,37 @@
const pay = { ...list[pIdx] }
const schedules = [...(pay.schedules || [])]
schedules[sIdx] = { ...schedules[sIdx], ...patch }
const mode = String(pay.payMode || '')
const key = samplePayKey(pay, pIdx)
const activeIdx = activeScheduleIndices(schedules)
if ('dueAmount' in patch && mode === 'fq' && activeIdx.length >= 2) {
const lastIdx = activeIdx[activeIdx.length - 1]
if (sIdx === lastIdx) {
lastPeriodDueOverrideByKey.value = {
...lastPeriodDueOverrideByKey.value,
[key]: true
}
} else if (activeIdx.slice(0, -1).includes(sIdx)) {
lastPeriodDueOverrideByKey.value = {
...lastPeriodDueOverrideByKey.value,
[key]: false
}
const lastSch = schedules[lastIdx]
const canFill = shouldAutoFillLastPeriodDue({
lastManualOverride: false,
lastLocked: isScheduleDueLocked(lastSch)
})
if (canFill) {
const priors = activeIdx.slice(0, -1).map((i) => schedules[i]?.dueAmount)
const lastAmt = calcLastPeriodDueAmount(pay.contractAmount, priors)
if (lastAmt != null) {
schedules[lastIdx] = { ...schedules[lastIdx], dueAmount: lastAmt }
}
}
}
}
pay.schedules = schedules
if ('dueAmount' in patch) {
pay.installmentRatios = schedules.map((s: Record<string, any>) => {
@@ -469,6 +517,22 @@
emit('update:modelValue', list)
}
function onLumpSumDeadlineChange(pIdx: number, raw: string) {
const list = [...(props.modelValue || [])]
const pay = { ...list[pIdx] }
const deadline = raw || ''
pay.lumpSumDeadline = deadline
if (String(pay.payMode || '') === 'dj') {
pay.schedules = applyLumpSumDeadlineToLastDueDate(
[...(pay.schedules || [])],
deadline,
isScheduleDueLocked
)
}
list[pIdx] = pay
emit('update:modelValue', list)
}
function sampleTitle(pay: Record<string, any>) {
const sample = (props.samples || []).find(
(s) => String(s.sampleCode || '') === String(pay.sampleCode || '')