Files
FamilyBook/templates/movies/stats.php
T
laoyang e9b3b8666b v11: 家谱按户排布(夫妻并排/长幼排序/父母括号/姻亲);观影设置下拉收拢影院·搜索·统计·年度报告;观影人头像
家谱(用户第 2 点,重写模板与 Kinship::tree):
- 同代聚成「夫妻单元」(户):血亲当主位、配偶并排在其右,兄弟_姐妹按长幼(长子左)
- 血亲可达性判「姻亲」:只走 parent/child 边从「我」可达者为本家血亲,妻子的父母
  走不到 → 标「姻亲」并排到本代末尾(旧判据「上无父母且下无子女」对岳父母恒为假,是真 bug)
- 父母括号连线:横杆盖住父母列到其子女各列,竖线落父母列中心、每子女列一道下线
- 称呼按长幼定死:姐夫/妹夫、嫂子/弟媳、大/小舅子、大/小姨子、大伯子/小叔子、
  大姑子/小姑子、侄子/侄女、外甥/外甥女、堂/表
- 列宽地板 minmax(min-content,1fr):原 minmax(0,1fr) 在窄屏+户数多时把列压到卡片
  (96px)以下 → 同代卡片互相重叠;.tnode 加 flex:0 0 auto 防被 flex 压扁

观影(用户第 3~5 点):
- 顶栏删「影院」入口(影院是观影模块的子功能);观影列表右侧加「⚙ 设置」下拉,
  内含 影院管理 / 高级搜索 / 统计 / 年度报告(后三者从 MovieLog 迁入,
  app/MovieStats.php + 三个只读路由,MySQL 方言;图表全用纯 CSS,不引 CDN)
- 评价区删除「我」的长条徽标(被 flex column stretch 拉成整行宽的丑色带)
- 观影人弹窗列表与已选 chips 带头像;syncChips 点名取 .att-name/.att-av
  (旧写法取「第一个 span」,v11 起那是隐藏占位,chip 上会没名字)

验证:e2e 432/0、桌面几何 268/0、移动几何 464/0、交互几何 97/0、
静态空跑 199/0、反向验证 v11 十条 10抓/0漏。本轮无 DB 变更(Schema 仍 v10)。
移动几何抓出两个真溢出并已修:高级搜索页 select 长选项撑破 .field、
原生 date 输入撑破 .date-range(360/375px)。
2026-10-01 12:11:43 +08:00

