@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
@@ -0,0 +1,184 @@
1
+ import { defineStore } from 'pinia'
2
+ import { ref, computed } from 'vue'
3
+ import { useUserStore } from './user'
4
+
5
+ export type Permission = string
6
+ export type Role = string
7
+
8
+ export interface PermissionConfig {
9
+ // 权限标识
10
+ permission: Permission
11
+ // 权限名称
12
+ name: string
13
+ // 权限描述
14
+ description?: string
15
+ // 所属模块
16
+ module?: string
17
+ }
18
+
19
+ export interface RoleConfig {
20
+ // 角色标识
21
+ role: Role
22
+ // 角色名称
23
+ name: string
24
+ // 角色描述
25
+ description?: string
26
+ // 角色拥有的权限列表
27
+ permissions: Permission[]
28
+ }
29
+
30
+ export const usePermissionStore = defineStore(
31
+ 'permission',
32
+ () => {
33
+ // 所有权限配置
34
+ const allPermissions = ref<PermissionConfig[]>([
35
+ { permission: 'user:view', name: '查看用户', module: '用户管理' },
36
+ { permission: 'user:create', name: '创建用户', module: '用户管理' },
37
+ { permission: 'user:edit', name: '编辑用户', module: '用户管理' },
38
+ { permission: 'user:delete', name: '删除用户', module: '用户管理' },
39
+ { permission: 'role:view', name: '查看角色', module: '角色管理' },
40
+ { permission: 'role:create', name: '创建角色', module: '角色管理' },
41
+ { permission: 'role:edit', name: '编辑角色', module: '角色管理' },
42
+ { permission: 'role:delete', name: '删除角色', module: '角色管理' },
43
+ { permission: 'system:config', name: '系统配置', module: '系统管理' },
44
+ { permission: 'system:log', name: '查看日志', module: '系统管理' }
45
+ ])
46
+
47
+ // 所有角色配置
48
+ const allRoles = ref<RoleConfig[]>([
49
+ {
50
+ role: 'admin',
51
+ name: '超级管理员',
52
+ description: '拥有所有权限',
53
+ permissions: ['user:view', 'user:create', 'user:edit', 'user:delete', 'role:view', 'role:create', 'role:edit', 'role:delete', 'system:config', 'system:log']
54
+ },
55
+ {
56
+ role: 'editor',
57
+ name: '编辑',
58
+ description: '可以查看和编辑内容',
59
+ permissions: ['user:view', 'user:create', 'user:edit', 'role:view']
60
+ },
61
+ {
62
+ role: 'viewer',
63
+ name: '只读',
64
+ description: '只能查看内容',
65
+ permissions: ['user:view', 'role:view']
66
+ }
67
+ ])
68
+
69
+ // 当前用户拥有的权限(从 user store 获取)
70
+ const userPermissions = computed(() => {
71
+ const userStore = useUserStore()
72
+ if (!userStore.userInfo) return []
73
+
74
+ const userRoles = userStore.userInfo.roles || []
75
+ const permissions: Permission[] = []
76
+
77
+ // 根据用户角色获取所有权限
78
+ userRoles.forEach((role) => {
79
+ const roleConfig = allRoles.value.find((r) => r.role === role)
80
+ if (roleConfig) {
81
+ permissions.push(...roleConfig.permissions)
82
+ }
83
+ })
84
+
85
+ // 去重
86
+ return Array.from(new Set(permissions))
87
+ })
88
+
89
+ // 检查是否有指定权限
90
+ function hasPermission(permission: Permission): boolean {
91
+ return userPermissions.value.includes(permission)
92
+ }
93
+
94
+ // 检查是否有任意一个权限
95
+ function hasAnyPermission(permissions: Permission[]): boolean {
96
+ return permissions.some((p) => hasPermission(p))
97
+ }
98
+
99
+ // 检查是否有所有权限
100
+ function hasAllPermissions(permissions: Permission[]): boolean {
101
+ return permissions.every((p) => hasPermission(p))
102
+ }
103
+
104
+ // 检查是否有指定角色
105
+ function hasRole(role: Role): boolean {
106
+ const userStore = useUserStore()
107
+ if (!userStore.userInfo) return false
108
+ return userStore.userInfo.roles?.includes(role) || false
109
+ }
110
+
111
+ // 检查是否有任意一个角色
112
+ function hasAnyRole(roles: Role[]): boolean {
113
+ return roles.some((r) => hasRole(r))
114
+ }
115
+
116
+ // 获取角色配置
117
+ function getRoleConfig(role: Role): RoleConfig | undefined {
118
+ return allRoles.value.find((r) => r.role === role)
119
+ }
120
+
121
+ // 获取权限配置
122
+ function getPermissionConfig(permission: Permission): PermissionConfig | undefined {
123
+ return allPermissions.value.find((p) => p.permission === permission)
124
+ }
125
+
126
+ // 按模块分组权限
127
+ const permissionsByModule = computed(() => {
128
+ const grouped: Record<string, PermissionConfig[]> = {}
129
+ allPermissions.value.forEach((permission) => {
130
+ const module = permission.module || '其他'
131
+ if (!grouped[module]) {
132
+ grouped[module] = []
133
+ }
134
+ grouped[module].push(permission)
135
+ })
136
+ return grouped
137
+ })
138
+
139
+ // 添加权限配置
140
+ function addPermission(permission: PermissionConfig) {
141
+ if (!allPermissions.value.find((p) => p.permission === permission.permission)) {
142
+ allPermissions.value.push(permission)
143
+ }
144
+ }
145
+
146
+ // 添加角色配置
147
+ function addRole(role: RoleConfig) {
148
+ if (!allRoles.value.find((r) => r.role === role.role)) {
149
+ allRoles.value.push(role)
150
+ }
151
+ }
152
+
153
+ // 更新角色权限
154
+ function updateRolePermissions(role: Role, permissions: Permission[]) {
155
+ const roleConfig = allRoles.value.find((r) => r.role === role)
156
+ if (roleConfig) {
157
+ roleConfig.permissions = permissions
158
+ }
159
+ }
160
+
161
+ return {
162
+ allPermissions,
163
+ allRoles,
164
+ userPermissions,
165
+ permissionsByModule,
166
+ hasPermission,
167
+ hasAnyPermission,
168
+ hasAllPermissions,
169
+ hasRole,
170
+ hasAnyRole,
171
+ getRoleConfig,
172
+ getPermissionConfig,
173
+ addPermission,
174
+ addRole,
175
+ updateRolePermissions
176
+ }
177
+ },
178
+ {
179
+ persist: {
180
+ key: 'permission-store',
181
+ paths: ['allPermissions', 'allRoles']
182
+ }
183
+ }
184
+ )
@@ -1,5 +1,6 @@
1
1
  import { defineStore } from 'pinia'
