Files
FamilyBook/templates/ledger/index.php
T
laoyang 5d80b9480b v24 第 11 批:7 大条 20 子项(首页/名册/观影/裁剪/日程/财务)
用户 2026-10-04 第 11 批,7 大条:
- #1 首页:未来七天过滤掉「农历」(每日一条会淹掉真日程);右侧日历本标签
  从灰字(.dash-tag)改成**实心色块**(底色=日历色、白字、text-shadow 兜底浅色)。
- #2 名册:分组改**居中模态框**(用户要"像选观影人一样")。修掉"点第二次失效"
  ——根因是旧实现用**捕获阶段**监听 document click 做"点外面就关",与触发按钮
  的 click 打架;模态把关闭收敛到遮罩/✕/取消。卡片「编辑」后加「删除」
  (→ people/destroy);详情页底部删除改成 btn-danger + btn-block-lg 大红按钮 + 两步确认。
- #3 名册详情页重排:.detail(260px+1fr) → .pcard(头像左上 + 右侧姓名/标签)
  + .detail-stack 全宽往下。旧布局头像卡比右栏矮,头像下留一大片空白。
- #4 观影:影院下拉换**自定义组件**(原生 <option> 画不了色块)——按钮+列表、
  每行色点,值仍写隐藏 input[name=cinema_id];「管理影院」→「新建影院」弹框
  (带 back=movies/new,存完回本页并 ?cinema=<id> 自动选中)。
  修「开始/结束时间标签跑到控件下面」:upgradeRange 原来 insertBefore(host, group.firstChild)
  把控件插到了 label 之上 —— 改成插在第一个原生控件之前。
- #5 裁剪改 **Cropper.js**(assets/vendor/cropper/,按需加载走 fb-cropper meta):
  弹框 overflow:hidden + 固定画布(**无滚动条**)、**去掉缩放条**改滚轮 wheelZoomRatio、
  viewMode:1 + autoCropArea 保证框小于画布、8 个触点全露。
  结果仍写 *_crop = 归一化 x,y,w,h(提交契约不变)。
- #6 日程:内置节假日原来每天只标**一天**(国庆只出一条)→ FIXED 改成
  [名字, 放假天数] 按区间铺;月视图从周日改**周一**起(表头同步);
  跳转日期从"长条+原生框"改成**单个 btn-icon**,点开弹 Semi 日期选择器。
