Selaa lähdekoodia

Merge branch 'master' of http://git.ycjcjy.com/medical-plat/pc-admin

Your Name 4 vuotta sitten
vanhempi
commit
224c0bec91

+ 38
- 11
config/routes.js Näytä tiedosto

@@ -65,18 +65,32 @@ export default [
65 65
                 component: '../layouts/BlankLayout',
66 66
                 routes: [
67 67
                   {
68
-                    path: '/cms/banner',
68
+                    path: '/cms/banner/list',
69 69
                     name: 'Banner管理',
70 70
                     menuCode: 'cms.banner',
71
-                    component: '../layouts/BlankLayout',
71
+                    component: './Cms/Banner/List',
72 72
                   },
73 73
                   {
74
-                    path: '/cms/hot',
74
+                    path: '/cms/banner/edit',
75
+                    name: 'Banner编辑',
76
+                    menuCode: 'cms.banner',
77
+                    component: './Cms/Banner/Edit',
78
+                    hideInMenu: true,
79
+                  },
80
+                  {
81
+                    path: '/cms/hot/list',
75 82
                     name: '热门管理',
76 83
                     menuCode: 'cms.hot',
77
-                    component: '../layouts/BlankLayout',
84
+                    component: './Cms/Hot/List',
78 85
                   },
79
-                ]
86
+                  {
87
+                    path: '/cms/hot/edit',
88
+                    name: 'Banner编辑',
89
+                    menuCode: 'cms.hot',
90
+                    component: './Cms/Hot/Edit',
91
+                    hideInMenu: true,
92
+                  },
93
+                ],
80 94
               },
81 95
               {
82 96
                 path: '/post',
@@ -98,7 +112,7 @@ export default [
98 112
                     component: './Post/Edit',
99 113
                     hideInMenu: true,
100 114
                   },
101
-                ]
115
+                ],
102 116
               },
103 117
               {
104 118
                 path: '/student',
@@ -111,9 +125,22 @@ export default [
111 125
                     path: '/student/student',
112 126
                     name: '学生管理',
113 127
                     menuCode: 'student.student',
114
-                    component: '../layouts/BlankLayout',
128
+                    component: './Student/Student/List',
115 129
                   },
116
-                ]
130
+                  {
131
+                    path: '/student/school',
132
+                    name: '学校管理',
133
+                    menuCode: 'student.student',
134
+                    component: './Student/School/List',
135
+                  },
136
+                  {
137
+                    path: '/student/school/edit',
138
+                    name: '学校编辑',
139
+                    menuCode: 'student.student',
140
+                    hideInMenu: true,
141
+                    component: './Student/School/Edit',
142
+                  },
143
+                ],
117 144
               },
118 145
               {
119 146
                 path: '/medical',
@@ -134,7 +161,7 @@ export default [
134 161
                     menuCode: 'medical.test',
135 162
                     component: '../layouts/BlankLayout',
136 163
                   },
137
-                ]
164
+                ],
138 165
               },
139 166
               {
140 167
                 path: '/report',
@@ -155,7 +182,7 @@ export default [
155 182
                     menuCode: 'report.student',
156 183
                     component: '../layouts/BlankLayout',
157 184
                   },
158
-                ]
185
+                ],
159 186
               },
160 187
               {
161 188
                 path: '/system',
@@ -194,7 +221,7 @@ export default [
194 221
                     menuCode: 'system.clinic',
195 222
                     component: '../layouts/BlankLayout',
196 223
                   },
197
-                ]
224
+                ],
198 225
               },
