@lvetechs/create-app 1.0.3 → 1.0.5

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.
Files changed (59) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +18 -4
  3. package/templates/react/.env +3 -0
  4. package/templates/react/.env.development +3 -0
  5. package/templates/react/.env.production +3 -0
  6. package/templates/react/index.html +1 -1
  7. package/templates/react/package.json +45 -45
  8. package/templates/react/pnpm-lock.yaml +166 -5
  9. package/templates/react/src/hooks/useForm.ts +77 -0
  10. package/templates/react/src/layouts/DefaultLayout.tsx +6 -1
  11. package/templates/react/src/layouts/menuConfig.ts +3 -33
  12. package/templates/react/src/main.tsx +3 -0
  13. package/templates/react/src/router/index.tsx +0 -39
  14. package/templates/react/src/stores/app.ts +141 -3
  15. package/templates/react/src/stores/notification.ts +128 -0
  16. package/templates/react/src/stores/permission.ts +183 -0
  17. package/templates/react/src/stores/user.ts +151 -4
  18. package/templates/react/src/views/home/index.tsx +205 -43
  19. package/templates/react/src/views/system/user/index.tsx +171 -5
  20. package/templates/react/tsconfig.json +1 -1
  21. package/templates/react/vite.config.ts +7 -3
  22. package/templates/vue/.env +2 -2
  23. package/templates/vue/.env.development +2 -2
  24. package/templates/vue/.env.production +2 -2
  25. package/templates/vue/index.html +1 -1
  26. package/templates/vue/package.json +0 -1
  27. package/templates/vue/pnpm-lock.yaml +3307 -3307
  28. package/templates/vue/src/auto-imports.d.ts +5 -0
  29. package/templates/vue/src/layouts/DefaultLayout.vue +2 -3
  30. package/templates/vue/src/layouts/menuConfig.ts +3 -33
  31. package/templates/vue/src/router/index.ts +4 -89
  32. package/templates/vue/src/stores/app.ts +133 -2
  33. package/templates/vue/src/stores/notification.ts +172 -0
  34. package/templates/vue/src/stores/permission.ts +184 -0
  35. package/templates/vue/src/stores/user.ts +109 -2
  36. package/templates/vue/src/views/home/index.vue +8 -8
  37. package/templates/react/_gitignore +0 -25
  38. package/templates/react/src/views/about/index.tsx +0 -40
  39. package/templates/react/src/views/login/index.tsx +0 -138
  40. package/templates/react/src/views/register/index.tsx +0 -143
  41. package/templates/react/src/views/result/fail.tsx +0 -39
  42. package/templates/react/src/views/result/success.tsx +0 -35
  43. package/templates/react/src/views/screen/index.tsx +0 -120
  44. package/templates/react/src/views/system/log/login.tsx +0 -51
  45. package/templates/react/src/views/system/log/operation.tsx +0 -47
  46. package/templates/react/src/views/system/menu/index.tsx +0 -62
  47. package/templates/react/src/views/system/role/index.tsx +0 -63
  48. package/templates/react/tsconfig.node.json +0 -11
  49. package/templates/vue/_gitignore +0 -25
  50. package/templates/vue/src/views/about/index.vue +0 -67
  51. package/templates/vue/src/views/login/index.vue +0 -153
  52. package/templates/vue/src/views/register/index.vue +0 -169
  53. package/templates/vue/src/views/result/fail.vue +0 -92
  54. package/templates/vue/src/views/result/success.vue +0 -92
  55. package/templates/vue/src/views/screen/index.vue +0 -150
  56. package/templates/vue/src/views/system/log/login.vue +0 -51
  57. package/templates/vue/src/views/system/log/operation.vue +0 -47
  58. package/templates/vue/src/views/system/menu/index.vue +0 -58
  59. package/templates/vue/src/views/system/role/index.vue +0 -59
@@ -7,22 +7,10 @@ const DefaultLayout = lazy(() => import('@/layouts/DefaultLayout'))
7
7
 
