v63:卡片配色 / 日历本靠右 / 地图题图 / 设置页清理 / 公共标签颜色 / 令牌下线

- 卡片右上角按钮改用「来自哪个模块」的主题色:HomeCards 登记表新增 mod 字段
  与 module(),首页从登记表取色(原来是模板里硬编码 in_array(['agenda','tasks'])
  白名单,新加带按钮的卡片会默默变灰)
- 首页日程把日历本放到色块右侧:.ag-ev 改弹性行,「时间+标题」包进
  .ag-ev-main(flex:1 1 auto; min-width:0),日历本作右侧兄弟。
  原来 display:block 会把日历本顶到第二行,色块一矮就完全看不见
- 旅程地图显示题图:服务端把 media_url(cover_key) 塞进地图数据,
  地图卡片左侧 40x40 题图(object-fit:cover),没有题图不留灰框
- 系统设置第一块改叫「系统标语」;删掉「首页上那一行小字」提示、
  「不是全家的统一口号」说明、「上次结构升级有步骤失败了」整框;
  运行环境删「排错时看这一块」与「亲属连线」那一行(功能与数据未动)
- 公共标签:说明文字全删;列表与编辑合并成一行(色块+名字+N人+箭头+编辑);
  person_groups 加 color 列(Schema addColumn,老库可升);点「编辑」弹框
  改名字与颜色、点「完成」提交;下面那个独立编辑列表撤掉。删除挪进弹框
- 个人设置删掉「手动签发接口令牌」整块;settings/api_token 路由保留但挡下提交
  (只删界面会留下知道地址就能用的后门);令牌只由登录后自动签发,
  设备管理卡保留。apiTokens/apiPlain/apiLabel/apiBase 四个变量一并撤掉
