@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.
- package/package.json +1 -1
- package/templates/react/.env +3 -3
- package/templates/react/.env.development +3 -3
- package/templates/react/.env.production +3 -3
- package/templates/react/package.json +1 -1
- package/templates/react/pnpm-lock.yaml +5 -5
- package/templates/react/src/hooks/useForm.ts +77 -0
- package/templates/react/src/layouts/menuConfig.ts +3 -33
- package/templates/react/src/router/index.tsx +0 -39
- package/templates/react/src/stores/app.ts +141 -3
- package/templates/react/src/stores/notification.ts +128 -0
- package/templates/react/src/stores/permission.ts +183 -0
- package/templates/react/src/stores/user.ts +151 -4
- package/templates/react/src/views/home/index.tsx +167 -6
- package/templates/react/src/views/system/user/index.tsx +171 -5
- package/templates/vue/.env +2 -2
- package/templates/vue/.env.development +2 -2
- package/templates/vue/.env.production +2 -2
- package/templates/vue/pnpm-lock.yaml +3307 -3307
- package/templates/vue/src/auto-imports.d.ts +5 -0
- package/templates/vue/src/layouts/menuConfig.ts +3 -33
- package/templates/vue/src/router/index.ts +3 -88
- package/templates/vue/src/stores/app.ts +133 -2
- package/templates/vue/src/stores/notification.ts +172 -0
- package/templates/vue/src/stores/permission.ts +184 -0
- package/templates/vue/src/stores/user.ts +109 -2
- package/templates/vue/src/views/home/index.vue +7 -7
- package/templates/react/src/views/about/index.tsx +0 -40
- package/templates/react/src/views/login/index.tsx +0 -138
- package/templates/react/src/views/register/index.tsx +0 -143
- package/templates/react/src/views/result/fail.tsx +0 -39
- package/templates/react/src/views/result/success.tsx +0 -35
- package/templates/react/src/views/screen/index.tsx +0 -120
- package/templates/react/src/views/system/log/login.tsx +0 -51
- package/templates/react/src/views/system/log/operation.tsx +0 -47
- package/templates/react/src/views/system/menu/index.tsx +0 -62
- package/templates/react/src/views/system/role/index.tsx +0 -63
- package/templates/vue/src/views/about/index.vue +0 -67
- package/templates/vue/src/views/login/index.vue +0 -153
- package/templates/vue/src/views/register/index.vue +0 -169
- package/templates/vue/src/views/result/fail.vue +0 -92
- package/templates/vue/src/views/result/success.vue +0 -92
- package/templates/vue/src/views/screen/index.vue +0 -150
- package/templates/vue/src/views/system/log/login.vue +0 -51
- package/templates/vue/src/views/system/log/operation.vue +0 -47
- package/templates/vue/src/views/system/menu/index.vue +0 -58
- 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
|
-
<
|
|
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
|
-
<
|
|
209
|
+
<Button style={{ margin: '0 5px' }} type="button" onClick={() => navigate(`/user/detail/${user.id}`)}>
|
|
44
210
|
详情
|
|
45
|
-
</
|
|
46
|
-
<
|
|
47
|
-
<
|
|
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
|
))}
|
package/templates/vue/.env
CHANGED
|
@@ -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
|