App.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <script setup>
  2. import { ref, onMounted, nextTick, watch, onBeforeUnmount, getCurrentInstance } from 'vue';
  3. import { useI18n } from "vue-i18n";
  4. const { locale } = useI18n();
  5. import {
  6. onLoad,
  7. onShow,
  8. onLaunch
  9. } from '@dcloudio/uni-app'
  10. import {
  11. updateRoute
  12. } from "@/hooks/useRoute";
  13. import useGlobalStore from "@/stores/use-global-store";
  14. // import { useAppUpdate } from '@/hooks/useAppUpdate'
  15. // const { checkUpdate } = useAppUpdate()
  16. const globalStore = useGlobalStore()
  17. onLoad((options) => {
  18. updateRoute();
  19. // checkUpdate()
  20. })
  21. onShow((options) => {
  22. updateRoute();
  23. // checkUpdate()
  24. })
  25. // App.vue 或你的初始化文件中
  26. function initTheme() {
  27. // #ifdef H5
  28. // H5 端:使用 matchMedia 主动获取当前系统主题
  29. const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches
  30. const theme = isDarkMode ? 'dark' : 'light'
  31. globalStore.setGlobalTheme(theme)
  32. // 监听变化(你的 onThemeChange 已经能工作,但可以再加一层保险)
  33. const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)')
  34. const handleChange = (e) => {
  35. const newTheme = e.matches ? 'dark' : 'light'
  36. globalStore.setGlobalTheme(newTheme)
  37. }
  38. // 兼容旧版浏览器
  39. if (darkModeQuery.addEventListener) {
  40. darkModeQuery.addEventListener('change', handleChange)
  41. } else {
  42. darkModeQuery.addListener(handleChange)
  43. }
  44. // 设置 data-bs-theme 属性到 html 标签
  45. document.documentElement.setAttribute('data-bs-theme', theme);
  46. document.documentElement.setAttribute('data-color-theme', 'blue');
  47. // 同时设置到 body 标签
  48. document.body.setAttribute('data-bs-theme', theme);
  49. document.body.setAttribute('data-color-theme', 'blue');
  50. // #endif
  51. // #ifdef APP-PLUS
  52. // App 端:使用原生 API
  53. uni.getSystemInfo({
  54. success: (res) => {
  55. let theme = res.osTheme || 'light'
  56. globalStore.setGlobalTheme(theme)
  57. }
  58. })
  59. uni.onThemeChange((res) => {
  60. globalStore.setGlobalTheme(res.theme)
  61. })
  62. // #endif
  63. }
  64. onLaunch((options) => {
  65. // updateRoute();
  66. // checkUpdate()
  67. // 调用初始化
  68. // initTheme()
  69. // 处理 signup 路径
  70. handleSignupRoute(options)
  71. })
  72. // 解析 URL 参数
  73. const parseUrlParams = () => {
  74. const params = {}
  75. // #ifdef H5
  76. // H5 端:直接从浏览器 URL 解析
  77. if (typeof window !== 'undefined' && window.location) {
  78. const href = window.location.href
  79. // 解析 hash 部分
  80. const hashIndex = href.indexOf('#')
  81. if (hashIndex !== -1) {
  82. const hash = href.substring(hashIndex + 1)
  83. // 解析路径
  84. const pathMatch = hash.match(/^\/([^/?]+)/)
  85. if (pathMatch) {
  86. params.path = pathMatch[1]
  87. }
  88. // 解析路径参数 signup/19628/RHOP4WVa/B0
  89. const pathParams = hash.match(/^\/signup\/([^/]+)\/?([^/]+)?\/?([^/]+)?/)
  90. if (pathParams) {
  91. params.path = 'signup'
  92. params.p1 = pathParams[1]
  93. params.p2 = pathParams[2]
  94. params.p3 = pathParams[3]
  95. }
  96. // 解析 query 参数
  97. const queryIndex = hash.indexOf('?')
  98. if (queryIndex !== -1) {
  99. const queryStr = hash.substring(queryIndex + 1)
  100. const pairs = queryStr.split('&')
  101. pairs.forEach(pair => {
  102. const [key, value] = pair.split('=')
  103. if (key && value) {
  104. params[key] = decodeURIComponent(value)
  105. }
  106. })
  107. }
  108. }
  109. }
  110. // #endif
  111. // #ifndef H5
  112. // App 端:从 options 参数获取
  113. if (typeof options === 'object' && options !== null) {
  114. Object.assign(params, options)
  115. }
  116. // #endif
  117. return params
  118. }
  119. // 处理 signup 和 signin 路径(仅 H5 端)
  120. const handleSignupRoute = (options) => {
  121. // #ifdef H5
  122. // 解析 URL 参数(从浏览器 URL 解析)
  123. const query = parseUrlParams()
  124. console.log('解析到的参数:', query);
  125. // 处理 signin 路径(自动登录)
  126. if (query.path === 'signin' && query.sysLoginToken) {
  127. // 跳转到登录页面并携带 token 参数
  128. const loginUrl = `/pages/login/index?sysLoginToken=${encodeURIComponent(query.sysLoginToken)}`
  129. console.log('跳转到登录页面:', loginUrl);
  130. uni.reLaunch({
  131. url: loginUrl,
  132. success: () => {
  133. console.log('跳转成功');
  134. },
  135. fail: (err) => {
  136. console.error('跳转失败:', err);
  137. }
  138. })
  139. return
  140. }
  141. // 判断是否是 signup 路径
  142. const isSignup = query.path === 'signup' || query.s || query.signup || query.activeTab === '2'
  143. if (!isSignup) return
  144. // 获取参数
  145. const id = query.id || query.agentId || query.p1 || ''
  146. const subId = query.subId || query.w || query.p2 || ''
  147. const code = query.code || query.oc || query.p3 || ''
  148. // 构建登录页面 URL
  149. let loginUrl = '/pages/login/index?activeTab=2'
  150. // 携带参数
  151. if (id) loginUrl += `&id=${id}`
  152. if (subId) loginUrl += `&subId=${subId}`
  153. if (code) loginUrl += `&code=${code}`
  154. console.log('跳转到:', loginUrl);
  155. // 跳转到注册页面
  156. uni.reLaunch({
  157. url: loginUrl,
  158. success: () => {
  159. console.log('跳转成功');
  160. },
  161. fail: (err) => {
  162. console.error('跳转失败:', err);
  163. }
  164. })
  165. // #endif
  166. }
  167. watch(locale, () => {
  168. // const currentPath = route.path;
  169. // menu.value.forEach((item, index) => {
  170. // if (item.children) {
  171. // const isActive = item.children.some(child => child.path.includes(currentPath));
  172. // menu.value[index].isOpenMenu = isActive;
  173. // if (isActive) {
  174. // nextTick(() => {
  175. // updateSubmenuHeight(index);
  176. // });
  177. // }
  178. // }
  179. // });
  180. }, { immediate: true })
  181. // 检测版本号更新
  182. const checkWgtUpdate = async () => {
  183. try {
  184. const currentVersion = await getCurrentVersion()
  185. const res = await uni.request({
  186. url: `https://ucard.44a5c8109e4.com/wgt/list.json?_t=${Date.now()}`,
  187. method: 'GET',
  188. timeout: 5000
  189. })
  190. const files = res.data?.files || []
  191. if (!files.length) return
  192. const latestFile = files[files.length - 1]
  193. const latestVersion = latestFile
  194. if (!latestFile) return
  195. const lastInstalled = uni.getStorageSync('lastWgtVersion')
  196. console.log(lastInstalled,'lastInstalled');
  197. if (latestVersion === lastInstalled) return
  198. if (compareVersion(latestVersion, currentVersion) > 0) {
  199. downloadAndInstall(latestVersion)
  200. }
  201. } catch (e) {
  202. console.log('[wgt] update check failed', e)
  203. }
  204. }
  205. // 下载并安装
  206. const downloadAndInstall=(version) => {
  207. //TODO: 需要根据版本来确定url
  208. const url = `https://ucard.44a5c8109e4.com/wgt/__UNI__EFA7490.wgt`
  209. console.log(url,'downloadurl');
  210. uni.downloadFile({
  211. url,
  212. success: (res) => {
  213. if (res.statusCode === 200) {
  214. const filePath = res.tempFilePath
  215. plus.runtime.install(
  216. filePath, {
  217. force: true
  218. },
  219. () => {
  220. uni.setStorageSync('lastWgtVersion', version)
  221. console.log('[wgt] install success:', version)
  222. uni.setStorageSync('wgtNeedRestart', true)
  223. },
  224. (err) => {
  225. console.error('[wgt] install failed:', err)
  226. }
  227. )
  228. } else {
  229. console.error('[wgt] download status error:', res.statusCode)
  230. }
  231. },
  232. fail: (err) => {
  233. console.error('[wgt] download failed:', err)
  234. }
  235. })
  236. }
  237. // 获取当前版本
  238. const getCurrentVersion = async () => {
  239. return new Promise((resolve, reject) => {
  240. // #ifdef APP-PLUS
  241. try {
  242. plus.runtime.getProperty(plus.runtime.appid, (info) => {
  243. resolve(info.version)
  244. }, (error) => {
  245. reject(error)
  246. })
  247. } catch (error) {
  248. reject(error)
  249. }
  250. // #endif
  251. // #ifndef APP-PLUS
  252. reject(new Error('Not in APP-PLUS environment'))
  253. // #endif
  254. })
  255. }
  256. // 对比版本号
  257. const compareVersion = (v1, v2) => {
  258. const s1 = v1.split('.').map(Number)
  259. const s2 = v2.split('.').map(Number)
  260. const len = Math.max(s1.length, s2.length)
  261. for (let i = 0; i < len; i++) {
  262. const n1 = s1[i] || 0
  263. const n2 = s2[i] || 0
  264. if (n1 > n2) return 1
  265. if (n1 < n2) return -1
  266. }
  267. return 0
  268. }
  269. onMounted(() => {
  270. const sysInfo = uni.getSystemInfoSync();
  271. globalStore.setBarHeight(sysInfo.statusBarHeight || 60);
  272. // ---------- 新增 H5 端专属初始化 ----------
  273. // 仅在 H5 端执行(通过环境判断)
  274. // #ifdef H5
  275. if (typeof window !== 'undefined') {
  276. const instance = getCurrentInstance()
  277. if (instance) {
  278. window.vm = instance.proxy
  279. }
  280. }
  281. // #endif
  282. });
  283. </script>
  284. <style>
  285. /*每个页面公共css */
  286. </style>
  287. <style lang="scss">
  288. /* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
  289. @import "uview-plus/index.scss";
  290. @import "@/static/scss/global/global.scss";
  291. @import "@/static/scss/global/vu.css";
  292. @import "/static/scss/style.scss";
  293. @font-face {
  294. font-family: 'Google Sans';
  295. src: url('/static/Google_Sans/GoogleSans-VariableFont_GRAD,opsz,wght.ttf') format('truetype-variations');
  296. font-weight: 100 900;
  297. font-style: normal;
  298. font-display: swap;
  299. }
  300. /* 全局字体,不破坏 uni-icons 图标 */
  301. view,
  302. text,
  303. button,
  304. input,
  305. textarea,
  306. label {
  307. font-family: 'Google Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  308. font-weight: 400;
  309. }
  310. /* 专门保护 uni-icons 不被覆盖 */
  311. .uni-icon,
  312. [class*="uni-icons-"],
  313. .uni-icons {
  314. font-family: uniicons !important;
  315. }
  316. /* 让整个项目文字都能选中 */
  317. * {
  318. -webkit-user-select: text !important;
  319. user-select: text !important;
  320. }
  321. /* 修复滚动层无法选中 */
  322. view,
  323. text,
  324. div,
  325. span {
  326. -webkit-user-select: text !important;
  327. user-select: text !important;
  328. }
  329. /* 强制修复 uni-datetime-picker 重复渲染双日历 */
  330. // .uni-calendar+.uni-calendar {
  331. // display: none !important;
  332. // }
  333. :deep(.u-toolbar__wrapper__confirm) {
  334. font-size: 20px !important;
  335. }
  336. :deep(.u-toolbar__wrapper__cancel) {
  337. font-size: 20px !important;
  338. }
  339. .page {
  340. /* padding: 31px 31px 110px 31px; */
  341. box-sizing: border-box;
  342. /* background: var(--main-bg); */
  343. }
  344. html {
  345. --bs-bg-opacity: 1;
  346. background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;
  347. font-size: 16px !important;
  348. }
  349. uni-page-body {
  350. height: 100%;
  351. }
  352. page {
  353. --bs-bg-opacity: 1;
  354. background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;
  355. }
  356. </style>