diff --git a/code/.gitignore b/code/.gitignore
index 503ab2a..24233e4 100644
--- a/code/.gitignore
+++ b/code/.gitignore
@@ -16,4 +16,5 @@ Thumbs.db
/public/initdata/video/url_family_bind.php
public/static/css/compiled/*
public/static/js/compiled/*
+public/static/favicon/generated/*
/storage/*
diff --git a/code/app/common/helper/SiteStyle.php b/code/app/common/helper/SiteStyle.php
index e14e4b7..b501fe8 100644
--- a/code/app/common/helper/SiteStyle.php
+++ b/code/app/common/helper/SiteStyle.php
@@ -18,6 +18,10 @@ use think\facade\Cache;
*/
class SiteStyle
{
+ // favicon SVG 母型目录
+ private static string $faviconBaseDir = 'public/static/favicon/svg_base/';
+
+
private static function useCache(): bool
{
return (bool) config('view.view_site_style_cache');
@@ -268,16 +272,16 @@ class SiteStyle
}
}
-
// list_layout 只承载“形态库”(item/shell/grid/layout/行为/itv)。
// title/title_text/更多文案 等“语义”交给 pages.slot/context 决定。
- return [
+ $arrCfg = [
'meta' => [
'host' => $host,
'seed' => $seed,
'dom_prefix' => $domPrefix,
'static_hash' => $staticHash,
'version' => self::CFG_VERSION,
+
],
'global' => [
@@ -289,6 +293,8 @@ class SiteStyle
],
],
+ 'theme' => [],
+
'pages' => [
'home' => $homePage,
'category_index' => $categoryIndexPage,
@@ -347,6 +353,17 @@ class SiteStyle
'seo' => self::buildSeoCfg($seed),
];
+
+ // 主题色(运行时算,不冻结具体颜色)
+ $theme = self::buildTheme(
+ $seed,
+ $arrCfg['global']['theme']['mode'],
+ $arrCfg['global']['theme']['variant']
+ );
+ $arrCfg['theme'] = $theme;
+ $arrCfg['favicon'] = self::buildFavicon($arrCfg);
+
+ return $arrCfg;
}
private static function getUrlFamilyPool(): array
@@ -445,11 +462,7 @@ class SiteStyle
$staticHash = $cfg['meta']['static_hash'];
// 主题色(运行时算,不冻结具体颜色)
- $theme = self::buildTheme(
- $intSeed,
- $cfg['global']['theme']['mode'],
- $cfg['global']['theme']['variant']
- );
+ $theme = $cfg['theme'];
// 模板路径(你原来 include 用的方式完全保留)
$tpl = $cfg['components']['templates'];
@@ -483,6 +496,8 @@ class SiteStyle
'dom_prefix' => $domPrefix,
'static_hash' => $staticHash,
+ 'favicon' => $cfg['favicon'],
+
'lazy_img' => self::pickLazyImg([
'host' => $strHost,
'seed' => $intSeed,
@@ -795,13 +810,13 @@ class SiteStyle
'seo' => '',
];
}
-
+
$pool = require $file;
-
+
$primaryPool = $pool['primary'] ?? [];
$secondaryPool = $pool['secondary'] ?? [];
$seoPool = $pool['seo'] ?? [];
-
+
// 池兜底(防止被误删 / 配置异常)
if (!$primaryPool || !$secondaryPool || !$seoPool) {
return [
@@ -810,21 +825,21 @@ class SiteStyle
'seo' => '',
];
}
-
+
// 域名级 + 榜单级稳定
$hP = abs(crc32($seed . '|rkpri|' . $sortType));
$hS = abs(crc32($seed . '|rksec|' . $sortType));
$hE = abs(crc32($seed . '|rkseo|' . $sortType));
-
+
$primaryTpl = $primaryPool[$hP % count($primaryPool)];
$secondaryTpl = $secondaryPool[$hS % count($secondaryPool)];
$seoTpl = $seoPool[$hE % count($seoPool)];
-
+
// 统一占位符替换(rank_home 统一用 {name})
$replace = [
'{name}' => $titleName,
];
-
+
return [
'primary' => strtr($primaryTpl, $replace),
'secondary' => strtr($secondaryTpl, $replace),
@@ -2597,4 +2612,78 @@ class SiteStyle
return $memo[$key] = $data;
return $memo[$relFile] = (is_array($data) ? $data : []);
}
+
+ /**
+ * 生成并冻结 favicon(与 theme_color / dom_prefix / layout 联动)
+ * 只在 SiteStyle 构建阶段调用
+ */
+ private static function buildFavicon(array $TpStyle): string
+ {
+
+ $domain = $_SERVER['HTTP_HOST'] ?? 'cli';
+ $safe = preg_replace('/[^a-z0-9\.\-]/i', '_', $domain);
+
+ // ===== 下面开始“只执行一次”的生成逻辑 =====
+ $seed = (int)($TpStyle['meta']['seed'] ?? crc32($domain));
+ $bg = (string)($TpStyle['theme']['theme_color'] ?? '#263238');
+ $fg = '#ffffff';
+
+ $strSvgRoute = '/static/favicon/generated/' . $seed . '/';
+ $strSvgGenDir = root_path() . 'public/' . $strSvgRoute;
+ $strTmpSvgDir = $strSvgRoute . 'favicon.svg';
+
+ $svgFile = $strSvgGenDir . 'favicon.svg';
+ $icoFile = $strSvgGenDir . 'favicon.ico';
+
+ if (!is_dir($strSvgGenDir)) {
+ @mkdir($strSvgGenDir, 0755, true);
+ }
+
+ // 已生成过,直接复用(冻结)
+ if (is_file($icoFile) && is_file($svgFile)) {
+ return $strTmpSvgDir;
+ }
+
+ $svgBasePath = root_path() . self::$faviconBaseDir;
+ $bases = glob($svgBasePath . '*.svg');
+ if (!$bases) {
+ // 兜底,不影响上线
+ return '/favicon.ico';
+ }
+
+ // 用 dom_prefix + layout 决定母型(稳定、可回放)
+ $layout = (string)($TpStyle['global']['theme']['layout'] ?? 'A');
+ $dom = (string)($TpStyle['meta']['dom_prefix'] ?? '');
+
+ $idx = abs(crc32($layout . '|' . $dom . '|' . $seed)) % count($bases);
+ $baseSvg = $bases[$idx];
+
+ $svg = file_get_contents($baseSvg);
+ if ($svg === false) {
+ return '/favicon.ico';
+ }
+
+ // 注入主题色
+ $svg = str_replace(['VAR_BG', 'VAR_FG'], [$bg, $fg], $svg);
+
+ // 持久化 svg(便于排查、也便于后续升级)
+ @file_put_contents($svgFile, $svg);
+
+ // 转 ico(16 / 32)
+ $bin = 'magick';
+ if (!shell_exec('which magick')) {
+ $bin = 'convert';
+ }
+
+ $cmd = sprintf(
+ '%s %s -background none -define icon:auto-resize=16,32 %s',
+ $bin,
+ escapeshellarg($svgFile),
+ escapeshellarg($icoFile)
+ );
+
+ @exec($cmd);
+
+ return $strTmpSvgDir;
+ }
}
diff --git a/code/app/home/view/videoGpt1/base.html b/code/app/home/view/videoGpt1/base.html
index 5c84704..a8bbf7b 100644
--- a/code/app/home/view/videoGpt1/base.html
+++ b/code/app/home/view/videoGpt1/base.html
@@ -8,6 +8,9 @@
+
+
+
{// 动态主题颜色 }