2
2
  import { ref, computed } from 'vue'
3
+ import { usePermissionStore } from './permission'
3
4
 
4
5
  export interface UserInfo {
5
6
  id: number
@@ -7,6 +8,10 @@ export interface UserInfo {
7
8
  nickname: string
8
9
  avatar: string
9
10
  roles: string[]
11
+ email?: string
12
+ phone?: string
13
+ department?: string
14
+ lastLoginTime?: number
10
15
  }
11
16
 
12
17
  export const useUserStore = defineStore(
@@ -14,35 +19,137 @@ export const useUserStore = defineStore(
14
19
  () => {
15
20
  const token = ref<string>('')
16
21
  const userInfo = ref<UserInfo | null>(null)
22
+ const loginHistory = ref<Array<{ time: number; ip: string; location?: string }>>([])
17
23
 
18
24
  const isLoggedIn = computed(() => !!token.value)
19
25
 
26
+ // 计算属性:用户显示名称
27
+ const displayName = computed(() => {
28
+ return userInfo.value?.nickname || userInfo.value?.username || '未登录'
29
+ })
30
+
31
+ // 计算属性:用户头像
32
+ const avatar = computed(() => {
33
+ return userInfo.value?.avatar || '/default-avatar.png'
34
+ })
35
+
36
+ // 计算属性:是否是管理员
37
+ const isAdmin = computed(() => {
38
+ return userInfo.value?.roles?.includes('admin') || false
39
+ })
40
+
20
41
  function setToken(val: string) {
21
42
  token.value = val
22
43
  }
23
44
 
24
45
  function setUserInfo(info: UserInfo) {
25
46
  userInfo.value = info
47
+ // 更新最后登录时间
48
+ if (userInfo.value) {
49
+ userInfo.value.lastLoginTime = Date.now()
50
+ }
51
+ }
52
+
53
+ // 更新用户信息(部分更新)
54
+ function updateUserInfo(updates: Partial<UserInfo>) {
55
+ if (userInfo.value) {
56
+ userInfo.value = { ...userInfo.value, ...updates }
57
+ }
58
+ }
59
+
60
+ // 添加登录历史记录
61
+ function addLoginHistory(ip: string, location?: string) {
62
+ loginHistory.value.unshift({
63
+ time: Date.now(),
64
+ ip,
65
+ location
66
+ })
67
+ // 只保留最近 10 条记录
68
+ if (loginHistory.value.length > 10) {
69
+ loginHistory.value = loginHistory.value.slice(0, 10)
70
+ }
71
+ }
72
+
73
+ // 检查用户是否有指定权限(通过 permission store)
74
+ function hasPermission(permission: string): boolean {
75
+ const permissionStore = usePermissionStore()
76
+ return permissionStore.hasPermission(permission)
77
+ }
78
+
79
+ // 检查用户是否有指定角色
80
+ function hasRole(role: string): boolean {
81
+ return userInfo.value?.roles?.includes(role) || false
82
+ }
83
+
84
+ // 添加角色
85
+ function addRole(role: string) {
86
+ if (userInfo.value && !userInfo.value.roles.includes(role)) {
87
+ userInfo.value.roles.push(role)
88
+ }
89
+ }
90
+
91
+ // 移除角色
92
+ function removeRole(role: string) {
93
+ if (userInfo.value) {
94
+ userInfo.value.roles = userInfo.value.roles.filter((r) => r !== role)
95
+ }
26
96
  }
27
97
 
28
98
  function logout() {
29
99
  token.value = ''
30
100
  userInfo.value = null
101
+ // 清空登录历史(可选,根据需求决定)
102
+ // loginHistory.value = []
103
+ }
104
+
105
+ // 模拟登录
106
+ async function login(username: string, _password: string) {
107
+ // 这里应该是实际的 API 调用
108
+ // 模拟异步登录
109
+ await new Promise((resolve) => setTimeout(resolve, 1000))
110
+
111
+ // 模拟登录成功
112
+ const mockUserInfo: UserInfo = {
113
+ id: 1,
114
+ username,
115
+ nickname: username === 'admin' ? '管理员' : '普通用户',
116
+ avatar: '/avatar.png',
117
+ roles: username === 'admin' ? ['admin'] : ['viewer'],
118
+ email: `${username}@example.com`,
119
+ lastLoginTime: Date.now()
120
+ }
121
+
122
+ setToken('mock-token-' + Date.now())
123
+ setUserInfo(mockUserInfo)
124
+ addLoginHistory('192.168.1.100', '本地')
125
+
126
+ return mockUserInfo
31
127
  }
32
128
 
33
129
  return {
34
130
  token,
35
131
  userInfo,
132
+ loginHistory,
36
133
  isLoggedIn,
134
+ displayName,
135
+ avatar,
136
+ isAdmin,
37
137
  setToken,
38
138
  setUserInfo,
39
- logout
139
+ updateUserInfo,
140
+ addLoginHistory,
141
+ hasPermission,
142
+ hasRole,
143
+ addRole,
144
+ removeRole,
145
+ logout,
146
+ login
40
147
  }
41
148
  },
42
149
  {
43
150
  persist: {
44
151
  key: 'user-store',
45
- paths: ['token']
152
+ paths: ['token', 'userInfo', 'loginHistory']
46
153
  }
47
154
  }
48
155
  )
@@ -7,34 +7,34 @@
7
7
  <template>
8
8
  <div class="home-page">
9
9
  <div class="welcome-card">
10
- <h1>👋 欢迎使用 {{title}}</h1>
10
+ <h1>欢迎使用 {{title}}</h1>
11
11
  <p>
12
12
  该模板集成了 Vue 3 + Vite + TypeScript + Vue Router + Pinia + Axios + TailwindCSS
13
13
  等常用工具和最佳实践。
14
14
  </p>
15
15
  <div class="features">
16
16
  <div class="feature-item">
17
- <h3>⚡ Vite</h3>
17
+ <h3>Vite</h3>
18
18
  <p>下一代前端构建工具,极速开发体验</p>
19
19
  </div>
20
20
  <div class="feature-item">
21
- <h3>🦾 TypeScript</h3>
21
+ <h3>TypeScript</h3>
22
22
  <p>强类型语言支持,提高代码质量</p>
23
23
  </div>
24
24
  <div class="feature-item">
25
- <h3>🍍 Pinia</h3>
25
+ <h3>Pinia</h3>
26
26
  <p>轻量灵活的状态管理方案</p>
27
27
  </div>
28
28
  <div class="feature-item">
29
- <h3>🧭 Vue Router</h3>
29
+ <h3>Vue Router</h3>
30
30
  <p>官方路由管理,支持懒加载</p>
31
31
  </div>
32
32
  <div class="feature-item">
33
- <h3>📦 Auto Import</h3>
33
+ <h3>Auto Import</h3>
34
34
  <p>API 和组件自动按需导入</p>
35
35
  </div>
36
36
  <div class="feature-item">
37
- <h3>🎨 TailwindCSS</h3>
37
+ <h3>TailwindCSS</h3>
38
38
  <p>即时按需原子化 CSS 引擎</p>
39
39
  </div>
40
40
  </div>
@@ -1,40 +0,0 @@
1
- import '@/styles/page-common.scss'
2
-
3
- export default function About() {
4
- const features = [
5
- 'React 18 + Hooks',
6
- 'Vite 5 构建工具',
7
- 'TypeScript 5 类型支持',
8
- 'React Router 6 路由管理',
9
- 'Zustand 状态管理 (支持持久化)',
10
- 'Axios 请求封装 (拦截器 + 类型)',
11
- 'TailwindCSS 实用优先 CSS',
12
- 'ESLint + Prettier 代码规范'
13
- ]
14
-
15
- return (
16
- <div style={{ maxWidth: 700, margin: '0 auto' }}>
17
- <div className="page-card">
18
- <h1 style={{ fontSize: 24, marginBottom: 12 }}>关于</h1>
19
- <p style={{ color: 'var(--text-color-secondary)', marginBottom: 16 }}>
20
- 这是一个基于 React 18 的现代化前端模板项目。
21
- </p>
22
- <ul style={{ listStyle: 'none', padding: 0 }}>
23
- {features.map((item) => (
24
- <li
25
- key={item}
26
- style={{
27
- padding: '8px 0',
28
- borderBottom: '1px solid var(--border-color-light)',
29
- color: 'var(--text-color-regular)'
30
- }}
31
- >
32
- ✅ {item}
33
- </li>
34
- ))}
35
- </ul>
36
- </div>
37
- </div>
38
- )
39
- }
40
-
@@ -1,138 +0,0 @@
1
- import { useState } from 'react'
2
- import { useNavigate } from 'react-router-dom'
3
- import { useUserStore } from '@/stores/user'
4
-
5
- export default function Login() {
6
- const navigate = useNavigate()
7
- const { setToken, setUserInfo } = useUserStore()
8
-
9
- const [form, setForm] = useState({ username: '', password: '' })
10
- const [loading, setLoading] = useState(false)
11
-
12
- async function handleLogin(e: React.FormEvent) {
13
- e.preventDefault()
14
- if (!form.username || !form.password) {
15
- alert('请输入用户名和密码')
16
- return
17
- }
18
-
19
- setLoading(true)
20
- try {
21
- // TODO: 调用登录 API
22
- // const res = await loginApi(form)
23
- // setToken(res.data.token)
24
-
25
- // 模拟登录
26
- setToken('mock-token')
27
- setUserInfo({
28
- id: 1,
29
- username: form.username,
30
- nickname: '管理员',
31
- avatar: '',
32
- roles: ['admin']
33
- })
34
-
35
- navigate('/')
36
- } catch (error) {
37
- console.error('登录失败:', error)
38
- } finally {
39
- setLoading(false)
40
- }
41
- }
42
-
43
- return (
44
- <div className="login-page">
45
- <div className="login-card">
46
- <h2>用户登录</h2>
47
- <form onSubmit={handleLogin}>
48
- <div className="form-item">
49
- <label>用户名</label>
50
- <input
51
- type="text"
52
- placeholder="请输入用户名"
53
- autoComplete="username"
54
- value={form.username}
55
- onChange={(e) => setForm({ ...form, username: e.target.value })}
56
- />
57
- </div>
58
- <div className="form-item">
59
- <label>密码</label>
60
- <input
61
- type="password"
62
- placeholder="请输入密码"
63
- autoComplete="current-password"
64
- value={form.password}
65
- onChange={(e) => setForm({ ...form, password: e.target.value })}
66
- />
67
- </div>
68
- <button type="submit" className="login-btn" disabled={loading}>
69
- {loading ? '登录中...' : '登 录'}
70
- </button>
71
- </form>
72
- </div>
73
-
74
- <style>{`
75
- .login-page {
76
- height: 100vh;
77
- display: flex;
78
- align-items: center;
79
- justify-content: center;
80
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
81
- }
82
- .login-card {
83
- width: 380px;
84
- padding: 40px;
85
- background: #fff;
86
- border-radius: 12px;
87
- box-shadow: 0 20px 60px rgba(0,0,0,0.15);
88
- }
89
- .login-card h2 {
90
- text-align: center;
91
- margin-bottom: 32px;
92
- color: #333;
93
- font-size: 24px;
94
- }
95
- .form-item {
96
- margin-bottom: 20px;
97
- }
98
- .form-item label {
99
- display: block;
100
- margin-bottom: 6px;
101
- font-size: 14px;
102
- color: #606266;
103
- }
104
- .form-item input {
105
- width: 100%;
106
- padding: 10px 12px;
107
- border: 1px solid #dcdfe6;
108
- border-radius: 6px;
109
- font-size: 14px;
110
- outline: none;
111
- transition: border-color 0.2s;
112
- box-sizing: border-box;
113
- }
114
- .form-item input:focus {
115
- border-color: #409eff;
116
- }
117
- .form-item input::placeholder {
118
- color: #c0c4cc;
119
- }
120
- .login-btn {
121
- width: 100%;
122
- padding: 12px;
123
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
124
- color: #fff;
125
- border: none;
126
- border-radius: 6px;
127
- font-size: 16px;
128
- cursor: pointer;
129
- transition: opacity 0.2s;
130
- margin-top: 8px;
131
- }
132
- .login-btn:hover { opacity: 0.9; }
133
- .login-btn:disabled { opacity: 0.6; cursor: not-allowed; }
134
- `}</style>
135
- </div>
136
- )
137
- }
138
-
@@ -1,143 +0,0 @@
1
- import { useState } from 'react'
2
- import { useNavigate, Link } from 'react-router-dom'
3
-
4
- export default function Register() {
5
- const navigate = useNavigate()
6
- const [form, setForm] = useState({ username: '', password: '', confirmPassword: '' })
7
- const [loading, setLoading] = useState(false)
8
-
9
- async function handleRegister(e: React.FormEvent) {
10
- e.preventDefault()
11
- if (!form.username || !form.password) {
12
- alert('请填写完整信息')
13
- return
14
- }
15
- if (form.password !== form.confirmPassword) {
16
- alert('两次密码输入不一致')
17
- return
18
- }
19
-
20
- setLoading(true)
21
- try {
22
- // TODO: 调用注册 API
23
- alert('注册成功!')
24
- navigate('/login')
25
- } catch (error) {
26
- console.error('注册失败:', error)
27
- } finally {
28
- setLoading(false)
29
- }
30
- }
31
-
32
- return (
33
- <div className="register-page">
34
- <div className="register-card">
35
- <h2>用户注册</h2>
36
- <form onSubmit={handleRegister}>
37
- <div className="form-item">
38
- <label>用户名</label>
39
- <input
40
- type="text"
41
- placeholder="请输入用户名"
42
- autoComplete="username"
43
- value={form.username}
44
- onChange={(e) => setForm({ ...form, username: e.target.value })}
45
- />
46
- </div>
47
- <div className="form-item">
48
- <label>密码</label>
49
- <input
50
- type="password"
51
- placeholder="请输入密码"
52
- autoComplete="new-password"
53
- value={form.password}
54
- onChange={(e) => setForm({ ...form, password: e.target.value })}
55
- />
56
- </div>
57
- <div className="form-item">
58
- <label>确认密码</label>
59
- <input
60
- type="password"
61
- placeholder="请再次输入密码"
62
- autoComplete="new-password"
63
- value={form.confirmPassword}
64
- onChange={(e) => setForm({ ...form, confirmPassword: e.target.value })}
65
- />
66
- </div>
67
- <button type="submit" className="register-btn" disabled={loading}>
68
- {loading ? '注册中...' : '注 册'}
69
- </button>
70
- <div className="to-login">
71
- 已有账号?<Link to="/login">立即登录</Link>
72
- </div>
73
- </form>
74
- </div>
75
-
76
- <style>{`
77
- .register-page {
78
- height: 100vh;
79
- display: flex;
80
- align-items: center;
81
- justify-content: center;
82
- background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
83
- }
84
- .register-card {
85
- width: 400px;
86
- padding: 40px;
87
- background: #fff;
88
- border-radius: 12px;
89
- box-shadow: 0 20px 60px rgba(0,0,0,0.15);
90
- }
91
- .register-card h2 {
92
- text-align: center;
93
- margin-bottom: 32px;
94
- color: #333;
95
- font-size: 24px;
96
- }
97
- .form-item {
98
- margin-bottom: 20px;
99
- }
100
- .form-item label {
101
- display: block;
102
- margin-bottom: 6px;
103
- font-size: 14px;
104
- color: #606266;
105
- }
106
- .form-item input {
107
- width: 100%;
108
- padding: 10px 12px;
109
- border: 1px solid #dcdfe6;
110
- border-radius: 6px;
111
- font-size: 14px;
112
- outline: none;
113
- transition: border-color 0.2s;
114
- box-sizing: border-box;
115
- }
116
- .form-item input:focus { border-color: #43e97b; }
117
- .form-item input::placeholder { color: #c0c4cc; }
118
- .register-btn {
119
- width: 100%;
120
- padding: 12px;
121
- background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
122
- color: #fff;
123
- border: none;
124
- border-radius: 6px;
125
- font-size: 16px;
126
- cursor: pointer;
127
- transition: opacity 0.2s;
128
- margin-top: 8px;
129
- }
130
- .register-btn:hover { opacity: 0.9; }
131
- .register-btn:disabled { opacity: 0.6; cursor: not-allowed; }
132
- .to-login {
133
- text-align: center;
134
- margin-top: 16px;
135
- font-size: 14px;
136
- color: #909399;
137
- }
138
- .to-login a { color: #43e97b; font-weight: 500; }
139
- `}</style>
140
- </div>
141
- )
142
- }
143
-