index.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import { computed, ref } from "vue";
  2. import uniTheme from "@/theme.json";
  3. import { router } from "../router";
  4. import { ctx } from "../ctx";
  5. import { isNull } from "../utils";
  6. // 主题类型定义,仅支持 light 和 dark
  7. type Theme = "light" | "dark";
  8. // 是否为自动主题模式(跟随系统)
  9. export const isAuto = ref(true);
  10. /**
  11. * 获取页面样式
  12. * @param key 样式 key
  13. * @returns 样式值
  14. */
  15. export function getStyle(key: string): string | null {
  16. // 页面配置
  17. const style = router.route()?.style;
  18. // 页面配置 key 映射
  19. const names = {
  20. bgColor: "backgroundColor",
  21. bgContentColor: "backgroundColorContent",
  22. navBgColor: "navigationBarBackgroundColor",
  23. navTextStyle: "navigationBarTextStyle"
  24. };
  25. // 如果页面配置存在,则使用页面配置
  26. if (style != null) {
  27. if (names[key] != null) {
  28. const val = style[names[key]!] as string | null;
  29. if (val != null) {
  30. return val;
  31. }
  32. }
  33. }
  34. return null;
  35. }
  36. /**
  37. * 获取颜色
  38. * @param name 颜色名称
  39. * @returns 颜色值
  40. */
  41. export const getColor = (name: string) => {
  42. if (isNull(ctx.color)) {
  43. return "";
  44. }
  45. return ctx.color[name] as string;
  46. };
  47. /**
  48. * 获取 uniapp 主题配置
  49. */
  50. export function getConfig(key: string): string {
  51. // 主题配置
  52. const themeVal = ((isDark.value ? uniTheme.dark : uniTheme.light) as UTSJSONObject)[key] as
  53. | string
  54. | null;
  55. // 页面样式
  56. const styleVal = getStyle(key);
  57. return styleVal ?? themeVal ?? "";
  58. }
  59. /**
  60. * 获取当前主题
  61. * APP 下优先获取 appTheme,若为 auto 则跟随系统 osTheme
  62. * H5/小程序下优先获取 hostTheme,否则默认为 light
  63. */
  64. const getTheme = () => {
  65. let value: string | null;
  66. // #ifdef APP
  67. const appInfo = uni.getAppBaseInfo();
  68. // @ts-ignore
  69. const appTheme = appInfo.appTheme as string;
  70. const osTheme = uni.getSystemInfoSync().osTheme!;
  71. // 如果 appTheme 为 auto,则跟随系统主题,否则使用 appTheme
  72. value = appTheme == "auto" ? osTheme : appTheme;
  73. isAuto.value = appTheme == "auto";
  74. // #endif
  75. // #ifdef H5 || MP
  76. const hostTheme = uni.getAppBaseInfo().hostTheme;
  77. if (hostTheme) {
  78. // 如果有 hostTheme,则使用 hostTheme
  79. value = hostTheme;
  80. } else {
  81. // 默认使用 light 主题
  82. value = "light";
  83. }
  84. // #endif
  85. return value as Theme;
  86. };
  87. // 当前主题响应式变量
  88. export const theme = ref<Theme>(getTheme());
  89. /**
  90. * 是否为暗色模式
  91. */
  92. export const isDark = computed(() => {
  93. return theme.value == "dark";
  94. });
  95. /**
  96. * 切换自动主题模式(仅 APP 有效)
  97. */
  98. export const setIsAuto = () => {
  99. // #ifdef APP
  100. isAuto.value = !isAuto.value;
  101. if (isAuto.value) {
  102. // 设置为自动主题,跟随系统
  103. uni.setAppTheme({
  104. theme: "auto"
  105. });
  106. } else {
  107. // 关闭自动,使用当前 theme
  108. setTheme(theme.value);
  109. }
  110. // #endif
  111. };
  112. /**
  113. * 设置主题
  114. * @param value 主题值("light" | "dark")
  115. */
  116. export const setTheme = (value: Theme) => {
  117. // 如果当前主题与目标主题一致,则不做处理
  118. if (theme.value == value) return;
  119. // 关闭自动主题
  120. isAuto.value = false;
  121. // #ifdef APP
  122. uni.setAppTheme({
  123. theme: value,
  124. success: () => {
  125. // 设置成功后更新 theme
  126. theme.value = value;
  127. }
  128. });
  129. // #endif
  130. // #ifndef APP
  131. theme.value = value;
  132. // #endif
  133. // #ifdef H5
  134. setH5();
  135. // #endif
  136. };
  137. // 设置 H5 下的主题色
  138. export const setH5 = () => {
  139. const bgContentColor = getConfig("bgContentColor");
  140. const tabBgColor = getConfig("tabBgColor");
  141. const navBgColor = getConfig("navBgColor");
  142. const navTextStyle = getConfig("navTextStyle");
  143. // 设置主题类
  144. document.documentElement.classList.toggle("dark", isDark.value);
  145. // 设置背景色
  146. document.body.style.setProperty("--background-color-content", bgContentColor);
  147. // 设置 tabbar 背景色
  148. const tabbar = document.querySelector(".uni-tabbar");
  149. if (tabbar) {
  150. (tabbar as HTMLElement).style.backgroundColor = tabBgColor;
  151. }
  152. // 设置页面头部背景色和文字颜色
  153. const pageHead = document.querySelector(".uni-page-head");
  154. if (pageHead) {
  155. (pageHead as HTMLElement).style.backgroundColor = navBgColor;
  156. (pageHead as HTMLElement).style.color = navTextStyle;
  157. }
  158. // 设置页面头部按钮路径颜色
  159. const pageHeadBtnPath = document.querySelector(".uni-page-head-btn path");
  160. if (pageHeadBtnPath) {
  161. (pageHeadBtnPath as HTMLElement).style.fill = navTextStyle;
  162. }
  163. // 发送主题变化消息
  164. window.parent.postMessage(
  165. {
  166. type: "theme-change",
  167. isDark: isDark.value
  168. },
  169. "*"
  170. );
  171. };
  172. /**
  173. * 切换主题
  174. */
  175. export const toggleTheme = () => {
  176. if (isDark.value) {
  177. setTheme("light");
  178. } else {
  179. setTheme("dark");
  180. }
  181. };
  182. /**
  183. * 初始化主题监听
  184. * APP 下监听系统主题和 App 主题变化
  185. * H5/小程序下监听 hostTheme 变化
  186. */
  187. export const initTheme = () => {
  188. // #ifdef APP-ANDROID || APP-IOS
  189. uni.onOsThemeChange((res) => {
  190. if (isAuto.value) {
  191. setTimeout(() => {
  192. uni.setAppTheme({
  193. theme: res.osTheme,
  194. success: () => {
  195. theme.value = res.osTheme;
  196. }
  197. });
  198. }, 100);
  199. }
  200. });
  201. // 监听 App 主题变化
  202. uni.onAppThemeChange((res) => {
  203. theme.value = res.appTheme;
  204. });
  205. // #endif
  206. // #ifdef MP
  207. uni.onHostThemeChange((res) => {
  208. setTheme(res.hostTheme);
  209. });
  210. // #endif
  211. // #ifdef H5
  212. // 监听父窗口发送的主题变化消息
  213. // [BUG] uni.onHostThemeChange 打包会丢失
  214. // uni.onHostThemeChange((res) => {
  215. // setTheme(res.hostTheme);
  216. // });
  217. window.addEventListener("message", (e) => {
  218. if (e.data?.type == "theme-change") {
  219. setTheme(e.data.isDark ? "dark" : "light");
  220. }
  221. });
  222. // #endif
  223. };