xto-fronted 0.4.109 → 0.4.111

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,110 +1,110 @@
1
- /**
2
- * 菜单状态
3
- */
4
-
5
- import { defineStore } from 'pinia'
6
- import { ref, computed } from 'vue'
7
- import type { MenuItem } from '@/types/api'
8
- import { local } from '@/utils/storage'
9
-
10
- const MENU_LIST_KEY = 'menu_list'
11
- const PERMISSION_LIST_KEY = 'permission_list'
12
-
13
- // 首页菜单(参考 tineco-ui)
14
- const indexMenu: MenuItem = {
15
- menuCode: 'home',
16
- menuName: '首页',
17
- menuUrl: '/dashboard',
18
- icon: 'home',
19
- closable: false,
20
- isDefault: false,
21
- isOut: false
22
- }
23
-
24
- /**
25
- * 过滤菜单项,只保留菜单(type !== 1),过滤掉功能按钮(type === 1)
26
- * @param menus 原始菜单列表
27
- * @returns 过滤后的菜单列表
28
- */
29
- function filterMenus(menus: MenuItem[]): MenuItem[] {
30
- return menus
31
- .filter(menu => menu.type !== 1) // 过滤掉功能按钮
32
- .map(menu => ({
33
- ...menu,
34
- children: menu.children ? filterMenus(menu.children) : undefined
35
- }))
36
- }
37
-
38
- /**
39
- * 提取所有功能权限(type === 1 的项)
40
- * @param menus 原始菜单列表
41
- * @returns 权限标识列表
42
- */
43
- function extractPermissions(menus: MenuItem[]): string[] {
44
- const permissions: string[] = []
45
-
46
- function traverse(items: MenuItem[]) {
47
- items.forEach(item => {
48
- // type === 1 表示功能按钮
49
- if (item.type === 1 && item.menuCode) {
50
- permissions.push(item.menuCode)
51
- }
52
- // 递归处理子项
53
- if (item.children) {
54
- traverse(item.children)
55
- }
56
- })
57
- }
58
-
59
- traverse(menus)
60
- return permissions
61
- }
62
-
63
- export const useMenuStore = defineStore('menu', () => {
64
- // 状态
65
- const menuList = ref<MenuItem[]>(local.get<MenuItem[]>(MENU_LIST_KEY) || [])
66
- // 功能权限列表
67
- const permissions = ref<string[]>(local.get<string[]>(PERMISSION_LIST_KEY) || [])
68
-
69
- // 计算属性
70
- const hasMenu = computed(() => menuList.value.length > 0)
71
-
72
- // 设置菜单(会自动过滤功能项,并提取权限)
73
- const setMenuList = (menus: MenuItem[]) => {
74
- // 提取功能权限
75
- permissions.value = extractPermissions(menus)
76
- local.set(PERMISSION_LIST_KEY, permissions.value)
77
-
78
- // 过滤菜单,只保留菜单项(type !== 1)
79
- const filteredMenus = filterMenus(menus)
80
-
81
- // 添加首页菜单到开头
82
- menuList.value = [indexMenu, ...filteredMenus]
83
- local.set(MENU_LIST_KEY, menuList.value)
84
- }
85
-
86
- // 清除菜单
87
- const clearMenu = () => {
88
- menuList.value = []
89
- permissions.value = []
90
- local.remove(MENU_LIST_KEY)
91
- local.remove(PERMISSION_LIST_KEY)
92
- }
93
-
94
- // 检查是否有某个权限
95
- const hasPermission = (permission: string | string[]): boolean => {
96
- if (Array.isArray(permission)) {
97
- return permission.some(p => permissions.value.includes(p))
98
- }
99
- return permissions.value.includes(permission)
100
- }
101
-
102
- return {
103
- menuList,
104
- permissions,
105
- hasMenu,
106
- setMenuList,
107
- clearMenu,
108
- hasPermission
109
- }
1
+ /**
2
+ * 菜单状态
3
+ */
4
+
5
+ import { defineStore } from 'pinia'
6
+ import { ref, computed } from 'vue'
7
+ import type { MenuItem } from '@/types/api'
8
+ import { local } from '@/utils/storage'
9
+
10
+ const MENU_LIST_KEY = 'menu_list'
11
+ const PERMISSION_LIST_KEY = 'permission_list'
12
+
13
+ // 首页菜单(参考 tineco-ui)
14
+ const indexMenu: MenuItem = {
15
+ menuCode: 'home',
16
+ menuName: '首页',
17
+ menuUrl: '/dashboard',
18
+ icon: 'home',
19
+ closable: false,
20
+ isDefault: false,
21
+ isOut: false
22
+ }
23
+
24
+ /**
25
+ * 过滤菜单项,只保留菜单(type !== 1),过滤掉功能按钮(type === 1)
26
+ * @param menus 原始菜单列表
27
+ * @returns 过滤后的菜单列表
28
+ */
29
+ function filterMenus(menus: MenuItem[]): MenuItem[] {
30
+ return menus
31
+ .filter(menu => menu.type !== 1) // 过滤掉功能按钮
32
+ .map(menu => ({
33
+ ...menu,
34
+ children: menu.children ? filterMenus(menu.children) : undefined
35
+ }))
36
+ }
37
+
38
+ /**
39
+ * 提取所有功能权限(type === 1 的项)
40
+ * @param menus 原始菜单列表
41
+ * @returns 权限标识列表
42
+ */
43
+ function extractPermissions(menus: MenuItem[]): string[] {
44
+ const permissions: string[] = []
45
+
46
+ function traverse(items: MenuItem[]) {
47
+ items.forEach(item => {
48
+ // type === 1 表示功能按钮
49
+ if (item.type === 1 && item.menuCode) {
50
+ permissions.push(item.menuCode)
51
+ }
52
+ // 递归处理子项
53
+ if (item.children) {
54
+ traverse(item.children)
55
+ }
56
+ })
57
+ }
58
+
59
+ traverse(menus)
60
+ return permissions
61
+ }
62
+
63
+ export const useMenuStore = defineStore('menu', () => {
64
+ // 状态
65
+ const menuList = ref<MenuItem[]>(local.get<MenuItem[]>(MENU_LIST_KEY) || [])
66
+ // 功能权限列表
67
+ const permissions = ref<string[]>(local.get<string[]>(PERMISSION_LIST_KEY) || [])
68
+
69
+ // 计算属性
70
+ const hasMenu = computed(() => menuList.value.length > 0)
71
+
72
+ // 设置菜单(会自动过滤功能项,并提取权限)
73
+ const setMenuList = (menus: MenuItem[]) => {
74
+ // 提取功能权限
75
+ permissions.value = extractPermissions(menus)
76
+ local.set(PERMISSION_LIST_KEY, permissions.value)
77
+
78
+ // 过滤菜单,只保留菜单项(type !== 1)
79
+ const filteredMenus = filterMenus(menus)
80
+
81
+ // 添加首页菜单到开头
82
+ menuList.value = [indexMenu, ...filteredMenus]
83
+ local.set(MENU_LIST_KEY, menuList.value)
84
+ }
85
+
86
+ // 清除菜单
87
+ const clearMenu = () => {
88
+ menuList.value = []
89
+ permissions.value = []
90
+ local.remove(MENU_LIST_KEY)
91
+ local.remove(PERMISSION_LIST_KEY)
92
+ }
93
+
94
+ // 检查是否有某个权限
95
+ const hasPermission = (permission: string | string[]): boolean => {
96
+ if (Array.isArray(permission)) {
97
+ return permission.some(p => permissions.value.includes(p))
98
+ }
99
+ return permissions.value.includes(permission)
100
+ }
101
+
102
+ return {
103
+ menuList,
104
+ permissions,
105
+ hasMenu,
106
+ setMenuList,
107
+ clearMenu,
108
+ hasPermission
109
+ }
110
110
  })
@@ -1,51 +1,53 @@
1
- /**
2
- * 用户状态
3
- */
4
-
5
- import { defineStore } from 'pinia'
6
- import { ref, computed } from 'vue'
7
- import type { UserInfo } from '@/types/api'
8
- import { local } from '@/utils/storage'
9
-
10
- const USER_INFO_KEY = 'user_info'
11
-
12
- export const useUserStore = defineStore('user', () => {
13
- // 状态
14
- const userInfo = ref<UserInfo | null>(local.get<UserInfo>(USER_INFO_KEY))
15
-
16
- // 计算属性
17
- const isLoggedIn = computed(() => !!userInfo.value)
18
- const userId = computed(() => userInfo.value?.userId || '')
19
- const userName = computed(() => userInfo.value?.userName || '')
20
- const departmentName = computed(() => userInfo.value?.departmentName || '')
21
- const email = computed(() => userInfo.value?.email || '')
22
- const mobilePhone = computed(() => userInfo.value?.mobilePhone || '')
23
- const positionName = computed(() => userInfo.value?.positionName || '')
24
- const avatar = computed(() => userInfo.value?.avatar || '')
25
-
26
- // 设置用户信息
27
- const setUserInfo = (info: UserInfo) => {
28
- userInfo.value = info
29
- local.set(USER_INFO_KEY, info)
30
- }
31
-
32
- // 清除用户信息
33
- const clearUserInfo = () => {
34
- userInfo.value = null
35
- local.remove(USER_INFO_KEY)
36
- }
37
-
38
- return {
39
- userInfo,
40
- isLoggedIn,
41
- userId,
42
- userName,
43
- departmentName,
44
- email,
45
- mobilePhone,
46
- positionName,
47
- avatar,
48
- setUserInfo,
49
- clearUserInfo
50
- }
1
+ /**
2
+ * 用户状态
3
+ */
4
+
5
+ import { defineStore } from 'pinia'
6
+ import { ref, computed } from 'vue'
7
+ import type { UserInfo } from '@/types/api'
8
+ import { local } from '@/utils/storage'
9
+
10
+ const USER_INFO_KEY = 'user_info'
11
+
12
+ export const useUserStore = defineStore('user', () => {
13
+ // 状态
14
+ const userInfo = ref<UserInfo | null>(local.get<UserInfo>(USER_INFO_KEY))
15
+
16
+ // 计算属性
17
+ const isLoggedIn = computed(() => !!userInfo.value)
18
+ const userId = computed(() => userInfo.value?.userId || '')
19
+ const userName = computed(() => userInfo.value?.userName || '')
20
+ const departmentName = computed(() => userInfo.value?.departmentName || '')
21
+ const roleName = computed(() => userInfo.value?.roleName || '')
22
+ const email = computed(() => userInfo.value?.email || '')
23
+ const mobilePhone = computed(() => userInfo.value?.mobilePhone || '')
24
+ const positionName = computed(() => userInfo.value?.positionName || '')
25
+ const avatar = computed(() => userInfo.value?.avatar || '')
26
+
27
+ // 设置用户信息
28
+ const setUserInfo = (info: UserInfo) => {
29
+ userInfo.value = info
30
+ local.set(USER_INFO_KEY, info)
31
+ }
32
+
33
+ // 清除用户信息
34
+ const clearUserInfo = () => {
35
+ userInfo.value = null
36
+ local.remove(USER_INFO_KEY)
37
+ }
38
+
39
+ return {
40
+ userInfo,
41
+ isLoggedIn,
42
+ userId,
43
+ userName,
44
+ departmentName,
45
+ roleName,
46
+ email,
47
+ mobilePhone,
48
+ positionName,
49
+ avatar,
50
+ setUserInfo,
51
+ clearUserInfo
52
+ }
51
53
  })
@@ -1,81 +1,82 @@
1
- /**
2
- * API 通用类型定义
3
- */
4
-
5
- // 基础响应
6
- export interface ApiResponse<T = unknown> {
7
- code: number
8
- data: T
9
- message: string
10
- }
11
-
12
- // 分页请求参数
13
- export interface PageParams {
14
- page: number
15
- pageSize: number
16
- [key: string]: unknown
17
- }
18
-
19
- // 分页响应
20
- export interface PageResponse<T> {
21
- list: T[]
22
- total: number
23
- page: number
24
- pageSize: number
25
- }
26
-
27
- // 用户信息(参考 tineco-ui)
28
- export interface UserInfo {
29
- appId: string
30
- userId: string
31
- userName: string
32
- departmentName?: string
33
- email?: string
34
- mobilePhone?: string
35
- positionName?: string
36
- avatar?: string
37
- }
38
-
39
- // 登录请求参数(参考 tineco-ui)
40
- export interface LoginParams {
41
- appId: string
42
- clientId: string
43
- uid: string
44
- password: string
45
- code?: boolean
46
- }
47
-
48
- // 登录响应(参考 tineco-ui)
49
- export interface LoginResult {
50
- access_token: string
51
- token_type: string
52
- refresh_token: string
53
- expires_time: number
54
- refresh_time: number
55
- code?: string
56
- }
57
-
58
- // 菜单项(参考 tineco-ui)
59
- export interface MenuItem {
60
- menuCode: string
61
- menuName: string
62
- menuUrl: string
63
- icon?: string
64
- closable?: boolean
65
- isDefault?: boolean
66
- isOut?: boolean
67
- type?: number // 1 为按钮,其他为菜单
68
- children?: MenuItem[]
69
- }
70
-
71
- // 角色信息
72
- export interface RoleInfo {
73
- id: number | string
74
- name: string
75
- code: string
76
- description?: string
77
- status: number
78
- permissions: string[]
79
- createTime?: string
80
- updateTime?: string
1
+ /**
2
+ * API 通用类型定义
3
+ */
4
+
5
+ // 基础响应
6
+ export interface ApiResponse<T = unknown> {
7
+ code: number
8
+ data: T
9
+ message: string
10
+ }
11
+
12
+ // 分页请求参数
13
+ export interface PageParams {
14
+ page: number
15
+ pageSize: number
16
+ [key: string]: unknown
17
+ }
18
+
19
+ // 分页响应
20
+ export interface PageResponse<T> {
21
+ list: T[]
22
+ total: number
23
+ page: number
24
+ pageSize: number
25
+ }
26
+
27
+ // 用户信息(参考 tineco-ui)
28
+ export interface UserInfo {
29
+ appId: string
30
+ userId: string
31
+ userName: string
32
+ departmentName?: string
33
+ roleName?: string
34
+ email?: string
35
+ mobilePhone?: string
36
+ positionName?: string
37
+ avatar?: string
38
+ }
39
+
40
+ // 登录请求参数(参考 tineco-ui)
41
+ export interface LoginParams {
42
+ appId: string
43
+ clientId: string
44
+ uid: string
45
+ password: string
46
+ code?: boolean
47
+ }
48
+
49
+ // 登录响应(参考 tineco-ui)
50
+ export interface LoginResult {
51
+ access_token: string
52
+ token_type: string
53
+ refresh_token: string
54
+ expires_time: number
55
+ refresh_time: number
56
+ code?: string
57
+ }
58
+
59
+ // 菜单项(参考 tineco-ui)
60
+ export interface MenuItem {
61
+ menuCode: string
62
+ menuName: string
63
+ menuUrl: string
64
+ icon?: string
65
+ closable?: boolean
66
+ isDefault?: boolean
67
+ isOut?: boolean
68
+ type?: number // 1 为按钮,其他为菜单
69
+ children?: MenuItem[]
70
+ }
71
+
72
+ // 角色信息
73
+ export interface RoleInfo {
74
+ id: number | string
75
+ name: string
76
+ code: string
77
+ description?: string
78
+ status: number
79
+ permissions: string[]
80
+ createTime?: string
81
+ updateTime?: string
81
82
  }
@@ -12,6 +12,7 @@ import { ref } from 'vue'
12
12
  const appId = ref<string>('')
13
13
  const clientId = ref<string>('')
14
14
  const apiBaseUrl = ref<string>('')
15
+ const basePath = ref<string>('')
15
16
 
16
17
  // 运行时配置是否已加载
17
18
  let runtimeConfigLoaded = false
@@ -21,7 +22,12 @@ let runtimeConfig: Record<string, string> = {}
21
22
 
22
23
  /**
23
24
  * 加载运行时配置
24
- * 生产环境从 config.json 加载,开发环境跳过
25
+ * 仅在生产部署环境加载 config.json,开发环境自动跳过
26
+ *
27
+ * 注意:不能使用 import.meta.env.PROD 判断,因为作为 npm 库构建时
28
+ * Vite 会将 import.meta.env.PROD 静态替换为 true,导致开发环境也会尝试加载
29
+ * 改用 window.location 协议判断:file:// 或 localhost 开发服务器视为开发环境
30
+ *
25
31
  * @param basePath 应用基础路径,如 '/admin' 或 '/sea'
26
32
  */
27
33
  export async function loadRuntimeConfig(basePath?: string): Promise<Record<string, string>> {
@@ -30,8 +36,16 @@ export async function loadRuntimeConfig(basePath?: string): Promise<Record<strin
30
36
  return runtimeConfig
31
37
  }
32
38
 
33
- // 仅在生产环境加载 config.json
34
- if (import.meta.env.PROD) {
39
+ // 判断是否为生产部署环境
40
+ // 开发环境特征:localhost / 127.0.0.1 / 0.0.0.0 / file:// 协议
41
+ const isDevServer = typeof window !== 'undefined' && (
42
+ window.location.hostname === 'localhost' ||
43
+ window.location.hostname === '127.0.0.1' ||
44
+ window.location.hostname === '0.0.0.0' ||
45
+ window.location.protocol === 'file:'
46
+ )
47
+
48
+ if (!isDevServer) {
35
49
  try {
36
50
  // 确定配置文件路径
37
51
  const configPath = basePath ? `${basePath}/config.json` : '/config.json'
@@ -61,6 +75,7 @@ export function initAppConfig(config: {
61
75
  appId?: string
62
76
  clientId?: string
63
77
  apiBaseUrl?: string
78
+ basePath?: string
64
79
  }) {
65
80
  if (config.appId) {
66
81
  appId.value = config.appId
@@ -71,6 +86,9 @@ export function initAppConfig(config: {
71
86
  if (config.apiBaseUrl) {
72
87
  apiBaseUrl.value = config.apiBaseUrl
73
88
  }
89
+ if (config.basePath) {
90
+ basePath.value = config.basePath
91
+ }
74
92
  }
75
93
 
76
94
  /**
@@ -127,9 +145,17 @@ export function getApiBaseUrl(): string {
127
145
  }
128
146
  }
129
147
 
148
+ /**
149
+ * 获取应用基础路径(用于路由 base)
150
+ */
151
+ export function getBasePath(): string {
152
+ return basePath.value
153
+ }
154
+
130
155
  // 导出响应式引用(供特殊场景直接使用)
131
156
  export const appConfig = {
132
157
  appId,
133
158
  clientId,
134
- apiBaseUrl
159
+ apiBaseUrl,
160
+ basePath
135
161
  }
@@ -1,40 +1,40 @@
1
- /**
2
- * 权限工具函数
3
- */
4
-
5
- import { useMenuStore } from '@/stores/menu'
6
- import { useUserStore } from '@/stores/user'
7
-
8
- /**
9
- * 检查是否有权限
10
- * @param permission 权限标识(菜单 code)
11
- */
12
- export function hasPermission(permission: string | string[]): boolean {
13
- const userStore = useUserStore()
14
- const menuStore = useMenuStore()
15
-
16
- // 未登录则无权限
17
- if (!userStore.isLoggedIn) {
18
- return false
19
- }
20
-
21
- // 使用菜单 store 中的权限判断
22
- return menuStore.hasPermission(permission)
23
- }
24
-
25
- /**
26
- * 检查是否有角色
27
- * @param _role 角色标识(暂不使用)
28
- */
29
- export function hasRole(_role: string | string[]): boolean {
30
- const userStore = useUserStore()
31
- // tineco-ui 不支持 roles 字段,暂时返回已登录状态
32
- return userStore.isLoggedIn
33
- }
34
-
35
- /**
36
- * 检查是否是管理员
37
- */
38
- export function isAdmin(): boolean {
39
- return true // tineco-ui 不支持 roles 字段,暂时返回 true
1
+ /**
2
+ * 权限工具函数
3
+ */
4
+
5
+ import { useMenuStore } from '@/stores/menu'
6
+ import { useUserStore } from '@/stores/user'
7
+
8
+ /**
9
+ * 检查是否有权限
10
+ * @param permission 权限标识(菜单 code)
11
+ */
12
+ export function hasPermission(permission: string | string[]): boolean {
13
+ const userStore = useUserStore()
14
+ const menuStore = useMenuStore()
15
+
16
+ // 未登录则无权限
17
+ if (!userStore.isLoggedIn) {
18
+ return false
19
+ }
20
+
21
+ // 使用菜单 store 中的权限判断
22
+ return menuStore.hasPermission(permission)
23
+ }
24
+
25
+ /**
26
+ * 检查是否有角色
27
+ * @param _role 角色标识(暂不使用)
28
+ */
29
+ export function hasRole(_role: string | string[]): boolean {
30
+ const userStore = useUserStore()
31
+ // tineco-ui 不支持 roles 字段,暂时返回已登录状态
32
+ return userStore.isLoggedIn
33
+ }
34
+
35
+ /**
36
+ * 检查是否是管理员
37
+ */
38
+ export function isAdmin(): boolean {
39
+ return true // tineco-ui 不支持 roles 字段,暂时返回 true
40
40
  }