199 226
               {
200 227
                 component: './404',

+ 2
- 0
package.json Näytä tiedosto

@@ -52,6 +52,7 @@
52 52
     "@umijs/route-utils": "^1.0.33",
53 53
     "ali-oss": "^6.15.2",
54 54
     "antd": "^4.12.0",
55
+    "array-move": "^3.0.1",
55 56
     "classnames": "^2.2.6",
56 57
     "lodash": "^4.17.11",
57 58
     "md5": "^2.3.0",
@@ -62,6 +63,7 @@
62 63
     "react-dom": "^17.0.0",
63 64
     "react-helmet-async": "^1.0.4",
64 65
     "react-player": "^2.9.0",
66
+    "react-sortable-hoc": "^2.0.0",
65 67
     "umi": "^3.4.1",
66 68
     "umi-request": "^1.0.8",
67 69
     "wangeditor": "^4.6.15"

+ 6
- 0
src/common/constants.js Näytä tiedosto

@@ -0,0 +1,6 @@
1
+const stateOptions = {
2
+  0: { text: '未上架' },
3
+  1: { text: '已上架' },
4
+};
5
+
6
+export { stateOptions };

+ 133
- 0
src/pages/Cms/Banner/Edit/index.jsx Näytä tiedosto

@@ -0,0 +1,133 @@
1
+import React, { useEffect, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import Container from '@/components/Container';
5
+import request from '@/utils/request';
6
+
7
+import ProForm, { ProFormText, ProFormSelect } from '@ant-design/pro-form';
8
+import UploadImage from '@/components/UploadImage';
9
+import { notification, Form } from 'antd';
10
+
11
+const BannerEdit = (props) => {
12
+  const { id } = props.location.query;
13
+  const [loading, setLoading] = useState(false);
14
+  const [form] = Form.useForm();
15
+  //   const [bannerData, setBannerData] = useState({});
16
+
17
+  useEffect(() => {
18
+    if (id) {
19
+      setLoading(true);
20
+      request(`/banner/${id}`)
21
+        .then((res) => {
22
+          form.setFieldsValue(res);
23
+          //   setBannerData(res);
24
+          //   set
25
+          setLoading(false);
26
+        })
27
+        .catch((e) => {
28
+          setLoading(false);
29
+          notification.error({ message: e.message });
30
+        });
31
+    }
32
+  }, [form, id]);
33
+
34
+  const handleSubmit = (values) => {
35
+    console.log(values, form.getFieldsValue(), 'handleSubmit');
36
+    if (!id) {
37
+      setLoading(true);
38
+      return request('/banner', { method: 'post', data: values })
39
+        .then(() => {
40
+          // props.onChange(res)
41
+          // form.setFieldsValue(res)
42
+          notification.info({ message: '添加成功' });
43
+          setLoading(false);
44
+          history.go('-1');
45
+        })
46
+        .catch((e) => {
47
+          notification.error({ message: e.message });
48
+          setLoading(false);
49
+          return Promise.reject(e.message);
50
+        });
51
+    }
52
+    setLoading(true);
53
+    return request(`/banner/${id}`, { method: 'put', data: { serialNo: id, ...values } })
54
+      .then(() => {
55
+        // props.onChange(res)
56
+        // form.setFieldsValue(res)
57
+        notification.info({ message: '修改成功' });
58
+        setLoading(false);
59
+        history.go('-1');
60
+      })
61
+      .catch((e) => {
62
+        notification.error({ message: e.message });
63
+        setLoading(false);
64
+        return Promise.reject(e.message);
65
+      });
66
+  };
67
+
68
+  return (
69
+    <PageContainer>
70
+      <Container loading={loading}>
71
+        <ProForm form={form} onFinish={handleSubmit}>
72
+          <Form.Item
73
+            name="image"
74
+            label="图片"
75
+            placeholder="请选择图片"
76
+            extra="支持jpg/png文件,且不超过500kb"
77
+            rules={[{ required: true, message: '请上传图片' }]}
78
+          >
79
+            <UploadImage />
80
+          </Form.Item>
81
+
82
+          <ProFormText
83
+            label="标题"
84
+            placeholder="输入标题"
85
+            name="title"
86
+            rules={[{ required: true, message: '请填写标题' }]}
87
+          />
88
+
89
+          <ProFormSelect
90
+            label="板块"
91
+            placeholder="请选择板块"
92
+            initialValue={'post'}
93
+            name="targetType"
94
+            options={[
95
+              {
96
+                value: 'post',
97
+                label: '科普',
98
+              },
99
+            ]}
100
+            rules={[{ required: true, message: '请选择板块' }]}
101
+          />
102
+
103
+          <ProFormSelect
104
+            label="链接文章"
105
+            placeholder="请选择链接文章"
106
+            name="targetId"
107
+            showSearch
108
+            request={async ({ keyWords }) => {
109
+              // console.log()
110
+              // if (keyWords) {
111
+              return request('/post', {
112
+                method: 'get',
113
+                params: { name: keyWords, pageNum: 1, pageSize: 1000 },
114
+              })
115
+                .then((res) => {
116
+                  return res?.records?.map((x) => {
117
+                    return { label: x.name, value: x.postId };
118
+                  });
119
+                })
120
+                .catch((e) => {
121
+                  notification.error({ message: e.message });
122
+                  return Promise.reject(e.message);
123
+                });
124
+              // }
125
+            }}
126
+          />
127
+        </ProForm>
128
+      </Container>
129
+    </PageContainer>
130
+  );
131
+};
132
+
133
+export default connect(() => ({}))(BannerEdit);

+ 173
- 0
src/pages/Cms/Banner/List/index.jsx Näytä tiedosto

@@ -0,0 +1,173 @@
1
+import React, { useRef, useCallback, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import ProTable from '@ant-design/pro-table';
5
+import { PlusOutlined, MenuOutlined } from '@ant-design/icons';
6
+import { Button, Popconfirm, Image, Space, notification } from 'antd';
7
+import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc';
8
+import request, { queryTable } from '@/utils/request';
9
+import arrayMove from 'array-move';
10
+
11
+const DragHandle = sortableHandle(() => <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />);
12
+
13
+const SortableItem = sortableElement((props) => <tr {...props} />);
14
+const SortableContainer = sortableContainer((props) => <tbody {...props} />);
15
+
16
+const typeList = { post: '科普' };
17
+// const stateList = { post: '科普' };
18
+
19
+const BannerList = () => {
20
+  const [, setLoading] = useState(false);
21
+  const [dataSource, setDataSource] = useState([]);
22
+  const ref = useRef();
23
+  const handleBannerClick = useCallback((id) => {
24
+    history.push(id ? `/cms/banner/edit?id=${id}` : '/cms/banner/edit');
25
+  }, []);
26
+
27
+  const actions = [
28
+    <Button key="button" icon={<PlusOutlined />} type="primary" onClick={() => handleBannerClick()}>
29
+      新建
30
+    </Button>,
31
+  ];
32
+
33
+  const deleteBannerClick = (id) => {
34
+    setLoading(true);
35
+    console.log(ref, 'ref');
36
+
37
+    request(`/banner/${id}`, { method: 'delete' })
38
+      .then(() => {
39
+        notification.success({ message: '删除成功' });
40
+        ref.current.submit();
41
+      })
42
+      .catch((e) => {
43
+        setLoading(false);
44
+        notification.error({ message: e.message });
45
+      });
46
+  };
47
+
48
+  const columns = [
49
+    {
50
+      title: '排序',
51
+      dataIndex: 'sort',
52
+      width: 60,
53
+      className: 'drag-visible',
54
+      hideInSearch: true,
55
+      align: 'center',
56
+      render: () => <DragHandle />,
57
+    },
58
+    {
59
+      title: '主图',
60
+      dataIndex: 'image',
61
+      hideInSearch: true,
62
+
63
+      align: 'center',
64
+      render: (text) => <Image width={64} height={64} src={text} />,
65
+    },
66
+    {
67
+      title: '名称',
68
+      dataIndex: 'title',
69
+      align: 'center',
70
+      className: 'drag-visible',
71
+    },
72
+    {
73
+      title: '板块',
74
+      dataIndex: 'targetType',
75
+      hideInSearch: true,
76
+      align: 'center',
77
+      render: (text) => typeList[text] || '',
78
+    },
79
+
80
+    {
81
+      title: '状态',
82
+      dataIndex: 'status',
83
+      align: 'center',
84
+      hideInSearch: true,
85
+      // render: (text) => <Image src={text} />,
86
+    },
87
+    {
88
+      title: '操作',
89
+      dataIndex: 'action',
90
+      align: 'center',
91
+      hideInSearch: true,
92
+      render: (text, record) => (
93
+        <Space size="middle">
94
+          <a onClick={() => handleBannerClick(record.serialNo)}>编辑</a>
95
+          <Popconfirm
96
+            key="popconfirm"
97
+            title={`确认删除吗?`}
98
+            onConfirm={() => deleteBannerClick(record.serialNo)}
99
+            okText="是"
100
+            cancelText="否"
101
+          >
102
+            <a>删除</a>
103
+          </Popconfirm>
104
+        </Space>
105
+      ),
106
+    },
107
+  ];
108
+
109
+  const onSortEnd = ({ oldIndex, newIndex }) => {
110
+    // const { dataSource } = this.state;
111
+    console.log('Sorted items: ', dataSource, oldIndex, newIndex);
112
+    if (oldIndex !== newIndex) {
113
+      const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter((el) => !!el);
114
+      console.log('Sorted items: ', newData);
115
+      //   this.setState({ dataSource: newData });
116
+      setDataSource(newData);
117
+    }
118
+  };
119
+
120
+  const DraggableContainer = (props) => (
121
+    <SortableContainer
122
+      useDragHandle
123
+      disableAutoscroll
124
+      helperClass="row-dragging"
125
+      onSortEnd={onSortEnd}
126
+      {...props}
127
+    />
128
+  );
129
+
130
+  const DraggableBodyRow = ({ className, style, ...restProps }) => {
131
+    // const { dataSource } = this.state;
132
+    // function findIndex base on Table rowKey props and should always be a right array index
133
+    console.log(restProps, dataSource, 'restProps');
134
+    const index = dataSource?.findIndex((x) => x.serialNo === restProps['data-row-key']);
135
+    return <SortableItem index={index} {...restProps} />;
136
+  };
137
+
138
+  const getData = (params, sort, filter) => {
139
+    return queryTable('/banner')(params, sort, filter).then((res) => {
140
+      console.log(res, 'getData');
141
+      setDataSource(res.data);
142
+      return res;
143
+    });
144
+  };
145
+
146
+  return (
147
+    <PageContainer>
148
+      <ProTable
149
+        columns={columns}
150
+        dataSource={dataSource}
151
+        request={getData}
152
+        postData={(data) => data}
153
+        formRef={ref}
154
+        rowKey="serialNo"
155
+        headerTitle="科普列表"
156
+        search={{
157
+          labelWidth: '4em',
158
+        }}
159
+        components={{
160
+          body: {
161
+            wrapper: DraggableContainer,
162
+            row: DraggableBodyRow,
163
+          },
164
+        }}
165
+        toolBarRender={() => actions}
166
+      />
167
+    </PageContainer>
168
+  );
169
+};
170
+
171
+export default connect((s) => ({
172
+  typeList: s.post.typeList,
173
+}))(BannerList);

+ 18
- 0
src/pages/Cms/Banner/model.js Näytä tiedosto

@@ -0,0 +1,18 @@
1
+// import { message } from 'antd';
2
+// import request from '@/utils/request';
3
+
4
+// const Model = {
5
+//   namespace: 'banner',
6
+//   state: {
7
+//     data:{}
8
+//   },
9
+//   effects: {
10
+
11
+//   },
12
+
13
+//   reducers: {
14
+
15
+//   },
16
+// };
17
+
18
+// export default Model;

+ 141
- 0
src/pages/Cms/Hot/Edit/index.jsx Näytä tiedosto

@@ -0,0 +1,141 @@
1
+import React, { useEffect, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import Container from '@/components/Container';
5
+import request from '@/utils/request';
6
+
7
+import ProForm, { ProFormSelect } from '@ant-design/pro-form';
8
+import UploadImage from '@/components/UploadImage';
9
+import { notification, Form } from 'antd';
10
+
11
+const HotEdit = (props) => {
12
+  const { id } = props.location.query;
13
+  const [loading, setLoading] = useState(false);
14
+  const [form] = Form.useForm();
15
+  //   const [bannerData, setBannerData] = useState({});
16
+
17
+  useEffect(() => {
18
+    if (id) {
19
+      setLoading(true);
20
+      request(`/topic/${id}`)
21
+        .then((res) => {
22
+          form.setFieldsValue(res);
23
+          //   setBannerData(res);
24
+          //   set
25
+          setLoading(false);
26
+        })
27
+        .catch((e) => {
28
+          setLoading(false);
29
+          notification.error({ message: e.message });
30
+        });
31
+    }
32
+  }, [form, id]);
33
+
34
+  const handleSubmit = (values) => {
35
+    console.log(values, form.getFieldsValue(), 'handleSubmit');
36
+    if (!id) {
37
+      setLoading(true);
38
+      return request('/topic', { method: 'post', data: values })
39
+        .then(() => {
40
+          // props.onChange(res)
41
+          // form.setFieldsValue(res)
42
+          notification.info({ message: '添加成功' });
43
+          setLoading(false);
44
+          history.go('-1');
45
+        })
46
+        .catch((e) => {
47
+          notification.error({ message: e.message });
48
+          setLoading(false);
49
+          return Promise.reject(e.message);
50
+        });
51
+    }
52
+    setLoading(true);
53
+    return request(`/topic/${id}`, { method: 'put', data: { serialNo: id, ...values } })
54
+      .then(() => {
55
+        // props.onChange(res)
56
+        // form.setFieldsValue(res)
57
+        notification.info({ message: '修改成功' });
58
+        setLoading(false);
59
+        history.go('-1');
60
+      })
61
+      .catch((e) => {
62
+        notification.error({ message: e.message });
63
+        setLoading(false);
64
+        return Promise.reject(e.message);
65
+      });
66
+  };
67
+
68
+  return (
69
+    <PageContainer>
70
+      <Container loading={loading}>
71
+        <ProForm form={form} onFinish={handleSubmit}>
72
+          <Form.Item
73
+            name="poster"
74
+            label="图片"
75
+            placeholder="请选择图片"
76
+            extra="支持jpg/png文件,且不超过500kb"
77
+            rules={[{ required: true, message: '请上传图片' }]}
78
+          >
79
+            <UploadImage />
80
+          </Form.Item>
81
+
82
+          <ProFormSelect
83
+            label="类型"
84
+            placeholder="请选择类型"
85
+            initialValue={'post'}
86
+            name="targetType"
87
+            options={[
88
+              {
89
+                value: 'post',
90
+                label: '科普',
91
+              },
92
+            ]}
93
+            rules={[{ required: true, message: '请选择板块' }]}
94
+          />
95
+
96
+          <ProFormSelect
97
+            label="科普知识"
98
+            placeholder="请选择科普知识"
99
+            name="targetId"
100
+            showSearch
101
+            request={async ({ keyWords }) => {
102
+              // console.log()
103
+              // if (keyWords) {
104
+              return request('/post', {
105
+                method: 'get',
106
+                params: { name: keyWords, pageNum: 1, pageSize: 1000 },
107
+              })
108
+                .then((res) => {
109
+                  return res?.records?.map((x) => {
110
+                    return { label: x.name, value: x.postId };
111
+                  });
112
+                })
113
+                .catch((e) => {
114
+                  notification.error({ message: e.message });
115
+                  return Promise.reject(e.message);
116
+                });
117
+              // }
118
+            }}
119
+          />
120
+
121
+          {/* <Select
122
+        showSearch
123
+        value={this.state.value}
124
+        placeholder={this.props.placeholder}
125
+        style={this.props.style}
126
+        defaultActiveFirstOption={false}
127
+        showArrow={false}
128
+        filterOption={false}
129
+        onSearch={this.handleSearch}
130
+        onChange={this.handleChange}
131
+        notFoundContent={null}
132
+      >
133
+        {options}
134
+      </Select> */}
135
+        </ProForm>
136
+      </Container>
137
+    </PageContainer>
138
+  );
139
+};
140
+
141
+export default connect(() => ({}))(HotEdit);

+ 173
- 0
src/pages/Cms/Hot/List/index.jsx Näytä tiedosto

@@ -0,0 +1,173 @@
1
+import React, { useRef, useCallback, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import ProTable from '@ant-design/pro-table';
5
+import { PlusOutlined, MenuOutlined } from '@ant-design/icons';
6
+import { Button, Popconfirm, Image, Space, notification } from 'antd';
7
+import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc';
8
+import request, { queryTable } from '@/utils/request';
9
+import arrayMove from 'array-move';
10
+
11
+const DragHandle = sortableHandle(() => <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />);
12
+
13
+const SortableItem = sortableElement((props) => <tr {...props} />);
14
+const SortableContainer = sortableContainer((props) => <tbody {...props} />);
15
+
16
+const typeList = { post: '科普' };
17
+// const stateList = { post: '科普' };
18
+
19
+const HotList = () => {
20
+  const [, setLoading] = useState(false);
21
+  const [dataSource, setDataSource] = useState([]);
22
+  const ref = useRef();
23
+  const handleHotClick = useCallback((id) => {
24
+    history.push(id ? `/cms/hot/edit?id=${id}` : '/cms/hot/edit');
25
+  }, []);
26
+
27
+  const actions = [
28
+    <Button key="button" icon={<PlusOutlined />} type="primary" onClick={() => handleHotClick()}>
29
+      新建
30
+    </Button>,
31
+  ];
32
+
33
+  const deleteHotClick = (id) => {
34
+    setLoading(true);
35
+    console.log(ref, 'ref');
36
+
37
+    request(`/topic/${id}`, { method: 'delete' })
38
+      .then(() => {
39
+        notification.success({ message: '删除成功' });
40
+        ref.current.submit();
41
+      })
42
+      .catch((e) => {
43
+        setLoading(false);
44
+        notification.error({ message: e.message });
45
+      });
46
+  };
47
+
48
+  const columns = [
49
+    {
50
+      title: '排序',
51
+      dataIndex: 'sort',
52
+      width: 60,
53
+      className: 'drag-visible',
54
+      hideInSearch: true,
55
+      align: 'center',
56
+      render: () => <DragHandle />,
57
+    },
58
+    {
59
+      title: '主图',
60
+      dataIndex: 'poster',
61
+      hideInSearch: true,
62
+
63
+      align: 'center',
64
+      render: (text) => <Image width={64} height={64} src={text} />,
65
+    },
66
+    {
67
+      title: '文章标题',
68
+      dataIndex: 'title',
69
+      align: 'center',
70
+      className: 'drag-visible',
71
+    },
72
+    {
73
+      title: '板块',
74
+      dataIndex: 'targetType',
75
+      hideInSearch: true,
76
+      align: 'center',
77
+      render: (text) => typeList[text] || '',
78
+    },
79
+
80
+    {
81
+      title: '状态',
82
+      dataIndex: 'status',
83
+      align: 'center',
84
+      hideInSearch: true,
85
+      // render: (text) => <Image src={text} />,
86
+    },
87
+    {
88
+      title: '操作',
89
+      dataIndex: 'action',
90
+      align: 'center',
91
+      hideInSearch: true,
92
+      render: (text, record) => (
93
+        <Space size="middle">
94
+          <a onClick={() => handleHotClick(record.serialNo)}>编辑</a>
95
+          <Popconfirm
96
+            key="popconfirm"
97
+            title={`确认删除吗?`}
98
+            onConfirm={() => deleteHotClick(record.serialNo)}
99
+            okText="是"
100
+            cancelText="否"
101
+          >
102
+            <a>删除</a>
103
+          </Popconfirm>
104
+        </Space>
105
+      ),
106
+    },
107
+  ];
108
+
109
+  const onSortEnd = ({ oldIndex, newIndex }) => {
110
+    // const { dataSource } = this.state;
111
+    console.log('Sorted items: ', dataSource, oldIndex, newIndex);
112
+    if (oldIndex !== newIndex) {
113
+      const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter((el) => !!el);
114
+      console.log('Sorted items: ', newData);
115
+      //   this.setState({ dataSource: newData });
116
+      setDataSource(newData);
117
+    }
118
+  };
119
+
120
+  const DraggableContainer = (props) => (
121
+    <SortableContainer
122
+      useDragHandle
123
+      disableAutoscroll
124
+      helperClass="row-dragging"
125
+      onSortEnd={onSortEnd}
126
+      {...props}
127
+    />
128
+  );
129
+
130
+  const DraggableBodyRow = ({ className, style, ...restProps }) => {
131
+    // const { dataSource } = this.state;
132
+    // function findIndex base on Table rowKey props and should always be a right array index
133
+    console.log(restProps, dataSource, 'restProps');
134
+    const index = dataSource?.findIndex((x) => x.serialNo === restProps['data-row-key']);
135
+    return <SortableItem index={index} {...restProps} />;
136
+  };
137
+
138
+  const getData = (params, sort, filter) => {
139
+    return queryTable('/topic')(params, sort, filter).then((res) => {
140
+      console.log(res, 'getData');
141
+      setDataSource(res.data);
142
+      return res;
143
+    });
144
+  };
145
+
146
+  return (
147
+    <PageContainer>
148
+      <ProTable
149
+        columns={columns}
150
+        dataSource={dataSource}
151
+        request={getData}
152
+        postData={(data) => data}
153
+        formRef={ref}
154
+        rowKey="serialNo"
155
+        headerTitle="科普列表"
156
+        search={{
157
+          labelWidth: '4em',
158
+        }}
159
+        components={{
160
+          body: {
161
+            wrapper: DraggableContainer,
162
+            row: DraggableBodyRow,
163
+          },
164
+        }}
165
+        toolBarRender={() => actions}
166
+      />
167
+    </PageContainer>
168
+  );
169
+};
170
+
171
+export default connect((s) => ({
172
+  typeList: s.post.typeList,
173
+}))(HotList);

+ 101
- 0
src/pages/Student/School/Edit/index.jsx Näytä tiedosto

@@ -0,0 +1,101 @@
1
+import React, { useEffect, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import Container from '@/components/Container';
5
+import request from '@/utils/request';
6
+
7
+import ProForm, { ProFormText, ProFormTextArea } from '@ant-design/pro-form';
8
+import UploadImage from '@/components/UploadImage';
9
+import { notification, Form } from 'antd';
10
+
11
+const SchoolEdit = (props) => {
12
+  const { id } = props.location.query;
13
+  const [loading, setLoading] = useState(false);
14
+  const [form] = Form.useForm();
15
+  //   const [bannerData, setBannerData] = useState({});
16
+
17
+  useEffect(() => {
18
+    if (id) {
19
+      setLoading(true);
20
+      request(`/school/${id}`)
21
+        .then((res) => {
22
+          form.setFieldsValue(res);
23
+          //   setBannerData(res);
24
+          //   set
25
+          setLoading(false);
26
+        })
27
+        .catch((e) => {
28
+          setLoading(false);
29
+          notification.error({ message: e.message });
30
+        });
31
+    }
32
+  }, [form, id]);
33
+
34
+  const handleSubmit = (values) => {
35
+    console.log(values, form.getFieldsValue(), 'handleSubmit');
36
+    if (!id) {
37
+      setLoading(true);
38
+      return request('/school', { method: 'post', data: values })
39
+        .then(() => {
40
+          // props.onChange(res)
41
+          // form.setFieldsValue(res)
42
+          notification.info({ message: '添加成功' });
43
+          setLoading(false);
44
+          history.go('-1');
45
+        })
46
+        .catch((e) => {
47
+          notification.error({ message: e.message });
48
+          setLoading(false);
49
+          return Promise.reject(e.message);
50
+        });
51
+    }
52
+    setLoading(true);
53
+    return request(`/school/${id}`, { method: 'put', data: { schoolId: id, ...values } })
54
+      .then(() => {
55
+        // props.onChange(res)
56
+        // form.setFieldsValue(res)
57
+        notification.info({ message: '修改成功' });
58
+        setLoading(false);
59
+        history.go('-1');
60
+      })
61
+      .catch((e) => {
62
+        notification.error({ message: e.message });
63
+        setLoading(false);
64
+        return Promise.reject(e.message);
65
+      });
66
+  };
67
+
68
+  return (
69
+    <PageContainer>
70
+      <Container loading={loading}>
71
+        <ProForm form={form} onFinish={handleSubmit}>
72
+          <Form.Item
73
+            name="logo"
74
+            label="logo"
75
+            placeholder="请选择logo"
76
+            extra="支持jpg/png文件,且不超过500kb"
77
+            rules={[{ required: true, message: '请上传logo' }]}
78
+          >
79
+            <UploadImage />
80
+          </Form.Item>
81
+
82
+          <ProFormText
83
+            label="名称"
84
+            placeholder="输入名称"
85
+            name="name"
86
+            rules={[{ required: true, message: '请填写名称' }]}
87
+          />
88
+
89
+          <ProFormTextArea
90
+            label="简介"
91
+            placeholder="输入简介"
92
+            name="desc"
93
+            rules={[{ required: true, message: '请填写简介' }]}
94
+          />
95
+        </ProForm>
96
+      </Container>
97
+    </PageContainer>
98
+  );
99
+};
100
+
101
+export default connect(() => ({}))(SchoolEdit);

+ 122
- 0
src/pages/Student/School/List/index.jsx Näytä tiedosto

@@ -0,0 +1,122 @@
1
+import React, { useRef, useCallback, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import ProTable from '@ant-design/pro-table';
5
+import { PlusOutlined } from '@ant-design/icons';
6
+import { Button, Popconfirm, Image, Space, notification } from 'antd';
7
+import request, { queryTable } from '@/utils/request';
8
+
9
+// const stateList = { post: '科普' };
10
+
11
+const SchoolList = () => {
12
+  const [, setLoading] = useState(false);
13
+
14
+  const ref = useRef();
15
+  const handleSchoolClick = useCallback((id) => {
16
+    history.push(id ? `/student/school/edit?id=${id}` : '/student/school/edit');
17
+  }, []);
18
+
19
+  const actions = [
20
+    <Button key="button" icon={<PlusOutlined />} type="primary" onClick={() => handleSchoolClick()}>
21
+      新建
22
+    </Button>,
23
+  ];
24
+
25
+  const deleteSchoolClick = (id) => {
26
+    setLoading(true);
27
+
28
+    request(`/school/${id}`, { method: 'delete' })
29
+      .then(() => {
30
+        notification.success({ message: '删除成功' });
31
+        ref.current.submit();
32
+      })
33
+      .catch((e) => {
34
+        setLoading(false);
35
+        notification.error({ message: e.message });
36
+      });
37
+  };
38
+
39
+  const columns = [
40
+    {
41
+      title: 'logo',
42
+      dataIndex: 'logo',
43
+      hideInSearch: true,
44
+
45
+      align: 'center',
46
+      render: (text) => <Image width={64} height={64} src={text} />,
47
+    },
48
+    {
49
+      title: '学校名称',
50
+      dataIndex: 'name',
51
+      align: 'center',
52
+    },
53
+
54
+    {
55
+      title: '简介',
56
+      dataIndex: 'desc',
57
+      align: 'center',
58
+      hideInSearch: true,
59
+      width: '25%',
60
+      ellipsis: true,
61
+      // render: (text) => <Image src={text} />,
62
+    },
63
+    {
64
+      title: '上架状态',
65
+      dataIndex: 'status',
66
+      align: 'center',
67
+      // hideInTable: true,
68
+      valueType: 'select',
69
+      valueEnum: {
70
+        0: { text: '未上架' },
71
+        1: { text: '已上架' },
72
+      },
73
+    },
74
+    {
75
+      title: '上传时间',
76
+      dataIndex: 'createDate',
77
+      align: 'center',
78
+      hideInSearch: true,
79
+      valueType: 'date',
80
+    },
81
+    {
82
+      title: '操作',
83
+      dataIndex: 'action',
84
+      align: 'center',
85
+      hideInSearch: true,
86
+      render: (text, record) => (
87
+        <Space size="middle">
88
+          <a onClick={() => handleSchoolClick(record.schoolId)}>编辑</a>
89
+          <Popconfirm
90
+            key="popconfirm"
91
+            title={`确认删除吗?`}
92
+            onConfirm={() => deleteSchoolClick(record.schoolId)}
93
+            okText="是"
94
+            cancelText="否"
95
+          >
96
+            <a>删除</a>
97
+          </Popconfirm>
98
+        </Space>
99
+      ),
100
+    },
101
+  ];
102
+
103
+  return (
104
+    <PageContainer>
105
+      <ProTable
106
+        columns={columns}
107
+        request={queryTable('/school')}
108
+        formRef={ref}
109
+        rowKey="schoolId"
110
+        headerTitle="学校列表"
111
+        search={{
112
+          labelWidth: '4em',
113
+        }}
114
+        toolBarRender={() => actions}
115
+      />
116
+    </PageContainer>
117
+  );
118
+};
119
+
120
+export default connect((s) => ({
121
+  typeList: s.post.typeList,
122
+}))(SchoolList);

+ 133
- 0
src/pages/Student/Student/Edit/index.jsx Näytä tiedosto

@@ -0,0 +1,133 @@
1
+import React, { useEffect, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import Container from '@/components/Container';
5
+import request from '@/utils/request';
6
+
7
+import ProForm, { ProFormText, ProFormSelect } from '@ant-design/pro-form';
8
+import UploadImage from '@/components/UploadImage';
9
+import { notification, Form } from 'antd';
10
+
11
+const BannerEdit = (props) => {
12
+  const { id } = props.location.query;
13
+  const [loading, setLoading] = useState(false);
14
+  const [form] = Form.useForm();
15
+  //   const [bannerData, setBannerData] = useState({});
16
+
17
+  useEffect(() => {
18
+    if (id) {
19
+      setLoading(true);
20
+      request(`/banner/${id}`)
21
+        .then((res) => {
22
+          form.setFieldsValue(res);
23
+          //   setBannerData(res);
24
+          //   set
25
+          setLoading(false);
26
+        })
27
+        .catch((e) => {
28
+          setLoading(false);
29
+          notification.error({ message: e.message });
30
+        });
31
+    }
32
+  }, [form, id]);
33
+
34
+  const handleSubmit = (values) => {
35
+    console.log(values, form.getFieldsValue(), 'handleSubmit');
36
+    if (!id) {
37
+      setLoading(true);
38
+      return request('/banner', { method: 'post', data: values })
39
+        .then(() => {
40
+          // props.onChange(res)
41
+          // form.setFieldsValue(res)
42
+          notification.info({ message: '添加成功' });
43
+          setLoading(false);
44
+          history.go('-1');
45
+        })
46
+        .catch((e) => {
47
+          notification.error({ message: e.message });
48
+          setLoading(false);
49
+          return Promise.reject(e.message);
50
+        });
51
+    }
52
+    setLoading(true);
53
+    return request(`/banner/${id}`, { method: 'put', data: { serialNo: id, ...values } })
54
+      .then(() => {
55
+        // props.onChange(res)
56
+        // form.setFieldsValue(res)
57
+        notification.info({ message: '修改成功' });
58
+        setLoading(false);
59
+        history.go('-1');
60
+      })
61
+      .catch((e) => {
62
+        notification.error({ message: e.message });
63
+        setLoading(false);
64
+        return Promise.reject(e.message);
65
+      });
66
+  };
67
+
68
+  return (
69
+    <PageContainer>
70
+      <Container loading={loading}>
71
+        <ProForm form={form} onFinish={handleSubmit}>
72
+          <Form.Item
73
+            name="image"
74
+            label="图片"
75
+            placeholder="请选择图片"
76
+            extra="支持jpg/png文件,且不超过500kb"
77
+            rules={[{ required: true, message: '请上传图片' }]}
78
+          >
79
+            <UploadImage />
80
+          </Form.Item>
81
+
82
+          <ProFormText
83
+            label="标题"
84
+            placeholder="输入标题"
85
+            name="title"
86
+            rules={[{ required: true, message: '请填写标题' }]}
87
+          />
88
+
89
+          <ProFormSelect
90
+            label="板块"
91
+            placeholder="请选择板块"
92
+            initialValue={'post'}
93
+            name="targetType"
94
+            options={[
95
+              {
96
+                value: 'post',
97
+                label: '科普',
98
+              },
99
+            ]}
100
+            rules={[{ required: true, message: '请选择板块' }]}
101
+          />
102
+
103
+          <ProFormSelect
104
+            label="链接文章"
105
+            placeholder="请选择链接文章"
106
+            name="targetId"
107
+            showSearch
108
+            request={async ({ keyWords }) => {
109
+              // console.log()
110
+              // if (keyWords) {
111
+              return request('/post', {
112
+                method: 'get',
113
+                params: { name: keyWords, pageNum: 1, pageSize: 1000 },
114
+              })
115
+                .then((res) => {
116
+                  return res?.records?.map((x) => {
117
+                    return { label: x.name, value: x.postId };
118
+                  });
119
+                })
120
+                .catch((e) => {
121
+                  notification.error({ message: e.message });
122
+                  return Promise.reject(e.message);
123
+                });
124
+              // }
125
+            }}
126
+          />
127
+        </ProForm>
128
+      </Container>
129
+    </PageContainer>
130
+  );
131
+};
132
+
133
+export default connect(() => ({}))(BannerEdit);

+ 115
- 0
src/pages/Student/Student/List/index.jsx Näytä tiedosto

@@ -0,0 +1,115 @@
1
+import React, { useCallback, useState } from 'react';
2
+import { connect, history } from 'umi';
3
+import { PageContainer } from '@ant-design/pro-layout';
4
+import ProTable from '@ant-design/pro-table';
5
+import { PlusOutlined } from '@ant-design/icons';
6
+import { Button, Popconfirm, Image, Space, notification } from 'antd';
7
+import request, { queryTable } from '@/utils/request';
8
+
9
+const typeList = { post: '科普' };
10
+// const stateList = { post: '科普' };
11
+
12
+const BannerList = () => {
13
+  const [, setLoading] = useState(false);
14
+  const handleBannerClick = useCallback((id) => {
15
+    history.push(id ? `/cms/banner/edit?id=${id}` : '/cms/banner/edit');
16
+  }, []);
17
+
18
+  const actions = [
19
+    <Button key="button" icon={<PlusOutlined />} type="primary" onClick={() => handleBannerClick()}>
20
+      新建
21
+    </Button>,
22
+  ];
23
+
24
+  const deleteBannerClick = (id) => {
25
+    setLoading(true);
26
+
27
+    request(`/banner/${id}`, { method: 'delete' })
28
+      .then(() => {
29
+        notification.success({ message: '删除成功' });
30
+        // ref.current.submit();
31
+      })
32
+      .catch((e) => {
33
+        setLoading(false);
34
+        notification.error({ message: e.message });
35
+      });
36
+  };
37
+
38
+  const columns = [
39
+    {
40
+      title: '排序',
41
+      dataIndex: 'sort',
42
+      width: 60,
43
+      hideInSearch: true,
44
+      align: 'center',
45
+    },
46
+    {
47
+      title: '主图',
48
+      dataIndex: 'image',
49
+      hideInSearch: true,
50
+
51
+      align: 'center',
52
+      render: (text) => <Image width={64} height={64} src={text} />,
53
+    },
54
+    {
55
+      title: '名称',
56
+      dataIndex: 'title',
57
+      align: 'center',
58
+      className: 'drag-visible',
59
+    },
60
+    {
61
+      title: '板块',
62
+      dataIndex: 'targetType',
63
+      hideInSearch: true,
64
+      align: 'center',
65
+      render: (text) => typeList[text] || '',
66
+    },
67
+
68
+    {
69
+      title: '状态',
70
+      dataIndex: 'status',
71
+      align: 'center',
72
+      hideInSearch: true,
73
+      // render: (text) => <Image src={text} />,
74
+    },
75
+    {
76
+      title: '操作',
77
+      dataIndex: 'action',
78
+      align: 'center',
79
+      hideInSearch: true,
80
+      render: (text, record) => (
81
+        <Space size="middle">
82
+          <a onClick={() => handleBannerClick(record.serialNo)}>编辑</a>
83
+          <Popconfirm
84
+            key="popconfirm"
85
+            title={`确认删除吗?`}
86
+            onConfirm={() => deleteBannerClick(record.serialNo)}
87
+            okText="是"
88
+            cancelText="否"
89
+          >
90
+            <a>删除</a>
91
+          </Popconfirm>
92
+        </Space>
93
+      ),
94
+    },
95
+  ];
96
+
97
+  return (
98
+    <PageContainer>
99
+      <ProTable
100
+        columns={columns}
101
+        request={queryTable('/school')}
102
+        rowKey="schoolId"
103
+        headerTitle="学校列表"
104
+        search={{
105
+          labelWidth: '4em',
106
+        }}
107
+        toolBarRender={() => actions}
108
+      />
109
+    </PageContainer>
110
+  );
111
+};
112
+
113
+export default connect((s) => ({
114
+  typeList: s.post.typeList,
115
+}))(BannerList);