下拉框改为自绘列表;修复新建软件缺排序字段

用户两个反馈:
① 「排序那个怎么删了?排序还得留着呀」
② 「所有下拉框拉下来之后全是灰色,不好看,重新设计」

一、排序字段(是误解,但确实有缺陷)

排序字段没有被删,一直在 if ($esw) 分支里渲染。
用户看到"没有",是因为他打开的是「添加软件」——
新建时 $esw 为空,整块不渲染,所以确实没有这一项。
这是真实缺陷:新建软件时无法指定排序。

修法:
- 把排序字段移出 if ($esw),新建/编辑都显示
- 新建时默认值 = 当前最大 sort_order + 1(排到最后)
- 后端 sw_add 改为优先用表单值(用 isset 判断,
  用户明确填 0 是合法值——排最前面,不能被当成"没填")

与「下载地址」不同:排序是真实会被用到的字段
(前台软件列表、顶栏三横线菜单都按它排),不能删。

二、下拉框重设计

为什么必须自绘:原生 select 的展开列表由浏览器绘制,
CSS 只能改 option 的底色,改不了圆角/行高/hover 高亮,
各平台样式还不一样(Windows 是灰方框)。用户说的"全是灰色"就是这个。

但不重写 HTML:全站 16 个 select,逐个改容易漏,
也会破坏 onchange 提交这类内联逻辑。

做法:原生 select 保留(视觉隐藏,仍参与提交与 change 事件),
JS 在旁生成按钮 + 浮层列表。
- 浮层用 position:fixed 挂 body,避免被 overflow:hidden 裁(踩过)
- 位置自动避让视口,下方不够翻上方
- 支持键盘:上下键 Home End Enter Esc
- 选中项带对勾
- 无 JS 时原生 select 仍可用

配色踩坑:选中项最初用 var(--brand-soft),而该变量会被软件主题色覆盖 ——
同步日历主题色 #ff3f44 时 --brand-soft 成了 #ffe8e9(粉红),
下拉里一片粉很难看。下拉是工具性控件,不该跟软件换色,
改用中性色:#eef1f5 选中 / #f1f3f6 hover,四个软件下完全一致。

