verce-vue-test 0.0.28 → 0.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,7 @@
1
- <script setup lang="ts">
2
- import { useAppStore } from '@/stores/app'
3
-
4
- const appStore = useAppStore()
5
-
6
- onMounted(async () => {
7
- await appStore.initNativeDetection()
8
-
9
- // 测试环境 + 原生设备:加载 vConsole 调试面板
10
- if (import.meta.env.VITE_APP_ENV === 'test' && appStore.isNative) {
11
- import('vconsole').then(({ default: VConsole }) => new VConsole())
12
- }
13
- })
14
- </script>
1
+ <script setup lang="ts"></script>
15
2
 
16
3
  <template>
17
- <router-view v-if="appStore.ready" v-slot="{ Component }">
4
+ <router-view v-slot="{ Component }">
18
5
  <keep-alive include="Home">
19
6
  <component :is="Component" />
20
7
  </keep-alive>
@@ -161,17 +161,47 @@ export const showOptionMenu = (): void => MXWebui('showOptionMenu')
161
161
  export const setCustomHeaderMenu = (...args: unknown[]): void =>
162
162
  MXWebui('setCustomHeaderMenu', ...args)
163
163
 
164
+ /** 超时兜底:原生回调未触发时自动执行 onError */
165
+ function withCallbackTimeout<T extends (...args: any[]) => void>(
166
+ onSuccess: T,
167
+ onError: (reason: string) => void,
168
+ ms: number,
169
+ label: string,
170
+ ): T {
171
+ const timer = setTimeout(() => onError(`[MX] ${label} 超时(${ms}ms)`), ms)
172
+ return ((...args: unknown[]) => {
173
+ clearTimeout(timer)
174
+ onSuccess(...args)
175
+ }) as unknown as T
176
+ }
177
+
164
178
  /** 获取当前登录用户信息 */
165
- export const getCurrentUser = (): Promise<MXUser> =>
166
- new Promise((resolve) => {
167
- MXCommon('getCurrentUser', resolve)
168
- })
179
+ export const getCurrentUser = (
180
+ onSuccess: (user: MXUser) => void,
181
+ onError?: (reason: string) => void,
182
+ timeout = 5000,
183
+ ): void => {
184
+ MXCommon('getCurrentUser', withCallbackTimeout(
185
+ onSuccess,
186
+ (reason) => { console.error(reason); onError?.(reason) },
187
+ timeout,
188
+ 'getCurrentUser',
189
+ ))
190
+ }
169
191
 
170
192
  /** 从原生客户端获取加密密钥 */
171
- export const getEncryptString = (): Promise<string> =>
172
- new Promise((resolve) => {
173
- MXCommon('getEncryptString', resolve)
174
- })
193
+ export const getEncryptString = (
194
+ onSuccess: (secret: string) => void,
195
+ onError?: (reason: string) => void,
196
+ timeout = 5000,
197
+ ): void => {
198
+ MXCommon('getEncryptString', withCallbackTimeout(
199
+ onSuccess,
200
+ (reason) => { console.error(reason); onError?.(reason) },
201
+ timeout,
202
+ 'getEncryptString',
203
+ ))
204
+ }
175
205
 
176
206
  /** 打开原生选人组件 */
