index.ts 5.2 KB

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