主题色改为自绘取色面板(任意取色);图标上传加即时预览与状态提示
一、颜色不够丰富 → 点击弹取色面板
用户:「还是不行,颜色还是不够丰富。让用户选择吧:点击之后弹出一个颜色选择器。」
原来只有一个原生 <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:
+74
-1
@@ -363,6 +363,14 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
|
||||
}
|
||||
.icon-up-main{flex:1; min-width:220px; display:flex; flex-direction:column}
|
||||
.icon-up-main input[type=file]{font-size:13.5px; max-width:100%}
|
||||
/* 上传状态提示:选中文件后立刻告诉用户「选到了什么、合不合格」。
|
||||
* 用户 2026-11-04:「上传的图标没有显示,也没有成功/失败提示。」 */
|
||||
.icon-up-tip{
|
||||
display:block; margin-top:7px; font-size:12.5px; line-height:1.6;
|
||||
color:var(--muted); word-break:break-all;
|
||||
}
|
||||
.icon-up-tip.is-ok{color:var(--ok, #16794a)}
|
||||
.icon-up-tip.is-err{color:var(--danger, #c0392b); font-weight:600}
|
||||
/* chip 里也不用白底反色了 —— 图标本来就没有底色 */
|
||||
.chip.on .sw-badge-nm{color:#fff}
|
||||
.chip .sw-badge-ic{background:none !important; color:inherit !important; box-shadow:none}
|
||||
@@ -373,11 +381,76 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
|
||||
flex:none; margin-right:2px; vertical-align:middle;
|
||||
box-shadow:0 0 0 1px rgba(0,0,0,.12);
|
||||
}
|
||||
.color-row{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
|
||||
.color-row{display:flex; align-items:center; gap:10px; flex-wrap:wrap; position:relative}
|
||||
.color-input{
|
||||
width:46px; height:34px; padding:2px; border:1px solid var(--line-strong);
|
||||
border-radius:9px; background:var(--card); cursor:pointer; flex:none;
|
||||
}
|
||||
|
||||
/* ---------- 自绘取色面板 ----------
|
||||
* 用户 2026-11-04:「点击之后弹出一个颜色选择器,让用户自己选。」
|
||||
* 点击色块 → 面板出现在其下方;下方空间不够时翻到上方(JS 加 .cp-up)。 */
|
||||
.color-trigger{
|
||||
display:inline-flex; align-items:center; gap:9px;
|
||||
padding:5px 12px 5px 6px; height:34px;
|
||||
border:1px solid var(--line-strong); border-radius:9px;
|
||||
background:var(--card); cursor:pointer; flex:none;
|
||||
font-size:13px; font-family:ui-monospace,Menlo,Consolas,monospace;
|
||||
color:var(--fg);
|
||||
}
|
||||
.color-trigger:hover{border-color:var(--brand)}
|
||||
.color-trigger-chip{
|
||||
width:22px; height:22px; border-radius:6px; flex:none;
|
||||
background:var(--cur, #0057B8);
|
||||
box-shadow:inset 0 0 0 1px rgba(0,0,0,.10);
|
||||
}
|
||||
.color-trigger-hex{font-weight:600; letter-spacing:.2px}
|
||||
|
||||
.cp{
|
||||
position:absolute; top:calc(100% + 8px); left:0; z-index:80;
|
||||
width:248px; padding:10px;
|
||||
background:var(--card); border:1px solid var(--line-strong);
|
||||
border-radius:12px; box-shadow:0 10px 30px rgba(16,24,40,.16);
|
||||
user-select:none;
|
||||
}
|
||||
.cp.cp-up{top:auto; bottom:calc(100% + 8px)}
|
||||
.cp-sv{
|
||||
display:block; width:100%; height:150px; border-radius:8px;
|
||||
cursor:crosshair;
|
||||
}
|
||||
.cp-cursor{
|
||||
position:absolute; width:14px; height:14px; margin:-7px 0 0 -7px;
|
||||
border:2px solid #fff; border-radius:50%; pointer-events:none;
|
||||
box-shadow:0 0 0 1px rgba(0,0,0,.35);
|
||||
/* 位置由 JS 写入 left/top,相对面板内边距(10px)计算,这里不加偏移 */
|
||||
}
|
||||
.cp-hue{margin:9px 0 8px}
|
||||
.cp-hue input[type=range]{
|
||||
-webkit-appearance:none; appearance:none; width:100%; height:12px; border-radius:6px;
|
||||
background:linear-gradient(to right,
|
||||
#f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||
outline:none; cursor:pointer;
|
||||
}
|
||||
.cp-hue input[type=range]::-webkit-slider-thumb{
|
||||
-webkit-appearance:none; width:16px; height:16px; border-radius:50%;
|
||||
background:#fff; border:2px solid #fff; box-shadow:0 0 0 1px rgba(0,0,0,.35); cursor:pointer;
|
||||
}
|
||||
.cp-hue input[type=range]::-moz-range-thumb{
|
||||
width:14px; height:14px; border-radius:50%; background:#fff;
|
||||
border:2px solid #fff; box-shadow:0 0 0 1px rgba(0,0,0,.35); cursor:pointer;
|
||||
}
|
||||
.cp-foot{display:flex; align-items:center; gap:7px}
|
||||
.cp-preview{
|
||||
width:26px; height:26px; border-radius:7px; flex:none;
|
||||
box-shadow:inset 0 0 0 1px rgba(0,0,0,.12);
|
||||
}
|
||||
.cp-hex{
|
||||
flex:1; min-width:0; height:28px; padding:0 8px;
|
||||
border:1px solid var(--line-strong); border-radius:7px;
|
||||
font-family:ui-monospace,Menlo,Consolas,monospace; font-size:12.5px;
|
||||
text-transform:uppercase; background:var(--card); color:var(--fg);
|
||||
}
|
||||
.cp-ok{flex:none; padding:5px 11px !important; font-size:12.5px !important}
|
||||
.color-presets{display:flex; gap:6px; flex-wrap:wrap}
|
||||
.color-preset{
|
||||
width:26px; height:26px; border-radius:50%; border:2px solid transparent;
|
||||
|
||||
Reference in New Issue
Block a user