8
8
  // ==================== Layout 内页面 ====================
9
9
  const Home = lazy(() => import('@/views/home'))
10
- const About = lazy(() => import('@/views/about'))
11
10
 
12
11
  // 系统管理 (多级路由)
13
12
  const SystemUser = lazy(() => import('@/views/system/user'))
14
13
  const SystemUserDetail = lazy(() => import('@/views/system/user/detail'))
15
- const SystemRole = lazy(() => import('@/views/system/role'))
16
- const SystemMenu = lazy(() => import('@/views/system/menu'))
17
- const OperationLog = lazy(() => import('@/views/system/log/operation'))
18
- const LoginLog = lazy(() => import('@/views/system/log/login'))
19
-
20
- // ==================== 独立页面 (无 Layout) ====================
21
- const Login = lazy(() => import('@/views/login'))
22
- const Register = lazy(() => import('@/views/register'))
23
- const ResultSuccess = lazy(() => import('@/views/result/success'))
24
- const ResultFail = lazy(() => import('@/views/result/fail'))
25
- const DataScreen = lazy(() => import('@/views/screen'))
26
14
 
27
15
  // ==================== 错误页 ====================
28
16
  const Forbidden = lazy(() => import('@/views/error/403'))
@@ -63,39 +51,12 @@ export const routes: RouteObject[] = [
63
51
  children: [
64
52
  { index: true, element: <Navigate to="/system/user" replace /> },
65
53
  { path: 'user', element: lazyLoad(SystemUser) },
66
- { path: 'role', element: lazyLoad(SystemRole) },
67
- { path: 'menu', element: lazyLoad(SystemMenu) },
68
- // 三级路由: 系统管理 > 日志管理
69
- {
70
- path: 'log',
71
- children: [
72
- { index: true, element: <Navigate to="/system/log/operation" replace /> },
73
- { path: 'operation', element: lazyLoad(OperationLog) },
74
- { path: 'login', element: lazyLoad(LoginLog) }
75
- ]
76
- }
77
54
  ]
78
55
  },
79
- // 关于
80
- { path: 'about', element: lazyLoad(About) },
81
56
  // 详情页 (Layout 内但菜单隐藏)
82
57
  { path: 'user/detail/:id', element: lazyLoad(SystemUserDetail) }
83
58
  ]
84
59
  },
85
-
86
- // ==================== 独立页面 (无 Layout) ====================
87
- { path: '/login', element: lazyLoad(Login) },
88
- { path: '/register', element: lazyLoad(Register) },
89
- {
90
- path: '/result',
91
- children: [
92
- { index: true, element: <Navigate to="/result/success" replace /> },
93
- { path: 'success', element: lazyLoad(ResultSuccess) },
94
- { path: 'fail', element: lazyLoad(ResultFail) }
95
- ]
96
- },
97
- { path: '/screen', element: lazyLoad(DataScreen) },
98
-
99
60
  // ==================== 错误页 ====================
100
61
  { path: '/403', element: lazyLoad(Forbidden) },
101
62
  { path: '*', element: lazyLoad(NotFound) }
@@ -1,30 +1,168 @@
1
1
  import { create } from 'zustand'
2
2
  import { persist } from 'zustand/middleware'
3
3
 
