af-mobile-client-vue3 1.3.54 → 1.3.57
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.
- package/package.json +115 -115
- package/public/favicon.svg +4 -4
- package/scripts/verifyCommit.js +19 -19
- package/src/App.vue +1 -1
- package/src/api/user/index.ts +45 -45
- package/src/components/data/UserDetail/types.ts +1 -1
- package/src/components/data/XReportGrid/XAddReport/index.ts +1 -1
- package/src/components/data/XReportGrid/XReportDrawer/index.ts +1 -1
- package/src/components/data/XTag/index.vue +10 -10
- package/src/components/layout/TabBarLayout/index.vue +40 -40
- package/src/font-style/Inter-Bold.woff2 +0 -0
- package/src/font-style/Inter-Medium.woff2 +0 -0
- package/src/font-style/Inter-Regular.woff2 +0 -0
- package/src/font-style/Inter-SemiBold.woff2 +0 -0
- package/src/font-style/PingFangSC-Medium.subset.woff2 +0 -0
- package/src/font-style/PingFangSC-Medium.woff2 +0 -0
- package/src/font-style/PingFangSC-Regular.subset.woff2 +0 -0
- package/src/font-style/PingFangSC-Semibold.subset.woff2 +0 -0
- package/src/font-style/PingFangSC-Semibold.woff2 +0 -0
- package/src/font-style/font.css +59 -2
- package/src/hooks/useCommon.ts +9 -9
- package/src/plugins/AppData.ts +38 -38
- package/src/router/external-routes.ts +69 -69
- package/src/router/guards.ts +131 -131
- package/src/router/invoiceRoutes.ts +33 -33
- package/src/router/routes.ts +421 -421
- package/src/services/api/Login.ts +6 -6
- package/src/services/api/common.ts +109 -109
- package/src/services/api/manage.ts +8 -8
- package/src/services/api/search.ts +16 -16
- package/src/services/restTools.ts +56 -56
- package/src/services/v3Api.ts +11 -11
- package/src/stores/modules/user.ts +1 -1
- package/src/styles/login.less +109 -109
- package/src/types/platform.ts +194 -194
- package/src/utils/authority-utils.ts +84 -84
- package/src/utils/crypto.ts +39 -39
- package/src/utils/environment.ts +10 -10
- package/src/utils/platform-auth.ts +150 -150
- package/src/utils/routerUtil.ts +81 -2
- package/src/utils/runEvalFunction.ts +13 -13
- package/src/utils/wechat.ts +297 -297
- package/src/views/component/EvaluateRecordView/index.vue +40 -40
- package/src/views/component/XCellDetailView/index.vue +217 -217
- package/src/views/component/XReportFormIframeView/index.vue +47 -47
- package/src/views/component/XReportFormView/index.vue +13 -13
- package/src/views/component/XSignatureView/index.vue +50 -50
- package/src/views/component/notice.vue +46 -46
- package/src/views/component/topNav.vue +36 -36
- package/src/views/invoiceShow/index.vue +61 -61
- package/src/views/loading/AuthLoading.vue +378 -378
- package/src/views/user/login/index.vue +22 -22
- package/src/views/user/register/index.vue +1 -1
- package/vite.config.ts +114 -114
- package/certs/127.0.0.1+2-key.pem +0 -28
- package/certs/127.0.0.1+2.pem +0 -27
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 判断是否有路由的权限
|
|
3
|
-
* @param authority 路由权限配置
|
|
4
|
-
* @param permissions 用户权限集合
|
|
5
|
-
*/
|
|
6
|
-
function hasPermission(authority: any, permissions: any): boolean {
|
|
7
|
-
let required: string = '*'
|
|
8
|
-
if (typeof authority === 'string')
|
|
9
|
-
required = authority
|
|
10
|
-
else if (Array.isArray(authority))
|
|
11
|
-
required = authority.toString()
|
|
12
|
-
else if (typeof authority === 'object')
|
|
13
|
-
required = authority.permission
|
|
14
|
-
|
|
15
|
-
return required === '*' || hasAnyItem(required, permissions, (r, t) => !!(r === t || r === t.id))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 判断是否有路由需要的角色
|
|
20
|
-
* @param authority 路由权限配置
|
|
21
|
-
* @param roles 用户角色集合
|
|
22
|
-
*/
|
|
23
|
-
function hasRole(authority, roles) {
|
|
24
|
-
let required
|
|
25
|
-
if (typeof authority === 'object')
|
|
26
|
-
required = authority.role
|
|
27
|
-
|
|
28
|
-
return authority === '*' || hasAnyItem(required, roles, (r, t) => !!(r === t || r === t.id))
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* 判断目标数组是否有所需元素
|
|
33
|
-
* @param {string | string[]}required 所需元素,数组或单个元素
|
|
34
|
-
* @param {string[] | object[]} source 目标数组
|
|
35
|
-
* @param {Function} filter 匹配条件
|
|
36
|
-
* (r: String, s: String|Object) => boolean
|
|
37
|
-
*/
|
|
38
|
-
function hasAnyItem(required, source, filter): boolean {
|
|
39
|
-
if (!required)
|
|
40
|
-
return false
|
|
41
|
-
|
|
42
|
-
const checkedList = Array.isArray(required) ? required : [required]
|
|
43
|
-
return !!source.find(s => checkedList.find(r => filter(r, s)))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 路由权限校验
|
|
48
|
-
* @param route 路由
|
|
49
|
-
* @param permissions 用户权限集合
|
|
50
|
-
* @param roles 用户角色集合
|
|
51
|
-
*/
|
|
52
|
-
function hasAuthority(route, permissions, roles) {
|
|
53
|
-
// TODO 此处判断可能有问题
|
|
54
|
-
if (!route.meta.pAuthorities)
|
|
55
|
-
return true
|
|
56
|
-
|
|
57
|
-
const authorities = [...route.meta.pAuthorities, route.meta.authority]
|
|
58
|
-
for (const authority of authorities) {
|
|
59
|
-
if (!hasPermission(authority, permissions) && !hasRole(authority, roles))
|
|
60
|
-
return false
|
|
61
|
-
}
|
|
62
|
-
return true
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 根据权限配置过滤菜单数据
|
|
67
|
-
* @param menuData
|
|
68
|
-
* @param permissions
|
|
69
|
-
* @param roles
|
|
70
|
-
*/
|
|
71
|
-
function filterMenu(menuData, permissions, roles) {
|
|
72
|
-
return menuData.filter((menu) => {
|
|
73
|
-
if (menu.meta && menu.meta.invisible === undefined) {
|
|
74
|
-
if (!hasAuthority(menu, permissions, roles))
|
|
75
|
-
return false
|
|
76
|
-
}
|
|
77
|
-
if (menu.children && menu.children.length > 0)
|
|
78
|
-
menu.children = filterMenu(menu.children, permissions, roles)
|
|
79
|
-
|
|
80
|
-
return true
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export { filterMenu, hasAuthority }
|
|
1
|
+
/**
|
|
2
|
+
* 判断是否有路由的权限
|
|
3
|
+
* @param authority 路由权限配置
|
|
4
|
+
* @param permissions 用户权限集合
|
|
5
|
+
*/
|
|
6
|
+
function hasPermission(authority: any, permissions: any): boolean {
|
|
7
|
+
let required: string = '*'
|
|
8
|
+
if (typeof authority === 'string')
|
|
9
|
+
required = authority
|
|
10
|
+
else if (Array.isArray(authority))
|
|
11
|
+
required = authority.toString()
|
|
12
|
+
else if (typeof authority === 'object')
|
|
13
|
+
required = authority.permission
|
|
14
|
+
|
|
15
|
+
return required === '*' || hasAnyItem(required, permissions, (r, t) => !!(r === t || r === t.id))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 判断是否有路由需要的角色
|
|
20
|
+
* @param authority 路由权限配置
|
|
21
|
+
* @param roles 用户角色集合
|
|
22
|
+
*/
|
|
23
|
+
function hasRole(authority, roles) {
|
|
24
|
+
let required
|
|
25
|
+
if (typeof authority === 'object')
|
|
26
|
+
required = authority.role
|
|
27
|
+
|
|
28
|
+
return authority === '*' || hasAnyItem(required, roles, (r, t) => !!(r === t || r === t.id))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 判断目标数组是否有所需元素
|
|
33
|
+
* @param {string | string[]}required 所需元素,数组或单个元素
|
|
34
|
+
* @param {string[] | object[]} source 目标数组
|
|
35
|
+
* @param {Function} filter 匹配条件
|
|
36
|
+
* (r: String, s: String|Object) => boolean
|
|
37
|
+
*/
|
|
38
|
+
function hasAnyItem(required, source, filter): boolean {
|
|
39
|
+
if (!required)
|
|
40
|
+
return false
|
|
41
|
+
|
|
42
|
+
const checkedList = Array.isArray(required) ? required : [required]
|
|
43
|
+
return !!source.find(s => checkedList.find(r => filter(r, s)))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 路由权限校验
|
|
48
|
+
* @param route 路由
|
|
49
|
+
* @param permissions 用户权限集合
|
|
50
|
+
* @param roles 用户角色集合
|
|
51
|
+
*/
|
|
52
|
+
function hasAuthority(route, permissions, roles) {
|
|
53
|
+
// TODO 此处判断可能有问题
|
|
54
|
+
if (!route.meta.pAuthorities)
|
|
55
|
+
return true
|
|
56
|
+
|
|
57
|
+
const authorities = [...route.meta.pAuthorities, route.meta.authority]
|
|
58
|
+
for (const authority of authorities) {
|
|
59
|
+
if (!hasPermission(authority, permissions) && !hasRole(authority, roles))
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 根据权限配置过滤菜单数据
|
|
67
|
+
* @param menuData
|
|
68
|
+
* @param permissions
|
|
69
|
+
* @param roles
|
|
70
|
+
*/
|
|
71
|
+
function filterMenu(menuData, permissions, roles) {
|
|
72
|
+
return menuData.filter((menu) => {
|
|
73
|
+
if (menu.meta && menu.meta.invisible === undefined) {
|
|
74
|
+
if (!hasAuthority(menu, permissions, roles))
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
if (menu.children && menu.children.length > 0)
|
|
78
|
+
menu.children = filterMenu(menu.children, permissions, roles)
|
|
79
|
+
|
|
80
|
+
return true
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { filterMenu, hasAuthority }
|
package/src/utils/crypto.ts
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import AesEncryptJS from 'crypto-js'
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
/**
|
|
5
|
-
* AES加密
|
|
6
|
-
*
|
|
7
|
-
* @param word
|
|
8
|
-
* @param encryKey
|
|
9
|
-
*/
|
|
10
|
-
AESEncrypt(word: string, encryKey: string): string {
|
|
11
|
-
const key = AesEncryptJS.enc.Utf8.parse(encryKey)
|
|
12
|
-
const srcs = AesEncryptJS.enc.Utf8.parse(word)
|
|
13
|
-
const encrypted = AesEncryptJS.AES.encrypt(srcs, key, {
|
|
14
|
-
mode: AesEncryptJS.mode.ECB,
|
|
15
|
-
padding: AesEncryptJS.pad.Pkcs7,
|
|
16
|
-
})
|
|
17
|
-
return encrypted.toString()
|
|
18
|
-
},
|
|
19
|
-
/**
|
|
20
|
-
* AES解密
|
|
21
|
-
*
|
|
22
|
-
* @param word
|
|
23
|
-
* @param encryKey
|
|
24
|
-
*/
|
|
25
|
-
AESDecrypt(word: string, encryKey: string): any {
|
|
26
|
-
const key = AesEncryptJS.enc.Utf8.parse(encryKey)
|
|
27
|
-
const decrypt = AesEncryptJS.AES.decrypt(word, key, {
|
|
28
|
-
mode: AesEncryptJS.mode.ECB,
|
|
29
|
-
padding: AesEncryptJS.pad.Pkcs7,
|
|
30
|
-
})
|
|
31
|
-
const ret = AesEncryptJS.enc.Utf8.stringify(decrypt).toString()
|
|
32
|
-
try {
|
|
33
|
-
return JSON.parse(ret)
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return ret
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
}
|
|
1
|
+
import AesEncryptJS from 'crypto-js'
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
/**
|
|
5
|
+
* AES加密
|
|
6
|
+
*
|
|
7
|
+
* @param word
|
|
8
|
+
* @param encryKey
|
|
9
|
+
*/
|
|
10
|
+
AESEncrypt(word: string, encryKey: string): string {
|
|
11
|
+
const key = AesEncryptJS.enc.Utf8.parse(encryKey)
|
|
12
|
+
const srcs = AesEncryptJS.enc.Utf8.parse(word)
|
|
13
|
+
const encrypted = AesEncryptJS.AES.encrypt(srcs, key, {
|
|
14
|
+
mode: AesEncryptJS.mode.ECB,
|
|
15
|
+
padding: AesEncryptJS.pad.Pkcs7,
|
|
16
|
+
})
|
|
17
|
+
return encrypted.toString()
|
|
18
|
+
},
|
|
19
|
+
/**
|
|
20
|
+
* AES解密
|
|
21
|
+
*
|
|
22
|
+
* @param word
|
|
23
|
+
* @param encryKey
|
|
24
|
+
*/
|
|
25
|
+
AESDecrypt(word: string, encryKey: string): any {
|
|
26
|
+
const key = AesEncryptJS.enc.Utf8.parse(encryKey)
|
|
27
|
+
const decrypt = AesEncryptJS.AES.decrypt(word, key, {
|
|
28
|
+
mode: AesEncryptJS.mode.ECB,
|
|
29
|
+
padding: AesEncryptJS.pad.Pkcs7,
|
|
30
|
+
})
|
|
31
|
+
const ret = AesEncryptJS.enc.Utf8.stringify(decrypt).toString()
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(ret)
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return ret
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
}
|
package/src/utils/environment.ts
CHANGED
|
@@ -29,17 +29,17 @@ function getPlatformFromClient(): string {
|
|
|
29
29
|
|
|
30
30
|
function inferPlatformFromUserAgent(userAgent: string): string {
|
|
31
31
|
const ua = (userAgent || '').toLowerCase()
|
|
32
|
-
if (ua.includes('windows'))
|
|
32
|
+
if (ua.includes('windows'))
|
|
33
33
|
return 'windows'
|
|
34
|
-
if (ua.includes('mac os x') || ua.includes('macintosh') || ua.includes('mac os'))
|
|
34
|
+
if (ua.includes('mac os x') || ua.includes('macintosh') || ua.includes('mac os'))
|
|
35
35
|
return 'macos'
|
|
36
|
-
if (ua.includes('android'))
|
|
36
|
+
if (ua.includes('android'))
|
|
37
37
|
return 'android'
|
|
38
|
-
if (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod') || ua.includes('ios'))
|
|
38
|
+
if (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod') || ua.includes('ios'))
|
|
39
39
|
return 'ios'
|
|
40
|
-
if (ua.includes('cros'))
|
|
40
|
+
if (ua.includes('cros'))
|
|
41
41
|
return 'chrome os'
|
|
42
|
-
if (ua.includes('linux'))
|
|
42
|
+
if (ua.includes('linux'))
|
|
43
43
|
return 'linux'
|
|
44
44
|
return 'unknown'
|
|
45
45
|
}
|
|
@@ -89,13 +89,13 @@ export function detectEnvironment(): EnvironmentInfo {
|
|
|
89
89
|
export function getCurrentScene(): 'wechat' | 'browser' | 'miniprogram' | 'app' | 'unknown' {
|
|
90
90
|
const env = detectEnvironment()
|
|
91
91
|
|
|
92
|
-
if (env.isMiniprogram)
|
|
92
|
+
if (env.isMiniprogram)
|
|
93
93
|
return 'miniprogram'
|
|
94
|
-
if (env.isWechat)
|
|
94
|
+
if (env.isWechat)
|
|
95
95
|
return 'wechat'
|
|
96
|
-
if (env.isApp)
|
|
96
|
+
if (env.isApp)
|
|
97
97
|
return 'app'
|
|
98
|
-
if (env.isBrowser)
|
|
98
|
+
if (env.isBrowser)
|
|
99
99
|
return 'browser'
|
|
100
100
|
|
|
101
101
|
return 'unknown'
|
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 统一平台认证工具类
|
|
3
|
-
* 合并了 wechatUtil.ts, wechat.ts 和 auth-platform.ts 的功能
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { RouteLocationNormalized } from 'vue-router'
|
|
7
|
-
import { PLATFORM_ROUTE_MAP, PlatformType } from '@af-mobile-client-vue3/types/platform'
|
|
8
|
-
import { detectEnvironment } from '@af-mobile-client-vue3/utils/environment'
|
|
9
|
-
/**
|
|
10
|
-
* 认证参数接口
|
|
11
|
-
*/
|
|
12
|
-
export interface AuthParams {
|
|
13
|
-
/** 授权码 */
|
|
14
|
-
code?: string
|
|
15
|
-
/** 状态参数 */
|
|
16
|
-
state?: string
|
|
17
|
-
/** 租户名称 */
|
|
18
|
-
tenantName?: string
|
|
19
|
-
/** 平台类型 */
|
|
20
|
-
platformType?: PlatformType
|
|
21
|
-
/** 错误信息 */
|
|
22
|
-
error?: string
|
|
23
|
-
/** 错误描述 */
|
|
24
|
-
error_description?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 微信回调参数接口(向后兼容)
|
|
29
|
-
*/
|
|
30
|
-
export interface WechatCallbackParams extends AuthParams { }
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 外部用户检测结果接口
|
|
34
|
-
*/
|
|
35
|
-
export interface ExternalUserResult {
|
|
36
|
-
/** 是否为外部用户 */
|
|
37
|
-
isExternal: boolean
|
|
38
|
-
/** 认证参数(仅当 isExternal 为 true 且有授权参数时存在) */
|
|
39
|
-
authParams?: {
|
|
40
|
-
code?: string
|
|
41
|
-
state?: string
|
|
42
|
-
tenantName?: string
|
|
43
|
-
platformType?: string
|
|
44
|
-
platformUserId?: string
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 统一的外部用户检测函数
|
|
50
|
-
* 这是系统中唯一的用户类型检测入口,仅在路由守卫中调用
|
|
51
|
-
* 集成了用户类型检测和认证参数解析功能
|
|
52
|
-
*
|
|
53
|
-
* @param to 目标路由对象
|
|
54
|
-
* @returns 外部用户检测结果,包含是否为外部用户和认证参数
|
|
55
|
-
*/
|
|
56
|
-
export function isExternalUser(to: RouteLocationNormalized): ExternalUserResult {
|
|
57
|
-
console.log('[Platform Detection] 开始检测外部用户环境', { path: to.path, query: to.query })
|
|
58
|
-
|
|
59
|
-
// 第一层检测:URL参数检测(最高优先级)判断公众号登录逻辑
|
|
60
|
-
// 检查路由查询参数中是否有授权参数(code + state)
|
|
61
|
-
const { code, state, appData } = to.query
|
|
62
|
-
if (code && state) {
|
|
63
|
-
// 解析认证参数
|
|
64
|
-
const authParams = {
|
|
65
|
-
code: code as string,
|
|
66
|
-
state: state as string,
|
|
67
|
-
tenantName: state as string,
|
|
68
|
-
platformUserId: code as string,
|
|
69
|
-
platformType: PlatformType.WECHAT_OFFICIAL,
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
isExternal: true,
|
|
74
|
-
authParams,
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// 第二层检测:User-Agent环境检测
|
|
79
|
-
// 检查浏览器环境是否为第三方平台
|
|
80
|
-
const env = detectEnvironment()
|
|
81
|
-
// 微信小程序
|
|
82
|
-
if (env.isMiniprogram && appData) {
|
|
83
|
-
const authParams = {
|
|
84
|
-
platformType: PlatformType.WECHAT_MINI,
|
|
85
|
-
appData,
|
|
86
|
-
}
|
|
87
|
-
// 暂未实现 后续需要增加登陆参数
|
|
88
|
-
return {
|
|
89
|
-
isExternal: true,
|
|
90
|
-
authParams,
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// 微信环境检测
|
|
94
|
-
if (env.isWechat) {
|
|
95
|
-
console.log('[Platform Detection] 检测到微信环境,判定为外部用户(无授权参数)')
|
|
96
|
-
return {
|
|
97
|
-
isExternal: true,
|
|
98
|
-
authParams: Object.assign(to.query, {
|
|
99
|
-
platformType: PlatformType.WECHAT_OFFICIAL,
|
|
100
|
-
tenantName: to.query.state as string || '',
|
|
101
|
-
}),
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// 支付宝环境检测
|
|
106
|
-
if (env.isAlipayClient) {
|
|
107
|
-
// 暂未实现 后续需要增加登陆参数
|
|
108
|
-
return {
|
|
109
|
-
isExternal: true,
|
|
110
|
-
authParams: {
|
|
111
|
-
platformType: PlatformType.ALIPAY,
|
|
112
|
-
},
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// 钉钉环境检测
|
|
117
|
-
if (env.isDingTalk) {
|
|
118
|
-
// 暂未实现 后续需要增加登陆参数
|
|
119
|
-
return {
|
|
120
|
-
isExternal: true,
|
|
121
|
-
authParams: {
|
|
122
|
-
platformType: PlatformType.DINGTALK,
|
|
123
|
-
},
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// 第三层检测:路由前缀检测
|
|
128
|
-
// 检查路径是否以外部用户路由前缀开头
|
|
129
|
-
const platformPrefixes = Object.values(PLATFORM_ROUTE_MAP)
|
|
130
|
-
const isExternalRoute = platformPrefixes.some(prefix => to.path.startsWith(prefix)) || to.path.startsWith('/ex/')
|
|
131
|
-
if (isExternalRoute) {
|
|
132
|
-
// 暂未实现 后续需要增加登陆参数
|
|
133
|
-
return {
|
|
134
|
-
isExternal: true,
|
|
135
|
-
authParams: Object.assign(to.query, {
|
|
136
|
-
platformType: PlatformType.WECHAT_OFFICIAL,
|
|
137
|
-
tenantName: to.query.state as string || '',
|
|
138
|
-
}),
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// [Platform Detection] 未检测到外部用户环境,判定为内部用户
|
|
143
|
-
return { isExternal: false }
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// 兼容性导出 - 保持向后兼容
|
|
147
|
-
export function isWechat(): boolean {
|
|
148
|
-
const ua = navigator.userAgent.toLowerCase()
|
|
149
|
-
return /micromessenger/i.test(ua)
|
|
150
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 统一平台认证工具类
|
|
3
|
+
* 合并了 wechatUtil.ts, wechat.ts 和 auth-platform.ts 的功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RouteLocationNormalized } from 'vue-router'
|
|
7
|
+
import { PLATFORM_ROUTE_MAP, PlatformType } from '@af-mobile-client-vue3/types/platform'
|
|
8
|
+
import { detectEnvironment } from '@af-mobile-client-vue3/utils/environment'
|
|
9
|
+
/**
|
|
10
|
+
* 认证参数接口
|
|
11
|
+
*/
|
|
12
|
+
export interface AuthParams {
|
|
13
|
+
/** 授权码 */
|
|
14
|
+
code?: string
|
|
15
|
+
/** 状态参数 */
|
|
16
|
+
state?: string
|
|
17
|
+
/** 租户名称 */
|
|
18
|
+
tenantName?: string
|
|
19
|
+
/** 平台类型 */
|
|
20
|
+
platformType?: PlatformType
|
|
21
|
+
/** 错误信息 */
|
|
22
|
+
error?: string
|
|
23
|
+
/** 错误描述 */
|
|
24
|
+
error_description?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 微信回调参数接口(向后兼容)
|
|
29
|
+
*/
|
|
30
|
+
export interface WechatCallbackParams extends AuthParams { }
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 外部用户检测结果接口
|
|
34
|
+
*/
|
|
35
|
+
export interface ExternalUserResult {
|
|
36
|
+
/** 是否为外部用户 */
|
|
37
|
+
isExternal: boolean
|
|
38
|
+
/** 认证参数(仅当 isExternal 为 true 且有授权参数时存在) */
|
|
39
|
+
authParams?: {
|
|
40
|
+
code?: string
|
|
41
|
+
state?: string
|
|
42
|
+
tenantName?: string
|
|
43
|
+
platformType?: string
|
|
44
|
+
platformUserId?: string
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 统一的外部用户检测函数
|
|
50
|
+
* 这是系统中唯一的用户类型检测入口,仅在路由守卫中调用
|
|
51
|
+
* 集成了用户类型检测和认证参数解析功能
|
|
52
|
+
*
|
|
53
|
+
* @param to 目标路由对象
|
|
54
|
+
* @returns 外部用户检测结果,包含是否为外部用户和认证参数
|
|
55
|
+
*/
|
|
56
|
+
export function isExternalUser(to: RouteLocationNormalized): ExternalUserResult {
|
|
57
|
+
console.log('[Platform Detection] 开始检测外部用户环境', { path: to.path, query: to.query })
|
|
58
|
+
|
|
59
|
+
// 第一层检测:URL参数检测(最高优先级)判断公众号登录逻辑
|
|
60
|
+
// 检查路由查询参数中是否有授权参数(code + state)
|
|
61
|
+
const { code, state, appData } = to.query
|
|
62
|
+
if (code && state) {
|
|
63
|
+
// 解析认证参数
|
|
64
|
+
const authParams = {
|
|
65
|
+
code: code as string,
|
|
66
|
+
state: state as string,
|
|
67
|
+
tenantName: state as string,
|
|
68
|
+
platformUserId: code as string,
|
|
69
|
+
platformType: PlatformType.WECHAT_OFFICIAL,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
isExternal: true,
|
|
74
|
+
authParams,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 第二层检测:User-Agent环境检测
|
|
79
|
+
// 检查浏览器环境是否为第三方平台
|
|
80
|
+
const env = detectEnvironment()
|
|
81
|
+
// 微信小程序
|
|
82
|
+
if (env.isMiniprogram && appData) {
|
|
83
|
+
const authParams = {
|
|
84
|
+
platformType: PlatformType.WECHAT_MINI,
|
|
85
|
+
appData,
|
|
86
|
+
}
|
|
87
|
+
// 暂未实现 后续需要增加登陆参数
|
|
88
|
+
return {
|
|
89
|
+
isExternal: true,
|
|
90
|
+
authParams,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 微信环境检测
|
|
94
|
+
if (env.isWechat) {
|
|
95
|
+
console.log('[Platform Detection] 检测到微信环境,判定为外部用户(无授权参数)')
|
|
96
|
+
return {
|
|
97
|
+
isExternal: true,
|
|
98
|
+
authParams: Object.assign(to.query, {
|
|
99
|
+
platformType: PlatformType.WECHAT_OFFICIAL,
|
|
100
|
+
tenantName: to.query.state as string || '',
|
|
101
|
+
}),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 支付宝环境检测
|
|
106
|
+
if (env.isAlipayClient) {
|
|
107
|
+
// 暂未实现 后续需要增加登陆参数
|
|
108
|
+
return {
|
|
109
|
+
isExternal: true,
|
|
110
|
+
authParams: {
|
|
111
|
+
platformType: PlatformType.ALIPAY,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 钉钉环境检测
|
|
117
|
+
if (env.isDingTalk) {
|
|
118
|
+
// 暂未实现 后续需要增加登陆参数
|
|
119
|
+
return {
|
|
120
|
+
isExternal: true,
|
|
121
|
+
authParams: {
|
|
122
|
+
platformType: PlatformType.DINGTALK,
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 第三层检测:路由前缀检测
|
|
128
|
+
// 检查路径是否以外部用户路由前缀开头
|
|
129
|
+
const platformPrefixes = Object.values(PLATFORM_ROUTE_MAP)
|
|
130
|
+
const isExternalRoute = platformPrefixes.some(prefix => to.path.startsWith(prefix)) || to.path.startsWith('/ex/')
|
|
131
|
+
if (isExternalRoute) {
|
|
132
|
+
// 暂未实现 后续需要增加登陆参数
|
|
133
|
+
return {
|
|
134
|
+
isExternal: true,
|
|
135
|
+
authParams: Object.assign(to.query, {
|
|
136
|
+
platformType: PlatformType.WECHAT_OFFICIAL,
|
|
137
|
+
tenantName: to.query.state as string || '',
|
|
138
|
+
}),
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// [Platform Detection] 未检测到外部用户环境,判定为内部用户
|
|
143
|
+
return { isExternal: false }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 兼容性导出 - 保持向后兼容
|
|
147
|
+
export function isWechat(): boolean {
|
|
148
|
+
const ua = navigator.userAgent.toLowerCase()
|
|
149
|
+
return /micromessenger/i.test(ua)
|
|
150
|
+
}
|