验证(真实页面实测):
- 2 个 select 均被增强,原生已隐藏
- 面板白底 rgb(255,255,255)、圆角 11px、双层柔和阴影、7 个列表项
- 选中项 rgb(238,241,245)、hover rgb(241,243,246)、带对勾
- 四个软件(#0057b8/#ff3f44/#a556f7/#3571fb)下选中项底色完全相同,
  不再随主题色变粉
- 键盘下方向键三次 → 高亮「处理中」,底色正确
- 选择后 selectedIndex/value 同步、面板自动关闭、change 事件已触发
- 浮层 fixed 定位、完整可见、不被裁切
- 排序字段 3 处(软件/分类/状态)齐全;5 个路由 200
This commit is contained in:
2026-11-04 19:27:01 +08:00
parent 27614d8ca8
commit 9b971a5b92
4 changed files with 343 additions and 6 deletions
+105
View File
@@ -906,6 +906,111 @@ select:hover{
}
.num-wrap input[type=number]{padding-right:44px}
/* ---------- 自定义下拉(替换原生 select 的展开列表)----------
* 用户 2026-11-04:「下拉框拉下来之后怎么全是灰色的?不好看,重新设计。」
*
* 🔴 原生 <select> 的展开列表由浏览器绘制,CSS 改不动(只能改 option 底色),
* 各平台样式还不统一。所以必须自绘。
* 但**不重写 HTML**:原生 select 保留参与提交,JS 在旁边生成按钮+浮层。
* 这样全站 16 个 select 自动获得新样式,也不会破坏 onchange 内联逻辑。
*/
/* 原生 select:视觉隐藏但仍参与表单提交与事件(不能 display:none) */
.sel-native{
position:absolute !important;
width:1px !important; height:1px !important;
margin:0 !important; padding:0 !important; border:0 !important;
opacity:0 !important; pointer-events:none !important;
clip:rect(0 0 0 0) !important;
}
/* 外层:inline-flex 让它跟输入框一样参与行内布局 */
.sel{position:relative; display:inline-flex; vertical-align:middle; max-width:100%}
/* 触发按钮:外观与普通输入框完全一致(同高同圆角同边框) */
.sel-btn{
display:inline-flex; align-items:center; gap:10px;
height:var(--ctl-h); padding:0 12px;
border:var(--ctl-bd); border-radius:var(--ctl-r);
background:var(--card); color:var(--fg);
font-family:inherit; font-size:14.5px; line-height:1;
cursor:pointer; text-align:left;
box-shadow:var(--ctl-shadow);
transition:border-color .12s, box-shadow .12s;
max-width:100%; min-width:0;
}
.sel-btn:hover{border-color:#c3c9d1}
.sel-btn:focus-visible{outline:none; border-color:var(--brand); box-shadow:var(--ctl-shadow-focus)}
.sel-btn.is-open{border-color:var(--brand); box-shadow:var(--ctl-shadow-focus)}
.sel-txt{
flex:1; min-width:0;
overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* 未选择任何值时(如「全部状态」)文字用次要色,弱化存在感 */
.sel-btn.is-placeholder .sel-txt{color:var(--muted)}
/* 自绘箭头 */
.sel-caret{
width:11px; height:7px; flex:none;
background:currentColor; opacity:.55;
-webkit-mask:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7'%3E%3Cpath d='M1 1.2 5.5 5.8 10 1.2' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
mask:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7'%3E%3Cpath d='M1 1.2 5.5 5.8 10 1.2' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
transition:transform .16s;
}
.sel-btn.is-open .sel-caret{transform:rotate(180deg); opacity:.85}
/* 浮层:fixed 定位,不被任何 overflow 容器裁切。
* 用白底 + 细边框 + 柔和阴影,而不是浏览器默认的灰块。 */
.sel-panel{
position:fixed; z-index:9998;
max-height:min(320px, calc(100vh - 32px));
overflow-y:auto; overscroll-behavior:contain;
padding:5px;
background:var(--card);
border:1px solid var(--line-strong);
border-radius:11px;
box-shadow:0 10px 30px rgba(16,24,40,.16), 0 2px 6px rgba(16,24,40,.06);
animation:selIn .12s ease-out;
}
@keyframes selIn{ from{opacity:0; transform:translateY(-4px)} to{opacity:1; transform:none} }
.sel-panel.is-up{animation-name:selInUp}
@keyframes selInUp{ from{opacity:0; transform:translateY(4px)} to{opacity:1; transform:none} }
/* 列表项:圆角、行高一致、hover 有底色、选中项有对勾 */
.sel-item{
display:flex; align-items:center; gap:8px; width:100%;
padding:0 10px; height:34px;
border:0; border-radius:7px;
background:transparent; color:var(--fg);
font-family:inherit; font-size:14px; line-height:1;
cursor:pointer; text-align:left; white-space:nowrap;
transition:background .1s, color .1s;
-webkit-appearance:none; appearance:none;
}
.sel-item-txt{flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis}
/* hover / 键盘高亮:中性浅灰,**不要用 --brand-soft**。
* 🔴 踩过:最初选中项用 var(--brand-soft),但那个变量会被软件主题色覆盖 ——
* 同步日历是砖红色,--brand-soft 就成了 #ffe8e9(粉红),
* 下拉里一片粉,很难看。下拉是**工具性控件**,不该跟着软件换色,
* 统一用中性色才耐看。 */
.sel-item:hover, .sel-item.is-active{background:#f1f3f6; color:var(--fg)}
/* 选中项:浅灰蓝底 + 深色文字 + 右侧对勾(不依赖主题色) */
.sel-item.on{background:#eef1f5; color:var(--fg); font-weight:600}
.sel-item.on:hover, .sel-item.on.is-active{background:#e6eaf0; color:var(--fg)}
.sel-item-ck{
width:13px; height:10px; flex:none;
background:currentColor;
-webkit-mask:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='10' viewBox='0 0 13 10'%3E%3Cpath d='M1.5 5.2 4.8 8.4 11.5 1.6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
mask:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='10' viewBox='0 0 13 10'%3E%3Cpath d='M1.5 5.2 4.8 8.4 11.5 1.6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
}
/* 窄屏:让下拉至少占满可用宽度,别挤成一小坨 */
@media (max-width:640px){
.sel{flex:1 1 auto; min-width:0}
.sel-btn{width:100%}
}
/* ---------- 提交页容器 */
* 🔴 原来只有 max-width:640px,**没有居中** —— 在 880px 的内容区里,
* 表单贴左边、右侧空出 240px 白,看起来像"页面缺了一块"。