4
+ export interface AppError {
5
+ id: string
6
+ message: string
7
+ code?: string
8
+ timestamp: number
9
+ type?: 'error' | 'warning' | 'info' | 'success'
10
+ }
11
+
4
12
  interface AppState {
13
+ /** 侧边栏是否收起 */
5
14
  sidebarCollapsed: boolean
15
+ /** 主题 */
6
16
  theme: 'light' | 'dark'
17
+ /** 语言 */
7
18
  language: string
19
+
20
+ /** 全局加载状态(是否存在任何加载任务) */
21
+ loading: boolean
22
+ /** 加载文案 */
23
+ loadingText: string
24
+ /** 加载任务栈,支持多个并发任务 */
25
+ loadingStack: string[]
26
+
27
+ /** 全局错误 / 提示列表 */
28
+ errors: AppError[]
29
+
30
+ // ========== 计算相关(通过方法计算,而不是状态字段) ==========
31
+ /** 是否有加载任务 */
32
+ getIsLoading: () => boolean
33
+ /** 未处理错误数量(error 类型) */
34
+ getUnhandledErrorCount: () => number
35
+
36
+ // ========== 基础设置 ==========
8
37
  toggleSidebar: () => void
38
+ setSidebarCollapsed: (collapsed: boolean) => void
9
39
  setTheme: (val: 'light' | 'dark') => void
40
+ toggleTheme: () => void
10
41
  setLanguage: (val: string) => void
42
+
43
+ // ========== 加载状态管理 ==========
44
+ startLoading: (taskId?: string, text?: string) => string
45
+ stopLoading: (taskId?: string) => void
46
+
47
+ // ========== 错误管理 ==========
48
+ addError: (message: string, code?: string, type?: AppError['type']) => string
49
+ removeError: (id: string) => void
50
+ clearErrors: () => void
51
+ clearErrorsByType: (type: AppError['type']) => void
52
+ getErrorsByType: (type: AppError['type']) => AppError[]
53
+
54
+ /** 简单的全局消息(内部用 addError 存一份,可配合全局提示组件展示) */
55
+ showMessage: (message: string, type?: 'success' | 'error' | 'warning' | 'info') => void
11
56
  }
12
57
 
13
58
  export const useAppStore = create<AppState>()(
14
59
  persist(
15
- (set) => ({
60
+ (set, get) => ({
16
61
  sidebarCollapsed: false,
17
62
  theme: 'light',
18
63
  language: 'zh-CN',
19
64
 
65
+ loading: false,
66
+ loadingText: '加载中...',
67
+ loadingStack: [],
68
+
69
+ errors: [],
70
+
71
+ // 计算相关
72
+ getIsLoading: () => get().loadingStack.length > 0,
73
+ getUnhandledErrorCount: () =>
74
+ get().errors.filter((e) => !e.type || e.type === 'error').length,
75
+
76
+ // 基础设置
20
77
  toggleSidebar: () => set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })),
21
78
 
79
+ setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
80
+
22
81
  setTheme: (val) => {
23
- document.documentElement.setAttribute('data-theme', val)
82
+ if (typeof document !== 'undefined') {
83
+ document.documentElement.setAttribute('data-theme', val)
84
+ }
24
85
  set({ theme: val })
25
86
  },
26
87
 
27
- setLanguage: (val) => set({ language: val })
88
+ toggleTheme: () =>
89
+ set((state) => {
90
+ const next = state.theme === 'light' ? 'dark' : 'light'
91
+ if (typeof document !== 'undefined') {
92
+ document.documentElement.setAttribute('data-theme', next)
93
+ }
94
+ return { theme: next }
95
+ }),
96
+
97
+ setLanguage: (val) => set({ language: val }),
98
+
99
+ // 加载状态管理
100
+ startLoading: (taskId, text) => {
101
+ const id =
102
+ taskId || `loading-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
103
+ set((state) => ({
104
+ loading: true,
105
+ loadingText: text || state.loadingText,
106
+ loadingStack: [...state.loadingStack, id]
107
+ }))
108
+ return id
109
+ },
110
+
111
+ stopLoading: (taskId) =>
112
+ set((state) => {
113
+ const stack = [...state.loadingStack]
114
+ if (taskId) {
115
+ const index = stack.indexOf(taskId)
116
+ if (index > -1) stack.splice(index, 1)
117
+ } else {
118
+ stack.pop()
119
+ }
120
+ return {
121
+ loading: stack.length > 0,
122
+ loadingText: stack.length > 0 ? state.loadingText : '加载中...',
123
+ loadingStack: stack
124
+ }
125
+ }),
126
+
127
+ // 错误管理
128
+ addError: (message, code, type = 'error') => {
129
+ const error: AppError = {
130
+ id: `error-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
131
+ message,
132
+ code,
133
+ timestamp: Date.now(),
134
+ type
135
+ }
136
+
137
+ set((state) => {
138
+ const next = [error, ...state.errors]
139
+ // 最多保留 10 条
140
+ if (next.length > 10) {
141
+ next.length = 10
142
+ }
143
+ return { errors: next }
144
+ })
145
+
146
+ return error.id
147
+ },
148
+
149
+ removeError: (id) =>
150
+ set((state) => ({
151
+ errors: state.errors.filter((e) => e.id !== id)
152
+ })),
153
+
154
+ clearErrors: () => set({ errors: [] }),
155
+
156
+ clearErrorsByType: (type) =>
157
+ set((state) => ({
158
+ errors: state.errors.filter((e) => e.type !== type)
159
+ })),
160
+
161
+ getErrorsByType: (type) => get().errors.filter((e) => e.type === type),
162
+
163
+ showMessage: (message, type = 'info') => {
164
+ get().addError(message, undefined, type)
165
+ }
28
166
  }),
