@lvetechs/create-app 1.0.4 → 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 (47) hide show
  1. package/package.json +1 -1
  2. package/templates/react/.env +3 -3
  3. package/templates/react/.env.development +3 -3
  4. package/templates/react/.env.production +3 -3
  5. package/templates/react/package.json +1 -1
  6. package/templates/react/pnpm-lock.yaml +5 -5
  7. package/templates/react/src/hooks/useForm.ts +77 -0
  8. package/templates/react/src/layouts/menuConfig.ts +3 -33
  9. package/templates/react/src/router/index.tsx +0 -39
  10. package/templates/react/src/stores/app.ts +141 -3
  11. package/templates/react/src/stores/notification.ts +128 -0
  12. package/templates/react/src/stores/permission.ts +183 -0
  13. package/templates/react/src/stores/user.ts +151 -4
  14. package/templates/react/src/views/home/index.tsx +167 -6
  15. package/templates/react/src/views/system/user/index.tsx +171 -5
  16. package/templates/vue/.env +2 -2
  17. package/templates/vue/.env.development +2 -2
  18. package/templates/vue/.env.production +2 -2
  19. package/templates/vue/pnpm-lock.yaml +3307 -3307
  20. package/templates/vue/src/auto-imports.d.ts +5 -0
  21. package/templates/vue/src/layouts/menuConfig.ts +3 -33
  22. package/templates/vue/src/router/index.ts +3 -88
  23. package/templates/vue/src/stores/app.ts +133 -2
  24. package/templates/vue/src/stores/notification.ts +172 -0
  25. package/templates/vue/src/stores/permission.ts +184 -0
  26. package/templates/vue/src/stores/user.ts +109 -2
  27. package/templates/vue/src/views/home/index.vue +7 -7
  28. package/templates/react/src/views/about/index.tsx +0 -40
  29. package/templates/react/src/views/login/index.tsx +0 -138
  30. package/templates/react/src/views/register/index.tsx +0 -143
  31. package/templates/react/src/views/result/fail.tsx +0 -39
  32. package/templates/react/src/views/result/success.tsx +0 -35
  33. package/templates/react/src/views/screen/index.tsx +0 -120
  34. package/templates/react/src/views/system/log/login.tsx +0 -51
  35. package/templates/react/src/views/system/log/operation.tsx +0 -47
  36. package/templates/react/src/views/system/menu/index.tsx +0 -62
  37. package/templates/react/src/views/system/role/index.tsx +0 -63
  38. package/templates/vue/src/views/about/index.vue +0 -67
  39. package/templates/vue/src/views/login/index.vue +0 -153
  40. package/templates/vue/src/views/register/index.vue +0 -169
  41. package/templates/vue/src/views/result/fail.vue +0 -92
  42. package/templates/vue/src/views/result/success.vue +0 -92
  43. package/templates/vue/src/views/screen/index.vue +0 -150
  44. package/templates/vue/src/views/system/log/login.vue +0 -51
  45. package/templates/vue/src/views/system/log/operation.vue +0 -47
  46. package/templates/vue/src/views/system/menu/index.vue +0 -58
  47. package/templates/vue/src/views/system/role/index.vue +0 -59
@@ -1,5 +1,39 @@
1
+ import { useState } from 'react'
1
2
  import { useNavigate } from 'react-router-dom'
2
3
  import '@/styles/page-common.scss'