177
207
  export const MXSelectUsers = (
@@ -187,9 +217,10 @@ export const MXSelectUsers = (
187
217
  * 原生 AJAX 请求
188
218
  *
189
219
  * 使用原生应用的 HTTP 客户端发起请求,绕过浏览器同源策略限制。
220
+ * ajax 本身通过 success/error 回调,保留 Promise 包装;回调未触发时 15s 超时 reject。
190
221
  */
191
- export const ajax = <T = unknown>(params: Omit<MXAjaxParams, 'success' | 'error'>): Promise<MXAjaxResponse<T>> =>
192
- new Promise((resolve, reject) => {
222
+ export const ajax = <T = unknown>(params: Omit<MXAjaxParams, 'success' | 'error'>): Promise<MXAjaxResponse<T>> => {
223
+ const request = new Promise<MXAjaxResponse<T>>((resolve, reject) => {
193
224
  const requestParams: MXAjaxParams = {
194
225
  ...params,
195
226
  url: `${import.meta.env.VITE_API_BASE_URL || ''}${params.url}`,
@@ -205,6 +236,13 @@ export const ajax = <T = unknown>(params: Omit<MXAjaxParams, 'success' | 'error'
205
236
  MXCommon('ajax', requestParams)
206
237
  })
207
238
 
239
+ const timeout = new Promise<MXAjaxResponse<T>>((_, reject) =>
240
+ setTimeout(() => reject(new Error(`[MX] ajax ${params.type} ${params.url} 超时`)), 15000),
241
+ )
242
+
243
+ return Promise.race([request, timeout])
244
+ }
245
+
208
246
  /** 原生 GET 请求 */
209
247
  export const ajaxGet = <T = unknown>(url: string, query?: Record<string, unknown>): Promise<MXAjaxResponse<T>> => {
210
248
  const queryString = query
@@ -5,6 +5,8 @@ import 'vant/lib/index.css'
5
5
 
6
6
  import App from './App.vue'
7
7
  import router from './router'
8
+ import { useAppStore } from '@/stores/app'
9
+ import { hideWebViewTitle } from '@/core/mxApi'
8
10
 
9
11
  const app = createApp(App)
10
12
  const pinia = createPinia()
@@ -14,4 +16,19 @@ pinia.use(piniaPluginPersistedstate)
14
16
  app.use(pinia)
15
17
  app.use(router)
16
18
 
19
+ // 等待原生环境检测完成后再挂载,确保所有组件读到的 isNative 都是正确值
20
+ const appStore = useAppStore()
21
+ await appStore.initNativeDetection()
22
+
23
+ // 测试环境 + 原生设备:加载 vConsole 调试面板
24
+ if (import.meta.env.VITE_APP_ENV === 'test' && appStore.isNative) {
25
+ const { default: VConsole } = await import('vconsole')
26
+ new VConsole()
27
+ }
28
+
29
+ // 原生环境:隐藏原生标题栏
30
+ if (appStore.isNative) {
31
+ hideWebViewTitle()
32
+ }
33
+
17
34
  app.mount('#app')
@@ -5,12 +5,10 @@ import { whenNativeReady } from '@/core/mxApi'
5
5
  * 应用级状态 Store
6
6
  *
7
7
  * 存放全局共享的运行时状态,不持久化。
8
+ * initNativeDetection 在 main.ts 挂载前调用,组件读取时值已就绪。
8
9
  */
9
10
  export const useAppStore = defineStore('app', () => {
10
- /** 原生环境检测是否已完成(App.vue onMounted 后变为 true) */
11
- const ready = ref(false)
12
-
13
- /** 是否在原生 APP WebView 环境中(由 App.vue 初始化时检测并写入) */
11
+ /** 是否在原生 APP WebView 环境中 */
14
12
  const isNative = ref(false)
15
13
 
16
14
  /**
@@ -18,12 +16,10 @@ export const useAppStore = defineStore('app', () => {
18
16
  *
19
17
  * 等待 deviceready 事件后判断 window.MXCommon 是否存在,
20
18
  * 浏览器环境下 3 秒超时后返回 false。
21
- * 应在 App.vue onMounted 中调用,全局只需一次。
22
19
  */
23
20
  async function initNativeDetection() {
24
21
  isNative.value = await whenNativeReady()
25
- ready.value = true
26
22
  }
27
23
 
28
- return { ready, isNative, initNativeDetection }
24
+ return { isNative, initNativeDetection }
29
25
  })
@@ -13,7 +13,7 @@ export const useUserStore = defineStore(
13
13
  // -------------------------------- State --------------------------------
14
14
 
15
15
  /** 登录凭证,登录成功后由后端返回,后续请求通过请求头携带 */
16
- const token = ref('7')
16
+ const token = ref('')
17
17
 
18
18
  // ------------------------------ Actions --------------------------------
19
19
 
@@ -18,18 +18,23 @@ const form = ref({
18
18
  const loading = ref(false)
19
19
 
20
20
  // 原生环境:从客户端获取密钥,直接交换 token
21
- onMounted(async () => {
21
+ onMounted(() => {
22
22
  if (!appStore.isNative) return
23
23
 
24
24
  loading.value = true
25
- try {
26
- const secret = await getEncryptString()
27
- console.log(secret)
28
- } catch {
29
- // 错误已由 request.ts 拦截器统一处理
30
- } finally {
31
- loading.value = false
32
- }
25
+ getEncryptString(
26
+ (secret) => {
27
+ loading.value = false
28
+ console.log(secret)
29
+ // TODO: secret 自动换取 token 并跳转首页
30
+ },
31
+ (reason) => {
32
+ loading.value = false
33
+ console.error(reason)
34
+ showToast({ message: '获取设备凭证失败,请手动登录', duration: 3000 })
35
+ appStore.isNative = false
36
+ },
37
+ )
33
38
  })
34
39
 
35
40
  async function onSubmit() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verce-vue-test",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "scripts": {