@lvetechs/create-app 1.0.4 → 1.0.6

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 (58) 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/App.tsx +7 -1
  8. package/templates/react/src/api/notification.ts +43 -0
  9. package/templates/react/src/components/NotificationButton/index.tsx +219 -0
  10. package/templates/react/src/components/Toast/index.tsx +150 -0
  11. package/templates/react/src/hooks/useForm.ts +77 -0
  12. package/templates/react/src/layouts/DefaultLayout.tsx +2 -0
  13. package/templates/react/src/layouts/menuConfig.ts +3 -33
  14. package/templates/react/src/router/index.tsx +0 -39
  15. package/templates/react/src/stores/app.ts +141 -3
  16. package/templates/react/src/stores/notification.ts +146 -0
  17. package/templates/react/src/stores/permission.ts +173 -0
  18. package/templates/react/src/stores/user.ts +151 -4
  19. package/templates/react/src/views/home/index.tsx +167 -6
  20. package/templates/react/src/views/system/user/index.tsx +171 -5
  21. package/templates/vue/.env +2 -2
  22. package/templates/vue/.env.development +2 -2
  23. package/templates/vue/.env.production +2 -2
  24. package/templates/vue/pnpm-lock.yaml +3307 -3307
  25. package/templates/vue/src/App.vue +2 -0
  26. package/templates/vue/src/api/notification.ts +43 -0
  27. package/templates/vue/src/auto-imports.d.ts +5 -0
  28. package/templates/vue/src/components/NotificationButton/index.vue +242 -0
  29. package/templates/vue/src/components/Toast/index.vue +126 -0
  30. package/templates/vue/src/components.d.ts +2 -0
  31. package/templates/vue/src/layouts/DefaultLayout.vue +2 -0
  32. package/templates/vue/src/layouts/menuConfig.ts +3 -33
  33. package/templates/vue/src/router/index.ts +3 -88
  34. package/templates/vue/src/stores/app.ts +133 -2
  35. package/templates/vue/src/stores/notification.ts +189 -0
  36. package/templates/vue/src/stores/permission.ts +184 -0
  37. package/templates/vue/src/stores/user.ts +109 -2
  38. package/templates/vue/src/views/home/index.vue +7 -7
  39. package/templates/react/src/views/about/index.tsx +0 -40
  40. package/templates/react/src/views/login/index.tsx +0 -138
  41. package/templates/react/src/views/register/index.tsx +0 -143
  42. package/templates/react/src/views/result/fail.tsx +0 -39
  43. package/templates/react/src/views/result/success.tsx +0 -35
  44. package/templates/react/src/views/screen/index.tsx +0 -120
  45. package/templates/react/src/views/system/log/login.tsx +0 -51
  46. package/templates/react/src/views/system/log/operation.tsx +0 -47
  47. package/templates/react/src/views/system/menu/index.tsx +0 -62
  48. package/templates/react/src/views/system/role/index.tsx +0 -63
  49. package/templates/vue/src/views/about/index.vue +0 -67
  50. package/templates/vue/src/views/login/index.vue +0 -153
  51. package/templates/vue/src/views/register/index.vue +0 -169
  52. package/templates/vue/src/views/result/fail.vue +0 -92
  53. package/templates/vue/src/views/result/success.vue +0 -92
  54. package/templates/vue/src/views/screen/index.vue +0 -150
  55. package/templates/vue/src/views/system/log/login.vue +0 -51
  56. package/templates/vue/src/views/system/log/operation.vue +0 -47
  57. package/templates/vue/src/views/system/menu/index.vue +0 -58
  58. package/templates/vue/src/views/system/role/index.vue +0 -59
