zt-admin-template 1.0.0

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 (80) hide show
  1. package/package.json +11 -0
  2. package/template/.env.development +2 -0
  3. package/template/.env.production +2 -0
  4. package/template/.env.test +2 -0
  5. package/template/.kiro/specs/course-backend-integration/.config.kiro +1 -0
  6. package/template/.kiro/specs/course-backend-integration/design.md +234 -0
  7. package/template/.kiro/specs/course-backend-integration/requirements.md +116 -0
  8. package/template/.kiro/specs/course-backend-integration/tasks.md +0 -0
  9. package/template/COMPLETION_CHECKLIST.md +305 -0
  10. package/template/DEPLOYMENT_GUIDE.md +391 -0
  11. package/template/FINAL_SUMMARY.md +428 -0
  12. package/template/IMPLEMENTATION_SUMMARY.md +382 -0
  13. package/template/INTEGRATION_GUIDE.md +458 -0
  14. package/template/PROJECT_OVERVIEW.md +343 -0
  15. package/template/QUICK_START.md +273 -0
  16. package/template/RBAC_Tutorial.md +424 -0
  17. package/template/README.md +16 -0
  18. package/template/React_Antd_TS_Tutorial.md +279 -0
  19. package/template/START_ALL.md +163 -0
  20. package/template/SYSTEM_MANAGEMENT.md +247 -0
  21. package/template/eslint.config.js +29 -0
  22. package/template/index.html +13 -0
  23. package/template/koa-server/README.md +65 -0
  24. package/template/koa-server/app.js +625 -0
  25. package/template/koa-server/package-lock.json +1547 -0
  26. package/template/koa-server/package.json +26 -0
  27. package/template/koa-server/public/assets/index-B1Cj4mG9.css +1 -0
  28. package/template/koa-server/public/assets/index-Mgxg-xqT.js +503 -0
  29. package/template/koa-server/public/favicon.svg +1 -0
  30. package/template/koa-server/public/icons.svg +24 -0
  31. package/template/koa-server/public/index.html +14 -0
  32. package/template/koa-server/uploads/1774265088480-962006467.png +0 -0
  33. package/template/koa-server/uploads/file-1774346891704-610962013.png +0 -0
  34. package/template/koa-server/uploads/file-1774346898887-58636533.png +0 -0
  35. package/template/koa-server/uploads/file-1774346912676-771862547.png +0 -0
  36. package/template/koa-server/uploads/file-1774347025308-130037894.png +0 -0
  37. package/template/koa-server/uploads/file-1774347031104-766499773.png +0 -0
  38. package/template/koa-server/uploads/file-1774347094969-731402203.png +0 -0
  39. package/template/koa-server/uploads/file-1774347101948-330296656.png +0 -0
  40. package/template/koa-server/uploads/file-1774351682377-932868720.png +0 -0
  41. package/template/koa-server/uploads/file-1774352037654-877426905.png +0 -0
  42. package/template/koa-server/uploads/file-1774352175463-386248997.png +0 -0
  43. package/template/koa-server/uploads/file-1774361446433-405859961.png +0 -0
  44. package/template/koa-server/uploads/file-1774361512207-465806267.png +0 -0
  45. package/template/lianxi.html +15 -0
  46. package/template/package-lock.json +6307 -0
  47. package/template/package.json +36 -0
  48. package/template/public/favicon.svg +1 -0
  49. package/template/public/icons.svg +24 -0
  50. package/template/src/App.css +184 -0
  51. package/template/src/App.tsx +44 -0
  52. package/template/src/api/course.ts +86 -0
  53. package/template/src/api/menu.ts +55 -0
  54. package/template/src/api/role.ts +58 -0
  55. package/template/src/api/user.ts +58 -0
  56. package/template/src/assets/hero.png +0 -0
  57. package/template/src/assets/react.svg +1 -0
  58. package/template/src/assets/vite.svg +1 -0
  59. package/template/src/components/Child.tsx +10 -0
  60. package/template/src/components/MainLayout.tsx +169 -0
  61. package/template/src/components/SunZi.tsx +13 -0
  62. package/template/src/contexts/ThemeContext.tsx +33 -0
  63. package/template/src/hooks/usePermission.tsx +62 -0
  64. package/template/src/index.css +111 -0
  65. package/template/src/main.tsx +13 -0
  66. package/template/src/pages/Dashboard.tsx +39 -0
  67. package/template/src/pages/Users.tsx +95 -0
  68. package/template/src/pages/banner/BannerList.tsx +182 -0
  69. package/template/src/pages/course/Course.tsx +586 -0
  70. package/template/src/pages/course/CourseList.tsx +168 -0
  71. package/template/src/pages/system/menu/Menu.tsx +501 -0
  72. package/template/src/pages/system/role/Role.tsx +458 -0
  73. package/template/src/pages/system/user/User.tsx +364 -0
  74. package/template/src/types/permission.ts +21 -0
  75. package/template/src/utils/request.tsx +94 -0
  76. package/template/src/vite-env.d.ts +1 -0
  77. package/template/tsconfig.app.json +32 -0
  78. package/template/tsconfig.json +7 -0
  79. package/template/tsconfig.node.json +13 -0
  80. package/template/vite.config.ts +30 -0
