仰望星辰工作室

starmusic-pc

starmusic-pc/ frontend/src/common/theme.js 5.0 KB · 122 行 原始文件
1/**
2 * 主题色管理
3 *
4 * 预设主题通过 CSS 变量注入页面根元素(见 theme-mixin.js):
5 * - --theme-color 主色(按钮 / 选中态 / 强调色)
6 * - --theme-color-rgb 主色 RGB 三元组,用于 rgba(var(--theme-color-rgb), .12) 半透明场景
7 * - --theme-color-dark 主色加深(选中文案 / 强调文字)
8 * - --theme-color-soft 主色淡底(标签 / 卡片背景)
9 * 样式文件中以 var(--theme-color, #31c27c) 形式引用,未注入变量时回退默认主色。
10 *
11 * 主题色图标(static/icons/{name}-theme-{主题id}.svg)由 scripts/gen-icon-svgs.js 生成,
12 * 主题色 tabbar 图标由 scripts/gen-theme-tabbar.js 生成。
13 */
14import { getSetting, updateSetting } from './setting.js'
15import { DEFAULT_THEME_ID } from './constants.js'
16
17/** 预设主题(id 同时用于主题图标文件名) */
18export const THEMES = [
19 { id: 'green', name: '星野绿', color: '#31c27c', rgb: '49, 194, 124', dark: '#1ba56a', soft: '#e8f8f0' },
20 { id: 'blue', name: '晴空蓝', color: '#3b82f6', rgb: '59, 130, 246', dark: '#2563eb', soft: '#e8f1fe' },
21 { id: 'teal', name: '冰川青', color: '#0d9488', rgb: '13, 148, 136', dark: '#0f766e', soft: '#e0f5f3' },
22 { id: 'purple', name: '薰衣草', color: '#8b5cf6', rgb: '139, 92, 246', dark: '#7c3aed', soft: '#f1ecfe' },
23 { id: 'pink', name: '樱花粉', color: '#ec4899', rgb: '236, 72, 153', dark: '#db2777', soft: '#fdeaf5' },
24 { id: 'orange', name: '落日橙', color: '#f97316', rgb: '249, 115, 22', dark: '#ea580c', soft: '#fef0e6' },
25 { id: 'red', name: '赤霞红', color: '#ef4444', rgb: '239, 68, 68', dark: '#dc2626', soft: '#fdeaea' },
26 { id: 'gold', name: '琥珀金', color: '#c2870a', rgb: '194, 135, 10', dark: '#a16f08', soft: '#fdf3e0' },
27]
28
29/** 当前主题 id(无设置时回退默认主题) */
30export const getThemeId = () => {
31 const id = getSetting()['common.theme']
32 return THEMES.some((t) => t.id === id) ? id : DEFAULT_THEME_ID
33}
34
35/** 按 id 取主题(不传则取当前主题) */
36export const getTheme = (id) => {
37 const target = id || getThemeId()
38 return THEMES.find((t) => t.id === target) || THEMES[0]
39}
40
41/** 当前主题对应的图标文件名后缀(供 lx-icon 使用) */
42export const getThemeIconKey = () => `theme-${getThemeId()}`
43
44/** 生成注入页面根元素的 CSS 变量样式对象 */
45export const buildThemeStyle = (theme) => {
46 const t = theme || getTheme()
47 return {
48 '--theme-color': t.color,
49 '--theme-color-rgb': t.rgb,
50 '--theme-color-dark': t.dark,
51 '--theme-color-soft': t.soft,
52 }
53}
54
55/** 原生 tabBar 图标基名(选中态),顺序须与 pages.json 的 tabBar.list 一致 */
56const TABBAR_ACTIVE_ICONS = [
57 '/static/tabbar/search-active',
58 '/static/tabbar/my-active',
59 '/static/tabbar/setting-active',
60]
61
62/** tabBar 页面路由:只有这些页面能设置原生 tabBar */
63const TABBAR_ROUTES = ['pages/index/index', 'pages/my-list/my-list', 'pages/setting/setting']
64
65/**
66 * 原生 tabBar 是否已被自绘导航栏隐藏(由手机端 app-tab-bar 在隐藏成功后置位)
67 *
68 * 隐藏状态下再调用 setTabBarStyle / setTabBarItem,会让原生 tabBar 重新参与原生布局:
69 * 部分机型会把它短暂显示出来盖住自绘栏,表现为"切 tab 时点了没反应、要多点几次"。
70 * 桌面端没有原生 tabBar(相关 API 为空实现),这里保持与手机端同一份代码。
71 */
72let nativeTabBarHidden = false
73
74/** 标记原生 tabBar 已被隐藏(隐藏成功后调用) */
75export const markNativeTabBarHidden = () => {
76 nativeTabBarHidden = true
77}
78
79/**
80 * 同步原生 tabBar 主题(选中文字色 + 选中态图标)
81 * 多主题图标由 scripts/gen-theme-tabbar.js 生成(默认主题继续用原始图标)。
82 * 非 tabBar 页面调用会失败,忽略即可。
83 */
84export const applyTabBarTheme = (theme) => {
85 // 原生 tabBar 已隐藏:主题由自绘栏自行渲染,这里必须跳过(详见 nativeTabBarHidden 注释)
86 if (nativeTabBarHidden) return
87 const t = theme || getTheme()
88 try {
89 const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : []
90 const route = pages.length ? pages[pages.length - 1].route : ''
91 // 非 tabBar 页面没有原生 tabBar,跳过以避免无意义的报错
92 if (route && TABBAR_ROUTES.indexOf(route) < 0) return
93 } catch (e) {
94 // ignore
95 }
96 try {
97 uni.setTabBarStyle({
98 color: '#606266',
99 selectedColor: t.color,
100 backgroundColor: '#ffffff',
101 borderStyle: 'black',
102 })
103 const suffix = t.id === DEFAULT_THEME_ID ? '' : `-${t.id}`
104 TABBAR_ACTIVE_ICONS.forEach((base, index) => {
105 try {
106 uni.setTabBarItem({ index, selectedIconPath: `${base}${suffix}.png` })
107 } catch (e) {
108 // 单个图标切换失败不影响其它 tab
109 }
110 })
111 } catch (e) {
112 // ignore
113 }
114}
115
116/** 切换主题:写设置 → 广播 → 同步 tabBar */
117export const setTheme = (id) => {
118 const theme = getTheme(id)
119 updateSetting({ 'common.theme': theme.id })
120 applyTabBarTheme(theme)
121 return theme
122}