29
167
  {
30
168
  name: 'app-store',
@@ -0,0 +1,128 @@
1
+ import { create } from 'zustand'
2
+ import { persist } from 'zustand/middleware'
3
+
4
+ export type NotificationType = 'info' | 'success' | 'warning' | 'error'
5
+
6
+ export interface NotificationItem {
7
+ id: string
8
+ title: string
9
+ content: string
10
+ type: NotificationType
11
+ read: boolean
12
+ timestamp: number
13
+ category?: string
14
+ actionUrl?: string
15
+ }
16
+
17
+ interface NotificationState {
18
+ notifications: NotificationItem[]
19
+
20
+ // 计算相关(通过方法计算)
21
+ getUnreadCount: () => number
22
+ getNotificationsByType: (type: NotificationType) => NotificationItem[]
23
+ getNotificationsByCategory: (category: string) => NotificationItem[]
24
+
25
+ // 基本操作
26
+ addNotification: (
27
+ notification: Omit<NotificationItem, 'id' | 'read' | 'timestamp'>
28
+ ) => string
29
+ markAsRead: (id: string) => void
30
+ markAllAsRead: () => void
31
+ markCategoryAsRead: (category: string) => void
32
+ removeNotification: (id: string) => void
33
+ removeReadNotifications: () => void
34
+ clearAll: () => void
35
+
36
+ // 示例数据
37
+ initSampleData: () => void
38
+ }
39
+
40
+ export const useNotificationStore = create<NotificationState>()(
41
+ persist(
42
+ (set, get) => ({
43
+ notifications: [],
44
+
45
+ getUnreadCount: () => get().notifications.filter((n) => !n.read).length,
46
+
47
+ getNotificationsByType: (type) =>
48
+ get().notifications.filter((n) => n.type === type),
49
+
50
+ getNotificationsByCategory: (category) =>
51
+ get().notifications.filter((n) => n.category === category),
52
+
53
+ addNotification: (notification) => {
54
+ const newNotification: NotificationItem = {
55
+ ...notification,
56
+ id: `notif-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
57
+ read: false,
58
+ timestamp: Date.now()
59
+ }
60
+ set((state) => ({
61
+ notifications: [newNotification, ...state.notifications]
62
+ }))
63
+ return newNotification.id
64
+ },
65
+
66
+ markAsRead: (id) =>
67
+ set((state) => ({
68
+ notifications: state.notifications.map((n) =>
69
+ n.id === id ? { ...n, read: true } : n
70
+ )
71
+ })),
72
+
73
+ markAllAsRead: () =>
74
+ set((state) => ({
75
+ notifications: state.notifications.map((n) => ({ ...n, read: true }))
76
+ })),
77
+
78
+ markCategoryAsRead: (category) =>
79
+ set((state) => ({
80
+ notifications: state.notifications.map((n) =>
81
+ n.category === category ? { ...n, read: true } : n
82
+ )
83
+ })),
84
+
85
+ removeNotification: (id) =>
86
+ set((state) => ({
87
+ notifications: state.notifications.filter((n) => n.id !== id)
88
+ })),
89
+
90
+ removeReadNotifications: () =>
91
+ set((state) => ({
92
+ notifications: state.notifications.filter((n) => !n.read)
93
+ })),
94
+
95
+ clearAll: () => set({ notifications: [] }),
96
+
97
+ initSampleData: () => {
98
+ if (get().notifications.length > 0) return
99
+ const add = get().addNotification
100
+ add({
101
+ title: '系统通知',
102
+ content: '欢迎使用本系统!',
103
+ type: 'info',
104
+ category: '系统'
105
+ })
106
+ add({
107
+ title: '订单提醒',
108
+ content: '您有新的订单待处理',
109
+ type: 'warning',
110
+ category: '订单'
111
+ })
112
+ add({
113
+ title: '操作成功',
114
+ content: '数据保存成功',
115
+ type: 'success',
116
+ category: '操作'
117
+ })
118
+ }
119
+ }),
120
+ {
121
+ name: 'notification-store',
122
+ partialize: (state) => ({
123
+ notifications: state.notifications
124
+ })
125
+ }
126
+ )
127
+ )
128
+
@@ -0,0 +1,183 @@
1
+ import { create } from 'zustand'
2
+ import { persist } from 'zustand/middleware'
3
+ import { useUserStore } from './user'
4
+
5
+ export type Permission = string
6
+ export type Role = string
7
+
8
+ export interface PermissionConfig {
9
+ permission: Permission
10
+ name: string
11
+ description?: string
12
+ module?: string
13
+ }
14
+
15
+ export interface RoleConfig {
16
+ role: Role
17
+ name: string
18
+ description?: string
19
+ permissions: Permission[]
20
+ }
21
+
22
+ interface PermissionState {
23
+ allPermissions: PermissionConfig[]
24
+ allRoles: RoleConfig[]
25
+
26
+ // 计算相关
27
+ getUserPermissions: () => Permission[]
28
+ getPermissionsByModule: () => Record<string, PermissionConfig[]>
29
+
30
+ // 检查权限 / 角色
31
+ hasPermission: (permission: Permission) => boolean
32
+ hasAnyPermission: (permissions: Permission[]) => boolean
33
+ hasAllPermissions: (permissions: Permission[]) => boolean
34
+ hasRole: (role: Role) => boolean
35
+ hasAnyRole: (roles: Role[]) => boolean
36
+
37
+ // 查询配置
38
+ getRoleConfig: (role: Role) => RoleConfig | undefined
39
+ getPermissionConfig: (permission: Permission) => PermissionConfig | undefined
40
+
41
+ // 修改配置
42
+ addPermission: (permission: PermissionConfig) => void
43
+ addRole: (role: RoleConfig) => void
44
+ updateRolePermissions: (role: Role, permissions: Permission[]) => void
45
+ }
46
+
47
+ export const usePermissionStore = create<PermissionState>()(
48
+ persist(
49
+ (set, get) => ({
50
+ allPermissions: [
51
+ { permission: 'user:view', name: '查看用户', module: '用户管理' },
52
+ { permission: 'user:create', name: '创建用户', module: '用户管理' },
53
+ { permission: 'user:edit', name: '编辑用户', module: '用户管理' },
54
+ { permission: 'user:delete', name: '删除用户', module: '用户管理' },
55
+ { permission: 'role:view', name: '查看角色', module: '角色管理' },
56
+ { permission: 'role:create', name: '创建角色', module: '角色管理' },
57
+ { permission: 'role:edit', name: '编辑角色', module: '角色管理' },
58
+ { permission: 'role:delete', name: '删除角色', module: '角色管理' },
59
+ { permission: 'system:config', name: '系统配置', module: '系统管理' },
60
+ { permission: 'system:log', name: '查看日志', module: '系统管理' }
61
+ ],
62
+
63
+ allRoles: [
64
+ {
65
+ role: 'admin',
66
+ name: '超级管理员',
67
+ description: '拥有所有权限',
68
+ permissions: [
69
+ 'user:view',
70
+ 'user:create',
71
+ 'user:edit',
72
+ 'user:delete',
73
+ 'role:view',
74
+ 'role:create',
75
+ 'role:edit',
76
+ 'role:delete',
77
+ 'system:config',
78
+ 'system:log'
79
+ ]
80
+ },
81
+ {
82
+ role: 'editor',
83
+ name: '编辑',
84
+ description: '可以查看和编辑内容',
85
+ permissions: ['user:view', 'user:create', 'user:edit', 'role:view']
86
+ },
87
+ {
88
+ role: 'viewer',
89
+ name: '只读',
90
+ description: '只能查看内容',
91
+ permissions: ['user:view', 'role:view']
92
+ }
93
+ ],
94
+
95
+ getUserPermissions: () => {
96
+ const user = useUserStore.getState().userInfo
97
+ if (!user) return []
98
+
99
+ const roles = user.roles || []
100
+ const allRoles = get().allRoles
101
+
102
+ const permissions: Permission[] = []
103
+ roles.forEach((role) => {
104
+ const roleConfig = allRoles.find((r) => r.role === role)
105
+ if (roleConfig) {
106
+ permissions.push(...roleConfig.permissions)
107
+ }
108
+ })
109
+
110
+ return Array.from(new Set(permissions))
111
+ },
112
+
113
+ getPermissionsByModule: () => {
114
+ const grouped: Record<string, PermissionConfig[]> = {}
115
+ get().allPermissions.forEach((permission) => {
116
+ const module = permission.module || '其他'
117
+ if (!grouped[module]) grouped[module] = []
118
+ grouped[module].push(permission)
119
+ })
120
+ return grouped
121
+ },
122
+
123
+ hasPermission: (permission) => get().getUserPermissions().includes(permission),
124
+
125
+ hasAnyPermission: (permissions) => permissions.some((p) => get().hasPermission(p)),
126
+
127
+ hasAllPermissions: (permissions) => permissions.every((p) => get().hasPermission(p)),
128
+
129
+ hasRole: (role) => {
130
+ const user = useUserStore.getState().userInfo
131
+ return user?.roles?.includes(role) ?? false
132
+ },
133
+
134
+ hasAnyRole: (roles) => roles.some((r) => get().hasRole(r)),
135
+
136
+ getRoleConfig: (role) => get().allRoles.find((r) => r.role === role),
137
+
138
+ getPermissionConfig: (permission) =>
139
+ get().allPermissions.find((p) => p.permission === permission),
140
+
141
+ getPermissionsByModule: () => {
142
+ const grouped: Record<string, PermissionConfig[]> = {}
143
+ get().allPermissions.forEach((permission) => {
144
+ const module = permission.module || '其他'
145
+ if (!grouped[module]) grouped[module] = []
146
+ grouped[module].push(permission)
147
+ })
148
+ return grouped
149
+ },
150
+
151
+ addPermission: (permission) =>
152
+ set((state) => {
153
+ if (state.allPermissions.some((p) => p.permission === permission.permission)) {
154
+ return state
155
+ }
156
+ return { allPermissions: [...state.allPermissions, permission] }
157
+ }),
158
+
159
+ addRole: (role) =>
160
+ set((state) => {
161
+ if (state.allRoles.some((r) => r.role === role.role)) {
162
+ return state
163
+ }
164
+ return { allRoles: [...state.allRoles, role] }
165
+ }),
166
+
167
+ updateRolePermissions: (role, permissions) =>
168
+ set((state) => ({
169
+ allRoles: state.allRoles.map((r) =>
170
+ r.role === role ? { ...r, permissions } : r
171
+ )
172
+ }))
173
+ }),
174
+ {
175
+ name: 'permission-store',
176
+ partialize: (state) => ({
177
+ allPermissions: state.allPermissions,
178
+ allRoles: state.allRoles
179
+ })
180
+ }
181
+ )
182
+ )
183
+