@@ -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
-
@@ -1,39 +0,0 @@
1
- import { useNavigate } from 'react-router-dom'
2
- import '@/styles/page-common.scss'
3
-
4
- export default function ResultFail() {
5
- const navigate = useNavigate()
6
-
7
- return (
8
- <div
9
- style={{
10
- height: '100vh',
11
- display: 'flex',
12
- alignItems: 'center',
13
- justifyContent: 'center',
14
- backgroundColor: '#f5f7fa'
15
- }}
16
- >
17
- <div className="page-card" style={{ textAlign: 'center', padding: '60px 80px' }}>
18
- <div style={{ fontSize: 64, marginBottom: 20 }}>❌</div>
19
- <h1 style={{ fontSize: 24, marginBottom: 12 }}>操作失败</h1>
20
- <p style={{ fontSize: 15, color: '#909399', marginBottom: 32 }}>
21
- 抱歉,您的操作未能完成,请检查后重试。
22
- </p>
23
- <div style={{ display: 'flex', gap: 12, justifyContent: 'center' }}>
24
- <button
25
- className="btn-primary"
26
- style={{ backgroundColor: '#f56c6c' }}
27
- onClick={() => navigate(-1)}
28
- >
29
- 返回重试
30
- </button>
31
- <button className="btn-outline" onClick={() => navigate('/')}>
32
- 返回首页
33
- </button>
34
- </div>
35
- </div>
36
- </div>
37
- )
38
- }
39
-
@@ -1,35 +0,0 @@
1
- import { useNavigate } from 'react-router-dom'
2
- import '@/styles/page-common.scss'
3
-
4
- export default function ResultSuccess() {
5
- const navigate = useNavigate()
6
-
7
- return (
8
- <div
9
- style={{
10
- height: '100vh',
11
- display: 'flex',
12
- alignItems: 'center',
13
- justifyContent: 'center',
14
- backgroundColor: '#f5f7fa'
15
- }}
16
- >
17
- <div className="page-card" style={{ textAlign: 'center', padding: '60px 80px' }}>
18
- <div style={{ fontSize: 64, marginBottom: 20 }}>✅</div>
19
- <h1 style={{ fontSize: 24, marginBottom: 12 }}>操作成功</h1>
20
- <p style={{ fontSize: 15, color: '#909399', marginBottom: 32 }}>
21
- 您的操作已成功完成,可以返回首页继续操作。
22
- </p>
23
- <div style={{ display: 'flex', gap: 12, justifyContent: 'center' }}>
24
- <button className="btn-primary" onClick={() => navigate('/')}>
25
- 返回首页
26
- </button>
27
- <button className="btn-outline" onClick={() => navigate(-1)}>
28
- 返回上一页
29
- </button>
30
- </div>
31
- </div>
32
- </div>
33
- )
34
- }
35
-
@@ -1,120 +0,0 @@
1
- import { useNavigate } from 'react-router-dom'
2
-
3
- export default function DataScreen() {
4
- const navigate = useNavigate()
5
-
6
- const panels = [
7
- { title: '总用户数', value: '12,580', desc: '较昨日 +128' },
8
- { title: '今日活跃', value: '3,426', desc: '活跃率 27.2%' },
9
- { title: '今日订单', value: '856', desc: '较昨日 +52' },
10
- { title: '今日营收', value: '¥98,320', desc: '较昨日 +12.3%' }
11
- ]
12
-
13
- return (
14
- <div className="screen-page">
15
- {/* 顶部标题栏 */}
16
- <header className="screen-header">
17
- <button className="back-btn" onClick={() => navigate('/')}>
18
- ← 返回
19
- </button>
20
- <h1>数据大屏</h1>
21
- <span className="screen-time">{new Date().toLocaleString()}</span>
22
- </header>
23
-
24
- {/* 数据面板 */}
25
- <div className="screen-body">
26
- {panels.map((p) => (
27
- <div className="panel" key={p.title}>
28
- <h3>{p.title}</h3>
29
- <div className="panel-value">{p.value}</div>
30
- <p className="panel-desc">{p.desc}</p>
31
- </div>
32
- ))}
33
- <div className="panel wide">
34
- <h3>📊 数据图表区域</h3>
35
- <div className="chart-placeholder">
36
- <p>此处可集成 ECharts / Recharts 等图表库</p>
37
- </div>
38
- </div>
39
- <div className="panel wide">
40
- <h3>🗺️ 地图区域</h3>
41
- <div className="chart-placeholder">
42
- <p>此处可集成地图或其他可视化组件</p>
43
- </div>
44
- </div>
45
- </div>
46
-
47
- <style>{`
48
- .screen-page {
49
- height: 100vh;
50
- background: #0d1b2a;
51
- color: #e0e6ed;
52
- display: flex;
53
- flex-direction: column;
54
- overflow: hidden;
55
- }
56
- .screen-header {
57
- display: flex;
58
- align-items: center;
59
- justify-content: space-between;
60
- padding: 12px 24px;
61
- background: rgba(255,255,255,0.05);
62
- border-bottom: 1px solid rgba(255,255,255,0.1);
63
- }
64
- .screen-header h1 {
65
- font-size: 22px;
66
- background: linear-gradient(90deg, #00d2ff, #928dff);
67
- -webkit-background-clip: text;
68
- -webkit-text-fill-color: transparent;
69
- }
70
- .screen-time { font-size: 13px; color: #8892a4; }
71
- .back-btn {
72
- background: rgba(255,255,255,0.1);
73
- border: 1px solid rgba(255,255,255,0.2);
74
- color: #e0e6ed;
75
- padding: 6px 16px;
76
- border-radius: 4px;
77
- cursor: pointer;
78
- font-size: 13px;
79
- }
80
- .back-btn:hover { background: rgba(255,255,255,0.2); }
81
- .screen-body {
82
- flex: 1;
83
- display: grid;
84
- grid-template-columns: repeat(4, 1fr);
85
- gap: 16px;
86
- padding: 20px 24px;
87
- overflow-y: auto;
88
- }
89
- .panel {
90
- background: rgba(255,255,255,0.05);
91
- border: 1px solid rgba(255,255,255,0.08);
92
- border-radius: 8px;
93
- padding: 20px;
94
- }
95
- .panel h3 { font-size: 14px; color: #8892a4; margin-bottom: 12px; }
96
- .panel.wide { grid-column: span 2; }
97
- .panel-value {
98
- font-size: 32px;
99
- font-weight: 700;
100
- background: linear-gradient(90deg, #00d2ff, #928dff);
101
- -webkit-background-clip: text;
102
- -webkit-text-fill-color: transparent;
103
- margin-bottom: 8px;
104
- }
105
- .panel-desc { font-size: 13px; color: #67c23a; }
106
- .chart-placeholder {
107
- height: 200px;
108
- display: flex;
109
- align-items: center;
110
- justify-content: center;
111
- border: 1px dashed rgba(255,255,255,0.15);
112
- border-radius: 6px;
113
- color: #8892a4;
114
- font-size: 14px;
115
- }
116
- `}</style>
117
- </div>
118
- )
119
- }
120
-
@@ -1,51 +0,0 @@
1
- import '@/styles/page-common.scss'
2
-
3
- const logs = [
4
- { id: 1, user: 'admin', ip: '192.168.1.1', location: '本地', browser: 'Chrome 122', os: 'Windows 10', time: '2024-03-20 09:00:00', status: '成功' },
5
- { id: 2, user: 'editor', ip: '192.168.1.2', location: '本地', browser: 'Firefox 123', os: 'macOS', time: '2024-03-20 09:30:00', status: '成功' },
6
- { id: 3, user: 'hacker', ip: '10.0.0.1', location: '未知', browser: 'Unknown', os: 'Linux', time: '2024-03-20 12:00:00', status: '失败' }
7
- ]
8
-
9
- export default function LoginLog() {
10
- return (
11
- <div className="page-container">
12
- <div className="page-header">
13
- <h2>登录日志</h2>
14
- <button className="btn-primary">导出日志</button>
15
- </div>
16
- <table className="data-table">
17
- <thead>
18
- <tr>
19
- <th>ID</th>
20
- <th>用户名</th>
21
- <th>IP 地址</th>
22
- <th>登录地点</th>
23
- <th>浏览器</th>
24
- <th>操作系统</th>
25
- <th>登录时间</th>
26
- <th>状态</th>
27
- </tr>
28
- </thead>
29
- <tbody>
30
- {logs.map((log) => (
31
- <tr key={log.id}>
32
- <td>{log.id}</td>
33
- <td>{log.user}</td>
34
- <td>{log.ip}</td>
35
- <td>{log.location}</td>
36
- <td>{log.browser}</td>
37
- <td>{log.os}</td>
38
- <td>{log.time}</td>
39
- <td>
40
- <span className={log.status === '成功' ? 'status-active' : 'status-disabled'}>
41
- {log.status}
42
- </span>
43
- </td>
44
- </tr>
45
- ))}
46
- </tbody>
47
- </table>
48
- </div>
49
- )
50
- }
51
-
@@ -1,47 +0,0 @@
1
- import '@/styles/page-common.scss'
2
-
3
- const logs = [
4
- { id: 1, user: 'admin', action: '新增用户', ip: '192.168.1.1', time: '2024-03-20 10:30:00', status: '成功' },
5
- { id: 2, user: 'admin', action: '修改角色', ip: '192.168.1.1', time: '2024-03-20 11:15:00', status: '成功' },
6
- { id: 3, user: 'editor', action: '删除文章', ip: '192.168.1.2', time: '2024-03-20 14:22:00', status: '失败' }
7
- ]
8
-
9
- export default function OperationLog() {
10
- return (
11
- <div className="page-container">
12
- <div className="page-header">
13
- <h2>操作日志</h2>
14
- <button className="btn-primary">导出日志</button>
15
- </div>
16
- <table className="data-table">
17
- <thead>
18
- <tr>
19
- <th>ID</th>
20
- <th>操作人</th>
21
- <th>操作内容</th>
22
- <th>IP 地址</th>
23
- <th>操作时间</th>
24
- <th>状态</th>
25
- </tr>
26
- </thead>
27
- <tbody>
28
- {logs.map((log) => (
29
- <tr key={log.id}>
30
- <td>{log.id}</td>
31
- <td>{log.user}</td>
32
- <td>{log.action}</td>
33
- <td>{log.ip}</td>
34
- <td>{log.time}</td>
35
- <td>
36
- <span className={log.status === '成功' ? 'status-active' : 'status-disabled'}>
37
- {log.status}
38
- </span>
39
- </td>
40
- </tr>
41
- ))}
42
- </tbody>
43
- </table>
44
- </div>
45
- )
46
- }
47
-