Files
FamilyBook/templates/movies/cinemas.php
T
laoyang 7f00d3e553 feat(kin,movies): 家谱改用第三方库 family-chart;观影设置按钮改纯图标并统一页头度量
家谱(v13):
- 自研布局是死结,不是样式问题:连线的横杆是网格项,只能横跨「父母列→子女列」
  整段区间,中间夹着与血缘无关的人(如媳妇那侧的岳父母)时必然从卡片上穿过。
  换成 family-chart(D3 生态,MIT)后,排布只走本家血脉 + 配偶,姻亲不进画布,
  这个死结在构造上就不存在了。
- 接入踩到并修掉的坑:main_id = data[0].id(「我」必须排最前);
  卡片回调里自定义字段在 d.data.data(双层);库的 family-chart.css 是死文件
  (全挂在 .f3 作用域,运行时没有 .f3 元素);库的 svg 撑满选择器永不命中
  (必须自己补 .kin-chart svg.main_svg);setCanAdd/setOnCardClick 不在 chart 上
  (写错会抛异常,把 updateTree 一起带走 → 整张图空白,页面照样 200)。
- 关掉 setSingleParentEmptyCard:库默认给孩子只登记一位父母的人自动补一张无名
  配偶卡,看着像「凭空多出一个配偶」。
- 卡片用原生 <a> 链成员页,不依赖库的点击回调。

观影按钮(用户本轮原话):
- 「记一场电影」与「设置」高度仍不一致 —— 根因是文字按钮高度由 padding+line-height
  自然推出(43.8px),而 .btn-icon 写死了 40px。抽出 --act-* / --act-h 变量,两边同源。
- 设置按钮去掉「设置」二字,只留图标,图标换成三条横线(☰)。
- 设置按钮在所有页面排最右(.movie-set 用 margin-left:auto 顶到行尾)。
- 除设置外其他按钮都有颜色,与设置区分(主按钮实心 / 次要浅底 / 危险淡红)。
  顺带修掉一个特异性 bug:删除按钮被 .btn 品牌底盖成实心红,与「编辑」难分辨。
- 设置下拉四个条目文字前都加图标(行内 SVG,不引外部字体)。

测试与部署:
- 新增 _fb_kin_check.py(桌面,12 条)+ _fb_kin_falsify.py(6/6)证明
  「整张家谱空白」这类只靠通用几何抓不到的坏法能被抓住。
- _fb_geom_mobile.py 家谱段重写为窄屏语义(库按容器 fit 缩放是正常行为,
  改为断「比例没被压扁 / 缩放不至于看不清」),并加 _fb_kin_mobile_falsify.py(4/4)。
- _fb_btn_check.py(59 条)+ _fb_btn_falsify.py(3/3)覆盖观影页头按钮五条需求。
- familybook_deploy.sh / familybook_smoke.sh 的家谱断言改为第三方库形态,
  设置按钮断言改为 btn-icon 纯图标 + 三条横线 + 条目图标。
- 新增 .gitattributes:vendor 库与 .min.js 标 -text,原样入库不被改写行尾。
2026-10-01 19:35:48 +08:00

