Files
laoyang e0562b8627 v24 第 5 轮:十条清单(天气填城市 / 输入即筛选 / 看板列=看板 / 账本盘点折线图 / 纪念日分类)
用户 2026-10-03 的十条,逐条落点:

1. 天气改为**个人设置里填城市**(每人一份):新增 app/Geo.php,内置 197 个
   地级市坐标(离线、逐条核对,生成脚本 _tooling/_fb_geo_gen.py)。填坐标仍
   作为内置表没有时的出口;老的家庭级坐标保留为兜底,升级当天天气不会凭空消失。
2. 所有"找个人"的地方**输入即筛选**:新增 assets/js/filter.js(纯前端显隐,
   不发请求、回车不刷新)+ app/PersonQuery.php 把服务端判据收敛成一份
   (名册 / 挑人 / 高级搜索共用,避免三处实现漂移)。
3. 名册列表:有昵称就不重复显示本名、行变矮、补齐手机/阴历阳历生日(带年份)/
   邮箱/工作单位;「清除」与「搜索」同款同尺寸。
4/5. 日程:撤掉左侧日历本栏改满宽,功能(显隐/颜色/导入/默认视图)全收进
   右侧 ☰;**色块高度改为按起止时长**(旧实现一律 30 分钟,三小时的会跟
   十五分钟的同高);点日历空白处即建日程,开始时间=点的那一格。
6/10. 资源:搜索提到最上;按"最近改动/最新新增"排序,默认只显示最新 10 条并
   给「显示全部」出口。
7. 任务:看板的**列=用户自定义的看板**(不再是写死的待办/进行中),
   拖动/左右键=换看板(改 board_id,不动 status)。
8. 财务:概览在上账目在下、记一笔改右侧按钮、账本设置(共享/改名/新建/删除)
   收进 ☰、默认账本名「xxx的账本」、"挂在谁名下"默认自己且用标准选人框;
   **新增盘点**(新增 ledger_entry_snapshots 只增不改的历史表,旧值不被清空)
   + 上面画金额变化折线图(app/chart.php 服务端画 SVG,零依赖)。
9. 纪念日:手动添加的与名册生日派生的**分两块**、手动的在上;右侧设置可增删
   分类(家庭级,删分类不强改已用的历史)。

本轮另外修掉/加固的四类问题(都由新增的静态或运行时守卫兜住):
- index.php 少写 2 个 use 导入(Pinyin / UserSetting)→ 名册与个人设置
  返回 200 但正文是 Fatal;顺带修了一处早已存在的 Attachment 未导入。
  → 新增 _fb_import_check.py(1f 段)。
- 一次不精确替换弄丢了 app/Calendar.php 开头的 `<?php`,整份源码被当 HTML
  吐给浏览器,而 `php -l` 报"无语法错误"(纯文本输出确实合法)。
  → 新增 _fb_php_open_check.py(1e 段)。
- 模板片段 unset 了宿主模板的变量(账本片段 unset($canWrite))→ 正文里一片
  Undefined variable 而 HTTP 仍 200。同 _datetime.php 早年 $t 的坑。
  → 新增 _fb_fragment_scope_check.py(1g 段)+ _fb_batch_e.py 的 26 页无告警巡检。
- 窄屏下页头动作区不换行 → 周视图横向溢出且按钮被挤成三种高度(用户提过三次
  "按钮要同高")→ .page-head .actions 加 flex-wrap。

验证战绩(全绿):
  e2e 773/0 · 第 5 轮专项 91/0 · 结构升级 126/0 · 几何 428/0 · 移动端 684/0 ·
  交互 97/0 · 家谱 26/0 · 按钮 60/0;三轮反向验证各 3/3 抓到,还原后复绿。
线上:schema v14、指纹一致(9df6c9fccc6796db)、snapshots 表已建、197 城、
  8 个关键文件 md5 与本地逐字节一致、app/ 403 无源码泄漏、错误日志无新增。
2026-10-03 15:01:38 +08:00

