ico
1
code/.gitignore
vendored
@@ -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/*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
<meta name="keywords" content='{block name="keywords"}{/block}'>
|
||||
<meta name="description" content='{block name="description"}{/block}'>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
|
||||
<link rel="icon" href="{$TpStyle.favicon}" type="image/svg+xml">
|
||||
|
||||
|
||||
{// 动态主题颜色 }
|
||||
<style>
|
||||
:root{
|
||||
|
||||
4
code/public/static/favicon/svg_base/base_01.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<path d="M5 4h4c2 0 3 1 3 2.5S11 9 9 9H7v3H5z" fill="VAR_FG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 179 B |
4
code/public/static/favicon/svg_base/base_02.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<path d="M9 1L3 9h4l-1 6 7-9H9z" fill="VAR_FG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 165 B |
8
code/public/static/favicon/svg_base/base_03.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<circle cx="8" cy="8" r="5" fill="VAR_FG"/>
|
||||
<circle cx="6" cy="6" r="1" fill="VAR_BG"/>
|
||||
<circle cx="10" cy="6" r="1" fill="VAR_BG"/>
|
||||
<circle cx="6" cy="10" r="1" fill="VAR_BG"/>
|
||||
<circle cx="10" cy="10" r="1" fill="VAR_BG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 348 B |
4
code/public/static/favicon/svg_base/base_04.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<path d="M8 3l5 10H3z" fill="VAR_FG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 155 B |
4
code/public/static/favicon/svg_base/base_05.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<polygon points="6,4 12,8 6,12" fill="VAR_FG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 164 B |
5
code/public/static/favicon/svg_base/base_06.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<rect x="4" y="3" width="3" height="10" fill="VAR_FG"/>
|
||||
<rect x="9" y="3" width="3" height="10" fill="VAR_FG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 230 B |
4
code/public/static/favicon/svg_base/base_07.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<path d="M3 4h10v6H7l-2 2v-2H3z" fill="VAR_FG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 165 B |
5
code/public/static/favicon/svg_base/base_08.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<ellipse cx="8" cy="8" rx="5" ry="3" fill="VAR_FG"/>
|
||||
<circle cx="8" cy="8" r="1.5" fill="VAR_BG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 217 B |
4
code/public/static/favicon/svg_base/base_09.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<rect x="4" y="4" width="8" height="8" fill="VAR_FG"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 171 B |
4
code/public/static/favicon/svg_base/base_10.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="VAR_BG"/>
|
||||
<path d="M2 9c2-3 4 3 6 0s4 3 6 0" stroke="VAR_FG" stroke-width="2" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 198 B |