This commit is contained in:
2026-10-19 17:27:37 +08:00
parent 2e40133d8f
commit 2f7dc6ca23
11 changed files with 450 additions and 189 deletions
+69 -10
View File
@@ -24,6 +24,44 @@ final class PersonGroup
/** 公共分组(管理员维护,全家共用)的 owner_id */
public const PUBLIC_OWNER = 0;
/**
* 🔴 v63(用户 2026-10-19 第 5 条):「每个标签增加一个颜色。」
*
* 色板 = 标签可以挑的颜色。与「模块主题色」(`Module::PRESET_COLORS`)**同一条
* 审美规矩**:中低饱和、中低明度 —— 标签是纸面上的一行字,颜色太艳会盖过内容。
*
* ⚠️ 挑色是"选"不是"填":界面上只给这些色块(外加一个系统取色器兜底),
* 服务端一律按白名单校验(`sanitizeColor`),别让手改表单塞进 CSS。
*/
public const SWATCHES = [
'#A84A28', '#8A6B4B', '#4A7C82', '#7A4E8C', '#3F7D5A', '#B0763A',
'#5566A8', '#A8574F', '#5C8A48', '#6B5B95', '#9A5C7A', '#3D6B8E',
];
/** 颜色白名单:只接受 #rrggbb,其余回退到 $fallback(同 `Nav::safeColor` 的做法) */
public static function sanitizeColor(string $c, string $fallback): string
{
return preg_match('/^#[0-9a-fA-F]{6}$/', trim($c)) ? strtolower(trim($c)) : $fallback;
}
/**
* 这个标签"实际显示成什么颜色"。
*
* ⚠️ 没选过颜色(`color` 为 NULL/空)时**按 id 稳定地**从色板里挑一个 ——
* 不能用"列表里的第几个"来挑:那样改一次顺序,满屏标签颜色全跳一遍,
* 用户会以为自己点错了。用 id 取模则"这个标签一辈子是这个色",直到用户改它。
*/
public static function swatch(array $g): string
{
$raw = trim((string)($g['color'] ?? ''));
if ($raw !== '') {
return self::sanitizeColor($raw, self::SWATCHES[0]);
}
$id = (int)($g['id'] ?? 0);
$n = count(self::SWATCHES);
return self::SWATCHES[$id % $n];
}
/** 预置分组的来源在 Schema::DEFAULT_GROUPS(安装与升级共用一份,避免两处漂移) */
/**
@@ -185,7 +223,8 @@ final class PersonGroup
* @param int $ownerId 0=公共(只有管理员能建,调用方把关);否则=建给自己的
* @return array{ok:bool,id?:int,error?:string}
*/
public static function add(int $familyId, string $name, int $ownerId = self::PUBLIC_OWNER): array
public static function add(int $familyId, string $name, int $ownerId = self::PUBLIC_OWNER,
string $color = ''): array
{
$ownerId = max(0, $ownerId);
$name = trim($name);
@@ -208,19 +247,28 @@ final class PersonGroup
);
$st2->execute([$familyId, $ownerId]);
$max = (int)$st2->fetchColumn();
// 🔴 v63:颜色可空 —— 空串存 NULL(= "还没选过",见 swatch() 的说明)。
// 传了颜色就按白名单归一(非法值退回空 = 也当没选过,不写脏数据进库)。
$col = trim($color) === '' ? null : self::sanitizeColor($color, '');
$db->prepare(
'INSERT INTO person_groups (family_id, owner_id, name, position, is_builtin)
VALUES (?, ?, ?, ?, 0)'
)->execute([$familyId, $ownerId, $name, max($max + 10, 80)]);
'INSERT INTO person_groups (family_id, owner_id, name, color, position, is_builtin)
VALUES (?, ?, ?, ?, ?, 0)'
)->execute([$familyId, $ownerId, $name, $col, max($max + 10, 80)]);
return ['ok' => true, 'id' => (int)$db->lastInsertId()];
}
/**
* 改名。$ownerId 用来确认"你改的是你自己那一套里的分组"——
* 公共分组只有管理员能改(调用方把关),别人的私自分组连管理员也不该动。
*
* 🔴 v63:顺手支持改颜色(`$color === null` = **不动颜色**)。
* ⚠️ 用 null 而不是空串表示"不改":空串是"清掉颜色"这个合法意图
* (退回按 id 取默认色),两者必须能区分 —— 这是"部分更新"接口
* 最容易写错的一处(传不传那个字段,语义完全不同)。
* @return array{ok:bool,error?:string}
*/
public static function rename(int $familyId, int $id, string $name, int $ownerId): array
public static function rename(int $familyId, int $id, string $name, int $ownerId,
?string $color = null): array
{
$ownerId = max(0, $ownerId);
$name = trim($name);
@@ -243,12 +291,23 @@ final class PersonGroup
if ((int)$st->fetchColumn() > 0) {
return ['ok' => false, 'error' => '已经有叫「' . $name . '」的分组了。'];
}
$up = $db->prepare(
'UPDATE person_groups SET name = ? WHERE family_id = ? AND owner_id = ? AND id = ?'
);
$up->execute([$name, $familyId, $ownerId, $id]);
if ($color === null) {
$up = $db->prepare(
'UPDATE person_groups SET name = ? WHERE family_id = ? AND owner_id = ? AND id = ?'
);
$up->execute([$name, $familyId, $ownerId, $id]);
} else {
$col = trim($color) === '' ? null : self::sanitizeColor($color, '');
$up = $db->prepare(
'UPDATE person_groups SET name = ?, color = ? WHERE family_id = ? AND owner_id = ? AND id = ?'
);
$up->execute([$name, $col, $familyId, $ownerId, $id]);
}
if ($up->rowCount() === 0) {
return ['ok' => false, 'error' => '找不到这个分组。'];
// ⚠️ rowCount()===0 有两种可能:找不到(不可能,上面刚查过)与
// **改的值和原来一模一样**(MySQL 对无变化的 UPDATE 不计数)。
// 后者不是错误 —— 用户点了「完成」但什么都没改,不该报"找不到"。
return ['ok' => true];
}
return ['ok' => true];
}