统一全部表单控件尺寸与外观;删除下载地址字段

用户:「输入框、按钮、下拉框特别丑,一点设计感都没有,高度太随意,
      全面检查一下。还有那个下载链接要它干什么?」

一、表单控件(这是主要工作量)
  原来样式散在 6 处(.field / .filter-row / .filters / .mail-test / .admin 各一套),
  padding 有 8/9/12px 三种,select 与 input 高度不等,并排放参差不齐。

  改成一套体系:
  - CSS 变量统一尺度:--ctl-h 38px / --ctl-h-sm 32px / --ctl-h-lg 42px,
    统一圆角 9px、统一左右内边距 12px、统一边框与阴影
  - 高度用「固定 height + line-height 居中」而不是 padding 撑
  - 🔴 选择器必须**宽松**(按 type 通吃,不用 .field 之类限定)。
     第一版限制了容器,导致后台零散布局里的裸 select/input 只有 19/21px,
     和旁边 38px 按钮并排,正是用户说的高度随意。
     放开后实测:19px → 38px,全部一致
  - 下拉框自绘箭头(内联 SVG),不再用各平台不一的原生箭头,右侧留 34px
  - 搜索框加左侧放大镜图标,并去掉 Chrome 自带清除按钮
  - 聚焦统一 brand 光晕,悬停边框加深
  - 按钮也改固定 height(原来 padding 撑出 38.3px,与输入框差 0.3px)
  - .btn.sm 必须显式改 height,否则 padding 被 height 吃掉、小按钮还是 38px

  文件选择按钮:原生控件重做成真按钮
    <label class=file-btn> 包住 input[type=file]
    input 用 clip 视觉隐藏(不能用 display:none,那样点不到也聚焦不了)
    按钮内显示「选择图片」+ 图标 + 选中后的文件名

二、删除「下载地址」字段
  它唯一用途:状态改为「已解决」时把邮件按钮换成「下载最新版本」。
  收益小、维护成本高(每个软件都要填一条 URL)。
  - 后台表单项删除
  - mailer 里对应的判断分支也删掉,按钮统一「查看详情」
  - ⚠️ 数据库 download_url 列**保留不删**:删列不可逆,万一要恢复还得迁一次

验证(用控件总览测试页 + 真实页面实测):
  10 类控件高度全部 38px(文本/数字/下拉/搜索/文件按钮/主按钮/小按钮…),
  文本域 110px,同一行垂直中心完全一致(935.3)
  裸 select/input 从 19/21px 修正到 38px
  下拉箭头 SVG 生效、右内边距 34px;搜索框放大镜生效、左内边距 36px
  真实三个页面(提交页/列表页/详情页)逐一检查:
    没有 checkbox/radio/file 被误改,所有可见控件高度收敛为 38(文本域 110)
  下载地址字段:admin.php 输出与源码中均无 download_url 表单项
  5 个路由 200
