-- 分类图标:emoji → 图标名(2026-09-24) -- -- 背景: -- 分类图标原本是 emoji 存在 category.icon 里,但同一个 emoji 在安卓和 -- iOS 上长得不一样(谷歌版 vs 苹果版),跨端画风不统一。换成自绘图标集的 -- 名字之后,手机端和网页端用同一份 SVG,画风才真正一致。 -- -- 配套改动: -- App 端 lib/widgets/cute_icon.dart 的 resolveIconName() -- —— 认图标名,也认 emoji(迁移没跑或用户自己填 emoji 时兜底) -- 图标本体:assets/icons/*.svg,由 tools/make_icons.py 生成 -- -- ⚠️ 必须用 BINARY 比较(2026-09-24 实打实踩过的坑): -- category 表用的排序规则是 utf8mb4_unicode_ci,它**没有给 4 字节 emoji -- 定义权重**,于是所有 emoji 互相比较都相等。用简单 CASE -- (`CASE icon WHEN '🍔' THEN 'food' ...`)时,随便哪个 emoji 都会命中 -- 第一个分支,结果是整列被写成同一个图标(当时 15 行全变成了 'food')。 -- BINARY 强制按字节比较,才不会串。 -- -- 安全性: -- · 幂等:WHERE 里排除了已经是图标名的行,重复执行无副作用 -- · 不匹配的 emoji 原样保留,App 会走 emoji 映射或兜底到 other,不会变空白 -- -- 执行方式(在 231 上): -- docker exec -i mysql-8.0.30 mysql --default-character-set=utf8mb4 \ -- -uroot -p密码 accounting_db < migration_icons_to_names.sql UPDATE `category` SET `icon` = CASE -- 支出 WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '🍔' THEN 'food' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '🚗' THEN 'transport' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '🛍' THEN 'shopping' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '🎬' THEN 'entertainment' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '🏥' THEN 'medical' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '📚' THEN 'education' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '🏠' THEN 'housing' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '💡' THEN 'utilities' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '📱' THEN 'phone' -- 收入 WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '💰' THEN 'salary' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '🎁' THEN 'bonus' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '📈' THEN 'invest' WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '💼' THEN 'parttime' -- 收支共用的「其他」 WHEN BINARY REPLACE(`icon`, CONVERT(0xEFB88F USING utf8mb4), '') = BINARY '📦' THEN 'other' ELSE `icon` END -- 只处理「还不是图标名」的行:图标名只含 a-z / 0-9 / 连字符, -- 出现别的字符就说明还是 emoji WHERE `icon` IS NOT NULL AND `icon` REGEXP '[^a-zA-Z0-9_-]'; -- 核对:跑完 15 个分类的图标应该各不相同(两个「其他」都是 other) -- SELECT id, name, icon, type FROM category ORDER BY type, sort_order;