网页端下拉框改成自绘浮层(照 FeedLite 的做法)(#85)

用户 2026-11-05:「网页版的下拉框也太丑了。你参考一下那个反馈系统里面的
下拉框到底是怎么做的?按它的做法做,它也有主题颜色,我们这个也有主题颜色,
就按照一样的做法去做。」

看了 FeedLite(~/code/FeedLite)的实现,关键结论:
  **原生 <select> 收起时能改样式,但展开的选项列表由浏览器绘制,CSS 完全改不动**
  (只能改 option 底色,各平台还不统一)⇒ 想好看只有自绘这条路。

照它的做法移植:

① CSS(assets/css/app.css)
   .sel-native  原生 select 视觉隐藏(**不能 display:none**,那样不参与提交)
   .sel         外层 inline-flex
   .sel-btn     触发按钮 —— 外观与普通输入框完全一致(40 高、同圆角同边框)
   .sel-caret   自绘箭头(CSS mask + 内联 SVG),**打开时转 180° 并变主题色**
   .sel-panel   浮层,position:fixed 挂 body
   .sel-item    列表项:34 高、圆角 7
   .sel-item.on 选中项:深一档主题底色 + 主题色文字 + 右侧对勾

② 主题色三档浅色(照 FeedLite 的比例)
   helpers.php 新增 fb_color_tint()(把 hex 往白色混)。
   layout.php 在 <body> 的 style 里输出:
     --brand-tint-1(混 91% 白)→ hover
     --brand-tint-2(混 80% 白)→ 选中项
     --brand-tint-3(混 72% 白)→ 选中项 hover
   ⚠️ 在 PHP 里算好,**不用 CSS 的 color-mix()** —— FeedLite 实测过:
      `color-mix(in srgb, var(--brand) 9%, #fff)` 在 Chrome 里解析成透明。

③ JS(assets/js/app.js 新增 fbBindSelects())
   遍历所有 select,在旁边生成按钮 + 浮层,原生 select 保留参与提交。
   · 派发原生 change → 兼容 onchange="this.form.submit()" 的内联写法
   · 浮层 fixed 定位(面板/浮层里大量 overflow:hidden,放旁边会被裁)
   · 下方空间不足且上方更宽裕时**向上翻**
   · 键盘:↑↓ / Home / End / Enter / Esc
   · 浮层 minWidth 跟随触发按钮 → "和输入框一样宽"
   · data-sel-enhanced 幂等标记

④ AJAX 路径也要增强
   task_panel.paint() 与 entry_popup.loadPanel() 里,紧跟 fbBootAntd() 之后
   调一次 window.fbBindSelects(document) —— 否则动态插入的 select 是裸的。

⚠️ app.js 整个是 IIFE 包裹的(结尾 `})();`),新函数必须插在闭包内部;
   直接追加到结尾会跑到全局作用域。

实测(Playwright + 站点真实 app.js/css):
  增强:data-sel-enhanced=1、原生 select 已隐藏、按钮文字「工作相关」✅
  尺寸:按钮 648 x 40,行宽 648 → 占满整行 ✅
  浮层:宽 648(与按钮同宽)✅ 6 项、选中项有对勾 ✅
  打开态:底色 rgb(250,242,238)=tint-1、文字 rgb(168,74,40)=brand、
          箭头 rotate 180° ✅
  选「个人事务」→ select.value=3、按钮文字同步、浮层关闭 ✅
This commit is contained in:
2026-11-05 20:23:16 +08:00
parent 47a0027ef7
commit 69c47c3c43
6 changed files with 414 additions and 1 deletions
+34
View File
@@ -426,6 +426,40 @@ function days_until_birthday(?string $birthday): ?int
* @param array $extra 额外要显示的行 [[标签, 值], …]
* @return string 可直接放进 data-fb-entry 的 JSON 字符串(已转义)
*/
/**
* 把 hex 主题色往白色方向混,得到一档浅色。
*
* 🔴 #85(用户 2026-11-05):「参考一下那个反馈系统里面的下拉框到底是怎么做的…
* 它也有主题颜色,我们这个也有主题颜色,就按照一样的做法去做。」
*
* FeedLite 那边用三档浅色区分下拉项的三态:
* tint-1(混 91% 白)→ hover
* tint-2(混 80% 白)→ 选中项(比 hover 深一档,一眼分开)
* tint-3(混 72% 白)→ 选中项 hover
*
* ⚠️ 在 PHP 里算好再输出,**不用 CSS 的 `color-mix()`** ——
* FeedLite 实测过:`color-mix(in srgb, var(--brand) 9%, #fff)`
* 在 Chrome 里解析成透明(规则匹配上了但不生效)。
* ⚠️ `$ratio` 是"往白里混多少",不是"保留多少":
* 0.91 = 91% 白 + 9% 原色(很浅),0.0 = 原色本身。
*/
function fb_color_tint(string $hex, float $ratio): string
{
$hex = trim($hex);
if (preg_match('/^#?([0-9a-fA-F]{6})$/', $hex, $m) !== 1) {
return ''; // 不是 6 位 hex(比如 rgba()/色名)就放弃,调用方退回默认
}
$h = $m[1];
$r = (int)hexdec(substr($h, 0, 2));
$g = (int)hexdec(substr($h, 2, 2));
$b = (int)hexdec(substr($h, 4, 2));
$ratio = max(0.0, min(1.0, $ratio));
return sprintf('#%02x%02x%02x',
(int)round($r + (255 - $r) * $ratio),
(int)round($g + (255 - $g) * $ratio),
(int)round($b + (255 - $b) * $ratio));
}
function fb_entry_json(array $ev, string $detail = '', string $edit = '', array $extra = []): string
{
$date = (string)($ev['date'] ?? '');