This commit is contained in:
2026-11-04 19:17:20 +08:00
parent a2a3306169
commit 27614d8ca8
5 changed files with 250 additions and 51 deletions
+19 -5
View File
@@ -993,21 +993,29 @@
var wrap = input.closest('.icon-up');
if (!wrap) { return; }
var prev = wrap.querySelector('.icon-prev');
// 按钮上的文件名(可选,新样式才有)
var nameEl = wrap.querySelector('[data-file-name]');
// 状态条:放在文件输入框下面,比页面顶部的提示显眼得多
/* 状态条插在**整个文件按钮之后**(不是 input 之后)。
* input 现在被包在 <label class="file-btn"> 里,如果插到 input.nextSibling
* 会跑到 label 内部,跟按钮文字挤在一起。 */
var btn = input.closest('.file-btn') || input;
var tip = document.createElement('span');
tip.className = 'icon-up-tip';
tip.setAttribute('role', 'status');
input.parentNode.insertBefore(tip, input.nextSibling);
if (btn.parentNode) { btn.parentNode.insertBefore(tip, btn.nextSibling); }
function setTip(msg, kind) {
tip.textContent = msg || '';
tip.className = 'icon-up-tip' + (kind ? ' is-' + kind : '');
}
function setName(txt) {
if (nameEl) { nameEl.textContent = txt || ''; }
}
input.addEventListener('change', function () {
var f = this.files && this.files[0];
if (!f) { setTip(''); return; }
if (!f) { setTip(''); setName(''); return; }
var KB = Math.round(f.size / 1024);
var sizeTxt = KB >= 1024 ? (KB / 1024).toFixed(1) + ' MB' : KB + ' KB';
@@ -1016,17 +1024,20 @@
var okType = /^image\/(png|jpeg|webp)$/.test(f.type);
if (!okType) {
setTip('✕ 只能选 PNG / JPG / WEBP 图片,当前是「' + (f.type || '未知类型') + '」', 'err');
setName('');
this.value = '';
return;
}
// ② 大小(后端限 2MB)
if (f.size > 2 * 1024 * 1024) {
setTip('✕ 图片 ' + sizeTxt + ',超过 2 MB 上限,请压缩后再传', 'err');
setName('');
this.value = '';
return;
}
if (f.size <= 0) {
setTip('✕ 这是个空文件', 'err');
setName('');
this.value = '';
return;
}
@@ -1038,6 +1049,7 @@
img.onload = function () {
if (img.width < 32 || img.height < 32) {
setTip('✕ 图片只有 ' + img.width + '×' + img.height + ',至少 32×32', 'err');
setName('');
input.value = '';
return;
}
@@ -1050,16 +1062,18 @@
prev.appendChild(el);
}
var warn = (img.width !== img.height) ? ' ⚠ 不是正方形,显示时会被裁切' : '';
setTip('✓ 已选择:' + f.name + '(' + img.width + '×' + img.height + ',' + sizeTxt + ')'
setName(f.name);
setTip('✓ ' + img.width + '×' + img.height + ',' + sizeTxt
+ warn + ' 保存后生效', 'ok');
};
img.onerror = function () {
setTip('✕ 这张图读不出来,可能已损坏', 'err');
setName('');
input.value = '';
};
img.src = ev.target.result;
};
fr.onerror = function () { setTip('✕ 读取文件失败', 'err'); };
fr.onerror = function () { setTip('✕ 读取文件失败', 'err'); setName(''); };
fr.readAsDataURL(f);
});
})(inputs[i]);
+197 -26
View File
@@ -6,6 +6,21 @@
--icon-mark:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAADsOAAA7DgHMtqGDAAADj0lEQVR4nO1b205TQRTdGB/UB78B0BdfRNA3NUT/QPHMHIgaP0BeMPqoog8mJqDyCfoBImJnDuCFyEWhIGiUegONimkFodYEaqpj9qEEwZbS0zOXXlaykqZNO2uvM9M9Z8/ZABkhysBy9gLhl4Dwh0B5CAj7CZQLo4iaCJtwNRLWDITVZI4tY+CMAOGvtQfnnSGwnWNuLFmBBnYAZU8NCMAvDoLlVGwseCtwAAgLGyDaZ7IZsJ3D6wdP+CEg7Jd+sdIYBytQm+bKOxVAecQAkfJngsV3rolelBXYms/EgdV/jJRTA0SpJeF1K1c/v1OdRwPYxFL8dtc+7WJ00Q5U4/S/rF2INrKLkNzeiqIkYfcBCHujXYg+hgAoixkgRBNZDHL5gU22IxraxsXdkYiYnlsUid9/hGzgGDhWRzAi6tvGXQ05mQAev1jZ2CtGp6JCN0Ymo64WpQZUNvaKSDQuTEE4GhcVXk0AD9PehCu/FsH3UW/LAbL8Aq55U2HfGJNvQOdoRJiKO8GwfAO+zi0KU/Hl+6J8AxIKUp1XoDbpBqTCp9kFsftsf/aDe2TVuX7xeXYhpRYtBrR0TikLfpmt9z6YY8DQu3mx5XiXsuC3nuh2054xBiBwe9rzYkZ0P5dLHGO9P2JtBpiCkgG0NANEQaTBbSe7xdWOyfxYAi2S0uD2Uz35YcCQpDRoXRvLDwNkpMEnb+c9bbu1GWAKSgbQ0gwQUpdAIsW69JoGNzc4oq71mW+32Epuh6dT7MNzTYNY2fUDeIss3YCOYMTXNIjV3NhCwhcDbg8pKInVpymKekmDj17O+hY8gl5XUBTdZDu+TVk/MayqLA7JgxE8jDAFqKX8tKKDEfhn7aaryqi+8p5PhWgOBiwvBzyMwHo8lqRVHY7iWO3DYXfNazscBQ88cyuUMcCmmyFlekC1Acgr7env8fEz1XpA+YDJ88WP31bq+vga39OhBbQMmtwG72p67BJf69IB2gY2haBbgG4C5T+0i9BFwqNQlI/JrnpclrIHxf6gZLN2Ifp4Hg2oKd4ZwKuSzRI8VHzBs1crDRPYWqZbkGragSNrWmb4oHZRysj6/u8lpKy8aJqmsC8yTc9grdtapl2kNMaBsoMZeged/QXbOIl9kRuC5fYQDhRQ8H3uEs8OArvJ6tztovYAckh1lB2FnGEHqt0GI9w6uoaY2GXCYkvt86iRXwDL2bOR0P4Cy8Hrhv+SooQAAAAASUVORK5CYII=");
--ok:#0f9d58; --ok-soft:#e9f7ef; --warn:#b7791f; --warn-soft:#fff8e6;
--danger:#d93025; --danger-soft:#fdecea; --shadow:0 1px 3px rgba(16,24,40,.06);
/* ── 表单控件尺度(2026-11-04 统一)──
* 用户:「输入框、下拉框高度太随意,一点设计感都没有。」
* 之前 padding 有 8px/9px/12px 三种,select 和 input 高度还不一样,
* 并排放在一起参差不齐。现在只保留三档,全部按高度对齐:
* --ctl-h-sm 32px 表格内 / 紧凑行
* --ctl-h 38px 常规表单(默认)
* --ctl-h-lg 42px 主要操作区
* 高度用固定值 + line-height 居中的方式,而不是靠 padding 撑,
* 这样 input / select / button 放同一行时基线必然一致。 */
--ctl-h-sm:32px; --ctl-h:38px; --ctl-h-lg:42px;
--ctl-r:9px; /* 统一圆角 */
--ctl-px:12px; /* 统一左右内边距 */
--ctl-bd:1px solid var(--line-strong);
--ctl-shadow:0 1px 2px rgba(16,24,40,.04);
--ctl-shadow-focus:0 0 0 3px var(--brand-soft);
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
@@ -181,10 +196,19 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
.foot-line .dot{padding:0}
/* ---------- 按钮 ---------- */
/* 按钮高度与输入框严格一致(--ctl-h)。
* 🔴 原来靠 padding:8px + line-height:1.4 撑,算出来 38.3px,
* 而输入框是固定 38px —— 并排时差 0.3px,放大看会有轻微的错位感。
* 改成固定 height + inline-flex 垂直居中,与控件完全同高。 */
.btn{
display:inline-block; border:1px solid var(--line-strong); background:var(--card);
color:var(--fg); border-radius:9px; padding:8px 16px; font-size:14.5px;
cursor:pointer; line-height:1.4; font-family:inherit;
display:inline-flex; align-items:center; justify-content:center;
height:var(--ctl-h); padding:0 16px;
border:1px solid var(--line-strong); background:var(--card);
color:var(--fg); border-radius:var(--ctl-r);
font-size:14.5px; line-height:1;
cursor:pointer; font-family:inherit; white-space:nowrap;
box-shadow:var(--ctl-shadow);
transition:border-color .12s, background .12s, color .12s;
}
.btn:hover{border-color:var(--brand); color:var(--brand); text-decoration:none}
.btn.primary{background:var(--brand); border-color:var(--brand); color:#fff; font-weight:600}
@@ -361,8 +385,10 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
width:100%; height:100%; display:flex; align-items:center; justify-content:center;
color:#fff; font-size:24px; font-weight:600;
}
.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%}
.icon-up-main{flex:1; min-width:220px; display:flex; flex-direction:column; align-items:flex-start}
/* ⚠️ 不要在这里给 input[type=file] 设任何样式 ——
* 它已被 .file-btn 视觉隐藏(clip),设 max-width/font-size 之类
* 会把它从"视觉隐藏"变成"露出一个小方块",反而破坏新按钮外观。 */
/* 上传状态提示:选中文件后立刻告诉用户「选到了什么、合不合格」。
* 用户 2026-11-04:「上传的图标没有显示,也没有成功/失败提示。」 */
.icon-up-tip{
@@ -469,7 +495,7 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px;
text-transform:uppercase; background:var(--card); color:var(--fg);
}
.cp-ok{flex:none; padding:6px 14px !important; font-size:13px !important}
.cp-ok{flex:none; height:32px !important; padding:0 14px !important; font-size:13px !important}
.color-presets{display:flex; gap:6px; flex-wrap:wrap}
.color-preset{
width:26px; height:26px; border-radius:50%; border:2px solid transparent;
@@ -486,8 +512,7 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
}
.mail-test input[type=email]{
flex:1 1 220px; min-width:180px;
border:1px solid var(--line-strong); border-radius:9px; padding:8px 10px;
font-size:14px; background:var(--card); color:var(--fg); font-family:inherit;
/* 边框/高度/圆角统一在「统一表单控件」段里定义,这里只管宽度 */
}
.mail-test-out{flex:1 1 100%; font-size:13px; line-height:1.6}
.mail-test-out.ok{color:var(--ok)}
@@ -538,7 +563,9 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
.tax-note{margin-top:5px; font-size:13px; color:var(--muted); line-height:1.5}
.tax-acts{display:flex; flex-wrap:wrap; gap:6px; align-items:center}
.inline-form{display:inline}
.btn.sm{padding:5px 11px; font-size:13px; border-radius:8px}
/* 小号按钮:基类是固定 height,这里必须显式改 height 而不是改 padding,
* 否则 padding 会被 height 吃掉、按钮还是 38px 高。 */
.btn.sm{height:var(--ctl-h-sm); padding:0 11px; font-size:13px; border-radius:8px}
.btn.danger{border-color:#e0b4b4; color:#b4242c}
.btn.danger:hover{background:#fdf2f2; border-color:#b4242c}
.tax-form-box{
@@ -571,12 +598,8 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
}
.chip:hover{border-color:var(--brand); color:var(--brand); text-decoration:none}
.chip.on{background:var(--brand); border-color:var(--brand); color:#fff; font-weight:600}
.filter-row{display:flex; flex-wrap:wrap; gap:8px}
.filter-row select,.filter-row input[type=search]{
border:1px solid var(--line-strong); border-radius:9px; padding:8px 10px;
font-size:14px; background:var(--card); color:var(--fg); font-family:inherit;
}
.filter-row input[type=search]{flex:1; min-width:160px}
.filter-row{display:flex; flex-wrap:wrap; gap:9px; align-items:center}
/* 控件的具体样式统一在上面的「统一表单控件」段里定义(避免多处覆盖打架) */
/* ---------- 列表 ---------- */
.list{display:flex; flex-direction:column; gap:12px}
@@ -741,7 +764,149 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
.reply-body{word-break:break-word; line-height:1.8}
/* ---------- 表单 ---------- */
/* 提交页容器。
/* ══════════════════════════════════════════════════════════════
* 统一表单控件(2026-11-04 重做)
*
* 用户:「输入框、下拉框、选择图片那个按钮、排序输入框、前端筛选的
* 输入框下拉框,都特别丑,一点设计感都没有。高度太随意。」
*
* 之前的问题:
* ① 样式散落在 5-6 处(.field / .filter-row / .filters / .admin 各自一套)
* ② padding 有 8px / 9px / 12px 三种,select 与 input 高度不等,
* 并排放一起参差不齐
* ③ select 用浏览器默认箭头,各平台样式不一,还贴边
* ④ file input 完全是原始外观
*
* 现在:一套基类 .ctl + 统一变量,所有控件高度/圆角/边框/聚焦环一致。
* 高度用「固定 height + line-height 居中」而不是 padding 撑,
* 保证 input / select / button 同行时基线必然对齐。
* ══════════════════════════════════════════════════════════════ */
/* --- 基类:所有文本类控件 ---
* 🔴 选择器要**宽松**。之前限定在 .field / .filters 里面,
* 结果任何不在这些容器里的控件(后台零散布局里的 select、input)
* 都退化成浏览器默认外观,高度只有 19~21px,跟旁边 38px 的按钮并排
* 参差不齐 —— 这正是用户说的"高度太随意"。
* 现在按 type 通吃,再用 :not() 排除掉不该套的(checkbox/radio/color/file/range)。 */
input[type=text]:not(.cp-hex),
input[type=email],
input[type=tel],
input[type=password],
input[type=url],
input[type=number],
input[type=search],
select,
textarea:not(.cp-hex){
height:var(--ctl-h);
padding:0 var(--ctl-px);
border:var(--ctl-bd);
border-radius:var(--ctl-r);
background:var(--card);
color:var(--fg);
font-family:inherit;
font-size:14.5px;
line-height:calc(var(--ctl-h) - 2px); /* 减掉上下边框,垂直居中 */
box-shadow:var(--ctl-shadow);
transition:border-color .12s, box-shadow .12s;
-webkit-appearance:none;
appearance:none;
}
/* 文本域不能用固定高度 */
textarea{height:auto; min-height:110px; padding:9px var(--ctl-px); line-height:1.7; resize:vertical}
/* 数字框保留原生上下箭头(用户要改排序,有箭头更直观) */
input[type=number]{-moz-appearance:auto; appearance:auto; -webkit-appearance:auto}
/* --- 聚焦态:统一一圈柔和的 brand 光晕 --- */
input[type=text]:focus, input[type=email]:focus, input[type=tel]:focus,
input[type=password]:focus, input[type=url]:focus, input[type=number]:focus,
input[type=search]:focus, select:focus, textarea:focus{
outline:none;
border-color:var(--brand);
box-shadow:var(--ctl-shadow-focus);
}
/* --- 悬停:边框略深,给出可交互暗示 --- */
input[type=text]:hover:not(:focus), input[type=email]:hover:not(:focus),
input[type=tel]:hover:not(:focus), input[type=password]:hover:not(:focus),
input[type=url]:hover:not(:focus), input[type=number]:hover:not(:focus),
input[type=search]:hover:not(:focus), select:hover:not(:focus),
textarea:hover:not(:focus){
border-color:#c3c9d1;
}
/* --- 下拉框:自绘箭头 ---
* 原生箭头各平台差异大且贴边。这里用内联 SVG 画一个 12px 的 V 形,
* 右侧留 34px 空间,再给 padding-right 避开它。
* ⚠️ 背景必须同时写 background-color 和 background-image,
* 否则 -webkit-appearance:none 后底色会丢失。 */
.field select,
.filter-row select,
.filters select,
select{
padding-right:34px;
background-color:var(--card);
background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat:no-repeat;
background-position:right 12px center;
cursor:pointer;
}
select:hover{
background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%231f2329' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* --- 搜索框:左侧放大镜图标 --- */
.filter-row input[type=search]{flex:1; min-width:170px; padding-left:36px;
background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Ccircle cx='6.2' cy='6.2' r='4.6' fill='none' stroke='%236b7280' stroke-width='1.5'/%3E%3Cpath d='M9.8 9.8 13.4 13.4' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat:no-repeat;
background-position:12px center;
}
/* 去掉 Chrome 自带的搜索框清除按钮(和我们的图标风格不搭) */
.filter-row input[type=search]::-webkit-search-decoration,
.filter-row input[type=search]::-webkit-search-cancel-button{
-webkit-appearance:none; appearance:none;
}
/* --- 文件选择按钮:把原生丑控件重做成一个真正的按钮 ---
* 用户:「选择图片那个按钮太丑。」
* 做法:隐藏原生 input(但不能 display:none,否则不可点/不可聚焦),
* 用 <label class="file-btn"> 包一个自绘按钮,点击穿透到 input。 */
.file-btn{
display:inline-flex; align-items:center; gap:9px;
height:var(--ctl-h); padding:0 14px 0 12px;
border:var(--ctl-bd); border-radius:var(--ctl-r);
background:var(--card); color:var(--fg);
font-size:14px; font-weight:600; cursor:pointer;
box-shadow:var(--ctl-shadow);
transition:border-color .12s, background .12s;
max-width:100%;
}
.file-btn:hover{border-color:var(--brand); background:var(--brand-soft); color:var(--brand)}
.file-btn:active{transform:translateY(.5px)}
.file-btn-ic{
width:17px; height:17px; flex:none;
background:currentColor;
-webkit-mask:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 3v10M6 9l4 4 4-4M4 16h12' fill='none' stroke='black' stroke-width='1.8' 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' viewBox='0 0 20 20'%3E%3Cpath d='M10 3v10M6 9l4 4 4-4M4 16h12' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
}
.file-btn-name{
font-weight:400; color:var(--muted); font-size:13px;
overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:190px;
}
/* 原生 input 视觉隐藏,但仍可点击/可聚焦(不能用 display:none) */
.file-btn input[type=file]{
position:absolute; width:1px; height:1px; opacity:0;
overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap;
}
/* --- 数字框 + 前后缀,让「排序」这种小输入看起来有设计感 --- */
.num-wrap{display:inline-flex; align-items:center; gap:0; position:relative}
.num-wrap .num-suffix{
position:absolute; right:12px; font-size:12.5px; color:var(--muted);
pointer-events:none;
}
.num-wrap input[type=number]{padding-right:44px}
/* ---------- 提交页容器 */
* 🔴 原来只有 max-width:640px,**没有居中** —— 在 880px 的内容区里,
* 表单贴左边、右侧空出 240px 白,看起来像"页面缺了一块"。
* 用户 2026-11-03 指出这一点。现在:加 margin 居中,宽度放到 720px,
@@ -818,16 +983,22 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh}
.field > span{display:block; font-size:14px; font-weight:600; margin-bottom:6px}
.field .req{color:var(--danger)}
.field .hint{font-style:normal; font-weight:400; font-size:12.5px; color:var(--muted); margin-left:6px}
.field input[type=text],.field input[type=email],.field input[type=tel],
.field input[type=password],.field textarea,.field select{
width:100%; border:1px solid var(--line-strong); border-radius:9px;
padding:9px 12px; font-size:15px; font-family:inherit; background:var(--card);
color:var(--fg); line-height:1.6;
}
.field textarea{resize:vertical; min-height:110px}
.field input:focus,.field textarea:focus,.field select:focus{
outline:none; border-color:var(--brand); box-shadow:0 0 0 3px var(--brand-soft);
}
/* 控件的边框/高度/圆角/聚焦态统一在上方「统一表单控件」段里定义。
* 这里只保留 .field 的宽度约束 —— 表单里的控件一律占满整行。 */
.field > input[type=text],
.field > input[type=email],
.field > input[type=tel],
.field > input[type=password],
.field > input[type=url],
.field > input[type=number],
.field > input[type=search],
.field > select,
.field > textarea{width:100%}
/* 短小的数字类字段(如「排序」)不该铺满整行 —— 收窄到 140px,
* 视觉上更像"一个小参数"而不是"一个大输入框"。 */
.field-narrow{max-width:140px}
.field-narrow > input{width:100% !important}
@media (max-width:560px){ .field-narrow{max-width:none} }
.row2{display:grid; grid-template-columns:1fr 1fr; gap:14px}
@media (max-width:560px){ .row2{grid-template-columns:1fr} }
+27 -10
View File
@@ -510,10 +510,19 @@ function fb_admin_taxonomy_page($pdo, $editSw, $editCat) {
}
echo ' </div>';
echo ' <div class="icon-up-main">';
echo ' <input type="file" name="icon_file" accept="image/png,image/jpeg,image/webp" id="iconFile' . ($esw ? (int)$esw['id'] : 'new') . '">';
echo ' <i class="hint" style="margin-top:4px">建议 256×256 或 512×512 的方形图;带透明背景的 PNG 效果最好。</i>';
/* 文件选择按钮重做成真按钮(用户:原生那个太丑)。
* <label class="file-btn"> 包住 input[type=file],点 label 就等于点 input;
* input 用 clip 视觉隐藏但仍可聚焦(不能用 display:none,那样点不到)。 */
$ficonFileId = 'iconFile' . ($esw ? (int)$esw['id'] : 'new');
echo ' <label class="file-btn" for="' . $ficonFileId . '">'
. '<span class="file-btn-ic" aria-hidden="true"></span>'
. '<span class="file-btn-txt">选择图片</span>'
. '<span class="file-btn-name" data-file-name></span>'
. '<input type="file" name="icon_file" accept="image/png,image/jpeg,image/webp" id="' . $ficonFileId . '">'
. '</label>';
echo ' <i class="hint" style="margin-top:6px">建议 256×256 或 512×512 的方形图;带透明背景的 PNG 效果最好。</i>';
if ($curIconUrl !== '') {
echo ' <label class="check" style="margin-top:6px"><input type="checkbox" name="icon_clear" value="1"> 移除当前图片,改用下面的字符图标</label>';
echo ' <label class="check" style="margin-top:8px"><input type="checkbox" name="icon_clear" value="1"> 移除当前图片,改用下面的字符图标</label>';
}
echo ' </div>';
echo ' </div>';
@@ -529,11 +538,17 @@ function fb_admin_taxonomy_page($pdo, $editSw, $editCat) {
}
echo '</span></label>';
// 下载地址:状态改成「已解决」时,邮件里会给用户一个去下新版的按钮
echo ' <label class="field"><span>下载地址<i class="hint">可留空。填了之后「已解决」的通知邮件里会多一个「下载最新版」按钮</i></span>'
. '<input type="url" name="download_url" maxlength="300" value="'
. e($esw ? (string)(isset($esw['download_url']) ? $esw['download_url'] : '') : '')
. '" placeholder="https://..."></label>';
/* ── 「下载地址」字段已删除 ──
* 用户 2026-11-04:「有个什么下载链接,这个东西不要,要它干什么?」
*
* 它原本的唯一用途:当反馈状态改成「已解决」时,把通知邮件里的按钮
* 从「查看详情」换成「下载最新版本」。属于可有可无的小功能,
* 却要求管理员为每个软件再维护一条 URL —— 收益远小于维护成本。
*
* ⚠️ 数据库的 download_url 列**保留不删**:
* ① 删列会让历史数据不可逆丢失;
* ② 万一以后想恢复,不用再走一次迁移。
* 只是前台表单不再暴露,mailer 里也已改为始终用「查看详情」。 */
// 主题色:前台按软件换色,用短链接进不同软件一眼就能区分
$curColor = $esw ? fb_color_norm((string)$esw['color']) : '';
@@ -577,8 +592,10 @@ function fb_admin_taxonomy_page($pdo, $editSw, $editCat) {
echo ' </div>';
echo ' </div>';
if ($esw) {
echo ' <label class="field"><span>排序<i class="hint">数字小的排前面</i></span>'
. '<input type="number" name="sort_order" value="' . (int)$esw['sort_order'] . '"></label>';
/* 排序是 1-2 位的小整数,占满整行很难看 —— 收窄成定宽。
* 用 .field-narrow 限制宽度,而不是让它跟着 .field 铺满。 */
echo ' <label class="field field-narrow"><span>排序<i class="hint">数字小的排前面,同级按名称排</i></span>'
. '<input type="number" name="sort_order" min="0" max="999" value="' . (int)$esw['sort_order'] . '"></label>';
echo ' <button class="btn primary" name="act" value="sw_edit" type="submit">保存</button> '
. '<a class="btn" href="admin.php?tab=taxonomy">取消</a>';
} else {
+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', 45); }
if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 47); }
}
/* ------------------------------------------------------------------
+6 -9
View File
@@ -997,17 +997,14 @@ if (!function_exists('fb_notify_render')) {
$blocks[] = ['type' => 'callout', 'tone' => $meta['tone'], 'text' => $meta['tip']];
}
/* 「已解决」要真的推用户一把去验证:
* 软件配了下载地址就给一个下载按钮 —— 只说「修好了」用户不知道去哪拿新版本。 */
$dlUrl = $sw ? trim((string)(isset($sw['download_url']) ? $sw['download_url'] : '')) : '';
/* 「已解决」时给用户一个按钮去验证。
*
* ⚠️ 2026-11-04:后台的「下载地址」字段已删除,
* 所以这里不再判断 $sw['download_url'] —— 那个值只可能是历史遗留。
* 按钮统一为「查看详情」,不再有「下载最新版本」分支。
* 想要恢复的话:后台加回字段 + 这里加回判断即可。 */
$btnText = '查看详情';
$btnUrl = $url;
if ($newKey === 'resolved' && $dlUrl !== '') {
$blocks[] = ['type' => 'callout', 'tone' => 'ok',
'text' => '新版本已经发布,建议下载最新版后验证一下。'];
$btnText = '下载最新版本';
$btnUrl = $dlUrl;
}
$layout = [
'software' => $sw,