HEL-413: restore single-row compact repayment plan layout

Keep each installment on one desktop row with content-based column widths so 1080P/1440 do not need inner scrolling or two-column cards.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
美术师
2026-09-02 18:16:54 +08:00
co-authored by Cursor multica-agent
parent 70918309e1
commit de7b4f79a2
2 changed files with 109 additions and 55 deletions
+50 -47
View File
@@ -150,7 +150,8 @@
</div>
<div class="form-field">
<label>计息说明</label>
<div class="field-hint">到期日前可选“提前还款”;到期日后可选“按期还款/延期/逾期”。逾期未归还计划本金按上浮利率计息,剩余额度按已还款计划与手动归还本金自动计算。</div>
<?php $planHint = '到期日前可选“提前还款”;到期日后可选“按期还款/延期/逾期”。逾期未归还计划本金按上浮利率计息,剩余额度按已还款计划与手动归还本金自动计算。'; ?>
<div class="field-hint plan-lead-hint" title="<?= e($planHint) ?>"><?= e($planHint) ?></div>
</div>
</div>
<div id="plan_wrap" class="plan-editor"<?= empty($planCfg['enabled']) ? ' hidden' : '' ?>>
@@ -159,31 +160,29 @@
<?php $st = (string)($row['status'] ?? 'pending'); ?>
<div class="plan-row plan-edit-row">
<input type="hidden" name="plan_id[]" value="<?= e((string)($row['id'] ?? '')) ?>">
<div class="form-grid">
<div class="form-field">
<label>还款日期</label>
<input type="date" name="plan_due_date[]" value="<?= e((string)($row['due_date'] ?? '')) ?>" onchange="syncPlanAction(this)">
</div>
<div class="form-field">
<label>还款金额</label>
<input type="number" class="num" step="0.01" name="plan_amount[]" value="<?= e((string)($row['amount'] ?? '')) ?>">
</div>
<div class="form-field">
<label>状态操作</label>
<select name="plan_status[]" onchange="syncDelayInput(this)">
<option value="pending" <?= $st === 'pending' ? 'selected' : '' ?>>未还款</option>
<option value="paid" <?= $st === 'paid' ? 'selected' : '' ?>><?= (!empty($row['due_date']) && $row['due_date'] > date('Y-m-d')) ? '提前还款' : '按期还款' ?></option>
<option value="delayed" <?= $st === 'delayed' ? 'selected' : '' ?>>延期</option>
<option value="overdue" <?= $st === 'overdue' ? 'selected' : '' ?>>逾期</option>
</select>
</div>
<div class="form-field">
<label>延期日期</label>
<input type="date" name="plan_delay_date[]" value="" <?= $st === 'delayed' ? '' : 'disabled' ?>>
</div>
<div class="form-field" data-plan-col="date">
<label title="还款日期">还款日期</label>
<input type="date" class="num" name="plan_due_date[]" value="<?= e((string)($row['due_date'] ?? '')) ?>" title="还款日期" onchange="syncPlanAction(this)">
</div>
<div class="form-field" data-plan-col="amount">
<label title="还款金额">还款金额</label>
<input type="number" class="num" step="0.01" name="plan_amount[]" value="<?= e((string)($row['amount'] ?? '')) ?>" title="还款金额">
</div>
<div class="form-field" data-plan-col="status">
<label title="状态操作">状态操作</label>
<select name="plan_status[]" title="状态操作" onchange="syncDelayInput(this)">
<option value="pending" <?= $st === 'pending' ? 'selected' : '' ?>>未还款</option>
<option value="paid" <?= $st === 'paid' ? 'selected' : '' ?>><?= (!empty($row['due_date']) && $row['due_date'] > date('Y-m-d')) ? '提前还款' : '按期还款' ?></option>
<option value="delayed" <?= $st === 'delayed' ? 'selected' : '' ?>>延期</option>
<option value="overdue" <?= $st === 'overdue' ? 'selected' : '' ?>>逾期</option>
</select>
</div>
<div class="form-field" data-plan-col="delay">
<label title="延期日期">延期日期</label>
<input type="date" class="num" name="plan_delay_date[]" value="" title="延期日期" <?= $st === 'delayed' ? '' : 'disabled' ?>>
</div>
<div class="plan-edit-act">
<button type="button" class="btn btn--danger btn--sm" onclick="this.closest('.plan-edit-row').remove()">删除</button>
<button type="button" class="btn btn--danger btn--sm" title="删除本期" onclick="this.closest('.plan-edit-row').remove()">删除</button>
</div>
</div>
<?php endforeach; ?>
@@ -193,31 +192,29 @@
<template id="plan_row_tpl">
<div class="plan-row plan-edit-row">
<input type="hidden" name="plan_id[]" value="">
<div class="form-grid">
<div class="form-field">
<label>还款日期</label>
<input type="date" name="plan_due_date[]" onchange="syncPlanAction(this)">
</div>
<div class="form-field">
<label>还款金额</label>
<input type="number" class="num" step="0.01" name="plan_amount[]">
</div>
<div class="form-field">
<label>状态操作</label>
<select name="plan_status[]" onchange="syncDelayInput(this)">
<option value="pending">未还款</option>
<option value="paid">提前还款</option>
<option value="delayed">延期</option>
<option value="overdue">逾期</option>
</select>
</div>
<div class="form-field">
<label>延期日期</label>
<input type="date" name="plan_delay_date[]" disabled>
</div>
<div class="form-field" data-plan-col="date">
<label title="还款日期">还款日期</label>
<input type="date" class="num" name="plan_due_date[]" title="还款日期" onchange="syncPlanAction(this)">
</div>
<div class="form-field" data-plan-col="amount">
<label title="还款金额">还款金额</label>
<input type="number" class="num" step="0.01" name="plan_amount[]" title="还款金额">
</div>
<div class="form-field" data-plan-col="status">
<label title="状态操作">状态操作</label>
<select name="plan_status[]" title="状态操作" onchange="syncDelayInput(this)">
<option value="pending">未还款</option>
<option value="paid">提前还款</option>
<option value="delayed">延期</option>
<option value="overdue">逾期</option>
</select>
</div>
<div class="form-field" data-plan-col="delay">
<label title="延期日期">延期日期</label>
<input type="date" class="num" name="plan_delay_date[]" title="延期日期" disabled>
</div>
<div class="plan-edit-act">
<button type="button" class="btn btn--danger btn--sm" onclick="this.closest('.plan-edit-row').remove()">删除</button>
<button type="button" class="btn btn--danger btn--sm" title="删除本期" onclick="this.closest('.plan-edit-row').remove()">删除</button>
</div>
</div>
</template>
@@ -614,12 +611,18 @@
}
syncDelayInput(sel);
}
function syncPlanStatusTitle(sel){
if(!sel){return;}
var opt=sel.options[sel.selectedIndex];
sel.title=opt&&opt.text?opt.text:'状态操作';
}
function syncDelayInput(el){
var tr=el.closest('.plan-edit-row'); if(!tr){return;}
var d=tr.querySelector('input[name="plan_delay_date[]"]');
if(!d){return;}
d.disabled=(el.value!=='delayed');
if(d.disabled){d.value='';}
syncPlanStatusTitle(el);
}
function addPlanRow(){
var tb=document.getElementById('plan_table');
+59 -8
View File
@@ -576,25 +576,63 @@ img { display: block; }
flex: 0 0 auto;
}
.v-fusion .editor-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: 14px; padding: 20px 22px; }
/* HEL-404:分期行改为两列表单网格,去掉模块内滚动(overflow-x:auto 会把 overflow-y 也变成滚动容器) */
/* HEL-413:分期恢复“每期一整行”。列宽按内容价值分配,overflow:visible 避免模块内滚动。 */
.v-fusion .editor-page [data-form-block="plan"] .plan-lead-hint {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.v-fusion .editor-page .plan-editor { overflow: visible; margin-top: 12px; min-width: 0; }
.v-fusion .editor-page .plan-editor[hidden] { display: none; }
.v-fusion .editor-page .plan-list { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.v-fusion .editor-page .plan-list { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.v-fusion .editor-page .plan-edit-row {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 8px;
display: grid;
grid-template-columns:
minmax(0, 1.2fr)
minmax(0, 1fr)
minmax(0, 0.86fr)
minmax(0, 1.2fr)
max-content;
align-items: start;
column-gap: 6px;
row-gap: 0;
padding: 8px;
margin-bottom: 0;
min-width: 0;
}
.v-fusion .editor-page .plan-edit-row .form-grid { padding-top: 0; gap: 8px 12px; width: 100%; min-width: 0; }
.v-fusion .editor-page .plan-edit-row .form-field {
min-width: 0;
gap: 4px;
}
.v-fusion .editor-page .plan-edit-row .form-field label {
font-size: 11.5px;
letter-spacing: 0.03em;
color: var(--text-3);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.v-fusion .editor-page .plan-edit-row .form-field input,
.v-fusion .editor-page .plan-edit-row .form-field select {
width: 100%;
min-width: 0;
padding: 0 8px;
font-size: 13.5px;
font-variant-numeric: tabular-nums;
}
.v-fusion .editor-page .plan-edit-act { display: flex; justify-content: flex-end; }
.v-fusion .editor-page .plan-edit-row .form-field select { padding-right: 22px; }
.v-fusion .editor-page .plan-edit-act {
display: grid;
grid-template-rows: 11.5px 34px;
align-items: center;
justify-items: end;
gap: 4px;
padding-bottom: 0;
flex: 0 0 auto;
}
.v-fusion .editor-page .plan-edit-act::before { content: ""; }
.v-fusion .editor-page .plan-edit-act .btn { grid-row: 2; }
.v-fusion .editor-page .plan-edit-add { margin: 12px 0 0; }
.v-fusion .summary-card {
position: sticky; top: 34px;
@@ -958,6 +996,19 @@ img { display: block; }
.v-fusion .table-card { overflow-x: auto; }
.v-fusion .search { min-width: 0; flex: 1; }
.v-fusion .field-grid, .v-fusion .form-grid { grid-template-columns: 1fr; }
.v-fusion .editor-page .plan-edit-row {
grid-template-columns: 1fr;
align-items: stretch;
row-gap: 8px;
}
.v-fusion .editor-page .plan-edit-act {
display: flex;
justify-content: flex-start;
padding-bottom: 0;
grid-template-rows: none;
}
.v-fusion .editor-page .plan-edit-act::before { content: none; }
.v-fusion .editor-page .plan-edit-act .btn { grid-row: auto; }
.v-fusion .summary-banner { grid-template-columns: 1fr; }
.v-fusion .hero-amount { flex-direction: column; align-items: flex-start; gap: 8px; }
.v-fusion .hero-amount .ha-side { text-align: left; }