161 lines
8.9 KiB
PHP
161 lines
8.9 KiB
PHP
<?php
|
||||
|
|
/**
|
|||
|
|
* 纪念日「分类设置」页(v62 用户 2026-10-18 第 2 条)。
|
|||
|
|
*
|
|||
|
|
* 用户原话:「设置按钮,点击后弹菜单,第一个菜单是分类设置,进去之后可以设置分类,
|
|||
|
|
* 每一类纪念日都有不同的图标,现在的分类,你自己把图标补全。如果是用的
|
|||
|
|
* fontawesome,那么如果用户添加了自己的分类,就让用户自己选。
|
|||
|
|
* 在列表中、日历中,都要根据分类,显示不同的图标。」
|
|||
|
|
*
|
|||
|
|
* ⇒ 这一页做三件事(都在**一张表单**里,改完点一次保存):
|
|||
|
|
* ① 改名(已用它的纪念日**不会**跟着改名 —— 页面底下有一句提示,别让用户以为丢了);
|
|||
|
|
* ② 换图标(下拉里是 `AnnivIcon` 那一套,选完左边立刻能预览);
|
|||
|
|
* ③ 调顺序 / 增 / 删。
|
|||
|
|
*
|
|||
|
|
* ⚠️ 三个动作(保存 / 删除 / 上移下移 / 新增)**共用同一个表单**,靠 submit 按钮的
|
|||
|
|
* `name` 区分(`del` / `move` / `add`)。为什么不用"每行一个小表单":
|
|||
|
|
* 一行一个小表单的话,用户改了 A 行的图标、又点了 B 行的「删」,
|
|||
|
|
* A 行那次修改会被**静默丢弃**(浏览器只提交被点的那张表单)。
|
|||
|
|
* 一张表单 = 一次提交 = 屏幕上看到的改动全部生效,没有"改了一半"的中间态。
|
|||
|
|
*
|
|||
|
|
* ⚠️ 分类是**家庭级**的(settings.anniv_kinds):全家共用一套叫法。
|
|||
|
|
* 所以这一页对**所有能写的成员**开放(不是只有管理员),但改的是同一份。
|
|||
|
|
*
|
|||
|
|
* @param array $kindsFull array<int,array{name:string,icon:string}>
|
|||
|
|
* @param array $kindCounts 分类名 => 用了几条
|
|||
|
|
*/
|
|||
|
|
$kindList = (array)($kindsFull ?? \App\Anniversary::kindsFull((int)\App\Auth::familyId()));
|
|||
|
|
$kindCount = (array)($kindCounts ?? []);
|
|||
|
|
$canEdit = \App\Auth::canWrite();
|
|||
|
|
$n = count($kindList);
|
|||
|
|
?>
|
|||
|
|
<div class="page-head">
|
|||
|
|
<h1>纪念日分类</h1>
|
|||
|
|
<span class="sub">每个分类一个图标 · 全家共用一套</span>
|
|||
|
|
<div class="actions">
|
|||
|
|
<a class="btn btn-ghost" href="<?= e(url('days')) ?>">返回纪念日</a>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card card-pad">
|
|||
|
|
<h2 class="cinema-h2">
|
|||
|
|
分类与图标
|
|||
|
|
<span class="hint">列表、日历、卡片上都按这里的图标显示</span>
|
|||
|
|
</h2>
|
|||
|
|
|
|||
|
|
<?php if (!$canEdit): ?>
|
|||
|
|
<p class="empty-hint">你没有编辑权限,这里只显示现有分类。</p>
|
|||
|
|
<ul class="kind-list">
|
|||
|
|
<?php foreach ($kindList as $k): ?>
|
|||
|
|
<li class="kind-row is-ro">
|
|||
|
|
<span class="kind-prev"><?= \App\AnnivIcon::svg((string)$k['icon'], 16) ?></span>
|
|||
|
|
<span class="kind-name-ro"><?= e((string)$k['name']) ?></span>
|
|||
|
|
<span class="kind-use"><?= (int)($kindCount[(string)$k['name']] ?? 0) ?> 条</span>
|
|||
|
|
</li>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</ul>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<form method="post" action="<?= e(url('days/settings')) ?>" id="kindForm">
|
|||
|
|
<?= csrf_field() ?>
|
|||
|
|
<ol class="kind-list" id="kindList">
|
|||
|
|
<?php foreach ($kindList as $i => $k): ?>
|
|||
|
|
<li class="kind-row">
|
|||
|
|
<span class="kind-move">
|
|||
|
|
<button class="btn btn-ghost btn-sm" type="submit" name="move" value="<?= (int)$i ?>:up"
|
|||
|
|
title="上移" <?= $i === 0 ? 'disabled' : '' ?>>▲</button>
|
|||
|
|
<button class="btn btn-ghost btn-sm" type="submit" name="move" value="<?= (int)$i ?>:down"
|
|||
|
|
title="下移" <?= $i === $n - 1 ? 'disabled' : '' ?>>▼</button>
|
|||
|
|
</span>
|
|||
|
|
<?php /* 左边这个 svg 是"当前图标"的预览;右边下拉一动,JS 立刻换成新的
|
|||
|
|
(图标库见页面底部的 .kind-icon-bank,不在 JS 里手抄一遍 path)。 */ ?>
|
|||
|
|
<span class="kind-prev" data-kind-prev="<?= (int)$i ?>"><?= \App\AnnivIcon::svg((string)$k['icon'], 16) ?></span>
|
|||
|
|
<select class="select kind-sel" name="k_icon[<?= (int)$i ?>]"
|
|||
|
|
aria-label="<?= e((string)$k['name']) ?>的图标" data-kind-sel="<?= (int)$i ?>">
|
|||
|
|
<?php foreach (\App\AnnivIcon::choices() as $icKey => $icLabel): ?>
|
|||
|
|
<option value="<?= e($icKey) ?>" <?= (string)$k['icon'] === $icKey ? 'selected' : '' ?>><?= e($icLabel) ?></option>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</select>
|
|||
|
|
<input class="input kind-name-in" name="k_name[<?= (int)$i ?>]" maxlength="20"
|
|||
|
|
value="<?= e((string)$k['name']) ?>" aria-label="分类名">
|
|||
|
|
<span class="kind-use"><?= (int)($kindCount[(string)$k['name']] ?? 0) ?> 条</span>
|
|||
|
|
<?php /* ⚠️ confirm 里**不要**拼分类名:e() 把单引号转成 `'`,
|
|||
|
|
而浏览器会先解实体再交给 JS ⇒ 名字里带引号时这段脚本直接语法错,
|
|||
|
|
「删除」按钮点了没反应(本项目踩过同型的坑)。用固定文案最稳。 */ ?>
|
|||
|
|
<button class="btn btn-warn btn-sm" type="submit" name="del" value="<?= (int)$i ?>"
|
|||
|
|
onclick="return confirm('删掉这个分类?已用它的纪念日不变,只是新建时不再出现。')">删除</button>
|
|||
|
|
</li>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</ol>
|
|||
|
|
|
|||
|
|
<?php if (!$kindList): ?>
|
|||
|
|
<p class="empty-hint" style="margin:8px 0 14px">还没有分类。下面加一个。</p>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<div class="kind-add">
|
|||
|
|
<span class="kind-add-lb">加一个分类</span>
|
|||
|
|
<select class="select" name="new_icon" aria-label="新分类的图标">
|
|||
|
|
<?php foreach (\App\AnnivIcon::choices() as $icKey => $icLabel): ?>
|
|||
|
|
<option value="<?= e($icKey) ?>" <?= $icKey === 'star' ? 'selected' : '' ?>><?= e($icLabel) ?></option>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</select>
|
|||
|
|
<input class="input" name="new_name" maxlength="20" placeholder="如:相识纪念日">
|
|||
|
|
<button class="btn btn-sm" type="submit" name="add" value="1">加分类</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="btn-row" style="margin-top:16px">
|
|||
|
|
<button class="btn" type="submit" name="save" value="1">保存分类</button>
|
|||
|
|
<?php /* 动过图标/名字但还没保存时 JS 会把这句显出来(见页面底部脚本):
|
|||
|
|
这一页**不自动保存**,不提示的话用户挑完图标直接返回,改动就静默丢了。 */ ?>
|
|||
|
|
<span class="small kind-dirty" id="kindDirty" hidden>改过了,记得点左边的「保存分类」</span>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<p class="small muted" style="margin:14px 0 0">
|
|||
|
|
改名<b>不会</b>改掉已经用过旧名字的那些纪念日(它们仍记着旧分类);
|
|||
|
|
删掉一个分类也只是"新建时不再出现",历史记录照旧。
|
|||
|
|
<br>分类加得越多,新建时的下拉越长 —— 建议按"这件事是什么性质"来分,不要按人来分(人有关联人那一列)。
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<?php /* 图标库:全部图标在这里渲染一次,JS 只做"挑一个塞进预览",
|
|||
|
|
避免在 JS 里再抄一份 SVG path(两份迟早不一致)。 */ ?>
|
|||
|
|
<div class="kind-icon-bank" hidden aria-hidden="true">
|
|||
|
|
<?php foreach (array_keys(\App\AnnivIcon::LIST) as $icKey): ?>
|
|||
|
|
<span data-ic="<?= e($icKey) ?>"><?= \App\AnnivIcon::svg($icKey, 16) ?></span>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
<script>
|
|||
|
|
(function () {
|
|||
|
|
var bank = document.querySelector('.kind-icon-bank');
|
|||
|
|
if (!bank) { return; }
|
|||
|
|
/* 改完图标/名字之后**一定要点保存**(这一页不自动存)。
|
|||
|
|
不提示的话,用户挑完图标顺手点「返回纪念日」,改动就静默丢了 ——
|
|||
|
|
所以只要动过,就把保存按钮标脏,并挂一句小字。 */
|
|||
|
|
var saveBtn = document.querySelector('button[name=save]');
|
|||
|
|
var dirty = document.getElementById('kindDirty');
|
|||
|
|
function markDirty() {
|
|||
|
|
if (saveBtn) { saveBtn.classList.add('is-dirty'); }
|
|||
|
|
if (dirty) { dirty.hidden = false; }
|
|||
|
|
}
|
|||
|
|
Array.prototype.forEach.call(document.querySelectorAll('[data-kind-sel]'), function (sel) {
|
|||
|
|
sel.addEventListener('change', function () {
|
|||
|
|
var i = sel.getAttribute('data-kind-sel');
|
|||
|
|
var prev = document.querySelector('[data-kind-prev="' + i + '"]');
|
|||
|
|
var src = bank.querySelector('[data-ic="' + sel.value + '"]');
|
|||
|
|
if (prev && src) { prev.innerHTML = src.innerHTML; }
|
|||
|
|
markDirty();
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
Array.prototype.forEach.call(document.querySelectorAll('#kindList input.kind-name-in'),
|
|||
|
|
function (inp) { inp.addEventListener('input', markDirty); });
|
|||
|
|
/* 任何一次提交(保存 / 加分类 / 删除 / 上移下移)都算"处理过了",
|
|||
|
|
标志就地清掉 —— 免得提交完回来还挂着红点。 */
|
|||
|
|
var f = document.getElementById('kindForm');
|
|||
|
|
if (f) { f.addEventListener('submit', function () {
|
|||
|
|
if (saveBtn) { saveBtn.classList.remove('is-dirty'); }
|
|||
|
|
if (dirty) { dirty.hidden = true; }
|
|||
|
|
}); }
|
|||
|
|
})();
|
|||
|
|
</script>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|