diff --git a/assets/style.css b/assets/style.css index 4ae8b52..b44bcba 100644 --- a/assets/style.css +++ b/assets/style.css @@ -117,28 +117,29 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh} } /* ---------- 软件标识:图标 + 名字(多软件区分的主视觉) ---------- - * 邮件模板里也有一套等价的 table 写法,两边的观感要对得上: - * 圆角色块 + 居中字符 + 紧跟软件名。 */ + * 🔴 一律不要背景色:图标本身就是一张图/一个 emoji,再垫一个色块会糊在一起, + * 反而看不清。直接「图标 + 文字」并排是识别度最高的做法。 + * 邮件模板里有等价的 table 写法,两边观感要对得上。 */ .sw-badge{display:inline-flex; align-items:center; gap:7px; min-width:0; vertical-align:middle} .sw-badge-ic{ display:inline-flex; align-items:center; justify-content:center; - border-radius:7px; color:#fff; font-weight:600; flex:none; - line-height:1; letter-spacing:0; box-shadow:0 1px 2px rgba(15,25,45,.18); + flex:none; line-height:1; letter-spacing:0; + background:none; border:0; box-shadow:none; } .sw-badge-nm{ font-weight:600; font-size:14px; color:var(--fg); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } -.sw-badge-sm .sw-badge-nm{font-size:13px} -.sw-badge-lg .sw-badge-nm{font-size:17px} -/* 页头大标题位置放软件标识:清掉 h1 的行高影响,让图标与文字垂直居中 */ -.page-h1-sw{margin:0; font-size:0; line-height:0} -.page-h1-sw .sw-badge-nm{font-size:22px; font-weight:700; color:var(--fg)} -/* 提交页顶部的软件上下文条 */ +.sw-badge-sm .sw-badge-nm{font-size:13.5px} +.sw-badge-lg .sw-badge-nm{font-size:26px} +/* 页头大标题:就是纯文字,不要 h1 的默认行高干扰 */ +.page-h1-sw{margin:0; font-size:inherit; line-height:1.3} +/* 页头副行:图标 + 软件名,紧跟在「XX 反馈系统」下面 */ +.page-sw-id{margin-top:8px; display:flex; align-items:center} +/* 提交页顶部的软件上下文条:同样无背景色,只留一行轻分隔 */ .sw-context{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; - margin:0 0 16px; padding:11px 14px; border-radius:11px; - background:var(--brand-soft); border:1px solid var(--line); + margin:0 0 16px; padding:0 0 12px; border-bottom:1px solid var(--line); } .sw-context .sw-badge-nm{color:var(--fg)} /* 后台软件编辑:图标快选 */ @@ -152,9 +153,10 @@ main.wrap{padding-top:24px; padding-bottom:40px; min-height:60vh} /* ---------- 软件图标(上传图片) ---------- */ /* 图标图片:外框与字符版完全一致,换图不会让布局跳动 */ +/* 图标图片:不留背景、不加圆角底衬,直接贴图 */ .sw-badge-img{ - display:inline-block; border-radius:7px; object-fit:cover; flex:none; - box-shadow:0 1px 2px rgba(15,25,45,.18); background:var(--card); + display:inline-block; object-fit:contain; flex:none; + background:none; border:0; box-shadow:none; } .chip.on .sw-badge-img{box-shadow:0 0 0 1px rgba(255,255,255,.5)} /* 后台图标上传行 */ @@ -171,10 +173,9 @@ 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%} -/* 选中态的 chip 里,图标底衬白、字符取软件色,避免同色糊在一起 */ -.chip.on .sw-badge-ic{background:#fff !important; color:var(--brand) !important; box-shadow:none} +/* chip 里也不用白底反色了 —— 图标本来就没有底色 */ .chip.on .sw-badge-nm{color:#fff} -.chip .sw-badge-ic{box-shadow:none} +.chip .sw-badge-ic{background:none !important; color:inherit !important; box-shadow:none} /* ---------- 主题色选择器 ---------- */ .sw-dot{ diff --git a/index.php b/index.php index 568ce82..25e8c59 100644 --- a/index.php +++ b/index.php @@ -76,14 +76,18 @@ $attImgs = $tids ? fb_attach_map_images($pdo, $tids, 3) : []; $attCount = $tids ? fb_attach_count_map($pdo, $tids) : []; $siteName = (string)fb_cfg('site_name'); -$headTitle = $siteName !== '' ? $siteName . ' · 问题反馈' : '问题反馈'; -fb_layout_head($sw ? ((string)$sw['name']) : $headTitle, ['software' => $sw]); +/* 页面标题(浏览器标签 / 页面大标题): + * 选中某个软件时用「XX 反馈系统」,让用户一眼确认自己在这个软件的反馈区; + * 没选软件时退回站点名。 */ +$headTitle = $sw ? fb_software_site_title($sw) + : ($siteName !== '' ? $siteName . ' · 问题反馈' : '问题反馈'); +fb_layout_head($sw ? fb_software_site_title($sw) : $headTitle, ['software' => $sw]); ?>
-

+

全部问题

@@ -91,6 +95,10 @@ fb_layout_head($sw ? ((string)$sw['name']) : $headTitle, ['software' => $sw]); $siteDesc = (string)fb_cfg('site_desc'); $swNote = ($sw && !empty($sw['note'])) ? (string)$sw['note'] : ''; ?> + +
+ + ?>

diff --git a/lib/attach.php b/lib/attach.php index 305b891..aca8c72 100644 --- a/lib/attach.php +++ b/lib/attach.php @@ -184,9 +184,13 @@ function fb_icon_allowed_ext() { return ['png', 'jpg', 'jpeg', 'webp']; } -/** 这个键是不是软件图标(local/icons/… 或 /icons/…) */ +/** + * 这个键是不是软件图标。 + * 同时认正常形态(local/icons/… 、/icons/…)与历史形态(icons/…)。 + * 历史形态必须认,否则用户早先上传、仍存在桶里的图标就成了读不到的孤儿。 + */ function fb_icon_is_key($key) { - return (bool)preg_match('#^(?:local|[A-Za-z0-9][A-Za-z0-9._-]*)/icons/#', (string)$key); + return (bool)preg_match('#^(?:(?:local|[A-Za-z0-9][A-Za-z0-9._-]*)/)?icons/#', (string)$key); } /** @@ -208,8 +212,13 @@ function fb_icon_make_key($ext) { /** * 图标键是否合法。 - * 与 fb_upload_key_ok 分开写:图标键在附件键的基础路径里多一段 icons/, - * 塞进原函数会被判为不合法。 + * 接受两种形态: + * ① 正常形态:/icons/YYYY/MM/<16hex>. + * ② 历史形态:icons/YYYY/MM/<16hex>.(无存储前缀) + * —— 2026-11-03 之前的代码把键硬编码成这种样子,用户当时上传的图 + * 真实存在于桶根下的 icons/ 目录。这些对象是有效资产, + * 不能因为键「格式不规范」就让图标显示不出来,所以这里认它。 + * 新写入一律走正常形态,见 fb_icon_make_key()。 * * 🔴 扩展名必须是白名单里的图片类型,不能只写 `[a-z0-9]{2,5}` —— * 那样 `.php`(3 个字母)也会通过,等于给自己留了个写脚本的口子。 @@ -219,7 +228,8 @@ function fb_icon_key_ok($key) { $key = (string)$key; if (strlen($key) < 16 || strlen($key) > 200) { return false; } if (strpos($key, '..') !== false || strpos($key, '//') !== false) { return false; } - if (!preg_match('#^(?:local|[A-Za-z0-9][A-Za-z0-9._-]*)/icons/\d{4}/\d{2}/[a-f0-9]{16}\.([a-z0-9]{2,5})$#', $key, $m)) { + // 带存储前缀,或历史无前缀形态 + if (!preg_match('#^(?:(?:local|[A-Za-z0-9][A-Za-z0-9._-]*)/)?icons/\d{4}/\d{2}/[a-f0-9]{16}\.([a-z0-9]{2,5})$#', $key, $m)) { return false; } return in_array(strtolower($m[1]), fb_icon_allowed_ext(), true); diff --git a/lib/bootstrap.php b/lib/bootstrap.php index e45a419..9f11211 100644 --- a/lib/bootstrap.php +++ b/lib/bootstrap.php @@ -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', 19); } + if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 20); } } /* ------------------------------------------------------------------ diff --git a/lib/mailer.php b/lib/mailer.php index 122d444..bdda69b 100644 --- a/lib/mailer.php +++ b/lib/mailer.php @@ -191,9 +191,11 @@ if (!function_exists('fb_mail_sw_header')) { $icon = fb_software_icon($sw); $name = (string)$sw['name']; - /* 有上传的图标图片就用图片(管理员自己的 logo),没有才用「色块 + 字符」。 + /* 有上传的图标图片就用图片(管理员自己的 logo),没有才用字符。 + * 一律不加背景色块 —— 图标本身已经能说明是哪个软件, + * 再垫个色块反而糊在一起(与前端 fb_software_badge 保持同一观感)。 * 邮件客户端对 flex 支持参差,这里一律用 table + 内联样式; - * 图片必须给死宽高和 alt,否则 Outlook 里会撑变形。 */ + * 图片必须给死宽高,否则 Outlook 里会撑变形。 */ $imgKey = isset($sw['icon_key']) ? trim((string)$sw['icon_key']) : ''; $imgUrl = $imgKey !== '' ? fb_icon_url($imgKey, 60) : ''; @@ -201,11 +203,9 @@ if (!function_exists('fb_mail_sw_header')) { if ($imgUrl !== '') { $cell = '' . fb_mail_esc($name) . ''; + . ' style="width:30px;height:30px;display:block;border:0;outline:none;text-decoration:none">'; } else { - $cell = '
' + $cell = '
' . '' . fb_mail_esc($icon) . '
'; } diff --git a/lib/util.php b/lib/util.php index 08d13a5..18ea4bf 100644 --- a/lib/util.php +++ b/lib/util.php @@ -323,24 +323,26 @@ function fb_software_icon($software) { * 图标 + 名字的组合标签(HTML)。 * 前端所有「这是哪个软件」的地方都用它,保证视觉一致。 * + * 🔴 不要背景色:图标本身就是一张图/一个 emoji,再垫一个色块反而糊在一起。 + * 直接「图标 + 文字」并排,识别度最高(用户 2026-11-03 明确要求)。 + * * @param array $sw 软件行 * @param string $size 'sm' | 'md' | 'lg' */ function fb_software_badge($software, $size = 'md') { if (!is_array($software) || empty($software['name'])) { return ''; } - list($c) = fb_software_colors($software); - if ($c === '') { $c = '#0057B8'; } $icon = fb_software_icon($software); $name = (string)$software['name']; - $px = ['sm' => 20, 'md' => 26, 'lg' => 34]; - $fs = ['sm' => 11, 'md' => 14, 'lg' => 17]; - $box = isset($px[$size]) ? $px[$size] : 26; - $fnt = isset($fs[$size]) ? $fs[$size] : 14; + /* md 用于页头副行,要跟上方 26px 的标题文字视觉匹配,所以比 sm 明显大一档; + * lg 只在大标题位置用(现在标题是纯文字,暂时没用到,留着备用)。 */ + $px = ['sm' => 18, 'md' => 28, 'lg' => 40]; + $fs = ['sm' => 14, 'md' => 19, 'lg' => 30]; + $box = isset($px[$size]) ? $px[$size] : 28; + $fnt = isset($fs[$size]) ? $fs[$size] : 19; - /* 有上传的图标图片就用图片(管理员自己传的 logo), - * 没有才退回「色块 + 字符」。两者的外框尺寸完全一致, - * 换用图片不会让布局跳动。 */ + /* 有上传的图标图片就用图片(管理员自己传的 logo),没有才用字符。 + * 两者外框尺寸一致,换用图片不会让布局跳动。 */ $imgKey = isset($software['icon_key']) ? trim((string)$software['icon_key']) : ''; $imgUrl = $imgKey !== '' ? fb_icon_url($imgKey, $box * 2) : ''; if ($imgUrl !== '') { @@ -353,12 +355,29 @@ function fb_software_badge($software, $size = 'md') { } return '' - . '' + . '' . '' . e($name) . '' . ''; } +/** + * 「XX 反馈系统」—— 当前软件下的站点标题。 + * 多软件共用一个反馈站时,标题必须点明是哪个软件的反馈系统, + * 用户才知道自己没走错地方。没传软件时退回站点名。 + * + * ⚠️ 软件名本身已含「反馈」二字时不再叠加,避免出现「轻反馈反馈系统」。 + */ +function fb_software_site_title($software) { + if (!is_array($software) || empty($software['name'])) { + return (string)fb_cfg('site_name', '轻反馈'); + } + $name = trim((string)$software['name']); + if ($name === '') { return (string)fb_cfg('site_name', '轻反馈'); } + if (mb_strpos($name, '反馈', 0, 'UTF-8') !== false) { return $name; } + return $name . '反馈系统'; +} + /** 某软件下的分类;不传软件则返回空数组 */ function fb_categories($softwareId = null, $onlyEnabled = true) { if ($softwareId === null) { return []; }