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,182 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ Table, Button, Modal, Form, Input, Upload,
4
+ message, Space, Typography, Tag, Popconfirm, Select
5
+ } from 'antd';
6
+ import { PlusOutlined, EditOutlined, DeleteOutlined, LoadingOutlined } from '@ant-design/icons';
7
+ import { uploadFile } from '@/api/course';
8
+
9
+ const { Title } = Typography;
10
+
11
+ interface Banner {
12
+ id: string;
13
+ title: string;
14
+ imageUrl: string;
15
+ link: string;
16
+ sort: number;
17
+ status: 'active' | 'inactive';
18
+ createdAt: string;
19
+ }
20
+
21
+ const BannerList: React.FC = () => {
22
+ const [banners, setBanners] = useState<Banner[]>([]);
23
+ const [isModalVisible, setIsModalVisible] = useState(false);
24
+ const [isEditing, setIsEditing] = useState(false);
25
+ const [currentBanner, setCurrentBanner] = useState<Banner | null>(null);
26
+ const [form] = Form.useForm();
27
+ const [imageUrl, setImageUrl] = useState<string>('');
28
+ const [uploading, setUploading] = useState(false);
29
+
30
+ useEffect(() => {
31
+ const saved = localStorage.getItem('banners');
32
+ if (saved) setBanners(JSON.parse(saved));
33
+ }, []);
34
+
35
+ const save = (data: Banner[]) => {
36
+ localStorage.setItem('banners', JSON.stringify(data));
37
+ setBanners(data);
38
+ };
39
+
40
+ const handleAdd = () => {
41
+ setIsEditing(false);
42
+ setCurrentBanner(null);
43
+ setImageUrl('');
44
+ form.resetFields();
45
+ setIsModalVisible(true);
46
+ };
47
+
48
+ const handleEdit = (banner: Banner) => {
49
+ setIsEditing(true);
50
+ setCurrentBanner(banner);
51
+ setImageUrl(banner.imageUrl);
52
+ form.setFieldsValue({ title: banner.title, link: banner.link, sort: banner.sort, status: banner.status });
53
+ setIsModalVisible(true);
54
+ };
55
+
56
+ const handleDelete = (id: string) => {
57
+ save(banners.filter(b => b.id !== id));
58
+ message.success('删除成功');
59
+ };
60
+
61
+ const handleUpload = async (file: File) => {
62
+ setUploading(true);
63
+ try {
64
+ const url = await uploadFile(file);
65
+ setImageUrl(url);
66
+ message.success('上传成功');
67
+ } catch {
68
+ message.error('上传失败');
69
+ } finally {
70
+ setUploading(false);
71
+ }
72
+ return false; // 阻止默认上传行为
73
+ };
74
+
75
+ const handleSubmit = () => {
76
+ form.validateFields().then(values => {
77
+ if (!imageUrl) {
78
+ message.error('请上传轮播图');
79
+ return;
80
+ }
81
+ if (isEditing && currentBanner) {
82
+ save(banners.map(b => b.id === currentBanner.id ? { ...b, ...values, imageUrl } : b));
83
+ message.success('更新成功');
84
+ } else {
85
+ save([...banners, {
86
+ id: Date.now().toString(),
87
+ ...values,
88
+ imageUrl,
89
+ createdAt: new Date().toISOString(),
90
+ }]);
91
+ message.success('添加成功');
92
+ }
93
+ setIsModalVisible(false);
94
+ });
95
+ };
96
+
97
+ const columns = [
98
+ { title: 'ID', dataIndex: 'id', key: 'id', width: 80 },
99
+ { title: '标题', dataIndex: 'title', key: 'title' },
100
+ {
101
+ title: '轮播图', dataIndex: 'imageUrl', key: 'imageUrl',
102
+ render: (url: string) => url
103
+ ? <img src={url} alt="banner" style={{ width: 100, height: 50, objectFit: 'cover' }} />
104
+ : '-'
105
+ },
106
+ { title: '链接', dataIndex: 'link', key: 'link', ellipsis: true },
107
+ { title: '排序', dataIndex: 'sort', key: 'sort', width: 80 },
108
+ {
109
+ title: '状态', dataIndex: 'status', key: 'status', width: 100,
110
+ render: (s: string) => <Tag color={s === 'active' ? 'green' : 'red'}>{s === 'active' ? '启用' : '禁用'}</Tag>
111
+ },
112
+ {
113
+ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt',
114
+ render: (t: string) => new Date(t).toLocaleString()
115
+ },
116
+ {
117
+ title: '操作', key: 'action',
118
+ render: (_: any, record: Banner) => (
119
+ <Space>
120
+ <Button icon={<EditOutlined />} size="small" onClick={() => handleEdit(record)}>编辑</Button>
121
+ <Popconfirm title="确定删除?" onConfirm={() => handleDelete(record.id)} okText="确定" cancelText="取消">
122
+ <Button danger icon={<DeleteOutlined />} size="small">删除</Button>
123
+ </Popconfirm>
124
+ </Space>
125
+ )
126
+ }
127
+ ];
128
+
129
+ return (
130
+ <div>
131
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
132
+ <Title level={4} style={{ margin: 0 }}>Banner管理</Title>
133
+ <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>添加Banner</Button>
134
+ </div>
135
+
136
+ <Table columns={columns} dataSource={banners} rowKey="id"
137
+ pagination={{ showSizeChanger: true, defaultPageSize: 10 }} />
138
+
139
+ <Modal
140
+ title={isEditing ? '编辑Banner' : '添加Banner'}
141
+ open={isModalVisible}
142
+ onOk={handleSubmit}
143
+ onCancel={() => setIsModalVisible(false)}
144
+ width={600}
145
+ >
146
+ <Form form={form} layout="vertical" initialValues={{ sort: 0, status: 'active' }}>
147
+ <Form.Item name="title" label="Banner标题" rules={[{ required: true, message: '请输入标题' }]}>
148
+ <Input placeholder="请输入Banner标题" />
149
+ </Form.Item>
150
+ <Form.Item name="link" label="跳转链接" rules={[{ required: true, message: '请输入链接' }]}>
151
+ <Input placeholder="请输入跳转链接" />
152
+ </Form.Item>
153
+ <Form.Item name="sort" label="排序" rules={[{ required: true, message: '请输入排序' }]}>
154
+ <Input type="number" placeholder="请输入排序" />
155
+ </Form.Item>
156
+ <Form.Item name="status" label="状态">
157
+ <Select options={[{ value: 'active', label: '启用' }, { value: 'inactive', label: '禁用' }]} />
158
+ </Form.Item>
159
+ <Form.Item label="轮播图" required>
160
+ <Upload
161
+ listType="picture-card"
162
+ showUploadList={false}
163
+ beforeUpload={handleUpload}
164
+ accept="image/*"
165
+ >
166
+ {imageUrl ? (
167
+ <img src={imageUrl} alt="banner" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
168
+ ) : (
169
+ <div>
170
+ {uploading ? <LoadingOutlined /> : <PlusOutlined />}
171
+ <div style={{ marginTop: 8 }}>上传图片</div>
172
+ </div>
173
+ )}
174
+ </Upload>
175
+ </Form.Item>
176
+ </Form>
177
+ </Modal>
178
+ </div>
179
+ );
180
+ };
181
+
182
+ export default BannerList;