143 lines
6.1 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
/**
* 影院管理(v9,MovieLog cinemas.php 同款):上面加/编表单,下面列表。
*
* 颜色用「取色器 + 50 色预设板」两路输入(内联小段 JS 只是取色联动,
* 不依赖 JS 也能手填 hex 或直接用取色器)。删除是 POST 小表单 + confirm;
* 删掉的影院只把关联观影记录的 cinema_id 置空,记录本身不动。
*
* ⚠️ $editing 是路由传进来的「被编辑影院的记录数组」(或 null)。
* 曾经这里写成 `$editing = !empty($editing);` —— 数组被原地变成布尔值,
* 表面看标题还会显示「编辑影院」,但 hidden id 变 0、四个字段全部回填为空,
* 点「编辑」就像没带入一样(v9 线上实测)。布尔化结果必须存进**另一个**变量。
*/
$isEditing = !empty($editing);
$canWrite = \App\Auth::canWrite();
?>
<div class="page-head">
<h1>影院管理</h1>
<span class="sub">共 <?= count($cinemas) ?> 家影院</span>
<div class="actions">
<a class="btn btn-ghost" href="<?= e(url('movies')) ?>">返回观影记录</a>
<?php $setCurrent = 'cinemas'; $setCanWrite = $canWrite; require __DIR__ . '/_settings_menu.php'; ?>
</div>
</div>
<?php if (!$canWrite): ?>
<div class="card card-pad empty"><b>只读账号</b>访客只能查看影院列表。</div>
<?php else: ?>
<div class="card card-pad cinema-form-card">
<h2 class="cinema-h2"><?= $isEditing ? '编辑影院' : '添加影院' ?></h2>
<form method="post" action="<?= e(url('movies/cinemas_save')) ?>">
<?= csrf_field() ?>
<?php if ($isEditing): ?>
<input type="hidden" name="id" value="<?= (int)$editing['id'] ?>">
<?php endif; ?>
<div class="form-grid">
<div class="field">
<label for="cname">影院名称 *</label>
<input class="input" id="cname" name="name" required
value="<?= e(old('name', $editing['name'] ?? '')) ?>" placeholder="如:万达影城(IMAX 店)">
</div>
<div class="field">
<label for="cplace">所在地方</label>
<input class="input" id="cplace" name="place"
value="<?= e(old('place', $editing['place'] ?? '')) ?>" placeholder="如:青岛 · 崂山区">
</div>
<div class="field full">
<label for="caddress">详细地址</label>
<input class="input" id="caddress" name="address"
value="<?= e(old('address', $editing['address'] ?? '')) ?>">
</div>
<div class="field full">
<label for="ccolor">主题颜色</label>
<div class="color-row">
<input type="color" id="ccolor" name="color" class="color-picker"
value="<?= e(old('color', $editing['color'] ?? '#888888')) ?>">
<span class="color-hex" id="colorHex"><?= e(old('color', $editing['color'] ?? '#888888')) ?></span>
<span class="preset-palette">
<?php foreach (\App\Cinema::PRESET_COLORS as $pc): ?>
<span class="preset-color" data-color="<?= $pc ?>" style="background: <?= $pc ?>;" title="<?= $pc ?>"></span>
<?php endforeach; ?>
</span>
</div>
</div>
</div>
<div class="btn-row">
<button type="submit" class="btn"><?= $isEditing ? '保存' : '添加影院' ?></button>
<?php if ($isEditing): ?>
<a class="btn btn-ghost" href="<?= e(url('movies/cinemas')) ?>">取消编辑</a>
<?php endif; ?>
</div>
</form>
</div>
<?php endif; ?>
<div class="card card-pad">
<h2 class="cinema-h2">影院列表</h2>
<?php if (!$cinemas): ?>
<p class="empty-hint">还没有影院,先在上面添加一家。</p>
<?php else: ?>
<div class="table-wrap">
<table class="table">
<thead>
<tr><th>名称</th><th>所在地方</th><th>详细地址</th><th>观影</th><?php if ($canWrite): ?><th>操作</th><?php endif; ?></tr>
</thead>
<tbody>
<?php foreach ($cinemas as $c): ?>
<tr>
<td>
<span class="color-preview" style="background: <?= e((string)($c['color'] ?: '#888888')) ?>;"></span>
<b><?= e($c['name']) ?></b>
</td>
<td><?= e((string)($c['place'] ?? '')) !== '' ? e($c['place']) : '—' ?></td>
<td><?= !empty($c['address']) ? e($c['address']) : '—' ?></td>
<td><?= (int)$c['movie_count'] ?> 场</td>
<?php if ($canWrite): ?>
<td>
<div class="actions">
<a class="btn btn-ghost btn-sm" href="<?= e(url('movies/cinemas', ['edit' => (int)$c['id']])) ?>">编辑</a>
<form method="post" action="<?= e(url('movies/cinemas_del')) ?>"
onsubmit="return confirm('确定删除「<?= e($c['name']) ?>」吗?关联观影记录的影院信息将置空。')">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int)$c['id'] ?>">
<button type="submit" class="btn btn-danger btn-sm">删除</button>
</form>
</div>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<?php if ($canWrite): ?>
<script>
(function () {
var picker = document.getElementById('ccolor');
var hex = document.getElementById('colorHex');
if (!picker || !hex) return;
function setActive(color) {
Array.prototype.forEach.call(document.querySelectorAll('.preset-color'), function (el) {
el.classList.toggle('active', el.getAttribute('data-color') === color);
});
}
picker.addEventListener('input', function () {
hex.textContent = picker.value;
setActive(picker.value);
});
Array.prototype.forEach.call(document.querySelectorAll('.preset-color'), function (el) {
el.addEventListener('click', function () {
picker.value = el.getAttribute('data-color');
hex.textContent = picker.value;
setActive(picker.value);
});
});
setActive(picker.value);
})();
</script>
<?php endif; ?>