127 lines
6.5 KiB
PHP
Raw Permalink 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 用户 2026-10-03 第 7 条)
*
* 用户原话:「共享账本,改名等内容,都放在账本的设置中,也是一样的,
* 在新建账目按钮旁边,加一个设置按钮,在里面对账本进行设置、共享等。」
*
* 所以原来那个「共享与账本」大卡片撤了,里面的东西全搬进这个 ☰:
* 共享给谁 / 取消共享 / 账本改名 / 新建账本 / 删除这本账。
* 外观与观影、任务的设置下拉一致(只留图标、排最右、条目带小图标)。
*
* ⚠️⚠️ 本片段是 `require` 进**另一个模板**里的,而 PHP 的 require 共享同一个作用域 ——
* 所以这里**绝不能**定义 / 覆盖 / unset 宿主模板用到的名字(`$canWrite` / `$curId`…)。
* 本条已实测踩到:片段结尾写了 `unset($canWrite, $curId)`,把宿主模板的
* `$canWrite` 一起干掉了 → 后面每一行 `<?php if ($canWrite): ?>` 都变成
* "Undefined variable"(页面在 debug 下会**直接把 Warning 打进正文**,
* 看着像排版坏了一小段,其实是变量被偷走)。
* 同理,_datetime.php 早年在任务页覆盖过 $t —— 同一个坑,别再踩第三次。
* 所以下面所有局部变量一律加 `$ledSet` 前缀,并且只 unset **自己定义**的那些。
*
* 用法(require 前 set 好):
* $cur 当前账本(含 name / my_perm)
* $shares 当前账本的共享列表
* $people 名册(共享给谁)
*/
$ledSetCanWrite = \App\Auth::canWrite() && \App\Ledger::canWrite($cur ?? []);
$ledSetCurId = (int)($cur['id'] ?? 0);
$ledSetIcon = static function (string $name): string {
$paths = [
'share' => '<circle cx="6" cy="6.2" r="2.4"/><circle cx="12.4" cy="9.6" r="2"/><path d="M2.2 13.4c0-1.9 1.7-3 3.8-3s3.8 1.1 3.8 3M9.6 13.4c0-1.5 1.3-2.4 2.8-2.4s2.8.9 2.8 2.4"/>',
'rename'=> '<path d="M2.6 13.4l.6-3 7.4-7.4 2.4 2.4-7.4 7.4zM9.6 4l2.4 2.4"/>',
'add' => '<path d="M8 3.2v9.6M3.2 8h9.6"/>',
'del' => '<path d="M3.4 4.6h9.2M6.6 4.6V3.2h2.8v1.4M4.8 4.6l.7 8.8h5l.7-8.8"/>',
];
$d = $paths[$name] ?? '';
if ($d === '') { return ''; }
return '<svg class="mi" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"'
. ' fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"'
. ' stroke-linejoin="round">' . $d . '</svg>';
};
?>
<details class="movie-set menu led-set">
<summary class="btn btn-icon" title="账本设置" aria-label="账本设置"><svg viewBox="0 0 16 16" width="18" height="18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"><path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/></svg></summary>
<div class="menu-list led-set-list">
<?php if (!$ledSetCanWrite): ?>
<div class="led-set-block">
<p class="small muted" style="margin:0">这本账你只有只读权限,能看不能改。</p>
</div>
<?php else: ?>
<?php /* ---- 共享:给谁 + 权限 + 取消 ---- */ ?>
<div class="led-set-block">
<div class="cal-set-h">共享给谁(对方就能记能看)</div>
<?php if (!empty($shares)): ?>
<ul class="led-set-shares">
<?php foreach ($shares as $s): ?>
<li>
<?= person_chip([
'id' => (int)$s['person_id'], 'name' => (string)$s['name'],
'avatar' => $s['avatar'] ?? null, 'avatar_color' => $s['avatar_color'] ?? null,
]) ?>
<span class="node-badge"><?= ($s['perm'] ?? 'read') === 'write' ? '可写' : '只读' ?></span>
<form method="post" action="<?= e(url('ledger/unshare')) ?>" class="res-inline">
<?= csrf_field() ?>
<input type="hidden" name="ledger_id" value="<?= $ledSetCurId ?>">
<input type="hidden" name="person_id" value="<?= (int)$s['person_id'] ?>">
<button class="btn btn-ghost btn-sm" type="submit">取消</button>
</form>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p class="small muted" style="margin:0 0 6px">还没有共享给任何人。</p>
<?php endif; ?>
<form method="post" action="<?= e(url('ledger/share')) ?>" class="led-set-form-in">
<?= csrf_field() ?>
<input type="hidden" name="ledger_id" value="<?= $ledSetCurId ?>">
<?php
$ppName = 'person_id';
$ppPeople = $people;
$ppSelected = [];
$ppMode = 'single';
$ppOpenBtn = '选择共享给谁';
$ppTitle = '账本共享给谁';
$ppEmptyText= '未选择';
require __DIR__ . '/../_person_picker.php';
?>
<select class="select" name="perm">
<?php foreach (\App\Ledger::PERM as $pk => $pv): ?>
<option value="<?= e($pk) ?>"><?= e($pv) ?></option>
<?php endforeach; ?>
</select>
<button class="btn btn-sm" type="submit">共享</button>
</form>
</div>
<?php /* ---- 改名 ---- */ ?>
<div class="led-set-block">
<div class="cal-set-h">账本名字</div>
<form method="post" action="<?= e(url('ledger/book_rename')) ?>" class="led-set-form-in">
<?= csrf_field() ?>
<input type="hidden" name="ledger_id" value="<?= $ledSetCurId ?>">
<input class="input" name="name" value="<?= e((string)($cur['name'] ?? '')) ?>" required>
<button class="btn btn-sm" type="submit">改名</button>
</form>
</div>
<?php /* ---- 新建 / 删除 ---- */ ?>
<div class="led-set-block">
<form method="post" action="<?= e(url('ledger/book_add')) ?>" class="led-set-form-in">
<?= csrf_field() ?>
<input class="input" name="name" placeholder="新账本名,如:孩子的教育金" required>
<button class="btn btn-sm" type="submit">新建账本</button>
</form>
<form method="post" action="<?= e(url('ledger/book_del')) ?>" class="led-set-form-in" style="margin-top:6px">
<?= csrf_field() ?>
<input type="hidden" name="ledger_id" value="<?= $ledSetCurId ?>">
<button class="btn btn-ghost btn-sm" type="submit"
onclick="return confirm('删掉这本账?里面的账目会一起删掉。')">删除这本账</button>
</form>
</div>
<?php endif; ?>
</div>
</details>
<?php
unset($ledSetCanWrite, $ledSetCurId, $ledSetIcon);