主题色改为自绘取色面板(任意取色);图标上传加即时预览与状态提示

一、颜色不够丰富 → 点击弹取色面板
  用户:「还是不行,颜色还是不够丰富。让用户选择吧:点击之后弹出一个颜色选择器。」
  原来只有一个原生 <input type=color> 小方块,用户不知道能点,
  而且系统面板各浏览器样式不一、只能取纯色。
  改成自绘面板:
    色相/饱和度渐变板(Canvas)+ 明度滑块 + 十六进制输入 + 确定
    12 个预设圆点降级为快捷方式,真正的能力是任意取色
  交互:点色块弹出(下方空间不够自动翻上方)、板内拖动实时更新、
        十六进制可手输、点确定或面板外关闭、Esc 关闭

  ⚠️ 光标定位踩了两个坑,都写进注释了:
    ① 不能用 canvas.offsetLeft(相对 offsetParent,受父级 padding 影响,
       实测算出 236px 超出画布宽度 226px);要用两个 getBoundingClientRect 相减
    ② canvas 显示尺寸受 CSS width:100% 影响,≠ 内部像素 220×150,
       必须用 rect.width 而不是 canvas.width

二、图标上传没反馈
  用户:「上传的图标没有显示,也没有上传成功、失败之类的提示信息。」
  后端逻辑完整(格式/大小/尺寸/存储模式都校验了),问题在**前端零反馈**:
  裸 <input type=file>,选完文件什么都不发生,而提示条在页面顶部容易被错过。
  加了 bindIconUpload():
    ① 选中文件立刻 FileReader 本地预览,不等提交
    ② 显示文件名 + 尺寸 + 大小,让用户确认选对了
    ③ 选之前就拦掉明显不合格的(格式/超 2MB/小于 32×32/空文件)
    ④ 非正方形给出警告(显示时会被裁切)
  ⚠️ 前端校验只是体验优化,绝不能当安全边界 ——
     真正的校验仍在 PHP 的 fb_icon_store_upload() 里。

验证(用独立测试页 + 浏览器实测,不登录后台、不动数据):
  面板初始隐藏 → 点击后 248×246 出现
  Canvas 四角像素分别为灰白/蓝/黑 → 渐变确实画出来了
  板上取色 → 隐藏字段 #2178d9 同步到按钮文字/十六进制框/预览色
  点预设 → #2B822E;手输 → #a020f0;色相滑条 120° → #20f020
  光标中心 (238,54) 落在画布区域 (11..237, 11..161) 内
  4 个路由 200
This commit is contained in:
2026-11-04 18:52:45 +08:00
parent a00e2221c4
commit ad1b0eedf4
4 changed files with 394 additions and 27 deletions
+29 -5
View File
@@ -540,13 +540,25 @@ function fb_admin_taxonomy_page($pdo, $editSw, $editCat) {
$sugColor = $curColor !== '' ? $curColor : fb_color_default_for($esw ? (int)$esw['id'] : 0);
/* 🔴 必须用 fb_color_palette(),不要再在这里写死一份数组。
* 以前这里硬编码了另外 8 个色值,和 util.php 的调色板完全不同步 ——
* 结果后台只能选那 8 个,用户在后台挑不到新颜色,
* 而他从没配置过的软件却会被分到「调色板里另一批」颜色。 */
* 结果后台只能选那 8 个,用户在后台挑不到新颜色。 */
$presets = fb_color_palette();
/* 颜色选择:**点色块弹出取色面板**(色相/饱和度渐变 + 明度条 + 十六进制),
* 而不是只给一排预设圆点。
* 用户 2026-11-04:「还是不够丰富,让用户自己选:点击之后弹出一个颜色选择器。」
* ⇒ 预设只是快捷方式,真正要给的是**任意取色**能力。
* 原生 <input type="color"> 只有一个小方块,用户不知道能点,
* 而且它的系统面板不在我们控制内(各浏览器样式不一)。
* 这里自带一个面板:Canvas 渐变 + 拖动,谁都看得懂怎么用。 */
echo ' <div class="field"><span>主题色<i class="hint">前台进这个软件时整体配色跟着变,用来区分不同软件</i></span>';
echo ' <div class="color-row">';
echo ' <input type="color" name="color" value="' . e($sugColor) . '" class="color-input">';
echo ' <div class="color-presets">';
echo ' <div class="color-row" data-colorpicker>';
echo ' <button type="button" class="color-trigger" data-color-trigger'
. ' style="--cur:' . e($sugColor) . '" title="点击选择颜色">'
. ' <span class="color-trigger-chip"></span>'
. ' <span class="color-trigger-hex">' . e(strtoupper($sugColor)) . '</span>'
. ' </button>';
echo ' <input type="hidden" name="color" value="' . e($sugColor) . '" data-color-value>';
// 预设作为快捷色(点了就直接生效),保留但不占主位
echo ' <div class="color-presets" aria-label="常用颜色">';
foreach ($presets as $pc) {
$on = strcasecmp($pc, $sugColor) === 0 ? ' on' : '';
echo '<button type="button" class="color-preset' . $on . '" data-color="' . e($pc)
@@ -554,6 +566,18 @@ function fb_admin_taxonomy_page($pdo, $editSw, $editCat) {
}
echo ' </div>';
echo ' <button type="button" class="btn sm color-reset">用默认</button>';
/* 取色面板:默认隐藏,点色块才出现。位置由 JS 定位到触发按钮下方。 */
echo ' <div class="cp" data-color-panel hidden>';
echo ' <canvas class="cp-sv" width="220" height="150" data-cp-sv></canvas>';
echo ' <div class="cp-cursor" data-cp-cursor></div>';
echo ' <div class="cp-hue"><input type="range" min="0" max="360" value="0" data-cp-hue aria-label="色相"></div>';
echo ' <div class="cp-foot">';
echo ' <span class="cp-preview" data-cp-prev></span>';
echo ' <input type="text" class="cp-hex" maxlength="7" value="' . e(strtoupper($sugColor)) . '" data-cp-hex aria-label="十六进制颜色值">';
echo ' <button type="button" class="btn sm cp-ok" data-cp-ok>确定</button>';
echo ' </div>';
echo ' </div>';
echo ' </div>';
echo ' </div>';
if ($esw) {
+1 -1
View File
@@ -8,7 +8,7 @@ if (!defined('FB_ROOT')) {
define('FB_ROOT', dirname(__DIR__));
// 静态资源版本号:改 assets/ 下的 css/js 后把这个数字 +1,
// 让浏览器与 CDN 缓存失效。全站只此一处,别再往页面里写死数字。
if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 39); }
if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 41); }
}
/* ------------------------------------------------------------------