- #7 财务:单条曲线无盘点时只回一个点(图看着像全 0)→ 改成"从创建日(0)画到今天";
  图下条目从"每账目一张小图卡"改成**一张 chart_multi 多线图 + 颜色图例**
  (Ledger::LEGEND_COLORS 按 id 取模,保证颜色稳定);账目页卡顿同源治理
  (行上只留按钮、表单全页一份 #ledPanels);修改按钮改弹框并回填归属人。

## 顺带修的真 bug(本批最要紧的三条)
1. **观影表单提交静默失效**:「新建影院」弹框被误嵌在观影主表单 <form> 内。
   HTML 不允许 form 嵌套 → 浏览器拆开内层,弹框里 required 的"影院名称"变成
   主表单必填项、又因 hidden 无法聚焦 → 点「记下这场电影」什么都不发生
   (Chrome: invalid form control with name='name' is not focusable)。
   → 弹框移到主表单之后。部署脚本加防线:按顺序配平 form 开闭判断是否嵌套。
2. **裁剪取消没清空文件框**(换 Cropper 时丢)→ close(cancelled),4 个取消入口
   传 true 清空;否则"点了取消"仍会把没裁的原图传上去。
3. **裁剪预览退化成原图**(同批丢)→ 用 canvas 按 Cropper 的原图像素矩形重画,
   toDataURL 写回预览,恢复"所见即所得"。

## 连带修复:选人组件懒渲染(线上实测的必要性)
线上真实数据下账目页 6939 KB / 16788 个 input、观影表单 1194 个 input ——
根因是 _person_picker.php 首屏就把**全部候选人**渲成 <label>+<input>。
改成:首屏只留「不选」+ data-pp-people(JSON),**打开弹框时**才 buildList();
无 JS 兜底走 <noscript>(HTML5 规定启用脚本时其内容按纯文本解析,不产生节点)。
新增 window.ppSetSelected / decorateItems(动态项要补算首字母)。
实测账目页 6939 KB → 544 KB。新增 _fb_picker_lazy_check.py(11/0 + 反向 2 红)。

验证:e2e 786/0、交互 90/0、半集成 27/0、batch_e 91/0、batch10 46/0、batch9 38/0、
crop 17/0(+反向)、picker_unify 28/0、picker_lazy 11/0(+反向)、formbloat 9/0、
upgrade 126/0、mobile 688/0、drag 5/0、geom 5/5。
线上自检 19/19;线上真实账号验收 7/0(含"主表单无隐藏必填""线上 Cropper 接管+取消清空")。
2026-10-04 17:42:21 +08:00

500 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 财务 / 账本(v24 用户第 15 条;2026-10-03 第 7、8 条重排)。
*
* 用户要求的页面结构(第 7 条原话):
* 「上面是概览,下面就是具体账目,添加账目的功能和新增日程之类的一样,
* 放在右侧的按钮。……共享账本,改名等内容,都放在账本的设置中,
* 也是一样的,在新建账目按钮旁边,加一个设置按钮,在里面对账本进行设置、共享等。
* 挂在谁名下,应该默认是自己,如果要更改,点一下,弹出选人框。」
*
* 第 8 条(盘点):
* 「我点击盘点,让我对一个账目的金额进行重新输入,但是盘点之前的账目数目不能清空,
* 要记住,这样的话,每个账目的变化,就知道了……然后把这种变化,每个账目的
* 金额变化,还有总金额的变化,在账本列表上面,画出一个图,折线图这样的。」
* → 所以「账目」这块的最上面是**图**(整本账 + 每条有盘点记录的账目),
* 下面是账目表,每行带一个「盘点」入口。
*
* ⚠️ 金额全程以**分**参与计算(Ledger 里做的),这里只负责显示成元。
* 任何地方都不要拿元去加总。
*
* @param array $books 我能看到的账本(带 my_perm / entry_count)
* @param array $cur 当前账本
* @param array $entries 当前账本的账目
* @param array $summary ['asset','debt','net','by_kind','count'](单位:分)
* @param array $shares 当前账本的共享列表
* @param array $charts 有盘点历史的账目 → ['entry'=>行, 'series'=>[…] ]
* @param array $timeline 整本账的净资产时间线
* @param array $people 名册
*/
$canWrite = \App\Auth::canWrite() && \App\Ledger::canWrite($cur);
$curId = (int)$cur['id'];
$money = static fn($c): string => \App\Ledger::money($c);
$charts = $charts ?? [];
$timeline = $timeline ?? [];
// 用户第 8 条:「挂在谁名下,应该默认是自己」。这里的 $me 由 index.php 传进来(当前登录者)。
// ⚠️ 别用 `$me ?? 0` 再判 0 —— 那样一旦没传进来就会静默变成"不选",看着像默认值丢了。
$ledMe = (int)($me ?? 0);
?>
<div class="page-head">
<h1>财务</h1>
<span class="sub"><?= e((string)$cur['name']) ?> · 共享后双方都能记、都能看</span>
<div class="actions">
<?php /* 用户第 7 条:加账目 = 右侧按钮(与「新建日程」同一套) */ ?>
<?php if ($canWrite): ?>
<button class="btn" type="button" id="ledAddBtn">记一笔</button>
<?php else: ?>
<span class="link-quiet">只读</span>
<?php endif; ?>
<?php /* 用户第 7 条:共享 / 改名 / 新建 / 删除 全在账本设置里(☰) */ ?>
<?php $setCurrent = 'book'; require __DIR__ . '/_settings_menu.php'; ?>
</div>
</div>
<?php if (count($books) > 1): ?>
<div class="seg">
<?php foreach ($books as $b): ?>
<a class="seg-btn<?= (int)$b['id'] === $curId ? ' is-on' : '' ?>"
href="<?= e(url('ledger', ['book' => (int)$b['id']])) ?>">
<?= e((string)$b['name']) ?><?= ($b['my_perm'] ?? '') === 'read' ? '(只读)' : '' ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php /* ==================== ① 概览 ==================== */ ?>
<div class="card card-pad led-sum">
<div class="led-sum-head">
<h2 class="cinema-h2" style="margin:0"><?= e((string)$cur['name']) ?></h2>
<span class="small muted">
<?= (int)($cur['entry_count'] ?? count($entries)) ?> 笔账
<?php if (($cur['my_perm'] ?? '') === 'read'): ?>
· <b>只读</b>(不能记账)
<?php endif; ?>
</span>
</div>
<div class="led-total">
<div class="led-total-item">
<span class="led-total-lb">总资产</span>
<span class="led-total-num led-asset"><?= e($money($summary['asset'])) ?></span>
</div>
<div class="led-total-item">
<span class="led-total-lb">总负债</span>
<span class="led-total-num led-debt"><?= e($money($summary['debt'])) ?></span>
</div>
<div class="led-total-item led-net">
<span class="led-total-lb">净资产</span>
<span class="led-total-num"><?= e($money($summary['net'])) ?></span>
</div>
</div>
<?php if ($summary['by_kind']): ?>
<div class="led-kinds">
<?php foreach ($summary['by_kind'] as $k => $c): ?>
<span class="led-kind-chip<?= \App\Ledger::isDebt((string)$k) ? ' is-debt' : '' ?>">
<?= \App\Ledger::kindIcon((string)$k) ?> <?= e(\App\Ledger::kindWord((string)$k)) ?>
<b><?= e($money($c)) ?></b>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php if ($canWrite): ?>
<div class="card card-pad led-add" id="ledAddCard" hidden>
<h2 class="cinema-h2">记一笔</h2>
<form method="post" action="<?= e(url('ledger/entry_add')) ?>">
<?= csrf_field() ?>
<input type="hidden" name="ledger_id" value="<?= $curId ?>">
<div class="form-grid">
<div class="field">
<label for="le-kind">类别 *</label>
<select class="select" id="le-kind" name="kind">
<?php foreach (\App\Ledger::KINDS as $k => $v): ?>
<option value="<?= e($k) ?>"><?= e($v[2] . ' ' . $v[0]) ?><?= $v[1] === 'debt' ? '(负债)' : '' ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="field">
<label for="le-title">名称 *</label>
<input class="input" id="le-title" name="title" required placeholder="如:工商银行定期">
</div>
<div class="field">
<label for="le-amount">金额(元)*</label>
<input class="input" id="le-amount" name="amount" required inputmode="decimal"
placeholder="如:120000.00">
<span class="tip">负债(贷款/信用卡)也填正数,系统按类别算成负债。</span>
</div>
<div class="field">
<?php /* 用户第 7 条:「挂在谁名下,应该默认是自己,如果要更改,点一下,弹出选人框」
—— 用全局选人组件(与观影选人同一套,含拼音首字母联想)。 */ ?>
<label>挂在谁名下</label>
<?php
$ppName = 'person_id';
$ppPeople = $people;
// 默认就是**我自己**(用户第 8 条)。单选组件按 $ppSelected 勾选并渲染 chip,
// 传 [0] 的话会渲染成"未选择"——所以必须传真实 id。
$ppSelected = $ledMe > 0 ? [$ledMe] : [];
$ppMode = 'single';
$ppOpenBtn = '更换归属人';
$ppTitle = '这笔账挂在谁名下';
$ppEmptyText= '不指定';
$ppAllowEmpty = true;
require __DIR__ . '/../_person_picker.php';
?>
</div>
<div class="field full">
<label for="le-note">备注</label>
<input class="input" id="le-note" name="note" placeholder="可选">
</div>
</div>
<div class="btn-row"><button class="btn" type="submit">添加账目</button></div>
</form>
</div>
<?php endif; ?>
<?php /* ==================== ② 变化图(v24 第 10 批 #3 重做)====================
用户原话:「在账目上面,增加一个图表显示,**显示所有账目和总金额的变化曲线**,
根据盘点的金额进行显示。横轴就是时间,纵轴是金额,用折线图。」
⚠️ 与旧版的差别:① 总金额(净资产)**永远在最前**;② 每条账目都出线(不再只画盘点过的);
③ 加一条"按类别看"的切换条 —— 因为"所有账目"在几百条时会变成一坨,
把筛选交给用户,而不是我们偷偷少画几条。 */ ?>
<?php if ($entries): ?>
<div class="card card-pad led-charts">
<h2 class="cinema-h2">
金额变化
<span class="hint">横轴时间 · 纵轴金额;每次「盘点」记一个点,旧数字不会被清掉</span>
</h2>
<?php if ($timeline): ?>
<div class="ck-card ck-total">
<div class="ck-head">
<b>总金额 · 净资产</b>
<span class="small muted"><?= count($timeline) ?> 个时间点</span>
</div>
<?= chart_line($timeline, '净资产', 'asset', 900) ?>
</div>
<?php endif; ?>
<?php if ($charts): ?>
<?php
// 类别筛选条:只列出**真有账目**的类别,点一下只看这一类。
$kindsPresent = [];
foreach ($charts as $ch) {
$k = (string)$ch['entry']['kind'];
$kindsPresent[$k] = ($kindsPresent[$k] ?? 0) + 1;
}
?>
<div class="ck-filter" data-ck-filter>
<button type="button" class="ck-chip is-on" data-ck-kind="">全部(<?= count($charts) ?>)</button>
<?php foreach ($kindsPresent as $k => $n): ?>
<button type="button" class="ck-chip<?= \App\Ledger::isDebt((string)$k) ? ' is-debt' : '' ?>"
data-ck-kind="<?= e((string)$k) ?>">
<?= \App\Ledger::kindIcon((string)$k) ?> <?= e(\App\Ledger::kindWord((string)$k)) ?>(<?= (int)$n ?>)
</button>
<?php endforeach; ?>
</div>
<?php /* 用户 2026-10-04 第 11 批 #7 两句原话:
· 「那个图,怎么数据都是 0 啊,没有盘点数据,就用账目的初始数据就可以了」
→ 数据源已在 Ledger::entrySeries 修好(没盘点过就从 0 画到当前值)。
· 「图下面的条目不用显示详情,只需要显示每个账目的颜色,
就是说哪个颜色代表哪个账目,就可以了。」
→ 原来每条账目一张小卡(标题 + 类型 + 点数 + 一张小图),信息冗余;
现在改成**一张合并的多色折线图**(每条账目一条线、用图例色),
下面只留一排颜色图例(色块 + 名称),不再堆详情。 */ ?>
<?= chart_multi($charts, 900) ?>
<div class="ck-legend">
<?php foreach ($charts as $ch): ?>
<?php $en = $ch['entry']; $ek = (string)$en['kind']; ?>
<span class="ck-lg-item" data-ck-kind="<?= e($ek) ?>">
<i class="ck-lg-dot" style="background: <?= e((string)$ch['color']) ?>;"></i>
<?= e((string)$en['title']) ?>
</span>
<?php endforeach; ?>
</div>
<p class="small muted" style="margin:8px 2px 0">
没盘点过的账目按「从 0 到现在」画 —— 想让它出现真实波动,在下面账目表里点「盘点」。
</p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php /* ==================== ③ 账目 ==================== */ ?>
<div class="card card-pad">
<h2 class="cinema-h2">账目(<?= count($entries) ?>)</h2>
<?php if (!$entries): ?>
<p class="empty-hint">这本账还没有账目。上面「记一笔」开始记吧。</p>
<?php else: ?>
<div class="table-wrap">
<table class="table">
<thead>
<tr><th>类别</th><th>名称</th><th class="num">金额</th><th>挂在谁名下</th><th>备注</th><th></th></tr>
</thead>
<tbody>
<?php foreach ($entries as $en): ?>
<?php $ek = (string)$en['kind']; $debt = \App\Ledger::isDebt($ek); $eid = (int)$en['id']; ?>
<tr>
<td class="nowrap">
<span class="led-kind-chip<?= $debt ? ' is-debt' : '' ?>">
<?= \App\Ledger::kindIcon($ek) ?> <?= e(\App\Ledger::kindWord($ek)) ?>
</span>
</td>
<td><?= e((string)$en['title']) ?></td>
<td class="num<?= $debt ? ' led-debt' : ' led-asset' ?>">
<?= $debt ? '-' : '' ?><?= e($money((int)$en['amount_cents'])) ?>
</td>
<td class="nowrap">
<?php if (!empty($en['person_name'])): ?>
<?= person_chip([
'id' => (int)$en['person_id'], 'name' => (string)$en['person_name'],
'my_nickname' => (string)($en['person_nick'] ?? ''),
'avatar' => $en['avatar'] ?? null, 'avatar_color' => $en['avatar_color'] ?? null,
]) ?>
<?php else: ?><span class="small muted">—</span><?php endif; ?>
</td>
<td class="small muted"><?= e((string)($en['note'] ?? '')) ?></td>
<td class="nowrap led-acts">
<?php /* 用户 2026-10-04 第 3 条原话:「账目列表,右侧两个按钮,
一个**盘点**,点击后,输入最新的金额;一个按钮**修改**,
用来修改名称、挂在谁名下、备注等。」
→ 所以这两个是**并列的按钮**,各自弹自己的面板;
不再是一个「操作」里塞两坨表单(那要展开两层才够得着)。 */ ?>
<?php /* 用户第 11 批 #7:「修改按钮,点击后弹框,然后修改信息」;
同时这里也是**账目页卡顿的根源**:原来每行内联「盘点」「修改」
两个面板(各一个 <form> + 一个选人组件),账目多时就是几千个 form
—— 密码管理器(Bitwarden)逐项扫描把主线程占死。
→ 现在行上只留两个带数据的按钮,真正的表单全页只有一份(见页尾 #ledPanels)。 */ ?>
<?php if ($canWrite): $histN = count(\App\Ledger::history($eid)); ?>
<button type="button" class="btn btn-ghost btn-sm led-act"
data-led-open="recon"
data-led-id="<?= $eid ?>"
data-led-title="<?= e((string)$en['title']) ?>"
data-led-amt="<?= e(\App\Ledger::amountYuan((int)$en['amount_cents'])) ?>"
data-led-hist="<?= (int)$histN ?>">盘点</button>
<button type="button" class="btn btn-ghost btn-sm led-act"
data-led-open="edit"
data-led-id="<?= $eid ?>"
data-led-title="<?= e((string)$en['title']) ?>"
data-led-amt="<?= e(\App\Ledger::amountYuan((int)$en['amount_cents'])) ?>"
data-led-note="<?= e((string)($en['note'] ?? '')) ?>"
data-led-kind="<?= e($ek) ?>"
data-led-person="<?= (int)($en['person_id'] ?? 0) ?>">修改</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<th colspan="2">合计(资产 − 负债)</th>
<th class="num led-net"><?= e($money($summary['net'])) ?></th>
<th colspan="3"></th>
</tr>
</tfoot>
</table>
</div>
<?php endif; ?>
</div>
<?php /* ==================== 账目行的两个弹框(全页各一份)====================
与名册的 #rosterMenus 同一套思路:行上只留按钮,表单集中在这里。
这是账目页卡顿的**根治办法** —— 原来每行两个面板,
账目一多就是几千个 form(用户报的 Bitwarden 冲突就是这么来的)。 */ ?>
<?php if ($canWrite): ?>
<div class="led-modal" id="ledPanels" hidden>
<div class="led-mask" data-led-close></div>
<div class="led-panel-lg" id="ledReconPanel" hidden role="dialog" aria-modal="true" aria-label="盘点">
<div class="led-ph"><b>盘点「<span class="ld-title"></span>」</b>
<button type="button" class="led-x" data-led-close aria-label="关闭">✕</button></div>
<form method="post" action="<?= e(url('ledger/reconcile')) ?>" class="led-recon">
<?= csrf_field() ?>
<input type="hidden" name="ledger_id" value="<?= $curId ?>">
<input type="hidden" name="id" value="">
<div class="led-pb">
<div class="field">
<label>最新金额(元)</label>
<input class="input" name="amount" inputmode="decimal" required>
<span class="tip">盘点前的金额会记进历史,图上就多一个点。</span>
</div>
<div class="field">
<label>盘点备注</label>
<input class="input" name="note" placeholder="如:2026 年初盘点">
</div>
<details class="led-hist" hidden>
<summary class="link-quiet"></summary>
<div class="led-hist-box"></div>
</details>
</div>
<div class="led-pf">
<button class="btn" type="submit">记下这次盘点</button>
<button class="btn btn-ghost" type="button" data-led-close>取消</button>
</div>
</form>
</div>
<div class="led-panel-lg" id="ledEditPanel" hidden role="dialog" aria-modal="true" aria-label="修改">
<div class="led-ph"><b>修改「<span class="ld-title"></span>」</b>
<button type="button" class="led-x" data-led-close aria-label="关闭">✕</button></div>
<form method="post" action="<?= e(url('ledger/entry_update')) ?>">
<?= csrf_field() ?>
<input type="hidden" name="ledger_id" value="<?= $curId ?>">
<input type="hidden" name="id" value="">
<div class="led-pb">
<div class="field">
<label>名称</label>
<input class="input" name="title" required>
</div>
<div class="field">
<label>挂在谁名下</label>
<?php /* ⚠️ 选人组件只在这里渲染**一份**(原来每行一份 = 卡顿主因之一) */ ?>
<?php
$ppName = 'person_id';
$ppPeople = $people;
$ppSelected = [];
$ppMode = 'single';
$ppOpenBtn = '更换归属人';
$ppTitle = '这笔账挂在谁名下';
$ppEmptyText= '不指定';
$ppAllowEmpty = true;
require __DIR__ . '/../_person_picker.php';
?>
</div>
<div class="field">
<label>类别</label>
<select class="select" name="kind">
<?php foreach (\App\Ledger::KINDS as $k => $v): ?>
<option value="<?= e($k) ?>"><?= e($v[2] . ' ' . $v[0]) ?><?= $v[1] === 'debt' ? '(负债)' : '' ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="field">
<label>金额(元)</label>
<input class="input" name="amount" required>
</div>
<div class="field">
<label>备注</label>
<input class="input" name="note">
</div>
<p class="tip" style="margin:0">
改的是名称、归属与备注。若只是<b>金额变了</b>(不是录错),请用「盘点」——
那样才会记进历史、在图上多一个点。
</p>
</div>
<div class="led-pf">
<button class="btn" type="submit">保存</button>
<button class="btn btn-ghost" type="button" data-led-close>取消</button>
<button class="btn btn-danger" type="submit" formaction="<?= e(url('ledger/entry_del')) ?>"
onclick="return confirm('删掉这笔账目?')">删除这笔</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
<script>
(function () {
/* ---- 「记一笔」开合 ---- */
var btn = document.getElementById('ledAddBtn');
var card = document.getElementById('ledAddCard');
if (btn && card) {
btn.addEventListener('click', function () {
card.hidden = !card.hidden;
btn.textContent = card.hidden ? '记一笔' : '收起';
if (!card.hidden) { card.scrollIntoView({ block: 'nearest' }); }
});
}
/* ---- 行上「盘点 / 修改」两个按钮 → 打开**全页共享的弹框**(第 11 批 #7)----
⚠️ 旧版是"把面板插到那一行后面",于是每行都得内联一整套表单(含选人组件):
账目多的时候就是几千个 form,密码管理器逐个扫描 → 页面卡死
(用户报的"和之前的名录一样,还是 bitwarden 冲突")。
现在表单只有一份(#ledPanels),打开时把该行的数据灌进去。 */
var wrap = document.getElementById('ledPanels');
if (wrap) {
var reconPanel = document.getElementById('ledReconPanel');
var editPanel = document.getElementById('ledEditPanel');
function closeAll() {
wrap.hidden = true;
if (reconPanel) { reconPanel.hidden = true; }
if (editPanel) { editPanel.hidden = true; }
}
document.addEventListener('click', function (e) {
var t = e.target;
if (!t || !t.closest) { return; }
var trigger = t.closest('[data-led-open]');
if (trigger) {
e.preventDefault();
var which = trigger.getAttribute('data-led-open');
var id = trigger.getAttribute('data-led-id');
var title = trigger.getAttribute('data-led-title') || '';
var amt = trigger.getAttribute('data-led-amt') || '';
var panel = (which === 'recon') ? reconPanel : editPanel;
if (!panel) { return; }
panel.querySelector('input[name="id"]').value = id;
panel.querySelector('.ld-title').textContent = title;
var amtInp = panel.querySelector('input[name="amount"]');
if (amtInp) { amtInp.value = amt; }
if (which === 'edit') {
var tInp = panel.querySelector('input[name="title"]');
if (tInp) { tInp.value = title; }
var nInp = panel.querySelector('input[name="note"]');
if (nInp) { nInp.value = trigger.getAttribute('data-led-note') || ''; }
var kSel = panel.querySelector('select[name="kind"]');
if (kSel) { kSel.value = trigger.getAttribute('data-led-kind') || 'other'; }
// 归属人:选人组件是**单选**,用它的接口重置选中(见 _person_picker.php)
var pid = trigger.getAttribute('data-led-person') || '0';
if (window.ppSetSelected) { window.ppSetSelected('person_id', pid); }
}
if (which === 'recon') {
// 历史的条数以按钮为准(服务端已算好),点开才展开,避免每行都查一次库
var histN = parseInt(trigger.getAttribute('data-led-hist') || '0', 10) || 0;
var hist = panel.querySelector('.led-hist');
var hsum = panel.querySelector('.led-hist summary');
var hbox = panel.querySelector('.led-hist-box');
if (hist && hsum) {
hist.hidden = histN === 0;
hsum.textContent = '看历史(' + histN + ' 次)';
if (hbox) { hbox.innerHTML = ''; }
}
}
reconPanel.hidden = (which !== 'recon');
editPanel.hidden = (which !== 'edit');
wrap.hidden = false;
var focusInp = panel.querySelector('input:not([type=hidden]), select');
if (focusInp) { focusInp.focus(); }
return;
}
if (t.closest('[data-led-close]')) { e.preventDefault(); closeAll(); }
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') { closeAll(); }
});
}
/* ---- 图表按类别筛选("所有账目"在几百条时会变成一坨)---- */
var bar = document.querySelector('[data-ck-filter]');
if (bar) {
bar.addEventListener('click', function (e) {
var chip = e.target.closest('.ck-chip');
if (!chip) { return; }
var kind = chip.getAttribute('data-ck-kind') || '';
Array.prototype.forEach.call(bar.querySelectorAll('.ck-chip'), function (c) {
c.classList.toggle('is-on', c === chip);
});
Array.prototype.forEach.call(document.querySelectorAll('[data-ck-card]'), function (c) {
c.hidden = kind !== '' && c.getAttribute('data-ck-card') !== kind;
});
});
}
})();
</script>