125 lines
5.2 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
/**
* 观影统计仪表板(v11,从 MovieLog stats.php 迁入)。
*
* 版式对齐家书的暖纸配色(MovieLog 是深色卡片风 —— 只搬功能、不搬皮肤,
* 免得进到观影模块里突然变成另一个站点)。
* 条形图是纯 CSS 宽度百分比,不引任何图表库(线上不依赖 CDN)。
*/
$fmtPct = static function (int $v, int $max): float {
return $max > 0 ? round($v / $max * 100, 2) : 0.0;
};
$maxCinema = 1;
foreach ($byCinema as $r) { $maxCinema = max($maxCinema, (int)$r['cnt']); }
$maxPerson = 1;
foreach ($byPerson as $r) { $maxPerson = max($maxPerson, (int)$r['cnt']); }
$maxMonth = 1;
foreach ($byMonth as $r) { $maxMonth = max($maxMonth, (int)$r['cnt']); }
$maxYear = 1;
foreach ($byYear as $r) { $maxYear = max($maxYear, (int)$r['cnt']); }
$wd = static function (?string $d): string {
return ($d !== null && $d !== '') ? date('Y年n月j日', strtotime($d)) : '—';
};
?>
<div class="page-head">
<h1>观影统计</h1>
<span class="sub">一家人的观影概览</span>
<div class="actions">
<a class="btn btn-ghost btn-sm" href="<?= e(url('movies')) ?>">返回观影</a>
</div>
</div>
<div class="stats-grid">
<div class="stat-card"><div class="number"><?= (int)$ov['movies'] ?></div><div class="label">电影总数</div></div>
<div class="stat-card"><div class="number"><?= (int)$ov['cinemas'] ?></div><div class="label">影院数</div></div>
<div class="stat-card"><div class="number"><?= (int)$ov['persons'] ?></div><div class="label">观影人数</div></div>
<div class="stat-card"><div class="number"><?= (int)$ov['viewership'] ?></div><div class="label">总观影人次</div></div>
</div>
<?php if ($ov['first_date'] !== null && $ov['last_date'] !== null): ?>
<div class="card card-pad stats-span">
<span class="label">观影跨度</span>
<span class="value"><?= e($wd($ov['first_date'])) ?> ~ <?= e($wd($ov['last_date'])) ?></span>
</div>
<?php endif; ?>
<div class="chart-card">
<h3>按影院统计 <span class="hint">电影数量</span></h3>
<?php if ($byCinema): ?>
<div class="bar-chart">
<?php foreach ($byCinema as $r): $c = (string)($r['color'] ?: '#888888'); ?>
<div class="bar-item">
<span class="bar-label">
<span class="color-dot" style="background: <?= e($c) ?>"></span>
<?= e((string)$r['name']) ?><?php if (trim((string)$r['place']) !== ''): ?><span class="bar-sub"><?= e((string)$r['place']) ?></span><?php endif; ?>
</span>
<span class="bar-track"><span class="bar-fill" style="width: <?= e((string)$fmtPct((int)$r['cnt'], $maxCinema)) ?>%; background: <?= e($c) ?>"></span></span>
<span class="bar-count"><?= (int)$r['cnt'] ?></span>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p class="empty-hint">还没有影院数据。到「影院管理」加几家影院,记观影时就能选了。</p>
<?php endif; ?>
</div>
<div class="two-col">
<div class="chart-card">
<h3>按观影人统计 <span class="hint">参与场次</span></h3>
<?php if ($byPerson): ?>
<div class="bar-chart">
<?php foreach ($byPerson as $r): ?>
<div class="bar-item">
<span class="bar-label">
<img class="bar-avatar" src="<?= e(avatar_url($r['avatar'] ?? null)) ?>" alt="">
<?= e((string)$r['name']) ?>
</span>
<span class="bar-track"><span class="bar-fill" style="width: <?= e((string)$fmtPct((int)$r['cnt'], $maxPerson)) ?>%"></span></span>
<span class="bar-count"><?= (int)$r['cnt'] ?></span>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p class="empty-hint">还没有观影人数据。</p>
<?php endif; ?>
</div>
<div class="chart-card">
<h3>按年统计 <span class="hint">观影数量</span></h3>
<?php if ($byYear): ?>
<div class="bar-chart">
<?php foreach ($byYear as $r): ?>
<div class="bar-item">
<span class="bar-label"><?= e((string)$r['y']) ?> 年</span>
<span class="bar-track"><span class="bar-fill bar-fill-alt" style="width: <?= e((string)$fmtPct((int)$r['cnt'], $maxYear)) ?>%"></span></span>
<span class="bar-count"><?= (int)$r['cnt'] ?></span>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p class="empty-hint">还没有年份数据。</p>
<?php endif; ?>
</div>
</div>
<div class="chart-card">
<h3>月度趋势 <span class="hint">最近 12 个月</span></h3>
<?php if ($byMonth): ?>
<div class="bar-chart">
<?php foreach ($byMonth as $r): ?>
<div class="bar-item">
<span class="bar-label"><?= e((string)$r['ym']) ?></span>
<span class="bar-track"><span class="bar-fill bar-fill-green" style="width: <?= e((string)$fmtPct((int)$r['cnt'], $maxMonth)) ?>%"></span></span>
<span class="bar-count"><?= (int)$r['cnt'] ?></span>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p class="empty-hint">还没有月度数据。</p>
<?php endif; ?>
</div>
<div class="btn-row">
<a class="btn btn-ghost" href="<?= e(url('movies/report')) ?>">看年度报告</a>
<a class="btn btn-ghost" href="<?= e(url('movies/search')) ?>">高级搜索</a>
</div>