index.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. import { useCallback, useEffect, useState } from 'react';
  2. import { Button, Card, Col, Form, Image, Input, Radio, Row, Select, Tooltip } from 'antd';
  3. import { useIntl, useLocation } from 'umi';
  4. import { RadioCard, UploadImage } from '@/components';
  5. import { PlusOutlined } from '@ant-design/icons';
  6. import { service } from '../index';
  7. import SaveProductModal from './SaveProduct';
  8. import { getButtonPermission } from '@/utils/menu';
  9. import { onlyMessage } from '@/utils/util';
  10. import { PageContainer } from '@ant-design/pro-layout';
  11. import styles from '../../Cascade/Save/index.less';
  12. import { useDomFullHeight } from '@/hooks';
  13. const DefaultAccessType = 'gb28181-2016';
  14. const Save = () => {
  15. const location: any = useLocation();
  16. const id = location?.query?.id;
  17. const intl = useIntl();
  18. const [form] = Form.useForm();
  19. const { minHeight } = useDomFullHeight(`.mediaDevice`);
  20. const [productVisible, setProductVisible] = useState(false);
  21. const [accessType, setAccessType] = useState(DefaultAccessType);
  22. const [productList, setProductList] = useState<any[]>([]);
  23. const [oldPassword, setOldPassword] = useState('');
  24. const img1 = require('/public/images/media/doc1.png');
  25. const img2 = require('/public/images/media/doc2.png');
  26. const img3 = require('/public/images/media/doc3.png');
  27. const img4 = require('/public/images/media/doc4.png');
  28. const getProductList = async (productParams: any) => {
  29. const resp = await service.queryProductList(productParams);
  30. if (resp.status === 200) {
  31. setProductList(resp.result);
  32. }
  33. };
  34. const queryProduct = async (value: string) => {
  35. getProductList({
  36. terms: [
  37. { column: 'accessProvider', value: value },
  38. { column: 'state', value: 1 },
  39. ],
  40. });
  41. };
  42. useEffect(() => {
  43. if (id) {
  44. service.getDetail(id).then((res) => {
  45. if (res.status === 200) {
  46. form.setFieldsValue(res.result);
  47. const _accessType = res.result?.provider || DefaultAccessType;
  48. setAccessType(_accessType);
  49. queryProduct(_accessType);
  50. setOldPassword(res.result.password);
  51. }
  52. });
  53. } else {
  54. form.setFieldsValue({
  55. provider: DefaultAccessType,
  56. });
  57. queryProduct(DefaultAccessType);
  58. setAccessType(DefaultAccessType);
  59. }
  60. }, []);
  61. const handleSave = useCallback(async () => {
  62. const formData = await form.validateFields();
  63. if (formData) {
  64. const { provider, ...extraFormData } = formData;
  65. // if (formData.password === oldPassword) {
  66. // delete extraFormData.password;
  67. // }
  68. const resp =
  69. provider === DefaultAccessType
  70. ? await service.saveGB(extraFormData)
  71. : await service.saveFixed(extraFormData);
  72. if (resp.status === 200) {
  73. form.resetFields();
  74. onlyMessage('操作成功');
  75. history.back();
  76. } else {
  77. onlyMessage('操作失败', 'error');
  78. }
  79. }
  80. }, [oldPassword]);
  81. // const intlFormat = (
  82. // id: string,
  83. // defaultMessage: string,
  84. // paramsID?: string,
  85. // paramsMessage?: string,
  86. // ) => {
  87. // const paramsObj: Record<string, string> = {};
  88. // if (paramsID) {
  89. // const paramsMsg = intl.formatMessage({
  90. // id: paramsID,
  91. // defaultMessage: paramsMessage,
  92. // });
  93. // paramsObj.name = paramsMsg;
  94. // }
  95. // return intl.formatMessage(
  96. // {
  97. // id,
  98. // defaultMessage,
  99. // },
  100. // paramsObj,
  101. // );
  102. // };
  103. return (
  104. <>
  105. <PageContainer>
  106. <Card className="mediaDevice" style={{ minHeight }}>
  107. <Row gutter={24}>
  108. <Col span={12}>
  109. <Form
  110. form={form}
  111. layout={'vertical'}
  112. onFinish={() => {
  113. handleSave();
  114. }}
  115. labelCol={{
  116. style: { width: 100 },
  117. }}
  118. >
  119. <Row>
  120. <Col span={24}>
  121. <Form.Item
  122. name={'provider'}
  123. label={'接入方式'}
  124. required
  125. rules={[{ required: true, message: '请选择接入方式' }]}
  126. >
  127. <RadioCard
  128. model={'singular'}
  129. itemStyle={{ width: '50%' }}
  130. onSelect={(key) => {
  131. console.log(key);
  132. setAccessType(key);
  133. queryProduct(key);
  134. }}
  135. disabled={id}
  136. options={[
  137. {
  138. label: 'GB/T28181',
  139. value: DefaultAccessType,
  140. },
  141. {
  142. label: '固定地址',
  143. value: 'fixed-media',
  144. },
  145. ]}
  146. />
  147. </Form.Item>
  148. </Col>
  149. </Row>
  150. <Row>
  151. <Col span={8}>
  152. <Form.Item name={'photoUrl'}>
  153. <UploadImage />
  154. </Form.Item>
  155. </Col>
  156. <Col span={16}>
  157. <Form.Item
  158. label={'ID'}
  159. name={'id'}
  160. required
  161. rules={[
  162. { required: true, message: '请输入ID' },
  163. {
  164. pattern: /^[a-zA-Z0-9_\-]+$/,
  165. message: intl.formatMessage({
  166. id: 'pages.form.tip.id',
  167. defaultMessage: '请输入英文或者数字或者-或者_',
  168. }),
  169. },
  170. {
  171. max: 64,
  172. message: intl.formatMessage({
  173. id: 'pages.form.tip.max64',
  174. defaultMessage: '最多输入64个字符',
  175. }),
  176. },
  177. ]}
  178. >
  179. <Input placeholder={'请输入ID'} disabled={!!id} />
  180. </Form.Item>
  181. <Form.Item
  182. label={'设备名称'}
  183. name={'name'}
  184. required
  185. rules={[
  186. { required: true, message: '请输入名称' },
  187. { max: 64, message: '最多可输入64个字符' },
  188. ]}
  189. >
  190. <Input placeholder={'请输入设备名称'} />
  191. </Form.Item>
  192. </Col>
  193. </Row>
  194. <Row>
  195. <Col span={24}>
  196. <Form.Item
  197. label={'所属产品'}
  198. required
  199. rules={[{ required: true, message: '请选择所属产品' }]}
  200. >
  201. <Form.Item name={'productId'} noStyle>
  202. <Select
  203. fieldNames={{
  204. label: 'name',
  205. value: 'id',
  206. }}
  207. disabled={!!id}
  208. options={productList}
  209. placeholder={'请选择所属产品'}
  210. style={{ width: id ? '100%' : 'calc(100% - 36px)' }}
  211. onSelect={(_: any, node: any) => {
  212. const pwd = node.configuration ? node.configuration.access_pwd : '';
  213. form.setFieldsValue({
  214. password: pwd,
  215. });
  216. }}
  217. />
  218. </Form.Item>
  219. {!id && (
  220. <Form.Item noStyle>
  221. {getButtonPermission('device/Product', 'add') ? (
  222. <Tooltip title={'暂无权限,请联系管理员'}>
  223. <Button type={'link'} style={{ padding: '4px 10px' }} disabled>
  224. <PlusOutlined />
  225. </Button>
  226. </Tooltip>
  227. ) : (
  228. <Button
  229. type={'link'}
  230. style={{ padding: '4px 10px' }}
  231. onClick={() => {
  232. setProductVisible(true);
  233. }}
  234. >
  235. <PlusOutlined />
  236. </Button>
  237. )}
  238. </Form.Item>
  239. )}
  240. </Form.Item>
  241. </Col>
  242. {accessType === DefaultAccessType && (
  243. <Col span={24}>
  244. <Form.Item
  245. label={'接入密码'}
  246. name={'password'}
  247. required
  248. rules={[
  249. { required: true, message: '请输入接入密码' },
  250. { max: 64, message: '最大可输入64位' },
  251. ]}
  252. >
  253. <Input.Password placeholder={'请输入接入密码'} />
  254. </Form.Item>
  255. </Col>
  256. )}
  257. {id && (
  258. <>
  259. <Col span={24}>
  260. <Form.Item
  261. label={'流传输模式'}
  262. name={'streamMode'}
  263. required
  264. rules={[{ required: true, message: '请选择流传输模式' }]}
  265. >
  266. <Radio.Group
  267. optionType="button"
  268. buttonStyle="solid"
  269. options={[
  270. { label: 'UDP', value: 'UDP' },
  271. { label: 'TCP被动', value: 'TCP_PASSIVE' },
  272. ]}
  273. />
  274. </Form.Item>
  275. </Col>
  276. <Col span={24}>
  277. <Form.Item
  278. label={'设备厂商'}
  279. name={'manufacturer'}
  280. rules={[{ max: 64, message: '最多可输入64个字符' }]}
  281. >
  282. <Input placeholder={'请输入设备厂商'} />
  283. </Form.Item>
  284. </Col>
  285. <Col span={24}>
  286. <Form.Item
  287. label={'设备型号'}
  288. name={'model'}
  289. rules={[{ max: 64, message: '最多可输入64个字符' }]}
  290. >
  291. <Input placeholder={'请输入设备型号'} />
  292. </Form.Item>
  293. </Col>
  294. <Col span={24}>
  295. <Form.Item
  296. label={'固件版本'}
  297. name={'firmware'}
  298. rules={[{ max: 64, message: '最多可输入64个字符' }]}
  299. >
  300. <Input placeholder={'请输入固件版本'} />
  301. </Form.Item>
  302. </Col>
  303. </>
  304. )}
  305. <Col span={24}>
  306. <Form.Item label={'说明'} name={'description'}>
  307. <Input.TextArea
  308. // placeholder={intlFormat(
  309. // 'pages.form.tip.input.props',
  310. // '请输入',
  311. // 'pages.table.describe',
  312. // '说明',
  313. // )}
  314. placeholder="请输入说明"
  315. rows={4}
  316. style={{ width: '100%' }}
  317. maxLength={200}
  318. showCount={true}
  319. />
  320. </Form.Item>
  321. </Col>
  322. </Row>
  323. <Form.Item name={'id'} hidden>
  324. <Input />
  325. </Form.Item>
  326. <Col span={24}>
  327. <Form.Item>
  328. <Button type="primary" htmlType="submit">
  329. 保存
  330. </Button>
  331. </Form.Item>
  332. </Col>
  333. </Form>
  334. </Col>
  335. <Col span={12}>
  336. {accessType === DefaultAccessType ? (
  337. <div className={styles.doc} style={{ height: 800 }}>
  338. <h1>1.概述</h1>
  339. <div>
  340. 视频设备通过GB/T28181接入平台整体分为2部分,包括平台端配置和设备端配置,不同的设备端配置的路径或页面存在差异,但配置项基本大同小异。
  341. </div>
  342. <h1>2.配置说明</h1>
  343. <h1>平台端配置</h1>
  344. <h2>1、ID</h2>
  345. <div>设备唯一标识,若不填写,系统将自动生成唯一标识</div>
  346. <h2>2、所属产品</h2>
  347. <div>
  348. 只能选择接入方式为GB/T28281的产品,若当前无对应产品,可点击右侧快速添加按钮,填写产品名称和选择GB/T28181类型的网关完成产品创建
  349. </div>
  350. <h2>3、接入密码</h2>
  351. <div>
  352. 配置接入密码,设备端配置的密码需与该密码一致。该字段可在产品-设备接入页面进行统一配置,配置后所有设备将继承产品配置。设备单独修改后将脱离继承关系。
  353. </div>
  354. <h1>设备端配置</h1>
  355. <div>
  356. 各个厂家、不同设备型号的设备端配置页面布局存在差异,但配置项基本大同小异,此处以大华摄像头为例作为接入配置示例
  357. </div>
  358. <div className={styles.image}>
  359. <Image width="100%" src={img1} />
  360. </div>
  361. <h2>1、SIP服务器编号/SIP域</h2>
  362. <div>
  363. SIP服务器编号填入该设备所属产品-接入方式页面“连接信息”的SIP。
  364. SIP域通常为SIP服务器编号的前10位。
  365. </div>
  366. <div className={styles.image}>
  367. <Image width="100%" src={img2} />
  368. </div>
  369. <h2>2、SIP服务器IP/端口</h2>
  370. <div>SIP服务器IP/端口填入该设备所属产品-接入方式页面中“连接信息”的IP/端口。</div>
  371. <div className={styles.image}>
  372. <Image width="100%" src={img3} />
  373. </div>
  374. <h2>3、设备编号</h2>
  375. <div>
  376. 设备编号为设备唯一性标识,物联网平台的设备接入没有校验该字段,输入任意数字均不影响设备接入平台。
  377. </div>
  378. <h2>4、注册密码</h2>
  379. <div>填入该设备所属产品-接入方式页面中“GB28281配置”处的接入密码</div>
  380. <div className={styles.image}>
  381. <Image width="100%" src={img4} />
  382. </div>
  383. <h2>5、其他字段</h2>
  384. <div>不影响设备接入平台,可保持设备初始化值。</div>
  385. </div>
  386. ) : (
  387. <div className={styles.doc} style={{ height: 600 }}>
  388. <h1>1.概述</h1>
  389. <div>视频设备通过RTSP、RTMP固定地址接入平台分为2步。</div>
  390. <div>1.添加视频设备</div>
  391. <div>2.添加视频下的通道地址。</div>
  392. <div>注:当前页面为新增视频设备,新增完成后点击设备的“通道”按钮,添加通道。</div>
  393. <h1>2.配置说明</h1>
  394. <h2>1、ID</h2>
  395. <div>设备唯一标识,若不填写,系统将自动生成唯一标识。</div>
  396. <h2>2、所属产品</h2>
  397. <div>
  398. 只能选择接入方式为固定地址的产品,若当前无对应产品,可点击右侧快速添加按钮,填写产品名称和选择固定地址类型的网关完成产品创建。
  399. </div>
  400. </div>
  401. )}
  402. </Col>
  403. </Row>
  404. </Card>
  405. <SaveProductModal
  406. visible={productVisible}
  407. type={accessType}
  408. close={() => {
  409. setProductVisible(false);
  410. }}
  411. reload={(productId: string, name: string) => {
  412. form.setFieldsValue({ productId });
  413. productList.push({
  414. id: productId,
  415. name,
  416. });
  417. setProductList([...productList]);
  418. }}
  419. />
  420. </PageContainer>
  421. </>
  422. );
  423. };
  424. export default Save;