Просмотр исходного кода

fix: 视频设备单独页,通知管理优化,三方页验证码

wzyyy 3 лет назад
Родитель
Сommit
2fa0ca78b4

BIN
public/images/media/doc1.png


BIN
public/images/media/doc2.png


BIN
public/images/media/doc3.png


BIN
public/images/media/doc4.png


+ 344 - 295
src/pages/media/Device/Save/index.tsx

@@ -1,33 +1,32 @@
 import { useCallback, useEffect, useState } from 'react';
-import { Button, Col, Form, Input, Modal, Radio, Row, Select, Tooltip } from 'antd';
-import { useIntl } from 'umi';
+import { Button, Card, Col, Form, Input, Radio, Row, Select, Tooltip, Image } from 'antd';
+import { useIntl, useLocation } from 'umi';
 import { RadioCard, UploadImage } from '@/components';
 import { PlusOutlined } from '@ant-design/icons';
 import { service } from '../index';
 import SaveProductModal from './SaveProduct';
-import type { DeviceItem } from '../typings';
 import { getButtonPermission } from '@/utils/menu';
 import { onlyMessage } from '@/utils/util';
-
-interface SaveProps {
-  visible: boolean;
-  close: () => void;
-  reload: () => void;
-  data?: DeviceItem;
-  model: 'add' | 'edit';
-}
+import { PageContainer } from '@ant-design/pro-layout';
+import styles from '../../Cascade/Save/index.less';
+import { useDomFullHeight } from '@/hooks';
 
 const DefaultAccessType = 'gb28181-2016';
 
-export default (props: SaveProps) => {
-  const { visible, close, data } = props;
+const Save = () => {
+  const location: any = useLocation();
+  const id = location?.query?.id;
   const intl = useIntl();
   const [form] = Form.useForm();
-  const [loading, setLoading] = useState(false);
+  const { minHeight } = useDomFullHeight(`.mediaDevice`);
   const [productVisible, setProductVisible] = useState(false);
   const [accessType, setAccessType] = useState(DefaultAccessType);
   const [productList, setProductList] = useState<any[]>([]);
   const [oldPassword, setOldPassword] = useState('');
+  const img1 = require('/public/images/media/doc1.png');
+  const img2 = require('/public/images/media/doc2.png');
+  const img3 = require('/public/images/media/doc3.png');
+  const img4 = require('/public/images/media/doc4.png');
 
   const getProductList = async (productParams: any) => {
     const resp = await service.queryProductList(productParams);
@@ -46,23 +45,23 @@ export default (props: SaveProps) => {
   };
 
   useEffect(() => {
-    if (visible) {
-      setOldPassword('');
-      if (props.model === 'edit') {
-        form.setFieldsValue(data);
-        const _accessType = data?.provider || DefaultAccessType;
-        setAccessType(_accessType);
-
-        queryProduct(_accessType);
-      } else {
-        form.setFieldsValue({
-          provider: DefaultAccessType,
-        });
-        queryProduct(DefaultAccessType);
-        setAccessType(DefaultAccessType);
-      }
+    if (id) {
+      service.getDetail(id).then((res) => {
+        if (res.status === 200) {
+          form.setFieldsValue(res.result);
+          const _accessType = res.result?.provider || DefaultAccessType;
+          setAccessType(_accessType);
+          queryProduct(_accessType);
+        }
+      });
+    } else {
+      form.setFieldsValue({
+        provider: DefaultAccessType,
+      });
+      queryProduct(DefaultAccessType);
+      setAccessType(DefaultAccessType);
     }
-  }, [visible]);
+  }, []);
 
   const handleSave = useCallback(async () => {
     const formData = await form.validateFields();
@@ -71,296 +70,346 @@ export default (props: SaveProps) => {
       if (formData.password === oldPassword) {
         delete extraFormData.password;
       }
-      setLoading(true);
       const resp =
         provider === DefaultAccessType
           ? await service.saveGB(extraFormData)
           : await service.saveFixed(extraFormData);
-      setLoading(false);
       if (resp.status === 200) {
-        if (props.reload) {
-          props.reload();
-        }
         form.resetFields();
-        close();
         onlyMessage('操作成功');
+        history.back();
       } else {
         onlyMessage('操作失败', 'error');
       }
     }
-  }, [props.model, oldPassword]);
+  }, [oldPassword]);
 
