选择软件
diff --git a/lib/util.php b/lib/util.php
index c1603ee..333a475 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -335,19 +335,21 @@ function fb_software_badge($software, $size = 'md', $label = '') {
$icon = fb_software_icon($software);
$name = $label !== '' ? (string)$label : (string)$software['name'];
- /* 尺寸档位(以「选择软件」标签的 13.5px 为基准,逐档放大):
- * sm 18px 图标 / 14px 字 软件选择器里的小标签
- * md 26px 图标 / 19px 字 「选择软件」抽屉里的条目
- * lg 40px 图标 / 22px 字 内容区「图标 + 软件名」
- * brand 32px 图标 / 17px 字 顶栏主标题
+ /* 尺寸档位(以「选择软件」标签的 13.5px 为基准):
+ * sm 18px 图标 / 14px 字 小标签
+ * md 24px 图标 / 16px 字 抽屉里的条目
+ * lg 24px 图标 / 15px 字 内容区「图标 + 软件名」
+ * brand 30px 图标 / 17px 字 顶栏主标题
*
- * 🔴 用户明确指出:顶栏标题的字号「比下面还小」是错的,但给到 26px 又太大。
- * 折中标准 = 比「选择软件」那个 13.5px 标签大两号 ≈ 17px。
- * 图标同比缩到 32px,别让图标压过文字。 */
- $px = ['sm' => 18, 'md' => 26, 'lg' => 40, 'brand' => 32];
- $fs = ['sm' => 14, 'md' => 19, 'lg' => 22, 'brand' => 17];
- $box = isset($px[$size]) ? $px[$size] : 26;
- $fnt = isset($fs[$size]) ? $fs[$size] : 19;
+ * 🔴 两条硬约束(都踩过):
+ * 1. 顶栏标题 = 比「选择软件」的 13.5px 大两号 ≈ 17px,不要再放大到 26px;
+ * 2. **顶栏必须比内容区大**。之前顶栏 26px、内容区 30px,
+ * 主次直接颠倒,看着就是「上面的字比下面的小」。
+ * 现在 17 > 15,层级正确;内容区只是标识,不该抢标题的戏。 */
+ $px = ['sm' => 18, 'md' => 24, 'lg' => 24, 'brand' => 30];
+ $fs = ['sm' => 14, 'md' => 16, 'lg' => 15, 'brand' => 17];
+ $box = isset($px[$size]) ? $px[$size] : 24;
+ $fnt = isset($fs[$size]) ? $fs[$size] : 16;
/* 有上传的图标图片就用图片(管理员自己传的 logo),没有才用字符。
* 两者外框尺寸一致,换用图片不会让布局跳动。 */
@@ -386,6 +388,70 @@ function fb_software_site_title($software) {
return $name . '反馈系统';
}
+/**
+ * 软件徽标(后台/前台的小标签)—— 带图标,且用该软件自己的主题色。
+ *
+ * 为什么不复用 .badge 的固定蓝色:每个软件都配了主题色,
+ * 徽标却一律蓝色,等于把「颜色区分软件」这条设计浪费掉了。
+ *
+ * @param array $sw 软件行
+ * @param bool $icon 是否显示图标(默认显示)
+ */
+function fb_software_chip($sw, $withIcon = true) {
+ if (!is_array($sw) || empty($sw['name'])) { return ''; }
+ $name = (string)$sw['name'];
+
+ list($c) = fb_software_colors($sw);
+ if ($c === '') { $c = '#0057B8'; }
+ $soft = fb_color_soft($c); // 同色系浅底
+ $line = fb_color_line($c); // 同色系描边
+
+ $icHtml = '';
+ if ($withIcon) {
+ $imgKey = isset($sw['icon_key']) ? trim((string)$sw['icon_key']) : '';
+ $imgUrl = $imgKey !== '' ? fb_icon_url($imgKey, 30) : '';
+ if ($imgUrl !== '') {
+ $icHtml = '
 . ')
';
+ } else {
+ $icHtml = '
' . e(fb_software_icon($sw)) . '';
+ }
+ }
+
+ return '
'
+ . $icHtml
+ . '' . e($name) . ''
+ . '';
+}
+
+/** 把主色调淡成浅底(与主色同色系,避免灰化) */
+function fb_color_soft($hex) {
+ $rgb = fb_hex_to_rgb($hex);
+ if (!$rgb) { return '#eef2f8'; }
+ return sprintf('#%02x%02x%02x',
+ (int)round($rgb[0] + (255 - $rgb[0]) * 0.90),
+ (int)round($rgb[1] + (255 - $rgb[1]) * 0.90),
+ (int)round($rgb[2] + (255 - $rgb[2]) * 0.90));
+}
+
+/** 把主色调成描边色(略深于浅底) */
+function fb_color_line($hex) {
+ $rgb = fb_hex_to_rgb($hex);
+ if (!$rgb) { return '#cfdcee'; }
+ return sprintf('#%02x%02x%02x',
+ (int)round($rgb[0] + (255 - $rgb[0]) * 0.72),
+ (int)round($rgb[1] + (255 - $rgb[1]) * 0.72),
+ (int)round($rgb[2] + (255 - $rgb[2]) * 0.72));
+}
+
+/** #rrggbb -> [r,g,b],非法返回 null */
+function fb_hex_to_rgb($hex) {
+ $h = ltrim((string)$hex, '#');
+ if (strlen($h) === 3) { $h = $h[0].$h[0].$h[1].$h[1].$h[2].$h[2]; }
+ if (!preg_match('/^[0-9a-fA-F]{6}$/', $h)) { return null; }
+ return [hexdec(substr($h, 0, 2)), hexdec(substr($h, 2, 2)), hexdec(substr($h, 4, 2))];
+}
+
/** 某软件下的分类;不传软件则返回空数组 */
function fb_categories($softwareId = null, $onlyEnabled = true) {
if ($softwareId === null) { return []; }
diff --git a/thread.php b/thread.php
index d6e9904..ee36629 100644
--- a/thread.php
+++ b/thread.php
@@ -161,7 +161,7 @@ fb_layout_head(fb_excerpt($thread['title'], 50), ['software' => $thSwForColor]);
$thSw = !empty($thread['software_id']) ? fb_software((int)$thread['software_id']) : null;
$thCat = !empty($thread['category_id']) ? fb_category((int)$thread['category_id']) : null;
?>
-
+