소스 검색

fix(compiler): 新增设备在线管理功能

shylock 2 주 전
부모
커밋
e022f5f278

+ 19 - 0
config/routes.ts

@@ -201,6 +201,25 @@
     icon: 'Appstore',
     component: './OtaManagement/index',
   },
+  {
+    path: '/pushMessage',
+    name: '推送消息管理',
+    icon: 'Appstore',
+    routes: [
+      {
+        path: '/pushMessage/PushMessageManagement',
+        name: '推送消息',
+        icon: 'Appstore',
+        component: './PushMessageManagement/index',
+      },
+    ],
+  },
+  {
+    path: '/deviceOnline',
+    name: '设备在线管理',
+    icon: 'Appstore',
+    component: './DeviceOnlineManagement/index',
+  },
   // 房间列表
   {
     path: '/roomList',

+ 2 - 1
src/components/MyEditor/index.tsx

@@ -11,7 +11,7 @@ import { message } from 'antd';
  * @constructor
  */
 function MyEditor(props: any) {
-  const { data, onChange } = props;
+  const { data, onChange, readOnly } = props;
   /**
    * editor 实例
    */
@@ -102,6 +102,7 @@ function MyEditor(props: any) {
           value={html}
           onCreated={setEditor}
           onChange={handleChange}
+          readOnly={readOnly}
           mode="default"
           style={{ height: '300px', overflowY: 'hidden' }}
         />

+ 162 - 0
src/pages/DeviceOnlineManagement/index.tsx

@@ -0,0 +1,162 @@
+import React, { useEffect, useState } from 'react';
+import { Button, Card, Form, Input, Space, Table } from 'antd';
+import { PageContainer } from '@ant-design/pro-components';
+import type { ColumnsType } from 'antd/es/table';
+import { queryDeviceOnline } from '@/services/device';
+import moment from 'moment';
+import { ReloadOutlined, SearchOutlined } from '@ant-design/icons';
+
+interface DataType {
+  device_code: string;
+  created_at: string;
+  status: number;
+  record_id: string;
+}
+
+/**
+ * 设备在线管理
+ * @constructor
+ */
+const DeviceOnlineManagement: React.FC = () => {
+  const [form] = Form.useForm();
+  const [loading, setLoading] = useState(false);
+  const [searchData, setSearchData] = useState<object | null>({});
+  const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
+  const [dataList, setDataList] = useState([]);
+
+  // 获取列表内容
+  const getList = () => {
+    const params = {
+      q: 'page',
+      current: pagination.current,
+      pageSize: pagination.pageSize,
+      ...searchData,
+    };
+    queryDeviceOnline(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  useEffect(() => {
+    setLoading(true);
+    getList();
+  }, []);
+
+  useEffect(() => {
+    setLoading(true);
+    getList();
+  }, [searchData]);
+
+  // 搜索
+  const onFinish = () => {
+    form.validateFields().then((data) => {
+      setLoading(true);
+      setSearchData(data);
+    });
+  };
+
+  // 重置
+  const onReset = () => {
+    form.resetFields();
+    setLoading(true);
+    setSearchData(null);
+  };
+
+  // 分页切换
+  const tableChange = (page: any) => {
+    setLoading(true);
+    const params = {
+      q: 'page',
+      current: page.current,
+      pageSize: page.pageSize,
+      ...searchData,
+    };
+    queryDeviceOnline(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  const columns: ColumnsType<DataType> = [
+    {
+      title: '序号',
+      align: 'center',
+      key: 'index',
+      width: 250,
+      render: (_: any, row: any, index: number) => index + 1,
+    },
+    {
+      title: '设备编码',
+      dataIndex: 'device_code',
+      key: 'device_code',
+      width: 250,
+    },
+    {
+      title: '时间',
+      dataIndex: 'created_at',
+      key: 'created_at',
+      width: 250,
+      render: (time) => <span>{moment(time).format('YYYY-MM-DD HH:mm')}</span>,
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      width: 250,
+      render: (v) => (
+        <span style={{ color: v === 1 ? 'green' : 'red' }}>{{ 1: '在线', 0: '离线' }[v]}</span>
+      ),
+    },
+  ];
+
+  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} style={{ marginBottom: '20px' }}>
+            <Form.Item name="device_code" 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>
+          <Table
+            columns={columns}
+            dataSource={dataList}
+            rowKey={(record) => record.record_id}
+            pagination={paginationProps}
+            loading={loading}
+            onChange={tableChange}
+          />
+        </Card>
+      </div>
+    </PageContainer>
+  );
+};
+export default DeviceOnlineManagement;

+ 0 - 12
src/pages/MenuManagement/index.tsx

@@ -3,7 +3,6 @@ import React, { useEffect, useState } from 'react';
 import { Button, Card, Form, Input, message, Modal, Space, Table } from 'antd';
 import Icon, { PlusCircleOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons';
 import type { ColumnsType } from 'antd/es/table';
-import moment from 'moment/moment';
 import Edit from '@/pages/MenuManagement/edit';
 import IconMap from '@/components/Icon/IconMap';
 import Check from '@/pages/MenuManagement/check';
@@ -134,11 +133,6 @@ const MenuManagement: React.FC = () => {
       dataIndex: 'name',
       key: 'name',
     },
-    {
-      title: '排序值',
-      dataIndex: 'sequence',
-      key: 'sequence',
-    },
     {
       title: '状态',
       dataIndex: 'hidden',
@@ -167,12 +161,6 @@ const MenuManagement: React.FC = () => {
       dataIndex: 'router',
       key: 'router',
     },
-    {
-      title: '创建时间',
-      dataIndex: 'created_at',
-      key: 'created_at',
-      render: (v) => v && <span>{moment(v).format('YYYY-MM-DD HH:mm')}</span>,
-    },
     {
       title: '操作',
       key: 'action',

+ 136 - 0
src/pages/PushMessageManagement/edit.tsx

@@ -0,0 +1,136 @@
+import React from 'react';
+import { Col, Form, Input, message, Modal, Row, Select } from 'antd';
+import MyEditor from '@/components/MyEditor';
+import { createPushMessage, editPushMessage } from '@/services/pushMessage';
+
+const { TextArea } = Input;
+
+interface editPros {
+  visible: boolean;
+  editCallback: () => void;
+  params: any;
+}
+
+/**
+ * 推送消息 - 编辑
+ * @param props
+ * @constructor
+ */
+const Edit: React.FC<editPros> = (props) => {
+  const { params, visible, editCallback } = props;
+  const [form] = Form.useForm();
+
+  // 提交
+  const onOk = () => {
+    form.validateFields().then((values) => {
+      if (values) {
+        const data = { ...values };
+        if (params) {
+          data.record_id = params.record_id;
+          editPushMessage(data)
+            .then((res) => {
+              if (res && res.code === 0) {
+                message.success('编辑成功');
+                editCallback();
+              } else {
+                message.error('编辑失败');
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+            });
+        } else {
+          createPushMessage(data)
+            .then((res) => {
+              if (res && res.code === 0) {
+                message.success('新增成功');
+                editCallback();
+              } else {
+                message.error('新增失败');
+              }
+            })
+            .catch((e) => {
+              message.error(e?.message);
+            });
+        }
+      }
+    });
+  };
+
+  // 取消
+  const onCancel = () => {
+    editCallback();
+  };
+
+  const formItemLayout24 = {
+    labelCol: { span: 3 },
+    wrapperCol: { span: 21 },
+  };
+
+  return (
+    <Modal
+      title={`${params ? '编辑' : '新增'}`}
+      open={visible}
+      onOk={onOk}
+      onCancel={onCancel}
+      width={800}
+    >
+      <Form form={form}>
+        <Row>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout24}
+              name="title"
+              label="推送标题"
+              rules={[{ required: true, message: '请输入推送标题' }]}
+              initialValue={params?.title}
+            >
+              <Input placeholder="请输入推送标题" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout24}
+              name="content_type"
+              label="消息类型"
+              rules={[{ required: true, message: '请选择消息类型' }]}
+              initialValue={params?.content_type}
+            >
+              <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
+              {...formItemLayout24}
+              name="content"
+              label="展示内容"
+              rules={[{ required: true, message: '请输入展示内容' }]}
+              initialValue={params?.content}
+            >
+              <TextArea rows={2} placeholder="请输入展示内容" />
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout24}
+              name="body"
+              label="推送内容"
+              rules={[{ required: true, message: '请输入内容' }]}
+              initialValue={params?.body ? params?.body : ''}
+            >
+              <MyEditor data={params?.body ? params?.body : ''} />
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    </Modal>
+  );
+};
+export default Edit;

+ 221 - 0
src/pages/PushMessageManagement/index.tsx

@@ -0,0 +1,221 @@
+import React, { useEffect, useState } from 'react';
+import { PageContainer } from '@ant-design/pro-components';
+import { Button, Card, Form, Input, message, Modal, Space, Table } from 'antd';
+import { PlusCircleOutlined } from '@ant-design/icons';
+import type { ColumnsType } from 'antd/es/table';
+import { delPushMessage, queryPushMessage } from '@/services/pushMessage';
+import Edit from './edit';
+import PushModal from '@/pages/PushMessageManagement/pushModal';
+
+interface DataType {
+  title: string;
+  content: string;
+  content_type: number;
+  record_id: string;
+}
+
+const PushMessageManagement: React.FC = () => {
+  const [form] = Form.useForm();
+  const [editData, setEditData] = useState<object | null>({});
+  const [visible, setVisible] = useState(false);
+  const [searchData, setSearchData] = useState<object | null>({});
+  const [loading, setLoading] = useState(false);
+  const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
+  const [dataList, setDataList] = useState([]);
+  const [pushVisible, setPushVisible] = useState(false);
+  const [pushData, setPushData] = useState<object | null>({});
+
+  // 获取列表内容
+  const getList = () => {
+    const params = {
+      q: 'page',
+      current: pagination.current,
+      pageSize: pagination.pageSize,
+    };
+    queryPushMessage(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 onAdd = () => {
+    setEditData(null);
+    setVisible(true);
+  };
+
+  // 编辑
+  const toEdit = (data: any) => {
+    setEditData(data);
+    setVisible(true);
+  };
+
+  // 删除
+  const toDel = (record: any) => {
+    Modal.confirm({
+      title: '删除',
+      content: `确认删除${record.name}`,
+      onOk: () => {
+        delPushMessage(record.record_id).then((res) => {
+          if (res && res.code === 0) {
+            message.success('删除成功');
+            getList();
+          } else {
+            message.error('删除失败');
+          }
+        });
+      },
+    });
+  };
+
+  // 分页切换
+  const tableChange = (page: any) => {
+    setLoading(true);
+    const params = {
+      q: 'page',
+      current: page.current,
+      pageSize: page.pageSize,
+      ...searchData,
+    };
+    queryPushMessage(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  // 编辑回调
+  const handleEdit = () => {
+    setVisible(false);
+    setLoading(true);
+    getList();
+  };
+
+  // 推送消息
+  const toPush = (data: any) => {
+    setPushVisible(true);
+    setPushData(data);
+  };
+
+  // 推送回调
+  const handlePushCallback = () => {
+    setPushVisible(false);
+    setLoading(true);
+    getList();
+  };
+
+  const columns: ColumnsType<DataType> = [
+    {
+      title: '序号',
+      align: 'center',
+      key: 'index',
+      render: (_: any, row: any, index: number) => index + 1,
+    },
+    {
+      title: '推送标题',
+      dataIndex: 'title',
+      key: 'title',
+      width: 250,
+    },
+    {
+      title: '展示内容',
+      dataIndex: 'content',
+      key: 'content',
+      width: 250,
+    },
+    {
+      title: '消息类型',
+      dataIndex: 'content_type',
+      key: 'content_type',
+      width: 250,
+      render: (text) => <span>{{ 1: '系统消息', 2: '活动消息' }[text]}</span>,
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (_, record) => (
+        <Space size="middle">
+          <a
+            onClick={() => {
+              toEdit(record);
+            }}
+          >
+            编辑
+          </a>
+          <a
+            onClick={() => {
+              toPush(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="name" label="推送内容名称">
+              <Input placeholder="请输入推送内容名称" />
+            </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} />}
+          {pushVisible && (
+            <PushModal visible={pushVisible} pushCallback={handlePushCallback} params={pushData} />
+          )}
+        </Card>
+      </div>
+    </PageContainer>
+  );
+};
+export default PushMessageManagement;

+ 173 - 0
src/pages/PushMessageManagement/pushHistory.tsx

@@ -0,0 +1,173 @@
+import React, { useEffect, useState } from 'react';
+import { PageContainer } from '@ant-design/pro-components';
+import { Card, Form, Input, Space, Table } from 'antd';
+import type { ColumnsType } from 'antd/es/table';
+import { queryPushHistory } from '@/services/pushMessage';
+import moment from 'moment';
+import PushHistoryDetail from '@/pages/PushMessageManagement/pushHistoryDetail';
+
+interface DataType {
+  title: string;
+  content: string;
+  content_type: number;
+  time: string;
+  record_id: string;
+}
+
+/**
+ * 推送历史详情
+ * @constructor
+ */
+const PushHistory: React.FC = () => {
+  const [form] = Form.useForm();
+  const [detailId, setDetailId] = useState('');
+  const [visible, setVisible] = useState(false);
+  const [searchData, setSearchData] = useState<object | null>({});
+  const [loading, setLoading] = useState(false);
+  const [pagination, setPagination] = useState({ total: 0, current: 1, pageSize: 10 });
+  const [dataList, setDataList] = useState([]);
+
+  // 获取列表内容
+  const getList = () => {
+    const params = {
+      q: 'page',
+      current: pagination.current,
+      pageSize: pagination.pageSize,
+    };
+    queryPushHistory(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 toDetail = (data: any) => {
+    setDetailId(data);
+    setVisible(true);
+  };
+
+  // 分页切换
+  const tableChange = (page: any) => {
+    setLoading(true);
+    const params = {
+      q: 'page',
+      current: page.current,
+      pageSize: page.pageSize,
+      ...searchData,
+    };
+    queryPushHistory(params).then((res) => {
+      if (res && res.code === 0) {
+        setDataList(res.data.list);
+        setPagination(res.data.pagination);
+        setLoading(false);
+      }
+    });
+  };
+
+  // 编辑回调
+  const handleEdit = () => {
+    setVisible(false);
+    setLoading(true);
+    getList();
+  };
+
+  const columns: ColumnsType<DataType> = [
+    {
+      title: '序号',
+      align: 'center',
+      key: 'index',
+      render: (_: any, row: any, index: number) => index + 1,
+    },
+    {
+      title: '推送标题',
+      dataIndex: 'title',
+      key: 'title',
+      width: 250,
+    },
+    {
+      title: '展示内容',
+      dataIndex: 'content',
+      key: 'content',
+      width: 250,
+    },
+    {
+      title: '消息类型',
+      dataIndex: 'content_type',
+      key: 'content_type',
+      width: 250,
+      render: (text) => <span>{{ 1: '系统消息', 2: '活动消息' }[text]}</span>,
+    },
+    {
+      title: '推送时间',
+      dataIndex: 'time',
+      key: 'time',
+      width: 250,
+      render: (time) => <span>{moment(time).format('YYYY-MM-DD HH:mm')}</span>,
+    },
+    {
+      title: '操作',
+      key: 'action',
+      render: (_, record) => (
+        <Space size="middle">
+          <a
+            onClick={() => {
+              toDetail(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="name" label="推送内容名称">
+              <Input placeholder="请输入推送内容名称" />
+            </Form.Item>
+          </Form>
+          <Table
+            columns={columns}
+            dataSource={dataList}
+            rowKey={(record) => record.record_id}
+            pagination={paginationProps}
+            loading={loading}
+            onChange={tableChange}
+          />
+          {visible && (
+            <PushHistoryDetail visible={visible} onCallback={handleEdit} record_id={detailId} />
+          )}
+        </Card>
+      </div>
+    </PageContainer>
+  );
+};
+export default PushHistory;

+ 64 - 0
src/pages/PushMessageManagement/pushHistoryDetail.tsx

@@ -0,0 +1,64 @@
+import React, { useEffect, useState } from 'react';
+import { Descriptions, Modal } from 'antd';
+import moment from 'moment';
+import { queryPushHistoryDetail } from '@/services/pushMessage';
+
+interface userCheckPros {
+  visible: boolean;
+  record_id: string;
+  onCallback: () => void;
+}
+
+interface detailType {
+  title: string;
+  phone: string;
+  created_at: string;
+}
+
+/**
+ * 推送历史详情
+ * @param props
+ * @constructor
+ */
+const PushHistoryDetail: React.FC<userCheckPros> = (props) => {
+  const { visible, onCallback, record_id } = props;
+  const [dataDetail, setDataDetail] = useState<detailType>({
+    title: '',
+    phone: '',
+    created_at: '',
+  });
+
+  useEffect(() => {
+    queryPushHistoryDetail(record_id).then((res) => {
+      if (res.code === 0) {
+        setDataDetail(res.data);
+      }
+    });
+  }, []);
+
+  // 确定
+  const onOk = () => {
+    onCallback();
+  };
+
+  // 取消
+  const onCancel = () => {
+    onCallback();
+  };
+
+  return (
+    <Modal title="查看" open={visible} onOk={onOk} onCancel={onCancel} width={1000}>
+      <Descriptions title="历史记录" bordered column={2}>
+        <Descriptions.Item label="推送标题">{dataDetail?.title}</Descriptions.Item>
+        <Descriptions.Item label="推送类型">{dataDetail?.phone}</Descriptions.Item>
+        <Descriptions.Item label="推送时间">
+          {dataDetail?.created_at
+            ? moment(dataDetail?.created_at).format('YYYY-MM-DD HH:ss:mm')
+            : '暂无'}
+        </Descriptions.Item>
+        {/*<MyEditor data={dataDetail?.body ? dataDetail?.body : ''} readOnly={true} />*/}
+      </Descriptions>
+    </Modal>
+  );
+};
+export default PushHistoryDetail;

+ 187 - 0
src/pages/PushMessageManagement/pushModal.tsx

@@ -0,0 +1,187 @@
+import React, { useEffect, useState } from 'react';
+import { Col, Form, message, Modal, Radio, Row, Select } from 'antd';
+import { queryRole } from '@/services/role';
+import { queryUserList } from '@/services/setting';
+import { createPush } from '@/services/pushMessage';
+
+interface editPros {
+  visible: boolean;
+  pushCallback: () => void;
+  params: any;
+}
+
+/**
+ * 推送消息 - 编辑
+ * @param props
+ * @constructor
+ */
+const PushModal: React.FC<editPros> = (props) => {
+  const { params, visible, pushCallback } = props;
+  const [form] = Form.useForm();
+  const { setFieldsValue } = form;
+  const [roleList, setRoleList] = useState([]);
+  const [userList, setUserList] = useState([]);
+  const [type, setType] = useState(null);
+
+  // 获取角色列表
+  const getRoleList = () => {
+    queryRole({ q: 'list' }).then((res) => {
+      if (res && res.code === 0) {
+        setRoleList(res.data.list || []);
+      }
+    });
+  };
+
+  // 获取用户列表
+  const getUserList = () => {
+    queryUserList({ q: 'list' }).then((res) => {
+      if (res && res.code === 0) {
+        setUserList(res.data || []);
+      }
+    });
+  };
+
+  useEffect(() => {
+    getRoleList();
+    getUserList();
+  }, []);
+
+  // 提交
+  const onOk = () => {
+    form.validateFields().then((values) => {
+      if (values) {
+        const data = { ...values };
+        const res: any = {
+          type: 'single',
+        };
+        if (data.push_type === 1) {
+          res.all = true;
+        }
+
+        if (data.push_type === 2) {
+          res.role_id = data.role_id.join(',');
+        }
+
+        if (data.push_type === 3) {
+          res.user_id = data.user_id.join(',');
+        }
+
+        createPush(res)
+          .then((item) => {
+            if (item && item.code === 0) {
+              message.success('推送成功');
+              pushCallback();
+            } else {
+              message.error(item?.message || '推送失败');
+            }
+          })
+          .catch((e) => {
+            message.error(e?.message || '推送失败');
+          });
+      }
+    });
+  };
+
+  // 取消
+  const onCancel = () => {
+    pushCallback();
+  };
+
+  const formItemLayout24 = {
+    labelCol: { span: 3 },
+    wrapperCol: { span: 21 },
+  };
+
+  // 切换类型
+  const onRadioChange = (e: any) => {
+    setType(e.target.value);
+    setFieldsValue({ role_id: [], user_id: [] });
+  };
+
+  return (
+    <Modal
+      title={`${params ? '编辑' : '新增'}`}
+      open={visible}
+      onOk={onOk}
+      onCancel={onCancel}
+      width={800}
+    >
+      <Form form={form}>
+        <Row>
+          <Col span={24}>
+            <Form.Item
+              {...formItemLayout24}
+              name="push_type"
+              label="推送类型"
+              rules={[{ required: true, message: '请选择推送类型' }]}
+              initialValue={params?.push_type}
+            >
+              <Radio.Group onChange={onRadioChange}>
+                <Radio value={1}>全部推送</Radio>
+                <Radio value={2}>按角色推送</Radio>
+                <Radio value={3}>按用户推送</Radio>
+              </Radio.Group>
+            </Form.Item>
+          </Col>
+          {type === 2 && (
+            <Col span={24}>
+              <Form.Item
+                {...formItemLayout24}
+                name="role_id"
+                label="角色"
+                rules={[{ required: true, message: '请选择角色' }]}
+                initialValue={params?.role_id}
+              >
+                <Select placeholder="请选择角色" mode="multiple">
+                  {roleList &&
+                    roleList.map((item: any) => {
+                      return (
+                        <Select.Option value={item.record_id} key={item.record_id}>
+                          {item.name}
+                        </Select.Option>
+                      );
+                    })}
+                </Select>
+              </Form.Item>
+            </Col>
+          )}
+          {type === 3 && (
+            <Col span={24}>
+              <Form.Item
+                {...formItemLayout24}
+                name="user_id"
+                label="用户"
+                rules={[{ required: true, message: '请选择用户' }]}
+                initialValue={params?.user_id}
+              >
+                <Select
+                  placeholder="请选择用户"
+                  mode="multiple"
+                  showSearch
+                  optionFilterProp="user_name"
+                  filterOption={(input, option) =>
+                    (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
+                  }
+                >
+                  {userList &&
+                    userList.map((item: any) => {
+                      return (
+                        <Select.Option
+                          value={item.record_id}
+                          key={item.record_id}
+                          label={item.user_name}
+                        >
+                          {item.user_name}
+                        </Select.Option>
+                      );
+                    })}
+                </Select>
+              </Form.Item>
+            </Col>
+          )}
+        </Row>
+      </Form>
+    </Modal>
+  );
+};
+export default PushModal;

+ 1 - 1
src/pages/cms/ArticleManagement/edit.tsx

@@ -139,7 +139,7 @@ const Edit: React.FC<editPros> = (props) => {
               rules={[{ required: false, message: '请输入内容' }]}
               initialValue={params?.content ? params?.content : ''}
             >
-              <MyEditor data={params?.content ? params?.content : ''} />
+              <MyEditor data={params?.content ? params?.content : ''} readOnly={false} />
             </Form.Item>
           </Col>
         </Row>

+ 10 - 0
src/services/device.ts

@@ -0,0 +1,10 @@
+import { request } from '@@/plugin-request/request';
+import { stringify } from 'qs';
+
+/**
+ * 查询设备在线列表
+ * @param param
+ */
+export async function queryDeviceOnline(param: object) {
+  return request(`/web/v1/device_online?${stringify(param)}`);
+}

+ 68 - 0
src/services/pushMessage.ts

@@ -0,0 +1,68 @@
+import { request } from '@@/plugin-request/request';
+import { stringify } from 'qs';
+
+/**
+ * 查询推送消息列表
+ * @param param
+ */
+export async function queryPushMessage(param: object) {
+  return request(`/web/v2/push?${stringify(param)}`);
+}
+
+/**
+ * 删除推送消息
+ * @param record_id
+ */
+export async function delPushMessage(record_id: any) {
+  return request(`/web/v2/push/${record_id}`, {
+    method: 'DELETE',
+  });
+}
+/**
+ * 新增推送内容
+ * @param params
+ */
+export async function createPushMessage(params: object) {
+  return request(`/web/v2/push`, {
+    method: 'POST',
+    data: params,
+  });
+}
+
+/**
+ * 编辑推送内容
+ * @param params
+ */
+export async function editPushMessage(params: any) {
+  return request(`/web/v2/push/${params.record_id}`, {
+    method: 'PUT',
+    data: params,
+  });
+}
+
+/**
+ * 推送消息
+ * @param params
+ */
+export async function createPush(params: object) {
+  return request(`/web/v2/push/do`, {
+    method: 'POST',
+    data: params,
+  });
+}
+
+/**
+ * 查询推送历史列表
+ * @param param
+ */
+export async function queryPushHistory(param: object) {
+  return request(`/web/v2/push_his?${stringify(param)}`);
+}
+
+/**
+ * 查询推送历史详情
+ * @param record_id
+ */
+export async function queryPushHistoryDetail(record_id: string) {
+  return request(`/web/v2/push_his/${record_id}`);
+}