index.ts 5.0 KB

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