-  const intlFormat = (
-    id: string,
-    defaultMessage: string,
-    paramsID?: string,
-    paramsMessage?: string,
-  ) => {
-    const paramsObj: Record<string, string> = {};
-    if (paramsID) {
-      const paramsMsg = intl.formatMessage({
-        id: paramsID,
-        defaultMessage: paramsMessage,
-      });
-      paramsObj.name = paramsMsg;
-    }
+  // const intlFormat = (
+  //   id: string,
+  //   defaultMessage: string,
+  //   paramsID?: string,
+  //   paramsMessage?: string,
+  // ) => {
+  //   const paramsObj: Record<string, string> = {};
+  //   if (paramsID) {
+  //     const paramsMsg = intl.formatMessage({
+  //       id: paramsID,
+  //       defaultMessage: paramsMessage,
+  //     });
+  //     paramsObj.name = paramsMsg;
+  //   }
 
-    return intl.formatMessage(
-      {
-        id,
-        defaultMessage,
-      },
-      paramsObj,
-    );
-  };
+  //   return intl.formatMessage(
+  //     {
+  //       id,
+  //       defaultMessage,
+  //     },
+  //     paramsObj,
+  //   );
+  // };
 
   return (
     <>
-      <Modal
-        maskClosable={false}
-        visible={visible}
-        onCancel={() => {
-          form.resetFields();
-          close();
-        }}
-        width={640}
-        title={intl.formatMessage({
-          id: `pages.data.option.${props.model || 'add'}`,
-          defaultMessage: '新增',
-        })}
-        confirmLoading={loading}
-        onOk={handleSave}
-      >
-        <Form
-          form={form}
-          layout={'vertical'}
-          labelCol={{
-            style: { width: 100 },
-          }}
-        >
-          <Row>
-            <Col span={24}>
-              <Form.Item
-                name={'provider'}
-                label={'接入方式'}
-                required
-                rules={[{ required: true, message: '请选择接入方式' }]}
-              >
-                <RadioCard
-                  model={'singular'}
-                  itemStyle={{ width: '50%' }}
-                  onSelect={(key) => {
-                    setAccessType(key);
-                    queryProduct(key);
-                  }}
-                  disabled={props.model === 'edit'}
-                  options={[
-                    {
-                      label: 'GB/T28181',
-                      value: DefaultAccessType,
-                    },
-                    {
-                      label: '固定地址',
-                      value: 'fixed-media',
-                    },
-                  ]}
-                />
-              </Form.Item>
-            </Col>
-          </Row>
-          <Row>
-            <Col span={8}>
-              <Form.Item name={'photoUrl'}>
-                <UploadImage />
-              </Form.Item>
-            </Col>
-            <Col span={16}>
-              <Form.Item
-                label={'ID'}
-                name={'id'}
-                required
-                rules={[
-                  { required: true, message: '请输入ID' },
-                  {
-                    pattern: /^[a-zA-Z0-9_\-]+$/,
-                    message: intl.formatMessage({
-                      id: 'pages.form.tip.id',
-                      defaultMessage: '请输入英文或者数字或者-或者_',
-                    }),
-                  },
-                  {
-                    max: 64,
-                    message: intl.formatMessage({
-                      id: 'pages.form.tip.max64',
-                      defaultMessage: '最多输入64个字符',
-                    }),
-                  },
-                ]}
+      <PageContainer>
+        <Card className="mediaDevice" style={{ minHeight }}>
+          <Row gutter={24}>
+            <Col span={12}>
+              <Form
+                form={form}
+                layout={'vertical'}
+                onFinish={() => {
+                  handleSave();
+                }}
+                labelCol={{
+                  style: { width: 100 },
+                }}
               >
-                <Input placeholder={'请输入ID'} disabled={props.model === 'edit'} />
-              </Form.Item>
-              <Form.Item
-                label={'设备名称'}
-                name={'name'}
-                required
-                rules={[
-                  { required: true, message: '请输入名称' },
-                  { max: 64, message: '最多可输入64个字符' },
-                ]}
-              >
-                <Input placeholder={'请输入设备名称'} />
-              </Form.Item>
-            </Col>
-          </Row>
-          <Row>
-            <Col span={24}>
-              <Form.Item
-                label={'所属产品'}
-                required
-                rules={[{ required: true, message: '请选择所属产品' }]}
-              >
-                <Form.Item name={'productId'} noStyle>
-                  <Select
-                    fieldNames={{
-                      label: 'name',
-                      value: 'id',
-                    }}
-                    disabled={props.model === 'edit'}
-                    options={productList}
-                    placeholder={'请选择所属产品'}
-                    style={{ width: props.model === 'edit' ? '100%' : 'calc(100% - 36px)' }}
-                    onSelect={(_: any, node: any) => {
-                      const pwd = node.configuration ? node.configuration.access_pwd : '';
-                      form.setFieldsValue({
-                        password: pwd,
-                      });
-                      setOldPassword(pwd);
-                    }}
-                  />
-                </Form.Item>
-                {props.model !== 'edit' && (
-                  <Form.Item noStyle>
-                    {getButtonPermission('device/Product', 'add') ? (
-                      <Tooltip title={'暂无权限,请联系管理员'}>
-                        <Button type={'link'} style={{ padding: '4px 10px' }} disabled>
-                          <PlusOutlined />
-                        </Button>
-                      </Tooltip>
-                    ) : (
-                      <Button
-                        type={'link'}
-                        style={{ padding: '4px 10px' }}
-                        onClick={() => {
-                          setProductVisible(true);
+                <Row>
+                  <Col span={24}>
+                    <Form.Item
+                      name={'provider'}
+                      label={'接入方式'}
+                      required
+                      rules={[{ required: true, message: '请选择接入方式' }]}
+                    >
+                      <RadioCard
+                        model={'singular'}
+                        itemStyle={{ width: '50%' }}
+                        onSelect={(key) => {
+                          setAccessType(key);
+                          queryProduct(key);
                         }}
+                        disabled={id}
+                        options={[
+                          {
+                            label: 'GB/T28181',
+                            value: DefaultAccessType,
+                          },
+                          {
+                            label: '固定地址',
+                            value: 'fixed-media',
+                          },
+                        ]}
+                      />
+                    </Form.Item>
+                  </Col>
+                </Row>
+                <Row>
+                  <Col span={8}>
+                    <Form.Item name={'photoUrl'}>
+                      <UploadImage />
+                    </Form.Item>
+                  </Col>
+                  <Col span={16}>
+                    <Form.Item
+                      label={'ID'}
+                      name={'id'}
+                      required
+                      rules={[
+                        { required: true, message: '请输入ID' },
+                        {
+                          pattern: /^[a-zA-Z0-9_\-]+$/,
+                          message: intl.formatMessage({
+                            id: 'pages.form.tip.id',
+                            defaultMessage: '请输入英文或者数字或者-或者_',
+                          }),
+                        },
+                        {
+                          max: 64,
+                          message: intl.formatMessage({
+                            id: 'pages.form.tip.max64',
+                            defaultMessage: '最多输入64个字符',
+                          }),
+                        },
+                      ]}
+                    >
+                      <Input placeholder={'请输入ID'} disabled={!!id} />
+                    </Form.Item>
+                    <Form.Item
+                      label={'设备名称'}
+                      name={'name'}
+                      required
+                      rules={[
+                        { required: true, message: '请输入名称' },
+                        { max: 64, message: '最多可输入64个字符' },
+                      ]}
+                    >
+                      <Input placeholder={'请输入设备名称'} />
+                    </Form.Item>
+                  </Col>
+                </Row>
+                <Row>
+                  <Col span={24}>
+                    <Form.Item
+                      label={'所属产品'}
+                      required
+                      rules={[{ required: true, message: '请选择所属产品' }]}
+                    >
+                      <Form.Item name={'productId'} noStyle>
+                        <Select
+                          fieldNames={{
+                            label: 'name',
+                            value: 'id',
+                          }}
+                          disabled={!!id}
+                          options={productList}
+                          placeholder={'请选择所属产品'}
+                          style={{ width: id ? '100%' : 'calc(100% - 36px)' }}
+                          onSelect={(_: any, node: any) => {
+                            const pwd = node.configuration ? node.configuration.access_pwd : '';
+                            form.setFieldsValue({
+                              password: pwd,
+                            });
+                            setOldPassword(pwd);
+                          }}
+                        />
+                      </Form.Item>
+                      {!id && (
+                        <Form.Item noStyle>
+                          {getButtonPermission('device/Product', 'add') ? (
+                            <Tooltip title={'暂无权限,请联系管理员'}>
+                              <Button type={'link'} style={{ padding: '4px 10px' }} disabled>
+                                <PlusOutlined />
+                              </Button>
+                            </Tooltip>
+                          ) : (
+                            <Button
+                              type={'link'}
+                              style={{ padding: '4px 10px' }}
+                              onClick={() => {
+                                setProductVisible(true);
+                              }}
+                            >
+                              <PlusOutlined />
+                            </Button>
+                          )}
+                        </Form.Item>
+                      )}
+                    </Form.Item>
+                  </Col>
+                  {accessType === DefaultAccessType && (
+                    <Col span={24}>
+                      <Form.Item
+                        label={'接入密码'}
+                        name={'password'}
+                        required
+                        rules={[
+                          { required: true, message: '请输入接入密码' },
+                          { max: 64, message: '最大可输入64位' },
+                        ]}
                       >
-                        <PlusOutlined />
-                      </Button>
-                    )}
-                  </Form.Item>
-                )}
-              </Form.Item>
-            </Col>
-            {accessType === DefaultAccessType && (
-              <Col span={24}>
-                <Form.Item
-                  label={'接入密码'}
-                  name={'password'}
-                  required
-                  rules={[
-                    { required: true, message: '请输入接入密码' },
-                    { max: 64, message: '最大可输入64位' },
-                  ]}
-                >
-                  <Input.Password placeholder={'请输入接入密码'} />
+                        <Input.Password placeholder={'请输入接入密码'} />
+                      </Form.Item>
+                    </Col>
+                  )}
+                  {id && (
+                    <>
+                      <Col span={24}>
+                        <Form.Item
+                          label={'流传输模式'}
+                          name={'streamMode'}
+                          required
+                          rules={[{ required: true, message: '请选择流传输模式' }]}
+                        >
+                          <Radio.Group
+                            optionType="button"
+                            buttonStyle="solid"
+                            options={[
+                              { label: 'UDP', value: 'UDP' },
+                              { label: 'TCP', value: 'TCP_PASSIVE' },
+                            ]}
+                          />
+                        </Form.Item>
+                      </Col>
+                      <Col span={24}>
+                        <Form.Item
+                          label={'设备厂商'}
+                          name={'manufacturer'}
+                          rules={[{ max: 64, message: '最多可输入64个字符' }]}
+                        >
+                          <Input placeholder={'请输入设备厂商'} />
+                        </Form.Item>
+                      </Col>
+                      <Col span={24}>
+                        <Form.Item
+                          label={'设备型号'}
+                          name={'model'}
+                          rules={[{ max: 64, message: '最多可输入64个字符' }]}
+                        >
+                          <Input placeholder={'请输入设备型号'} />
+                        </Form.Item>
+                      </Col>
+                      <Col span={24}>
+                        <Form.Item
+                          label={'固件版本'}
+                          name={'firmware'}
+                          rules={[{ max: 64, message: '最多可输入64个字符' }]}
+                        >
+                          <Input placeholder={'请输入固件版本'} />
+                        </Form.Item>
+                      </Col>
+                    </>
+                  )}
+                  <Col span={24}>
+                    <Form.Item label={'说明'} name={'description'}>
+                      <Input.TextArea
+                        // placeholder={intlFormat(
+                        //   'pages.form.tip.input.props',
+                        //   '请输入',
+                        //   'pages.table.describe',
+                        //   '说明',
+                        // )}
+                        placeholder="请输入说明"
+                        rows={4}
+                        style={{ width: '100%' }}
+                        maxLength={200}
+                        showCount={true}
+                      />
+                    </Form.Item>
+                  </Col>
+                </Row>
+                <Form.Item name={'id'} hidden>
+                  <Input />
                 </Form.Item>
-              </Col>
-            )}
-            {props.model === 'edit' && (
-              <>
-                <Col span={24}>
-                  <Form.Item
-                    label={'流传输模式'}
-                    name={'streamMode'}
-                    required
-                    rules={[{ required: true, message: '请选择流传输模式' }]}
-                  >
-                    <Radio.Group
-                      optionType="button"
-                      buttonStyle="solid"
-                      options={[
-                        { label: 'UDP', value: 'UDP' },
-                        { label: 'TCP', value: 'TCP_PASSIVE' },
-                      ]}
-                    />
-                  </Form.Item>
-                </Col>
                 <Col span={24}>
-                  <Form.Item
-                    label={'设备厂商'}
-                    name={'manufacturer'}
-                    rules={[{ max: 64, message: '最多可输入64个字符' }]}
-                  >
-                    <Input placeholder={'请输入设备厂商'} />
+                  <Form.Item>
+                    <Button type="primary" htmlType="submit">
+                      保存
+                    </Button>
                   </Form.Item>
                 </Col>
-                <Col span={24}>
-                  <Form.Item
-                    label={'设备型号'}
-                    name={'model'}
-                    rules={[{ max: 64, message: '最多可输入64个字符' }]}
-                  >
-                    <Input placeholder={'请输入设备型号'} />
-                  </Form.Item>
-                </Col>
-                <Col span={24}>
-                  <Form.Item
-                    label={'固件版本'}
-                    name={'firmware'}
-                    rules={[{ max: 64, message: '最多可输入64个字符' }]}
-                  >
-                    <Input placeholder={'请输入固件版本'} />
-                  </Form.Item>
-                </Col>
-              </>
-            )}
-            <Col span={24}>
-              <Form.Item label={'说明'} name={'description'}>
-                <Input.TextArea
-                  placeholder={intlFormat(
-                    'pages.form.tip.input.props',
-                    '请输入',
-                    'pages.table.describe',
-                    '说明',
-                  )}
-                  rows={4}
-                  style={{ width: '100%' }}
-                  maxLength={200}
-                  showCount={true}
-                />
-              </Form.Item>
+              </Form>
+            </Col>
+            <Col span={12}>
+              <div className={styles.doc} style={{ height: 800 }}>
+                <h1>1、概述</h1>
+                <div>
+                  视频设备通过GB/T28181接入平台整体分为2部分,包括平台端配置和设备端配置,不同的设备端配置的路径或页面存在差异,但配置项基本大同小异。
+                </div>
+                <h1>2、配置说明</h1>
+                <h1>平台端配置</h1>
+                <h2>1、ID</h2>
+                <div>设备唯一标识,若不填写,系统将自动生成唯一标识</div>
+                <h2>2、所属产品</h2>
+                <div>
+                  只能选择接入方式为GB/T28281的产品,若当前无对应产品,可点击右侧快速添加按钮,填写产品名称和选择GB/T28181类型的网关完成产品创建
+                </div>
+                <h2>3、接入密码</h2>
+                <div>
+                  配置接入密码,设备端配置的密码需与该密码一致。该字段可在产品-设备接入页面进行统一配置,配置后所有设备将继承产品配置。设备单独修改后将脱离继承关系。
+                </div>
+                <h1>设备端配置</h1>
+                <div>
+                  各个厂家、不同设备型号的设备端配置页面布局存在差异,但配置项基本大同小异,此处以大华摄像头为例作为接入配置示例
+                </div>
+                <div className={styles.image}>
+                  <Image width="100%" src={img1} />
+                </div>
+                <h2>1、SIP服务器编号/SIP域</h2>
+                <div>
+                  SIP服务器编号填入该设备所属产品-接入方式页面“连接信息”的SIP。
+                  SIP域通常为SIP服务器编号的前10位。
+                </div>
+                <div className={styles.image}>
+                  <Image width="100%" src={img2} />
+                </div>
+                <h2>2、SIP服务器IP/端口</h2>
+                <div>SIP服务器IP/端口填入该设备所属产品-接入方式页面中“连接信息”的IP/端口。</div>
+                <div className={styles.image}>
+                  <Image width="100%" src={img3} />
+                </div>
+                <h2>3、设备编号</h2>
+                <div>
+                  设备编号为设备唯一性标识,物联网平台的设备接入没有校验该字段,输入任意数字均不影响设备接入平台。
+                </div>
+                <h2>4、注册密码</h2>
+                <div>填入该设备所属产品-接入方式页面中“GB28281配置”处的接入密码</div>
+                <div className={styles.image}>
+                  <Image width="100%" src={img4} />
+                </div>
+                <h2>5、其他字段</h2>
+                <div>不影响设备接入平台,可保持设备初始化值。</div>
+              </div>
             </Col>
           </Row>
-          <Form.Item name={'id'} hidden>
-            <Input />
-          </Form.Item>
-        </Form>
-      </Modal>
-      <SaveProductModal
-        visible={productVisible}
-        type={accessType}
-        close={() => {
-          setProductVisible(false);
-        }}
-        reload={(productId: string, name: string) => {
-          form.setFieldsValue({ productId });
-          productList.push({
-            id: productId,
-            name,
-          });
-          setProductList([...productList]);
-        }}
-      />
+        </Card>
+        <SaveProductModal
+          visible={productVisible}
+          type={accessType}
+          close={() => {
+            setProductVisible(false);
+          }}
+          reload={(productId: string, name: string) => {
+            form.setFieldsValue({ productId });
+            productList.push({
+              id: productId,
+              name,
+            });
+            setProductList([...productList]);
+          }}
+        />
+      </PageContainer>
     </>
   );
 };
+export default Save;

+ 9 - 21
src/pages/media/Device/index.tsx

@@ -1,6 +1,6 @@
 // 视频设备列表
 import { PageContainer } from '@ant-design/pro-layout';
-import { useEffect, useRef, useState } from 'react';
+import { useRef, useState } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { Button, Tooltip } from 'antd';
 import {
@@ -14,13 +14,11 @@ import {
 import type { DeviceItem } from '@/pages/media/Device/typings';
 import { useHistory, useIntl } from 'umi';
 import { BadgeStatus, PermissionButton, ProTableCard } from '@/components';
-import useLocation from '@/hooks/route/useLocation';
 import { StatusColorEnum } from '@/components/BadgeStatus';
 import SearchComponent from '@/components/SearchComponent';
 import MediaDevice from '@/components/ProTableCard/CardItems/mediaDevice';
 import { getMenuPathByCode, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import Service from './service';
-import Save from './Save';
 import { onlyMessage } from '@/utils/util';
 
 export const service = new Service('media/device');
@@ -38,19 +36,9 @@ export const ProviderValue = {
 const Device = () => {
   const intl = useIntl();
   const actionRef = useRef<ActionType>();
-  const [visible, setVisible] = useState<boolean>(false);
-  const [current, setCurrent] = useState<DeviceItem>();
   const [queryParam, setQueryParam] = useState({});
   const history = useHistory<Record<string, string>>();
   const { permission } = PermissionButton.usePermission('media/Device');
-  const location = useLocation();
-
-  useEffect(() => {
-    const { state } = location;
-    if (state && state.save) {
-      setVisible(true);
-    }
-  }, [location]);
 
   /**
    * table 查询参数
@@ -210,8 +198,8 @@ const Device = () => {
           style={{ padding: 0 }}
           type={'link'}
           onClick={() => {
-            setCurrent(record);
-            setVisible(true);
+            const url = getMenuPathByCode(MENUS_CODE[`media/Device/Save`]);
+            history.push(url + `?id=${record.id}`);
           }}
         >
           <EditOutlined />
@@ -330,8 +318,8 @@ const Device = () => {
         headerTitle={[
           <PermissionButton
             onClick={() => {
-              setCurrent(undefined);
-              setVisible(true);
+              const url = getMenuPathByCode(MENUS_CODE[`media/Device/Save`]);
+              history.push(url);
             }}
             key="button"
             icon={<PlusOutlined />}
@@ -364,8 +352,8 @@ const Device = () => {
                 key="edit"
                 isPermission={permission.update}
                 onClick={() => {
-                  setCurrent(record);
-                  setVisible(true);
+                  const url = getMenuPathByCode(MENUS_CODE[`media/Device/Save`]);
+                  history.push(url + `?id=${record.id}`);
                 }}
                 type={'link'}
                 style={{ padding: 0 }}
@@ -445,7 +433,7 @@ const Device = () => {
           />
         )}
       />
-      <Save
+      {/* <Save
         model={!current ? 'add' : 'edit'}
         data={current}
         close={() => {
@@ -455,7 +443,7 @@ const Device = () => {
           actionRef.current?.reload();
         }}
         visible={visible}
-      />
+      /> */}
     </PageContainer>
   );
 };

+ 2 - 0
src/pages/media/Device/service.ts

@@ -28,6 +28,8 @@ class Service extends BaseService<DeviceItem> {
   // 查询设备接入配置
   queryProvider = (data?: any) =>
     request(`/${SystemConst.API_BASE}/gateway/device/detail/_query`, { method: 'POST', data });
+  //视频设备详情
+  getDetail = (id: string) => request(`${this.uri}/${id}`, { method: 'GET' });
 }
 
 export default Service;

+ 37 - 14
src/pages/notice/Config/SyncUser/index.tsx

@@ -1,4 +1,16 @@
-import { Badge, Button, Col, Input, Modal, Popconfirm, Row, Spin, Tooltip, Tree } from 'antd';
+import {
+  Badge,
+  Button,
+  Col,
+  Empty,
+  Input,
+  Modal,
+  Popconfirm,
+  Row,
+  Spin,
+  Tooltip,
+  Tree,
+} from 'antd';
 import { observer } from '@formily/react';
 import { service, state } from '..';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
@@ -143,6 +155,11 @@ const SyncUser = observer(() => {
       title="同步用户"
       bodyStyle={{ height: '600px', overflowY: 'auto' }}
       visible={true}
+      footer={[
+        <Button key="back" onClick={() => (state.syncUser = false)}>
+          关闭
+        </Button>,
+      ]}
       onCancel={() => (state.syncUser = false)}
       width="80vw"
     >
@@ -155,21 +172,25 @@ const SyncUser = observer(() => {
                 style={{ marginBottom: 8 }}
                 placeholder="请输入部门名称"
               />
-              <Tree
-                fieldNames={{
-                  title: 'name',
-                  key: 'id',
-                }}
-                selectedKeys={[dept || '']}
-                onSelect={(key) => {
-                  setDept(key[0] as string);
-                }}
-                treeData={treeData}
-              />
+              {treeData && treeData.length !== 0 ? (
+                <Tree
+                  fieldNames={{
+                    title: 'name',
+                    key: 'id',
+                  }}
+                  selectedKeys={[dept || '']}
+                  onSelect={(key) => {
+                    setDept(key[0] as string);
+                  }}
+                  treeData={treeData}
+                />
+              ) : (
+                <Empty />
+              )}
             </div>
           </Col>
           <Col span={20}>
-            {dept && (
+            {dept ? (
               <ProTable
                 rowKey="thirdPartyUserId"
                 actionRef={actionRef}
@@ -227,10 +248,12 @@ const SyncUser = observer(() => {
                       }
                     }}
                   >
-                    <Button type="primary">保存</Button>
+                    <Button type="primary">自动绑定</Button>
                   </Popconfirm>
                 }
               />
+            ) : (
+              <Empty />
             )}
           </Col>
         </Row>

+ 65 - 30
src/pages/notice/Config/index.tsx

@@ -8,10 +8,11 @@ import {
   EditOutlined,
   EyeOutlined,
   PlusOutlined,
+  SmallDashOutlined,
   TeamOutlined,
   UnorderedListOutlined,
 } from '@ant-design/icons';
-import { Space, Upload } from 'antd';
+import { Dropdown, Menu, Space, Upload } from 'antd';
 import { useRef, useState } from 'react';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { downloadObject, onlyMessage } from '@/utils/util';
@@ -351,20 +352,20 @@ const Config = observer(() => {
               </div>
             }
             actions={[
-              (record.provider === 'dingTalkMessage' || record.provider === 'corpMessage') && (
-                <PermissionButton
-                  key="syncUser"
-                  isPermission={true}
-                  type="link"
-                  onClick={() => {
-                    state.syncUser = true;
-                    state.current = record;
-                  }}
-                >
-                  <TeamOutlined />
-                  同步用户
-                </PermissionButton>
-              ),
+              // (record.provider === 'dingTalkMessage' || record.provider === 'corpMessage') && (
+              //   <PermissionButton
+              //     key="syncUser"
+              //     isPermission={true}
+              //     type="link"
+              //     onClick={() => {
+              //       state.syncUser = true;
+              //       state.current = record;
+              //     }}
+              //   >
+              //     <TeamOutlined />
+              //     同步用户
+              //   </PermissionButton>
+              // ),
               <PermissionButton
                 isPermission={configPermission.update}
                 type={'link'}
@@ -391,20 +392,6 @@ const Config = observer(() => {
               </PermissionButton>,
               <PermissionButton
                 type={'link'}
-                key="export"
-                isPermission={configPermission.export}
-                onClick={() =>
-                  downloadObject(
-                    record,
-                    `通知配置${record.name}-${moment(new Date()).format('YYYY/MM/DD HH:mm:ss')}`,
-                  )
-                }
-              >
-                <ArrowDownOutlined />
-                导出
-              </PermissionButton>,
-              <PermissionButton
-                type={'link'}
                 key="log"
                 isPermission={configPermission.log}
                 onClick={() => {
@@ -415,7 +402,55 @@ const Config = observer(() => {
                 <UnorderedListOutlined />
                 通知记录
               </PermissionButton>,
-
+              <Dropdown
+                key={'more'}
+                placement="bottom"
+                overlay={
+                  <Menu>
+                    <Menu.Item key="export">
+                      <PermissionButton
+                        type={'link'}
+                        key="export"
+                        isPermission={configPermission.export}
+                        onClick={() =>
+                          downloadObject(
+                            record,
+                            `通知配置${record.name}-${moment(new Date()).format(
+                              'YYYY/MM/DD HH:mm:ss',
+                            )}`,
+                          )
+                        }
+                      >
+                        <ArrowDownOutlined />
+                        导出
+                      </PermissionButton>
+                      ,
+                    </Menu.Item>
+                    {(record.provider === 'dingTalkMessage' ||
+                      record.provider === 'corpMessage') && (
+                      <Menu.Item key="syncUser">
+                        <PermissionButton
+                          key="syncUser"
+                          isPermission={true}
+                          type="link"
+                          onClick={() => {
+                            state.syncUser = true;
+                            state.current = record;
+                          }}
+                        >
+                          <TeamOutlined />
+                          同步用户
+                        </PermissionButton>
+                      </Menu.Item>
+                    )}
+                  </Menu>
+                }
+              >
+                <PermissionButton type={'link'} isPermission={true} key="other">
+                  <SmallDashOutlined />
+                  其他
+                </PermissionButton>
+              </Dropdown>,
               <PermissionButton
                 key="delete"
                 isPermission={configPermission.delete}

+ 9 - 8
src/pages/notice/Template/Detail/index.tsx

@@ -783,8 +783,9 @@ const Detail = observer(() => {
                       minColumns: 2,
                     },
                     properties: {
-                      userIdList: {
-                        title: '收信人',
+                      departmentIdList: {
+                        title: '收信部门',
+                        required: true,
                         'x-component': 'Select',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
@@ -792,17 +793,17 @@ const Detail = observer(() => {
                           gridSpan: 1,
                         },
                         'x-component-props': {
-                          placeholder: '请选择收信',
+                          placeholder: '请选择收信部门',
                         },
                         // 'x-reactions': {
                         //   dependencies: ['configId'],
                         //   fulfill: {
-                        //     run: '{{useAsyncDataSource(getDingTalkUser($deps[0]))}}',
+                        //     run: '{{useAsyncDataSource(getDingTalkDept($deps[0]))}}',
                         //   },
                         // },
                       },
-                      departmentIdList: {
-                        title: '收信部门',
+                      userIdList: {
+                        title: '收信',
                         'x-component': 'Select',
                         'x-decorator': 'FormItem',
                         'x-decorator-props': {
@@ -810,12 +811,12 @@ const Detail = observer(() => {
                           gridSpan: 1,
                         },
                         'x-component-props': {
-                          placeholder: '请选择收信部门',
+                          placeholder: '请选择收信',
                         },
                         // 'x-reactions': {
                         //   dependencies: ['configId'],
                         //   fulfill: {
-                        //     run: '{{useAsyncDataSource(getDingTalkDept($deps[0]))}}',
+                        //     run: '{{useAsyncDataSource(getDingTalkUser($deps[0]))}}',
                         //   },
                         // },
                       },

+ 1 - 1
src/pages/user/Login/index.tsx

@@ -60,7 +60,7 @@ const Login: React.FC = () => {
       .pipe(
         filter((r) => r.enabled),
         mergeMap(Service.getCaptcha),
-        catchError(() => message.error('服务端挂了!')),
+        catchError(() => message.error('系统开小差,请稍后重试')),
       )
       .subscribe(setCaptcha);
   };

+ 1 - 0
src/utils/menu/index.ts

@@ -26,6 +26,7 @@ const extraRouteObj = {
   },
   'media/Device': {
     children: [
+      { code: 'Save', name: '详情' },
       { code: 'Channel', name: '通道列表' },
       { code: 'Playback', name: '回放' },
     ],

+ 1 - 0
src/utils/menu/router.ts

@@ -53,6 +53,7 @@ export enum MENUS_CODE {
   'media/Cascade/Channel' = 'media/Cascade/Channel',
   'media/Config' = 'media/Config',
   'media/Device' = 'media/Device',
+  'media/Device/Save' = 'media/Device/Save',
   'media/Device/Channel' = 'media/Device/Channel',
   'media/Device/Playback' = 'media/Device/Playback',
   'media/Reveal' = 'media/Reveal',