4
+ import { Button, Input, Dialog } from '@lvetechs/ui-lib'
5
+ import { useForm } from '@/hooks/useForm'
6
+
7
+ // 如果 @lvetechs/ui-lib 没有 Select,自己实现一个简单的 Select
8
+ interface SelectProps {
9
+ name?: string
10
+ value: string
11
+ onChange: (value: string) => void
12
+ options: Array<{ label: string; value: string }>
13
+ placeholder?: string
14
+ }
15
+
16
+ function Select({ name, value, onChange, options, placeholder }: SelectProps) {
17
+ return (
18
+ <select
19
+ name={name}
20
+ className="w-full rounded border border-slate-200 px-3 py-2 text-sm outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
21
+ value={value}
22
+ onChange={(e) => onChange(e.target.value)}
23
+ >
24
+ {placeholder && (
25
+ <option value="" disabled>
26
+ {placeholder}
27
+ </option>
28
+ )}
29
+ {options.map((option) => (
30
+ <option key={option.value} value={option.value}>
31
+ {option.label}
32
+ </option>
33
+ ))}
34
+ </select>
35
+ )
36
+ }
3
37
 
4
38
  const users = [
5
39
  { id: 1, username: 'admin', nickname: '管理员', role: '超级管理员', status: '启用' },
@@ -7,15 +41,147 @@ const users = [
7
41
  { id: 3, username: 'viewer', nickname: '访客', role: '只读', status: '禁用' }
8
42
  ]
9
43
 
44
+ interface UserFormValues {
45
+ username: string
46
+ nickname: string
47
+ email: string
48
+ role: string
49
+ }
50
+
51
+ function validateUserForm(values: UserFormValues) {
52
+ const errors: Partial<Record<keyof UserFormValues, string>> = {}
53
+ if (!values.username.trim()) {
54
+ errors.username = '请输入用户名'
55
+ }
56
+ if (!values.nickname.trim()) {
57
+ errors.nickname = '请输入昵称'
58
+ }
59
+ if (!values.email.trim()) {
60
+ errors.email = '请输入邮箱'
61
+ } else if (!/^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/.test(values.email)) {
62
+ errors.email = '邮箱格式不正确'
63
+ }
64
+ if (!values.role.trim()) {
65
+ errors.role = '请选择角色'
66
+ }
67
+ return errors
68
+ }
69
+
10
70
  export default function SystemUser() {
11
71
  const navigate = useNavigate()
72
+ const [showForm, setShowForm] = useState(false)
73
+
74
+ const { values, errors, submitting, handleChange, handleSubmit, reset } = useForm<UserFormValues>({
75
+ initialValues: {
76
+ username: '',
77
+ nickname: '',
78
+ email: '',
79
+ role: ''
80
+ },
81
+ validate: validateUserForm,
82
+ onSubmit: async (formValues) => {
83
+ // eslint-disable-next-line no-console
84
+ console.log('新增用户', formValues)
85
+ await new Promise((resolve) => setTimeout(resolve, 800))
86
+ alert(`用户 ${formValues.username} 创建成功!`)
87
+ setShowForm(false)
88
+ reset()
89
+ }
90
+ })
91
+
92
+ const handleOpenForm = () => {
93
+ setShowForm(true)
94
+ reset()
95
+ }
96
+
97
+ const handleCloseForm = () => {
98
+ setShowForm(false)
99
+ reset()
100
+ }
12
101
 
13
102
  return (
14
103
  <div className="page-container">
15
104
  <div className="page-header">
16
105
  <h2>用户管理</h2>
17
- <button className="btn-primary">+ 新增用户</button>
106
+ <Button onClick={handleOpenForm}>+ 新增用户</Button>
18
107
  </div>
108
+
109
+ {/* 新增用户表单弹窗(使用 @lvetechs/ui-lib 的 Dialog 作为容器) */}
110
+ {showForm && (
111
+ <Dialog
112
+ visible={showForm}
113
+ maskClosable={false}
114
+ onClose={handleCloseForm}
115
+ onOk={handleSubmit}
116
+ okText={submitting ? '提交中...' : '提交'}
117
+ cancelText='取消'
118
+ onCancel={handleCloseForm}
119
+ >
120
+ <div className="space-y-4">
121
+ <label className="mb-1 block text-sm font-medium text-slate-700" htmlFor="username">
122
+ 用户名 <span className="text-rose-500">*</span>
123
+ </label>
124
+ <Input
125
+ id="username"
126
+ name="username"
127
+ placeholder="请输入用户名"
128
+ value={values.username}
129
+ onChange={handleChange}
130
+ />
131
+ {errors.username && (
132
+ <p className="mt-1 text-xs text-rose-500">{errors.username}</p>
133
+ )}
134
+ </div>
135
+ <div>
136
+ <label className="mb-1 block text-sm font-medium text-slate-700" htmlFor="nickname">
137
+ 昵称 <span className="text-rose-500">*</span>
138
+ </label>
139
+ <Input
140
+ id="nickname"
141
+ name="nickname"
142
+ placeholder="请输入昵称"
143
+ value={values.nickname}
144
+ onChange={handleChange}
145
+ />
146
+ {errors.nickname && (
147
+ <p className="mt-1 text-xs text-rose-500">{errors.nickname}</p>
148
+ )}
149
+ </div>
150
+ <div>
151
+ <label className="mb-1 block text-sm font-medium text-slate-700" htmlFor="email">
152
+ 邮箱 <span className="text-rose-500">*</span>
153
+ </label>
154
+ <Input
155
+ id="email"
156
+ name="email"
157
+ type="email"
158
+ placeholder="请输入邮箱"
159
+ value={values.email}
160
+ onChange={handleChange}
161
+ />
162
+ {errors.email && <p className="mt-1 text-xs text-rose-500">{errors.email}</p>}
163
+ </div>
164
+ <div>
165
+ <label className="mb-1 block text-sm font-medium text-slate-700" htmlFor="role">
166
+ 角色 <span className="text-rose-500">*</span>
167
+ </label>
168
+ <Select
169
+ name="role"
170
+ value={values.role}
171
+ onChange={(value) => {
172
+ handleChange({ target: { name: 'role', value } } as any)
173
+ }}
174
+ placeholder="请选择角色"
175
+ options={[
176
+ { label: '超级管理员', value: '超级管理员' },
177
+ { label: '编辑', value: '编辑' },
178
+ { label: '只读', value: '只读' }
179
+ ]}
180
+ />
181
+ {errors.role && <p className="mt-1 text-xs text-rose-500">{errors.role}</p>}
182
+ </div>
183
+ </Dialog>
184
+ )}
19
185
  <table className="data-table">
20
186
  <thead>
21
187
  <tr>
@@ -40,11 +206,11 @@ export default function SystemUser() {
40
206
  </span>
41
207
  </td>
42
208
  <td>
43
- <button className="btn-link" onClick={() => navigate(`/user/detail/${user.id}`)}>
209
+ <Button style={{ margin: '0 5px' }} type="button" onClick={() => navigate(`/user/detail/${user.id}`)}>
44
210
  详情
45
- </button>
46
- <button className="btn-link">编辑</button>
47
- <button className="btn-link danger">删除</button>
211
+ </Button>
212
+ <Button style={{ margin: '0 5px' }} type="button">编辑</Button>
213
+ <Button style={{ margin: '0 5px' }} type="button">删除</Button>
48
214
  </td>
49
215
  </tr>
50
216
  ))}
@@ -1,3 +1,3 @@
1
- # 基础环境变量
2
- VITE_APP_TITLE=My Vue App
1
+ # 基础环境变量
2
+ VITE_APP_TITLE=My Vue App
3
3
  VITE_APP_BASE_API=/api
@@ -1,3 +1,3 @@
1
- # 开发环境
2
- VITE_APP_TITLE=My Vue App (Dev)
1
+ # 开发环境
2
+ VITE_APP_TITLE=My Vue App (Dev)
3
3
  VITE_APP_BASE_API=/api
@@ -1,3 +1,3 @@
1
- # 生产环境
2
- VITE_APP_TITLE=My Vue App
1
+ # 生产环境
2
+ VITE_APP_TITLE=My Vue App
3
3
  VITE_APP_BASE_API=https://api.example.com