@@ -0,0 +1,168 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import {
3
+ Table,
4
+ Button,
5
+ Space,
6
+ Typography,
7
+ message,
8
+ Card,
9
+ Empty,
10
+ Spin
11
+ } from 'antd';
12
+ import { EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
13
+ import { useLocation } from 'react-router-dom';
14
+
15
+ // 课程类型定义
16
+ interface Course {
17
+ id: string;
18
+ title: string;
19
+ price: number;
20
+ instructorName: string;
21
+ coverImage: string;
22
+ introduction: string;
23
+ totalDuration: number;
24
+ chapters: Array<{
25
+ id: string;
26
+ title: string;
27
+ sections: Array<{
28
+ id: string;
29
+ title: string;
30
+ duration: number;
31
+ videoFile: string;
32
+ }>;
33
+ }>;
34
+ }
35
+
36
+ const { Title } = Typography;
37
+
38
+ const CourseList: React.FC = () => {
39
+ const [courses, setCourses] = useState<Course[]>([]);
40
+ const [loading, setLoading] = useState(true);
41
+ const location = useLocation();
42
+
43
+ // 从localStorage加载课程数据
44
+ const loadCourses = () => {
45
+ try {
46
+ const savedCourses = localStorage.getItem('courses');
47
+ if (savedCourses) {
48
+ setCourses(JSON.parse(savedCourses));
49
+ } else {
50
+ setCourses([]);
51
+ }
52
+ } catch (error) {
53
+ message.error('加载课程数据失败');
54
+ } finally {
55
+ setLoading(false);
56
+ }
57
+ };
58
+
59
+ // 每次路由切换到此页面时重新加载
60
+ useEffect(() => {
61
+ loadCourses();
62
+ }, [location.pathname]);
63
+
64
+ // 表格列配置
65
+ const columns = [
66
+ {
67
+ title: '课程标题',
68
+ dataIndex: 'title',
69
+ key: 'title',
70
+ ellipsis: true,
71
+ render: (text: string) => <a>{text}</a>
72
+ },
73
+ {
74
+ title: '主讲老师',
75
+ dataIndex: 'instructorName',
76
+ key: 'instructorName'
77
+ },
78
+ {
79
+ title: '价格',
80
+ dataIndex: 'price',
81
+ key: 'price',
82
+ render: (price: number) => `¥${price}`
83
+ },
84
+ {
85
+ title: '总时长',
86
+ dataIndex: 'totalDuration',
87
+ key: 'totalDuration',
88
+ render: (duration: number) => `${duration} 小时`
89
+ },
90
+ {
91
+ title: '操作',
92
+ key: 'action',
93
+ render: (_: any, record: Course) => (
94
+ <Space size="middle">
95
+ <Button
96
+ type="primary"
97
+ icon={<EyeOutlined />}
98
+ size="small"
99
+ onClick={() => viewCourse(record)}
100
+ >
101
+ 查看
102
+ </Button>
103
+ <Button
104
+ icon={<EditOutlined />}
105
+ size="small"
106
+ onClick={() => editCourse(record)}
107
+ >
108
+ 编辑
109
+ </Button>
110
+ <Button
111
+ danger
112
+ icon={<DeleteOutlined />}
113
+ size="small"
114
+ onClick={() => deleteCourse(record.id)}
115
+ >
116
+ 删除
117
+ </Button>
118
+ </Space>
119
+ )
120
+ }
121
+ ];
122
+
123
+ // 查看课程详情
124
+ const viewCourse = (course: Course) => {
125
+ message.info(`查看课程: ${course.title}`);
126
+ // 这里可以实现查看课程详情的逻辑
127
+ };
128
+
129
+ // 编辑课程
130
+ const editCourse = (course: Course) => {
131
+ message.info(`编辑课程: ${course.title}`);
132
+ // 这里可以实现编辑课程的逻辑
133
+ };
134
+
135
+ // 删除课程
136
+ const deleteCourse = (id: string) => {
137
+ const updatedCourses = courses.filter(course => course.id !== id);
138
+ setCourses(updatedCourses);
139
+ localStorage.setItem('courses', JSON.stringify(updatedCourses));
140
+ message.success('课程已删除');
141
+ };
142
+
143
+ return (
144
+ <div style={{ padding: '24px' }}>
145
+ <Card>
146
+ <Title level={4}>课程列表</Title>
147
+ <Spin spinning={loading}>
148
+ {courses.length > 0 ? (
149
+ <Table
150
+ columns={columns}
151
+ dataSource={courses}
152
+ rowKey="id"
153
+ pagination={{
154
+ showSizeChanger: true,
155
+ pageSizeOptions: ['10', '20', '50'],
156
+ defaultPageSize: 10
157
+ }}
158
+ />
159
+ ) : (
160
+ <Empty description="暂无课程数据" />
161
+ )}
162
+ </Spin>
163
+ </Card>
164
+ </div>
165
+ );
166
+ };
167
+
168
+ export default CourseList;
@@ -0,0 +1,501 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ Table,
4
+ Button,
5
+ Modal,
6
+ Form,
7
+ Input,
8
+ Select,
9
+ Switch,
10
+ Space,
11
+ Popconfirm,
12
+ message,
13
+ Card,
14
+ Row,
15
+ Col,
16
+ Tooltip,
17
+ InputNumber,
18
+ } from 'antd';
19
+ import {
20
+ PlusOutlined,
21
+ EditOutlined,
22
+ DeleteOutlined,
23
+ SearchOutlined,
24
+ ReloadOutlined,
25
+ DownloadOutlined,
26
+ ExpandOutlined,
27
+ CompressOutlined,
28
+ } from '@ant-design/icons';
29
+ import type { TableColumnsType } from 'antd';
30
+ import { getMenuList, addMenu, updateMenu, deleteMenu, type MenuRecord } from '@/api/menu';
31
+
32
+ const Menu = () => {
33
+ const [form] = Form.useForm();
34
+ const [searchForm] = Form.useForm();
35
+ const [menus, setMenus] = useState<MenuRecord[]>([]);
36
+ const [originalMenus, setOriginalMenus] = useState<MenuRecord[]>([]);
37
+ const [isModalVisible, setIsModalVisible] = useState(false);
38
+ const [editingMenu, setEditingMenu] = useState<MenuRecord | null>(null);
39
+ const [loading, setLoading] = useState(false);
40
+ const [tableLoading, setTableLoading] = useState(false);
41
+ const [expandedRowKeys, setExpandedRowKeys] = useState<React.Key[]>([]);
42
+
43
+ // 获取菜单列表
44
+ const fetchMenus = async () => {
45
+ try {
46
+ setTableLoading(true);
47
+ const response = await getMenuList();
48
+ setMenus(response);
49
+ setOriginalMenus(response);
50
+ // 默认展开第一级
51
+ const firstLevelIds = response.map(item => item.id);
52
+ setExpandedRowKeys(firstLevelIds);
53
+ } catch (error) {
54
+ console.error('获取菜单列表失败:', error);
55
+ } finally {
56
+ setTableLoading(false);
57
+ }
58
+ };
59
+
60
+ // 初始化加载
61
+ useEffect(() => {
62
+ fetchMenus();
63
+ }, []);
64
+
65
+ const columns: TableColumnsType<MenuRecord> = [
66
+ {
67
+ title: '菜单名称',
68
+ dataIndex: 'menuName',
69
+ key: 'menuName',
70
+ width: 150,
71
+ },
72
+ {
73
+ title: '排序',
74
+ dataIndex: 'orderNum',
75
+ key: 'orderNum',
76
+ width: 80,
77
+ },
78
+ {
79
+ title: '权限标识',
80
+ dataIndex: 'perms',
81
+ key: 'perms',
82
+ width: 150,
83
+ render: (perms: any) => perms || '-',
84
+ },
85
+ {
86
+ title: '组件路径',
87
+ dataIndex: 'component',
88
+ key: 'component',
89
+ width: 150,
90
+ render: (component: any) => component || '-',
91
+ },
92
+ {
93
+ title: '菜单类型',
94
+ dataIndex: 'menuType',
95
+ key: 'menuType',
96
+ width: 100,
97
+ render: (type: string) => {
98
+ const typeMap: Record<string, string> = {
99
+ M: '目录',
100
+ C: '菜单',
101
+ F: '按钮',
102
+ };
103
+ return typeMap[type] || type;
104
+ },
105
+ },
106
+ {
107
+ title: '可见',
108
+ dataIndex: 'visible',
109
+ key: 'visible',
110
+ width: 80,
111
+ render: (visible: number) => (
112
+ <Switch checked={visible === 0} disabled />
113
+ ),
114
+ },
115
+ {
116
+ title: '状态',
117
+ dataIndex: 'status',
118
+ key: 'status',
119
+ width: 80,
120
+ render: (status: number) => (
121
+ <Switch checked={status === 0} disabled />
122
+ ),
123
+ },
124
+ {
125
+ title: '创建时间',
126
+ dataIndex: 'createTime',
127
+ key: 'createTime',
128
+ width: 180,
129
+ },
130
+ {
131
+ title: '操作',
132
+ key: 'action',
133
+ width: 200,
134
+ fixed: 'right',
135
+ render: (_, record) => (
136
+ <Space size="small">
137
+ <Tooltip title="新增">
138
+ <Button
139
+ type="primary"
140
+ size="small"
141
+ icon={<PlusOutlined />}
142
+ onClick={() => handleAddChild(record)}
143
+ />
144
+ </Tooltip>
145
+ <Tooltip title="编辑">
146
+ <Button
147
+ type="primary"
148
+ size="small"
149
+ icon={<EditOutlined />}
150
+ onClick={() => handleEdit(record)}
151
+ />
152
+ </Tooltip>
153
+ <Tooltip title="删除">
154
+ <Popconfirm
155
+ title="删除菜单"
156
+ description="确定删除该菜单吗?"
157
+ onConfirm={() => handleDelete(record.id)}
158
+ okText="确定"
159
+ cancelText="取消"
160
+ >
161
+ <Button danger size="small" icon={<DeleteOutlined />} />
162
+ </Popconfirm>
163
+ </Tooltip>
164
+ </Space>
165
+ ),
166
+ },
167
+ ];
168
+
169
+ const flattenMenus = (items: MenuRecord[]): MenuRecord[] => {
170
+ return items.reduce((acc: MenuRecord[], item) => {
171
+ acc.push(item);
172
+ if (item.children && item.children.length > 0) {
173
+ acc.push(...flattenMenus(item.children));
174
+ }
175
+ return acc;
176
+ }, []);
177
+ };
178
+
179
+ const handleAdd = () => {
180
+ setEditingMenu(null);
181
+ form.resetFields();
182
+ form.setFieldsValue({
183
+ parentId: '0',
184
+ menuType: 'M',
185
+ orderNum: 1,
186
+ isFrame: 1,
187
+ isCache: 0,
188
+ visible: 0,
189
+ status: 0,
190
+ });
191
+ setIsModalVisible(true);
192
+ };
193
+
194
+ const handleAddChild = (record: MenuRecord) => {
195
+ setEditingMenu(null);
196
+ form.resetFields();
197
+ form.setFieldsValue({
198
+ parentId: record.id,
199
+ menuType: 'C',
200
+ orderNum: 1,
201
+ isFrame: 0,
202
+ isCache: 0,
203
+ visible: 0,
204
+ status: 0,
205
+ });
206
+ setIsModalVisible(true);
207
+ };
208
+
209
+ const handleEdit = (record: MenuRecord) => {
210
+ setEditingMenu(record);
211
+ form.setFieldsValue({
212
+ menuName: record.menuName,
213
+ parentId: record.parentId,
214
+ orderNum: record.orderNum,
215
+ path: record.path,
216
+ component: record.component,
217
+ menuType: record.menuType,
218
+ isFrame: record.isFrame,
219
+ isCache: record.isCache,
220
+ visible: record.visible === 0,
221
+ status: record.status === 0,
222
+ perms: record.perms,
223
+ icon: record.icon,
224
+ remark: record.remark,
225
+ });
226
+ setIsModalVisible(true);
227
+ };
228
+
229
+ const handleDelete = async (id: string) => {
230
+ try {
231
+ await deleteMenu(id);
232
+ message.success('删除成功');
233
+ fetchMenus();
234
+ } catch (error) {
235
+ console.error('删除失败:', error);
236
+ }
237
+ };
238
+
239
+ const handleModalOk = async () => {
240
+ try {
241
+ const values = await form.validateFields();
242
+ setLoading(true);
243
+
244
+ if (editingMenu) {
245
+ await updateMenu({
246
+ ...editingMenu,
247
+ ...values,
248
+ visible: values.visible ? 0 : 1,
249
+ status: values.status ? 0 : 1,
250
+ });
251
+ message.success('编辑成功');
252
+ } else {
253
+ await addMenu({
254
+ ...values,
255
+ visible: values.visible ? 0 : 1,
256
+ status: values.status ? 0 : 1,
257
+ });
258
+ message.success('添加成功');
259
+ }
260
+
261
+ setIsModalVisible(false);
262
+ form.resetFields();
263
+ fetchMenus();
264
+ } catch (error) {
265
+ console.error('操作失败:', error);
266
+ } finally {
267
+ setLoading(false);
268
+ }
269
+ };
270
+
271
+ // 递归过滤菜单
272
+ const filterMenus = (items: MenuRecord[], menuName?: string, status?: number): MenuRecord[] => {
273
+ return items.reduce((acc: MenuRecord[], item) => {
274
+ // 过滤子菜单
275
+ let filteredChildren: MenuRecord[] = [];
276
+ if (item.children && item.children.length > 0) {
277
+ filteredChildren = filterMenus(item.children, menuName, status);
278
+ }
279
+
280
+ // 检查当前菜单项是否匹配搜索条件
281
+ const matchesMenuName = !menuName || item.menuName.toLowerCase().includes(menuName.toLowerCase());
282
+ const matchesStatus = status === undefined || item.status === status;
283
+
284
+ // 如果当前菜单项匹配,或者其子菜单匹配,则保留该菜单项
285
+ if ((matchesMenuName && matchesStatus) || filteredChildren.length > 0) {
286
+ acc.push({
287
+ ...item,
288
+ children: filteredChildren
289
+ });
290
+ }
291
+ return acc;
292
+ }, []);
293
+ };
294
+
295
+ const handleSearch = (values: any) => {
296
+ const { menuName, status } = values;
297
+ const filtered = filterMenus(originalMenus, menuName, status);
298
+ setMenus(filtered);
299
+ // 重置展开状态
300
+ const firstLevelIds = filtered.map(item => item.id);
301
+ setExpandedRowKeys(firstLevelIds);
302
+ };
303
+
304
+ const handleReset = () => {
305
+ searchForm.resetFields();
306
+ setMenus(originalMenus);
307
+ // 重置展开状态
308
+ const firstLevelIds = originalMenus.map(item => item.id);
309
+ setExpandedRowKeys(firstLevelIds);
310
+ };
311
+
312
+ const handleExport = () => {
313
+ message.success('导出成功');
314
+ };
315
+
316
+ return (
317
+ <div style={{ padding: '24px' }}>
318
+ <Card style={{ marginBottom: '24px' }}>
319
+ <Form
320
+ form={searchForm}
321
+ layout="inline"
322
+ onFinish={handleSearch}
323
+ style={{ marginBottom: '16px' }}
324
+ >
325
+ <Form.Item name="menuName" label="菜单名称">
326
+ <Input placeholder="请输入菜单名称" />
327
+ </Form.Item>
328
+ <Form.Item name="status" label="状态">
329
+ <Select placeholder="请选择状态" style={{ width: 120 }}>
330
+ <Select.Option value={0}>正常</Select.Option>
331
+ <Select.Option value={1}>停用</Select.Option>
332
+ </Select>
333
+ </Form.Item>
334
+ <Form.Item>
335
+ <Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
336
+ 搜索
337
+ </Button>
338
+ </Form.Item>
339
+ <Form.Item>
340
+ <Button onClick={handleReset}>重置</Button>
341
+ </Form.Item>
342
+ </Form>
343
+ </Card>
344
+
345
+ <Card>
346
+ <Row gutter={[16, 16]} style={{ marginBottom: '16px' }}>
347
+ <Col>
348
+ <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
349
+ 新增
350
+ </Button>
351
+ </Col>
352
+ <Col>
353
+ <Button icon={<ReloadOutlined />} onClick={fetchMenus}>
354
+ 刷新
355
+ </Button>
356
+ </Col>
357
+ <Col>
358
+ <Button icon={<ExpandOutlined />} onClick={() => {
359
+ const allKeys = menus.map(item => item.id);
360
+ setExpandedRowKeys(allKeys);
361
+ }}>
362
+ 展开
363
+ </Button>
364
+ </Col>
365
+ <Col>
366
+ <Button icon={<CompressOutlined />} onClick={() => setExpandedRowKeys([])}>
367
+ 折叠
368
+ </Button>
369
+ </Col>
370
+ <Col>
371
+ <Button icon={<DownloadOutlined />} onClick={handleExport}>
372
+ 导出
373
+ </Button>
374
+ </Col>
375
+ </Row>
376
+
377
+ <Table
378
+ columns={columns}
379
+ dataSource={menus}
380
+ rowKey="id"
381
+ loading={tableLoading}
382
+ pagination={false}
383
+ expandedRowKeys={expandedRowKeys}
384
+ onExpandedRowsChange={(keys: any) => setExpandedRowKeys(keys)}
385
+ scroll={{ x: 1400 }}
386
+ />
387
+ </Card>
388
+
389
+ <Modal
390
+ title={editingMenu ? '编辑菜单' : '新增菜单'}
391
+ open={isModalVisible}
392
+ onOk={handleModalOk}
393
+ onCancel={() => {
394
+ setIsModalVisible(false);
395
+ form.resetFields();
396
+ }}
397
+ width={700}
398
+ confirmLoading={loading}
399
+ >
400
+ <Form form={form} layout="vertical" autoComplete="off">
401
+ <Form.Item
402
+ name="menuName"
403
+ label="菜单名称"
404
+ rules={[{ required: true, message: '请输入菜单名称' }]}
405
+ >
406
+ <Input placeholder="请输入菜单名称" />
407
+ </Form.Item>
408
+
409
+ <Row gutter={16}>
410
+ <Col span={12}>
411
+ <Form.Item name="parentId" label="父菜单">
412
+ <Select placeholder="请选择父菜单">
413
+ <Select.Option value="0">主菜单</Select.Option>
414
+ {flattenMenus(menus).map(menu => (
415
+ <Select.Option key={menu.id} value={menu.id}>
416
+ {menu.menuName}
417
+ </Select.Option>
418
+ ))}
419
+ </Select>
420
+ </Form.Item>
421
+ </Col>
422
+ <Col span={12}>
423
+ <Form.Item
424
+ name="orderNum"
425
+ label="显示顺序"
426
+ rules={[{ required: true, message: '请输入显示顺序' }]}
427
+ >
428
+ <InputNumber placeholder="请输入显示顺序" style={{ width: '100%' }} />
429
+ </Form.Item>
430
+ </Col>
431
+ </Row>
432
+
433
+ <Row gutter={16}>
434
+ <Col span={12}>
435
+ <Form.Item
436
+ name="menuType"
437
+ label="菜单类型"
438
+ rules={[{ required: true, message: '请选择菜单类型' }]}
439
+ >
440
+ <Select placeholder="请选择菜单类型">
441
+ <Select.Option value="M">目录</Select.Option>
442
+ <Select.Option value="C">菜单</Select.Option>
443
+ <Select.Option value="F">按钮</Select.Option>
444
+ </Select>
445
+ </Form.Item>
446
+ </Col>
447
+ <Col span={12}>
448
+ <Form.Item name="icon" label="菜单图标">
449
+ <Input placeholder="请输入菜单图标" />
450
+ </Form.Item>
451
+ </Col>
452
+ </Row>
453
+
454
+ <Form.Item
455
+ name="path"
456
+ label="路由地址"
457
+ rules={[{ required: true, message: '请输入路由地址' }]}
458
+ >
459
+ <Input placeholder="请输入路由地址" />
460
+ </Form.Item>
461
+
462
+ <Form.Item name="component" label="组件路径">
463
+ <Input placeholder="请输入组件路径" />
464
+ </Form.Item>
465
+
466
+ <Form.Item name="perms" label="权限标识">
467
+ <Input placeholder="请输入权限标识" />
468
+ </Form.Item>
469
+
470
+ <Row gutter={16}>
471
+ <Col span={8}>
472
+ <Form.Item name="isFrame" label="是否外链" valuePropName="checked">
473
+ <Switch checkedChildren="是" unCheckedChildren="否" />
474
+ </Form.Item>
475
+ </Col>
476
+ <Col span={8}>
477
+ <Form.Item name="isCache" label="是否缓存" valuePropName="checked">
478
+ <Switch checkedChildren="是" unCheckedChildren="否" />
479
+ </Form.Item>
480
+ </Col>
481
+ <Col span={8}>
482
+ <Form.Item name="visible" label="是否可见" valuePropName="checked">
483
+ <Switch checkedChildren="是" unCheckedChildren="否" />
484
+ </Form.Item>
485
+ </Col>
486
+ </Row>
487
+
488
+ <Form.Item name="status" label="状态" valuePropName="checked">
489
+ <Switch checkedChildren="正常" unCheckedChildren="停用" />
490
+ </Form.Item>
491
+
492
+ <Form.Item name="remark" label="备注">
493
+ <Input.TextArea placeholder="请输入备注" rows={3} />
494
+ </Form.Item>
495
+ </Form>
496
+ </Modal>
497
+ </div>
498
+ );
499
+ };
500
+
501
+ export default Menu;