index.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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. document.body.style.setProperty("--background-color-content", bgContentColor);
  144. const tabbar = document.querySelector(".uni-tabbar");
  145. if (tabbar) {
  146. (tabbar as HTMLElement).style.backgroundColor = tabBgColor;
  147. }
  148. const pageHead = document.querySelector(".uni-page-head");
  149. if (pageHead) {
  150. (pageHead as HTMLElement).style.backgroundColor = navBgColor;
  151. (pageHead as HTMLElement).style.color = navTextStyle;
  152. }
  153. const pageHeadBtnPath = document.querySelector(".uni-page-head-btn path");
  154. if (pageHeadBtnPath) {
  155. (pageHeadBtnPath as HTMLElement).style.fill = navTextStyle;
  156. }
  157. window.parent.postMessage(
  158. {
  159. type: "theme-change",
  160. isDark: isDark.value
  161. },
  162. "*"
  163. );
  164. };
  165. /**
  166. * 切换主题
  167. */
  168. export const toggleTheme = () => {
  169. if (isDark.value) {
  170. setTheme("light");
  171. } else {
  172. setTheme("dark");
  173. }
  174. };
  175. /**
  176. * 初始化主题监听
  177. * APP 下监听系统主题和 App 主题变化
  178. * H5/小程序下监听 hostTheme 变化
  179. */
  180. export const initTheme = () => {
  181. // #ifdef APP-ANDROID || APP-IOS
  182. uni.onOsThemeChange((res) => {
  183. if (isAuto.value) {
  184. setTimeout(() => {
  185. uni.setAppTheme({
  186. theme: res.osTheme,
  187. success: () => {
  188. theme.value = res.osTheme;
  189. }
  190. });
  191. }, 100);
  192. }
  193. });
  194. // 监听 App 主题变化
  195. uni.onAppThemeChange((res) => {
  196. theme.value = res.appTheme;
  197. });
  198. // #endif
  199. // #ifdef MP
  200. uni.onHostThemeChange((res) => {
  201. setTheme(res.hostTheme);
  202. });
  203. // #endif
  204. // #ifdef H5
  205. // 监听父窗口发送的主题变化消息
  206. // [BUG] uni.onHostThemeChange 打包会丢失
  207. // uni.onHostThemeChange((res) => {
  208. // setTheme(res.hostTheme);
  209. // });
  210. window.addEventListener("message", (e) => {
  211. if (e.data?.type == "theme-change") {
  212. setTheme(e.data.isDark ? "dark" : "light");
  213. }
  214. });
  215. // #endif
  216. };