af-mobile-client-vue3 1.4.62 → 1.4.64

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,395 +1,395 @@
1
- <script setup lang="ts">
2
- import { getUserInfo } from '@af-mobile-client-vue3/api/user'
3
- import { getUserPermissions } from '@af-mobile-client-vue3/services/api/search'
4
- import { useSettingStore } from '@af-mobile-client-vue3/stores/modules/setting'
5
- import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
6
- import { UserType } from '@af-mobile-client-vue3/types/auth'
7
- import { PLATFORM_ROUTE_MAP, PlatformType } from '@af-mobile-client-vue3/types/platform'
8
- import { isWechat } from '@af-mobile-client-vue3/utils/platform-auth'
9
- import { funcToRouter, loadRoutes } from '@af-mobile-client-vue3/utils/routerUtil'
10
- import { secureStorageBatchWrite } from '@af-mobile-client-vue3/utils/secureStorage'
11
- import { Loading as VanLoading } from 'vant'
12
- import { onMounted, ref } from 'vue'
13
- import { useRoute, useRouter } from 'vue-router'
14
-
15
- const route = useRoute()
16
- const router = useRouter()
17
- const userStore = useUserStore()
18
- const setting = useSettingStore()
19
- // 响应式状态
20
- const isLoading = ref(true)
21
- const errorMessage = ref('')
22
- const hasError = ref(false)
23
-
24
- // 主要认证处理逻辑
25
- async function handleAuth() {
26
- try {
27
- const authParam = route.query as Record<string, string>
28
-
29
- if (authParam.platformType) {
30
- switch (authParam.platformType) {
31
- case PlatformType.WECHAT_OFFICIAL:
32
- case PlatformType.WECHAT_MINI:
33
- // 微信公众号,微信小程序授权登录参数
34
- await handleExternalLogin(authParam)
35
- break
36
- default:
37
- // 其他环境使用原生扫码(需要页面跳转或调用原生API)
38
- throw new Error('当前环境不支持授权登录')
39
- }
40
- }
41
- else {
42
- // 无授权参数,检查是否已登录或等待授权
43
- await handleWaitingForAuth()
44
- }
45
- }
46
- catch (error) {
47
- console.error('[AuthLoading] 认证处理失败:', error)
48
-
49
- isLoading.value = false
50
- hasError.value = true
51
-
52
- if (error instanceof Error) {
53
- errorMessage.value = '授权失败,请尝试重新进入此页面'
54
- }
55
- else {
56
- errorMessage.value = '认证失败'
57
- }
58
- }
59
- }
60
-
61
- // 处理外部用户登录
62
- async function handleExternalLogin(loginParams: any) {
63
- let data: any
64
- if (loginParams.platformType === PlatformType.WECHAT_OFFICIAL) {
65
- data = await userStore.loginExternal(Object.assign(loginParams, {
66
- isMobile: true,
67
- }))
68
- }
69
- else {
70
- data = await userStore.loginExternalMini(Object.assign(JSON.parse(loginParams.appData), {
71
- isMobile: true,
72
- }))
73
- }
74
-
75
- const loginInfo = {
76
- f: data,
77
- jwt: data.id,
78
- r: data.r,
79
- }
80
- userStore.setLogin(loginInfo)
81
- // 设置用户信息
82
- userStore.setUserType(data.resources.userType || UserType.SYSTEM)
83
- userStore.setPlatformType(loginParams.platformType || PlatformType.WECHAT_OFFICIAL)
84
- await afterGeneral(data.resources, data.access_token, userStore.getUserType())
85
-
86
- // 获取重定向地址并跳转
87
- let targetPath = route.query.redirect as string
88
- if (data.resources.userType === UserType.EXTERNAL) {
89
- // 外部用户没有重定向地址,直接跳转到外部用户首页
90
- // 对应的外部用户只能跳转到对应的页面
91
- const platformPrefixes = PLATFORM_ROUTE_MAP[data.resources.platformType]
92
- if (!targetPath.startsWith(platformPrefixes)) {
93
- targetPath = platformPrefixes
94
- }
95
- }
96
- // 方法1: 使用解构和删除属性
97
- await router.replace({
98
- path: targetPath,
99
- query: (() => {
100
- const query = { ...route.query }
101
- // 删除不需要的参数
102
- delete query.redirect
103
- delete query.state
104
- delete query.tenantName
105
- delete query.platformUserId
106
- delete query.token
107
- delete query.code
108
- delete query.platformType
109
- delete query.appData
110
- // 可以继续删除其他不需要的参数
111
- return query
112
- })(),
113
- })
114
- }
115
-
116
- function closeWindows() {
117
- if (isWechat()) {
118
- (window as any).WeixinJSBridge.call('closeWindow')
119
- }
120
- else {
121
- // 关闭页面
122
- if (navigator.userAgent.includes('Firefox') || navigator.userAgent.includes('Chrome')) {
123
- window.open('about:blank', '_self')
124
- window.close()
125
- }
126
- else {
127
- window.opener = null
128
- window.open('', '_self')
129
- window.close()
130
- }
131
- }
132
- }
133
-
134
- async function afterGeneral(result, token = '', userType = UserType.SYSTEM) {
135
- if (userType === UserType.SYSTEM && result.platformType && result.functions && result.functions.length === 0 && result.ename) {
136
- const res = await getUserInfo({
137
- username: result.ename,
138
- resourceName: setting.getSetting()?.routerName || '智慧手机',
139
- })
140
- console.log('获取用户信息', res)
141
- if (res.resources.functions && res.resources.functions.length > 0) {
142
- result.functions = res.resources.functions
143
- }
144
- if (res.resources.permissions && res.resources.permissions.length > 0) {
145
- result.permissions = res.resources.permissions
146
- }
147
- }
148
- // 排序 functions 及其嵌套的 children
149
- if (result.functions && Array.isArray(result.functions)) {
150
- // 对顶层菜单进行排序
151
- result.functions.sort((a, b) => (a.position || 0) - (b.position || 0))
152
-
153
- // 对每个菜单的子项进行排序
154
- for (const item of result.functions) {
155
- if (item.children && Array.isArray(item.children)) {
156
- item.children.sort((a, b) => (a.position || 0) - (b.position || 0))
157
- }
158
- }
159
- }
160
-
161
- const user: any = {
162
- ...result,
163
- username: result.ename,
164
- avatar: result.avatar,
165
- }
166
- userStore.setUserInfo(user)
167
- // 如果result中没有返回 权限 需要主动获取权限列表
168
- if (!result.permissions && userType !== UserType.EXTERNAL) {
169
- result.permissions = await getUserPermissions(result.id)
170
- }
171
- userStore.setPermissions(result.permissions)
172
- userStore.setRoles([{ id: userType, operation: ['add', 'edit', 'delete'] }])
173
- // 加载路由
174
- if (user.functions) {
175
- loadRoutes(funcToRouter(user.functions))
176
- }
177
-
178
- // 存储登录数据到本地缓存
179
- secureStorageBatchWrite([
180
- {
181
- key: 'loginData',
182
- value: {
183
- token,
184
- },
185
- },
186
- {
187
- key: 'userData',
188
- value: result,
189
- },
190
- ])
191
-
192
- // 每次重新登录时,清除indexedDB缓存
193
- // indexedDB.clear()
194
- }
195
-
196
- // TODO 处理等待授权的情况 暂未实现目前不确定三方对接方式
197
- async function handleWaitingForAuth() {
198
- console.log('[AuthLoading] 处理等待授权情况')
199
-
200
- // 检查是否已登录
201
- const token = userStore.getToken()
202
- const userType = userStore.getUserType()
203
-
204
- if (token) {
205
- const targetPath = userType === 'EXTERNAL' ? '/ex' : '/'
206
- router.replace(targetPath)
207
- }
208
- else {
209
- // 根据来源决定处理方式
210
- const source = route.query.source as string
211
- closeWindows()
212
-
213
- if (source === 'external') {
214
- // 外部用户需要重新获取授权
215
- throw new Error('第三方授权已过期,请重新尝试')
216
- }
217
- else {
218
- // 其他情况
219
- throw new Error('需要重新获取授权')
220
- }
221
- }
222
- }
223
-
224
- onMounted(() => {
225
- handleAuth()
226
- })
227
- </script>
228
-
229
- <template>
230
- <div class="auth-loading">
231
- <!-- Logo区域 -->
232
- <div class="logo-section">
233
- <div class="company-logo">
234
- <div class="logo-text">
235
- 智慧燃气
236
- </div>
237
- </div>
238
- </div>
239
-
240
- <!-- Loading区域 -->
241
- <div v-if="isLoading && !hasError" class="loading-section">
242
- <div class="loading-spinner">
243
- <VanLoading type="spinner" size="48px" color="#007BFF" />
244
- </div>
245
- <div class="loading-text">
246
- 稍等,这就好
247
- </div>
248
- </div>
249
-
250
- <!-- 错误处理区域 -->
251
- <div v-if="hasError" class="error-section">
252
- <div class="error-icon">
253
- ⚠️
254
- </div>
255
- <div class="error-message">
256
- {{ errorMessage }}
257
- </div>
258
- </div>
259
- </div>
260
- </template>
261
-
262
- <style scoped>
263
- /* 主容器 */
264
- .auth-loading {
265
- min-height: 100vh;
266
- background: #ffffff;
267
- display: flex;
268
- flex-direction: column;
269
- justify-content: center;
270
- align-items: center;
271
- padding: 32px 20px;
272
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
273
- }
274
-
275
- /* Logo区域 */
276
- .logo-section {
277
- margin-bottom: 64px;
278
- }
279
-
280
- .company-logo {
281
- display: flex;
282
- align-items: center;
283
- justify-content: center;
284
- }
285
-
286
- .logo-text {
287
- font-size: 28px;
288
- font-weight: 600;
289
- color: #007bff;
290
- letter-spacing: 2px;
291
- }
292
-
293
- /* Loading区域 */
294
- .loading-section {
295
- display: flex;
296
- flex-direction: column;
297
- align-items: center;
298
- text-align: center;
299
- }
300
-
301
- .loading-spinner {
302
- margin-bottom: 24px;
303
- }
304
-
305
- .loading-text {
306
- font-size: 18px;
307
- font-weight: 500;
308
- color: #2c3e50;
309
- margin-bottom: 12px;
310
- line-height: 1.4;
311
- }
312
-
313
- .platform-text {
314
- font-size: 14px;
315
- color: #6c757d;
316
- line-height: 1.4;
317
- }
318
-
319
- /* 错误处理区域 */
320
- .error-section {
321
- display: flex;
322
- flex-direction: column;
323
- align-items: center;
324
- text-align: center;
325
- max-width: 300px;
326
- }
327
-
328
- .error-icon {
329
- font-size: 48px;
330
- margin-bottom: 20px;
331
- opacity: 0.8;
332
- }
333
-
334
- .error-message {
335
- font-size: 16px;
336
- color: #dc3545;
337
- margin-bottom: 24px;
338
- line-height: 1.5;
339
- }
340
-
341
- /* 响应式设计 */
342
- @media (max-width: 480px) {
343
- .auth-loading {
344
- padding: 24px 16px;
345
- }
346
-
347
- .logo-text {
348
- font-size: 24px;
349
- }
350
-
351
- .loading-text {
352
- font-size: 16px;
353
- }
354
-
355
- .platform-text {
356
- font-size: 13px;
357
- }
358
-
359
- .error-message {
360
- font-size: 15px;
361
- }
362
- }
363
-
364
- /* 大屏适配 */
365
- @media (min-width: 768px) {
366
- .auth-loading {
367
- max-width: 400px;
368
- margin: 0 auto;
369
- }
370
- }
371
-
372
- /* 兼容性优化 - 避免使用可能有问题的CSS特性 */
373
- .loading-spinner :deep(.van-loading__spinner) {
374
- animation-duration: 1s;
375
- animation-timing-function: linear;
376
- animation-iteration-count: infinite;
377
- }
378
-
379
- /* 禁用选择,提升用户体验 */
380
- .auth-loading {
381
- -webkit-user-select: none;
382
- -moz-user-select: none;
383
- -ms-user-select: none;
384
- user-select: none;
385
- }
386
-
387
- /* 确保文字在各种设备上清晰显示 */
388
- .logo-text,
389
- .loading-text,
390
- .platform-text,
391
- .error-message {
392
- -webkit-font-smoothing: antialiased;
393
- -moz-osx-font-smoothing: grayscale;
394
- }
395
- </style>
1
+ <script setup lang="ts">
2
+ import { getUserInfo } from '@af-mobile-client-vue3/api/user'
3
+ import { getUserPermissions } from '@af-mobile-client-vue3/services/api/search'
4
+ import { useSettingStore } from '@af-mobile-client-vue3/stores/modules/setting'
5
+ import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
6
+ import { UserType } from '@af-mobile-client-vue3/types/auth'
7
+ import { PLATFORM_ROUTE_MAP, PlatformType } from '@af-mobile-client-vue3/types/platform'
8
+ import { isWechat } from '@af-mobile-client-vue3/utils/platform-auth'
9
+ import { funcToRouter, loadRoutes } from '@af-mobile-client-vue3/utils/routerUtil'
10
+ import { secureStorageBatchWrite } from '@af-mobile-client-vue3/utils/secureStorage'
11
+ import { Loading as VanLoading } from 'vant'
12
+ import { onMounted, ref } from 'vue'
13
+ import { useRoute, useRouter } from 'vue-router'
14
+
15
+ const route = useRoute()
16
+ const router = useRouter()
17
+ const userStore = useUserStore()
18
+ const setting = useSettingStore()
19
+ // 响应式状态
20
+ const isLoading = ref(true)
21
+ const errorMessage = ref('')
22
+ const hasError = ref(false)
23
+
24
+ // 主要认证处理逻辑
25
+ async function handleAuth() {
26
+ try {
27
+ const authParam = route.query as Record<string, string>
28
+
29
+ if (authParam.platformType) {
30
+ switch (authParam.platformType) {
31
+ case PlatformType.WECHAT_OFFICIAL:
32
+ case PlatformType.WECHAT_MINI:
33
+ // 微信公众号,微信小程序授权登录参数
34
+ await handleExternalLogin(authParam)
35
+ break
36
+ default:
37
+ // 其他环境使用原生扫码(需要页面跳转或调用原生API)
38
+ throw new Error('当前环境不支持授权登录')
39
+ }
40
+ }
41
+ else {
42
+ // 无授权参数,检查是否已登录或等待授权
43
+ await handleWaitingForAuth()
44
+ }
45
+ }
46
+ catch (error) {
47
+ console.error('[AuthLoading] 认证处理失败:', error)
48
+
49
+ isLoading.value = false
50
+ hasError.value = true
51
+
52
+ if (error instanceof Error) {
53
+ errorMessage.value = '授权失败,请尝试重新进入此页面'
54
+ }
55
+ else {
56
+ errorMessage.value = '认证失败'
57
+ }
58
+ }
59
+ }
60
+
61
+ // 处理外部用户登录
62
+ async function handleExternalLogin(loginParams: any) {
63
+ let data: any
64
+ if (loginParams.platformType === PlatformType.WECHAT_OFFICIAL) {
65
+ data = await userStore.loginExternal(Object.assign(loginParams, {
66
+ isMobile: true,
67
+ }))
68
+ }
69
+ else {
70
+ data = await userStore.loginExternalMini(Object.assign(JSON.parse(loginParams.appData), {
71
+ isMobile: true,
72
+ }))
73
+ }
74
+
75
+ const loginInfo = {
76
+ f: data,
77
+ jwt: data.id,
78
+ r: data.r,
79
+ }
80
+ userStore.setLogin(loginInfo)
81
+ // 设置用户信息
82
+ userStore.setUserType(data.resources.userType || UserType.SYSTEM)
83
+ userStore.setPlatformType(loginParams.platformType || PlatformType.WECHAT_OFFICIAL)
84
+ await afterGeneral(data.resources, data.access_token, userStore.getUserType())
85
+
86
+ // 获取重定向地址并跳转
87
+ let targetPath = route.query.redirect as string
88
+ if (data.resources.userType === UserType.EXTERNAL) {
89
+ // 外部用户没有重定向地址,直接跳转到外部用户首页
90
+ // 对应的外部用户只能跳转到对应的页面
91
+ const platformPrefixes = PLATFORM_ROUTE_MAP[data.resources.platformType]
92
+ if (!targetPath.startsWith(platformPrefixes)) {
93
+ targetPath = platformPrefixes
94
+ }
95
+ }
96
+ // 方法1: 使用解构和删除属性
97
+ await router.replace({
98
+ path: targetPath,
99
+ query: (() => {
100
+ const query = { ...route.query }
101
+ // 删除不需要的参数
102
+ delete query.redirect
103
+ delete query.state
104
+ delete query.tenantName
105
+ delete query.platformUserId
106
+ delete query.token
107
+ delete query.code
108
+ delete query.platformType
109
+ delete query.appData
110
+ // 可以继续删除其他不需要的参数
111
+ return query
112
+ })(),
113
+ })
114
+ }
115
+
116
+ function closeWindows() {
117
+ if (isWechat()) {
118
+ (window as any).WeixinJSBridge.call('closeWindow')
119
+ }
120
+ else {
121
+ // 关闭页面
122
+ if (navigator.userAgent.includes('Firefox') || navigator.userAgent.includes('Chrome')) {
123
+ window.open('about:blank', '_self')
124
+ window.close()
125
+ }
126
+ else {
127
+ window.opener = null
128
+ window.open('', '_self')
129
+ window.close()
130
+ }
131
+ }
132
+ }
133
+
134
+ async function afterGeneral(result, token = '', userType = UserType.SYSTEM) {
135
+ if (userType === UserType.SYSTEM && result.platformType && result.functions && result.functions.length === 0 && result.ename) {
136
+ const res = await getUserInfo({
137
+ username: result.ename,
138
+ resourceName: setting.getSetting()?.routerName || '智慧手机',
139
+ })
140
+ console.log('获取用户信息', res)
141
+ if (res.resources.functions && res.resources.functions.length > 0) {
142
+ result.functions = res.resources.functions
143
+ }
144
+ if (res.resources.permissions && res.resources.permissions.length > 0) {
145
+ result.permissions = res.resources.permissions
146
+ }
147
+ }
148
+ // 排序 functions 及其嵌套的 children
149
+ if (result.functions && Array.isArray(result.functions)) {
150
+ // 对顶层菜单进行排序
151
+ result.functions.sort((a, b) => (a.position || 0) - (b.position || 0))
152
+
153
+ // 对每个菜单的子项进行排序
154
+ for (const item of result.functions) {
155
+ if (item.children && Array.isArray(item.children)) {
156
+ item.children.sort((a, b) => (a.position || 0) - (b.position || 0))
157
+ }
158
+ }
159
+ }
160
+
161
+ const user: any = {
162
+ ...result,
163
+ username: result.ename,
164
+ avatar: result.avatar,
165
+ }
166
+ userStore.setUserInfo(user)
167
+ // 如果result中没有返回 权限 需要主动获取权限列表
168
+ if (!result.permissions && userType !== UserType.EXTERNAL) {
169
+ result.permissions = await getUserPermissions(result.id)
170
+ }
171
+ userStore.setPermissions(result.permissions)
172
+ userStore.setRoles([{ id: userType, operation: ['add', 'edit', 'delete'] }])
173
+ // 加载路由
174
+ if (user.functions) {
175
+ loadRoutes(funcToRouter(user.functions))
176
+ }
177
+
178
+ // 存储登录数据到本地缓存
179
+ secureStorageBatchWrite([
180
+ {
181
+ key: 'loginData',
182
+ value: {
183
+ token,
184
+ },
185
+ },
186
+ {
187
+ key: 'userData',
188
+ value: result,
189
+ },
190
+ ])
191
+
192
+ // 每次重新登录时,清除indexedDB缓存
193
+ // indexedDB.clear()
194
+ }
195
+
196
+ // TODO 处理等待授权的情况 暂未实现目前不确定三方对接方式
197
+ async function handleWaitingForAuth() {
198
+ console.log('[AuthLoading] 处理等待授权情况')
199
+
200
+ // 检查是否已登录
201
+ const token = userStore.getToken()
202
+ const userType = userStore.getUserType()
203
+
204
+ if (token) {
205
+ const targetPath = userType === 'EXTERNAL' ? '/ex' : '/'
206
+ router.replace(targetPath)
207
+ }
208
+ else {
209
+ // 根据来源决定处理方式
210
+ const source = route.query.source as string
211
+ closeWindows()
212
+
213
+ if (source === 'external') {
214
+ // 外部用户需要重新获取授权
215
+ throw new Error('第三方授权已过期,请重新尝试')
216
+ }
217
+ else {
218
+ // 其他情况
219
+ throw new Error('需要重新获取授权')
220
+ }
221
+ }
222
+ }
223
+
224
+ onMounted(() => {
225
+ handleAuth()
226
+ })
227
+ </script>
228
+
229
+ <template>
230
+ <div class="auth-loading">
231
+ <!-- Logo区域 -->
232
+ <div class="logo-section">
233
+ <div class="company-logo">
234
+ <div class="logo-text">
235
+ 智慧燃气
236
+ </div>
237
+ </div>
238
+ </div>
239
+
240
+ <!-- Loading区域 -->
241
+ <div v-if="isLoading && !hasError" class="loading-section">
242
+ <div class="loading-spinner">
243
+ <VanLoading type="spinner" size="48px" color="#007BFF" />
244
+ </div>
245
+ <div class="loading-text">
246
+ 稍等,这就好
247
+ </div>
248
+ </div>
249
+
250
+ <!-- 错误处理区域 -->
251
+ <div v-if="hasError" class="error-section">
252
+ <div class="error-icon">
253
+ ⚠️
254
+ </div>
255
+ <div class="error-message">
256
+ {{ errorMessage }}
257
+ </div>
258
+ </div>
259
+ </div>
260
+ </template>
261
+
262
+ <style scoped>
263
+ /* 主容器 */
264
+ .auth-loading {
265
+ min-height: 100vh;
266
+ background: #ffffff;
267
+ display: flex;
268
+ flex-direction: column;
269
+ justify-content: center;
270
+ align-items: center;
271
+ padding: 32px 20px;
272
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
273
+ }
274
+
275
+ /* Logo区域 */
276
+ .logo-section {
277
+ margin-bottom: 64px;
278
+ }
279
+
280
+ .company-logo {
281
+ display: flex;
282
+ align-items: center;
283
+ justify-content: center;
284
+ }
285
+
286
+ .logo-text {
287
+ font-size: 28px;
288
+ font-weight: 600;
289
+ color: #007bff;
290
+ letter-spacing: 2px;
291
+ }
292
+
293
+ /* Loading区域 */
294
+ .loading-section {
295
+ display: flex;
296
+ flex-direction: column;
297
+ align-items: center;
298
+ text-align: center;
299
+ }
300
+
301
+ .loading-spinner {
302
+ margin-bottom: 24px;
303
+ }
304
+
305
+ .loading-text {
306
+ font-size: 18px;
307
+ font-weight: 500;
308
+ color: #2c3e50;
309
+ margin-bottom: 12px;
310
+ line-height: 1.4;
311
+ }
312
+
313
+ .platform-text {
314
+ font-size: 14px;
315
+ color: #6c757d;
316
+ line-height: 1.4;
317
+ }
318
+
319
+ /* 错误处理区域 */
320
+ .error-section {
321
+ display: flex;
322
+ flex-direction: column;
323
+ align-items: center;
324
+ text-align: center;
325
+ max-width: 300px;
326
+ }
327
+
328
+ .error-icon {
329
+ font-size: 48px;
330
+ margin-bottom: 20px;
331
+ opacity: 0.8;
332
+ }
333
+
334
+ .error-message {
335
+ font-size: 16px;
336
+ color: #dc3545;
337
+ margin-bottom: 24px;
338
+ line-height: 1.5;
339
+ }
340
+
341
+ /* 响应式设计 */
342
+ @media (max-width: 480px) {
343
+ .auth-loading {
344
+ padding: 24px 16px;
345
+ }
346
+
347
+ .logo-text {
348
+ font-size: 24px;
349
+ }
350
+
351
+ .loading-text {
352
+ font-size: 16px;
353
+ }
354
+
355
+ .platform-text {
356
+ font-size: 13px;
357
+ }
358
+
359
+ .error-message {
360
+ font-size: 15px;
361
+ }
362
+ }
363
+
364
+ /* 大屏适配 */
365
+ @media (min-width: 768px) {
366
+ .auth-loading {
367
+ max-width: 400px;
368
+ margin: 0 auto;
369
+ }
370
+ }
371
+
372
+ /* 兼容性优化 - 避免使用可能有问题的CSS特性 */
373
+ .loading-spinner :deep(.van-loading__spinner) {
374
+ animation-duration: 1s;
375
+ animation-timing-function: linear;
376
+ animation-iteration-count: infinite;
377
+ }
378
+
379
+ /* 禁用选择,提升用户体验 */
380
+ .auth-loading {
381
+ -webkit-user-select: none;
382
+ -moz-user-select: none;
383
+ -ms-user-select: none;
384
+ user-select: none;
385
+ }
386
+
387
+ /* 确保文字在各种设备上清晰显示 */
388
+ .logo-text,
389
+ .loading-text,
390
+ .platform-text,
391
+ .error-message {
392
+ -webkit-font-smoothing: antialiased;
393
+ -moz-osx-font-smoothing: grayscale;
394
+ }
395
+ </style>