Bladeren bron

Merge branch 'master' of lizhiqi/yongxu-web into master

lizhiqi 2 jaren geleden
bovenliggende
commit
a7da44f0d4

+ 18 - 0
config/routes.ts

@@ -19,6 +19,24 @@
     icon: 'smile',
     component: './Welcome',
   },
+  {
+    name: '内容发布',
+    icon: 'file',
+    routes: [
+      {
+        path: '/ColumnList',
+        name: '栏目管理',
+        icon: 'file',
+        component: './cms/ColumnList',
+      },
+      {
+        path: '/ArticleManagement',
+        name: '文章管理',
+        icon: 'file',
+        component: './cms/ArticleManagement',
+      },
+    ],
+  },
   {
     path: '/userManagement',
     name: '用户管理',

+ 7 - 4
package.json

@@ -52,13 +52,15 @@
     "@babel/core": "^7.20.2",
     "@babel/preset-env": "^7.20.2",
     "@umijs/route-utils": "^2.0.0",
-    "antd": "^4.23.3",
+    "@wangeditor/editor-for-react": "^1.0.6",
+    "antd": "^4.21.2",
     "classnames": "^2.3.0",
     "echarts": "^5.4.0",
     "js-md5": "^0.7.3",
     "lodash": "^4.17.0",
     "moment": "^2.29.0",
     "omit.js": "^2.0.2",
+    "prop-types": "^15.8.1",
     "qs": "^6.11.0",
     "rc-menu": "^9.1.0",
     "rc-util": "^5.16.0",
@@ -66,6 +68,7 @@
     "react-dev-inspector": "^1.7.0",
     "react-dom": "^17.0.0",
     "react-helmet-async": "^1.2.0",
+    "react-quill": "^2.0.0",
     "umi": "3.5.0"
   },
   "devDependencies": {
@@ -77,8 +80,8 @@
     "@types/jest": "^26.0.0",
     "@types/js-md5": "^0.4.3",
     "@types/lodash": "^4.14.0",
-    "@types/react": "^17.0.0",
-    "@types/react-dom": "^17.0.0",
+    "@types/react": "^18.0.9",
+    "@types/react-dom": "^18.0.5",
     "@types/react-helmet": "^6.1.0",
     "@umijs/fabric": "^2.11.1",
     "@umijs/openapi": "^1.6.0",
@@ -100,7 +103,7 @@
     "prettier": "^2.5.0",
     "stylelint": "^13.0.0",
     "swagger-ui-dist": "^4.12.0",
-    "typescript": "^4.5.0",
+    "typescript": "^4.1.2",
     "umi-serve": "^1.9.10"
   },
   "engines": {

+ 57 - 0
src/components/MyEditor/index.tsx

@@ -0,0 +1,57 @@
+import '@wangeditor/editor/dist/css/style.css'; // 引入 css
+import { useState, useEffect } from 'react';
+import { Editor, Toolbar } from '@wangeditor/editor-for-react';
+import type { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor';
+
+function MyEditor(props: any) {
+  const { data } = props;
+  // editor 实例
+  const [editor, setEditor] = useState<IDomEditor | null>(null);
+
+  // 编辑器内容
+  const [html, setHtml] = useState('');
+
+  useEffect(() => {
+    setHtml(data);
+  }, []);
+
+  // 工具栏配置
+  const toolbarConfig: Partial<IToolbarConfig> = {};
+
+  // 编辑器配置
+  const editorConfig: Partial<IEditorConfig> = {
+    placeholder: '请输入内容...',
+  };
+
+  // 及时销毁 editor ,重要!
+  useEffect(() => {
+    return () => {
+      if (editor == null) return;
+      editor.destroy();
+      setEditor(null);
+    };
+  }, [editor]);
+
+  return (
+    <>
+      <div style={{ border: '1px solid #ccc', zIndex: 100 }}>
+        <Toolbar
+          editor={editor}
+          defaultConfig={toolbarConfig}
+          mode="default"
+          style={{ borderBottom: '1px solid #ccc' }}
+        />
+        <Editor
+          defaultConfig={editorConfig}
+          value={html}
+          onCreated={setEditor}
+          onChange={(el) => setHtml(el.getHtml())}
+          mode="default"
+          style={{ height: '300px', overflowY: 'hidden' }}
+        />
+      </div>
+    </>
+  );
+}
+
+export default MyEditor;

+ 41 - 0
src/components/RichText/index.tsx

@@ -0,0 +1,41 @@
+import { useEffect, useState } from 'react';
+import ReactQuill from 'react-quill';
+import 'react-quill/dist/quill.snow.css';
+
+const RichText = (props: { setContent: any }) => {
+  const { setContent } = props;
+  const [value, setValue] = useState('');
+
+  useEffect(() => {
+    if (value) {
+      setContent(value);
+    }
+  }, [value]);
+
+  const modules = {
+    toolbar: {
+      container: [
+        [{ header: [1, 2, 3, 4, 5, 6, false] }],
+        ['bold', 'italic', 'underline', 'strike'],
+        [{ list: 'ordered' }, { list: 'bullet' }],
+        [{ align: [] }],
+        [{ color: [] }, { background: [] }],
+        ['link', 'image'],
+        ['clean'],
+      ],
+    },
+  };
+
+  return (
+    <ReactQuill
+      style={{ height: 300 }}
+      theme="snow"
+      value={value}
+      onChange={setValue}
+      modules={modules}
+      className=" ql-editor"
+    />
+  );
+};
+
+export default RichText;

+ 153 - 0
src/pages/cms/ArticleManagement/edit.tsx

@@ -0,0 +1,153 @@
+import { Col, Form, Input, message, Modal, Row, Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { createColumn, editColumn, queryColumn } from '@/services/cms/ColumnList';
+// import RichText from '@/components/RichText';
+import MyEditor from '@/components/MyEditor';
+
+interface editPros {
+  visible: boolean;
+  editCallback: () => void;
+  params: any;
+}
+
+/**
+ * 文章管理编辑页面
+ * @param props
+ * @constructor
+ */
+const Edit: React.FC<editPros> = (props) => {
+  const { visible, editCallback, params } = props;
+  const [form] = Form.useForm();
+  const [dataList, setDataList]: any = useState([]);
+  // const [content, setContent] = useState();
+
+  // 获取列表数据
+  const getList = () => {
+    queryColumn({ q: 'list' }).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+      }
+    });
+  };
+
+  useEffect(() => {
+    getList();
+  }, []);
+
+  const onOk = () => {
+    form.validateFields().then((values) => {
+      if (values) {
+        const data = { ...values };
+        // data.content = content;
+        if (params) {
+          data.record_id = params.record_id;
+          editColumn(data)
+            .then((res) => {
+              if (res.code === 0) {
+                message.success('编辑成功');
+                editCallback();
+              } else {
+                message.error(res?.message);
+                editCallback();
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+              editCallback();
+            });
+        } else {
+          createColumn(data)
+            .then((res) => {
+              if (res.code === 0) {
+                message.success('保存成功');
+                editCallback();
+              } else {
+                message.error(res?.message);
+                editCallback();
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+              editCallback();
+            });
+        }
+      }
+    });
+  };
+
+  /**
+   * 取消
+   */
+  const onCancel = () => {
+    editCallback();
+  };
+
+  const formItemLayout = {
+    labelCol: {
+      span: 6,
+    },
+    wrapperCol: {
+      span: 16,
+    },
+  };
+
+  return (
+    <Modal
+      title={`${params ? '编辑' : '新增'}`}
+      open={visible}
+      onOk={onOk}
+      onCancel={onCancel}
+      width={800}
+    >
+      <Form form={form}>
+        <Row>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="title"
+              label="文章标题"
+              rules={[{ required: true, message: '请输入文章标题' }]}
+              initialValue={params?.title}
+            >
+              <Input placeholder="请输入文章标题" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="parent_id"
+              label="栏目名称"
+              rules={[{ required: true, message: '请选择栏目名称' }]}
+              initialValue={params?.parent_id}
+            >
+              <Select placeholder="请选择栏目名称">
+                {dataList && dataList.length
+                  ? dataList.map((res: any) => {
+                      return (
+                        <Select.Option value={res.record_id} key={res.record_id}>
+                          {res.name}
+                        </Select.Option>
+                      );
+                    })
+                  : null}
+              </Select>
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="content"
+              label="内容"
+              rules={[{ required: false, message: '请输入内容' }]}
+              initialValue={params?.content ? params?.content : ''}
+            >
+              {/*<RichText setContent={setContent} />*/}
+              <MyEditor data={params?.content ? params?.content : ''} />
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    </Modal>
+  );
+};
+export default Edit;

+ 214 - 0
src/pages/cms/ArticleManagement/index.tsx

@@ -0,0 +1,214 @@
+import React, { useEffect, useState } from 'react';
+import { Button, Card, Form, Input, message, Modal, Space, Table } from 'antd';
+import type { ColumnsType } from 'antd/es/table';
+import moment from 'moment/moment';
+import { PlusCircleOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons';
+import Edit from '@/pages/cms/ArticleManagement/edit';
+import { PageContainer } from '@ant-design/pro-components';
+import { queryArticle, delArticle } from '@/services/cms/ArticleManagement';
+
+interface DataType {
+  title: string;
+  content: string;
+  created_at: string;
+  creator: string;
+  record_id: string;
+}
+
+/**
+ * 文章管理
+ * @constructor
+ */
+const ArticleManagement: React.FC = () => {
+  const [form] = Form.useForm();
+  const [loading, setLoading] = useState(false);
+  const [searchData, setSearchData] = useState<object | null>({});
+  const [visible, setVisible] = useState(false);
+  const [editData, setEditData] = useState(null);
+  const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
+  const [dataList, setDataList]: any = useState([]);
+
+  // 获取列表数据
+  const getList = () => {
+    const params = {
+      q: 'page',
+      current: pagination.current,
+      pageSize: pagination.pageSize,
+      ...searchData,
+    };
+    queryArticle(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  useEffect(() => {
+    setLoading(true);
+    getList();
+  }, []);
+
+  //  搜索
+  const onFinish = () => {
+    form.validateFields().then((data) => {
+      setLoading(true);
+      setSearchData(data);
+    });
+  };
+  //  重置
+  const onReset = () => {
+    form.resetFields();
+    setLoading(true);
+    setSearchData(null);
+  };
+  // 新增弹框
+  const onAdd = () => {
+    setEditData(null);
+    setVisible(true);
+  };
+  // 打开编辑弹框
+  const toEdit = (data: any) => {
+    setEditData(data);
+    setVisible(true);
+  };
+  //  分页切换
+  const tableChange = (page: any) => {
+    setLoading(true);
+    const params = {
+      q: 'page',
+      current: page.current,
+      pageSize: page.pageSize,
+      ...searchData,
+    };
+    queryArticle(params).then((res) => {
+      if (res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+  // 删除
+  const toDel = (record: any) => {
+    console.log(record);
+    Modal.confirm({
+      title: '删除',
+      content: `确认删除项目:[${record.name}]`,
+      onOk: () => {
+        delArticle(record?.record_id)
+          .then((res) => {
+            if (res.code === 0) {
+              message.success('删除成功');
+            } else {
+              message.error('删除失败');
+            }
+          })
+          .catch((e) => {
+            message.error(e?.message);
+          });
+      },
+    });
+  };
+  // 编辑弹框回调
+  const handleEdit = () => {
+    setVisible(false);
+    getList();
+  };
+
+  const columns: ColumnsType<DataType> = [
+    {
+      title: '序号',
+      align: 'center',
+      key: 'index',
+      render: (_: any, row: any, index: number) => index + 1,
+    },
+    {
+      title: '文章标题',
+      dataIndex: 'title',
+      key: 'title',
+    },
+    {
+      title: '创建者',
+      dataIndex: 'creator',
+      key: 'creator',
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'created_at',
+      key: 'created_at',
+      render: (v) => v && <span>{moment(v).format('YYYY-MM-DD HH:mm')}</span>,
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (_, record) => (
+        <Space size="middle">
+          <a
+            onClick={() => {
+              toEdit(record);
+            }}
+          >
+            编辑
+          </a>
+          <a
+            style={{ color: 'red' }}
+            onClick={() => {
+              toDel(record);
+            }}
+          >
+            删除
+          </a>
+        </Space>
+      ),
+    },
+  ];
+  const paginationProps = {
+    showSizeChanger: true,
+    showQuickJumper: true,
+    showTotal: (total: number) => {
+      return <span> 共 {total}条 </span>;
+    },
+    ...pagination,
+  };
+  return (
+    <PageContainer>
+      <div>
+        <Card>
+          <Form form={form} layout="inline" onFinish={onFinish}>
+            <Form.Item name="title" label="文章标题">
+              <Input placeholder="请输入文章标题" />
+            </Form.Item>
+            <Form.Item style={{ marginBottom: '10px' }}>
+              <Space>
+                <Button type="primary" htmlType="submit">
+                  <SearchOutlined />
+                  查询
+                </Button>
+                <Button htmlType="button" onClick={onReset}>
+                  <ReloadOutlined />
+                  重置
+                </Button>
+              </Space>
+            </Form.Item>
+          </Form>
+          <Button htmlType="button" type="primary" style={{ margin: '20px 0' }} onClick={onAdd}>
+            <PlusCircleOutlined />
+            新增文章
+          </Button>
+          <Table
+            columns={columns}
+            dataSource={dataList}
+            rowKey={(record) => record.record_id}
+            pagination={paginationProps}
+            loading={loading}
+            onChange={tableChange}
+          />
+          {visible && <Edit visible={visible} editCallback={handleEdit} params={editData} />}
+        </Card>
+      </div>
+    </PageContainer>
+  );
+};
+export default ArticleManagement;

+ 174 - 0
src/pages/cms/ColumnList/edit.tsx

@@ -0,0 +1,174 @@
+import { Col, Form, Input, message, Modal, Row, Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { createColumn, editColumn, queryColumn } from '@/services/cms/ColumnList';
+
+const { TextArea } = Input;
+
+interface editPros {
+  visible: boolean;
+  editCallback: () => void;
+  params: any;
+}
+
+const Edit: React.FC<editPros> = (props) => {
+  const { visible, editCallback, params } = props;
+  const [form] = Form.useForm();
+  const [dataList, setDataList]: any = useState([]);
+
+  // 获取列表数据
+  const getList = () => {
+    queryColumn({ q: 'list' }).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+      }
+    });
+  };
+
+  useEffect(() => {
+    getList();
+  }, []);
+
+  const onOk = () => {
+    form.validateFields().then((values) => {
+      if (values) {
+        const data = { ...values };
+        if (params) {
+          data.record_id = params.record_id;
+          editColumn(data)
+            .then((res) => {
+              if (res.code === 0) {
+                message.success('编辑成功');
+                editCallback();
+              } else {
+                message.error(res?.message);
+                editCallback();
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+              editCallback();
+            });
+        } else {
+          createColumn(data)
+            .then((res) => {
+              if (res.code === 0) {
+                message.success('保存成功');
+                editCallback();
+              } else {
+                message.error(res?.message);
+                editCallback();
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+              editCallback();
+            });
+        }
+      }
+    });
+  };
+
+  /**
+   * 取消
+   */
+  const onCancel = () => {
+    editCallback();
+  };
+
+  const formItemLayout = {
+    labelCol: {
+      span: 6,
+    },
+    wrapperCol: {
+      span: 16,
+    },
+  };
+
+  return (
+    <Modal
+      title={`${params ? '编辑' : '新增'}`}
+      open={visible}
+      onOk={onOk}
+      onCancel={onCancel}
+      width={800}
+    >
+      <Form form={form}>
+        <Row>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="name"
+              label="栏目名称"
+              rules={[{ required: true, message: '请输入栏目名称' }]}
+              initialValue={params?.name}
+            >
+              <Input placeholder="请输入栏目名称" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="code"
+              label="编号"
+              rules={[{ required: true, message: '请输入编号' }]}
+              initialValue={params?.code}
+            >
+              <Input placeholder="请输入编号" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="status"
+              label="状态"
+              rules={[{ required: true, message: '请选择状态' }]}
+              initialValue={params?.status}
+            >
+              <Select placeholder="请选择状态">
+                <Select.Option value={1} key={1}>
+                  启用
+                </Select.Option>
+                <Select.Option value={2} key={2}>
+                  停用
+                </Select.Option>
+              </Select>
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="parent_id"
+              label="上级栏目名称"
+              rules={[{ required: false, message: '请选择上级栏目名称' }]}
+              initialValue={params?.parent_id}
+            >
+              <Select placeholder="请选择上级栏目名称">
+                {dataList && dataList.length
+                  ? dataList.map((res: any) => {
+                      return (
+                        <Select.Option value={res.record_id} key={res.record_id}>
+                          {res.name}
+                        </Select.Option>
+                      );
+                    })
+                  : null}
+              </Select>
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout}
+              name="memo"
+              label="备注"
+              rules={[{ required: false, message: '请输入备注' }]}
+              initialValue={params?.memo}
+            >
+              <TextArea rows={4} placeholder="请输入备注" />
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    </Modal>
+  );
+};
+export default Edit;

+ 39 - 15
src/pages/cms/ColumnList/index.tsx

@@ -1,10 +1,11 @@
 import React, { useEffect, useState } from 'react';
 import { PageContainer } from '@ant-design/pro-components';
-import { Button, Card, Form, Input, Modal, Space, Table } from 'antd';
+import { Button, Card, Form, Input, message, Modal, Space, Table } from 'antd';
 import { PlusCircleOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons';
 import type { ColumnsType } from 'antd/es/table';
 import moment from 'moment';
-import { queryColumn } from '@/services/cms/ColumnList';
+import { delColumn, queryColumn } from '@/services/cms/ColumnList';
+import Edit from '@/pages/cms/ColumnList/edit';
 
 interface DataType {
   name: string;
@@ -33,13 +34,16 @@ const ColumnList: React.FC = () => {
       ...searchData,
     };
     queryColumn(params).then((res) => {
-      console.log('res----', res, visible, editData);
-      setDataList([]);
-      setPagination({ total: 0, current: 1, pageSize: 10 });
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
     });
   };
 
   useEffect(() => {
+    setLoading(true);
     getList();
   }, []);
 
@@ -68,30 +72,49 @@ const ColumnList: React.FC = () => {
   };
   //  分页切换
   const tableChange = (page: any) => {
-    // setLoading(true);
-    const param = {
+    setLoading(true);
+    const params = {
       q: 'page',
       current: page.current,
       pageSize: page.pageSize,
       ...searchData,
     };
-    console.log(param);
+    queryColumn(params).then((res) => {
+      if (res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
   };
   // 删除
   const toDel = (record: any) => {
+    console.log(record);
     Modal.confirm({
       title: '删除',
       content: `确认删除项目:[${record.name}]`,
       onOk: () => {
-        console.log('删除项目');
+        delColumn(record?.record_id)
+          .then((res) => {
+            if (res.code === 0) {
+              message.success('删除成功');
+            } else {
+              message.error('删除失败');
+            }
+          })
+          .catch((e) => {
+            message.error(e?.message);
+          });
       },
     });
+
     console.log(record);
   };
   // 编辑弹框回调
-  // const handleEdit = () => {
-  //   setVisible(false);
-  // };
+  const handleEdit = () => {
+    setVisible(false);
+    getList();
+  };
   const columns: ColumnsType<DataType> = [
     {
       title: '序号',
@@ -118,6 +141,7 @@ const ColumnList: React.FC = () => {
       title: '状态',
       dataIndex: 'status',
       key: 'status',
+      render: (v) => v && <span>{{ 1: '启用', 2: '停用' }[v]}</span>,
     },
     {
       title: '创建者',
@@ -126,8 +150,8 @@ const ColumnList: React.FC = () => {
     },
     {
       title: '创建时间',
-      dataIndex: 'create_at',
-      key: 'create_at',
+      dataIndex: 'created_at',
+      key: 'created_at',
       render: (v) => v && <span>{moment(v).format('YYYY-MM-DD HH:mm')}</span>,
     },
     {
@@ -200,7 +224,7 @@ const ColumnList: React.FC = () => {
             loading={loading}
             onChange={tableChange}
           />
-          {/*{visible && <Edit visible={visible} editCallback={handleEdit} params={editData} />}*/}
+          {visible && <Edit visible={visible} editCallback={handleEdit} params={editData} />}
         </Card>
       </div>
     </PageContainer>

+ 42 - 0
src/services/cms/ArticleManagement.ts

@@ -0,0 +1,42 @@
+import { request } from '@@/plugin-request/request';
+import { stringify } from 'qs';
+
+/**
+ * 查询文章列表
+ * @param param
+ */
+export async function queryArticle(param: object) {
+  return request(`web/v1/articles?${stringify(param)}`);
+}
+
+/**
+ * 新增栏目
+ * @param params
+ */
+export async function createArticle(params: object) {
+  return request(`/web/v1/articles`, {
+    method: 'POST',
+    data: params,
+  });
+}
+
+/**
+ * 编辑栏目
+ * @param params
+ */
+export async function editArticle(params: any) {
+  return request(`/web/v1/articles/${params.record_id}`, {
+    method: 'PUT',
+    data: params,
+  });
+}
+
+/**
+ * 删除文章
+ * @param record_id
+ */
+export async function delArticle(record_id: any) {
+  return request(`/web/v1/articles/${record_id}`, {
+    method: 'DELETE',
+  });
+}

+ 32 - 0
src/services/cms/ColumnList.ts

@@ -8,3 +8,35 @@ import { stringify } from 'qs';
 export async function queryColumn(param: object) {
   return request(`web/v1/columns?${stringify(param)}`);
 }
+
+/**
+ * 新增栏目
+ * @param params
+ */
+export async function createColumn(params: object) {
+  return request(`/web/v1/columns`, {
+    method: 'POST',
+    data: params,
+  });
+}
+
+/**
+ * 编辑栏目
+ * @param params
+ */
+export async function editColumn(params: any) {
+  return request(`/web/v1/columns/${params.record_id}`, {
+    method: 'PUT',
+    data: params,
+  });
+}
+
+/**
+ * 删除栏目
+ * @param record_id
+ */
+export async function delColumn(record_id: any) {
+  return request(`/web/v1/columns/${record_id}`, {
+    method: 'DELETE',
+  });
+}