index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. import { PageContainer } from '@ant-design/pro-layout';
  2. import { DeviceInstance } from '@/pages/device/Instance/typings';
  3. import SearchComponent from '@/components/SearchComponent';
  4. import { ActionType, ProColumns } from '@jetlinks/pro-table';
  5. import moment from 'moment';
  6. import { Badge, Button, Tooltip } from 'antd';
  7. import { service as categoryService } from '@/pages/device/Category';
  8. import { InstanceModel, service, statusMap } from '@/pages/device/Instance';
  9. import { useIntl } from '@@/plugin-locale/localeExports';
  10. import {
  11. ControlOutlined,
  12. DeleteOutlined,
  13. EditOutlined,
  14. EyeOutlined,
  15. ImportOutlined,
  16. PlayCircleOutlined,
  17. PlusOutlined,
  18. RedoOutlined,
  19. StopOutlined,
  20. } from '@ant-design/icons';
  21. import { PermissionButton, ProTableCard } from '@/components';
  22. import { useRef, useState } from 'react';
  23. import DeviceCard from '@/components/ProTableCard/CardItems/device';
  24. import { onlyMessage } from '@/utils/util';
  25. import Save from './Save';
  26. import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
  27. import { useHistory } from 'umi';
  28. import Import from '@/pages/device/Instance/Import';
  29. export default () => {
  30. const intl = useIntl();
  31. const actionRef = useRef<ActionType>();
  32. const [searchParams, setSearchParams] = useState<any>({});
  33. const [current, setCurrent] = useState<Partial<DeviceInstance>>({});
  34. const [visible, setVisible] = useState<boolean>(false);
  35. const history = useHistory<Record<string, string>>();
  36. const [importVisible, setImportVisible] = useState<boolean>(false);
  37. const { permission } = PermissionButton.usePermission('edge/Device');
  38. const tools = (record: DeviceInstance, type: 'card' | 'list') => [
  39. type === 'list' && (
  40. <Button
  41. type={'link'}
  42. style={{ padding: 0 }}
  43. key={'detail'}
  44. onClick={() => {
  45. InstanceModel.current = record;
  46. const url = getMenuPathByParams(MENUS_CODE['device/Instance/Detail'], record.id);
  47. history.push(url);
  48. }}
  49. >
  50. <Tooltip
  51. title={intl.formatMessage({
  52. id: 'pages.data.option.detail',
  53. defaultMessage: '查看',
  54. })}
  55. >
  56. <EyeOutlined />
  57. </Tooltip>
  58. </Button>
  59. ),
  60. <PermissionButton
  61. type={'link'}
  62. isPermission={permission.update}
  63. onClick={() => {
  64. setCurrent(record);
  65. setVisible(true);
  66. }}
  67. tooltip={{
  68. title: type === 'list' ? '编辑' : '',
  69. }}
  70. style={{ padding: 0 }}
  71. key={'edit'}
  72. >
  73. <EditOutlined />
  74. {type === 'list' ? '' : '编辑'}
  75. </PermissionButton>,
  76. <PermissionButton
  77. type={'link'}
  78. onClick={() => {
  79. onlyMessage('暂未开发', 'error');
  80. }}
  81. isPermission={permission.setting}
  82. style={{ padding: 0 }}
  83. key={'control'}
  84. >
  85. <ControlOutlined />
  86. {type === 'list' ? '' : '远程控制'}
  87. </PermissionButton>,
  88. <PermissionButton
  89. type={'link'}
  90. tooltip={{
  91. title: type !== 'list' ? '' : '重置密码',
  92. }}
  93. style={{ padding: 0 }}
  94. isPermission={permission.password}
  95. key={'reset'}
  96. popConfirm={{
  97. title: '确认重置密码?',
  98. onConfirm: () => {
  99. service.restPassword(record.id).then((resp: any) => {
  100. if (resp.status === 200) {
  101. onlyMessage(
  102. intl.formatMessage({
  103. id: 'pages.data.option.success',
  104. defaultMessage: '操作成功!',
  105. }),
  106. );
  107. actionRef.current?.reload();
  108. }
  109. });
  110. },
  111. }}
  112. >
  113. <RedoOutlined />
  114. {type === 'list' ? '' : '重置密码'}
  115. </PermissionButton>,
  116. <PermissionButton
  117. type={'link'}
  118. key={'state'}
  119. style={{ padding: 0 }}
  120. popConfirm={{
  121. title: intl.formatMessage({
  122. id: `pages.data.option.${
  123. record.state.value !== 'notActive' ? 'disabled' : 'enabled'
  124. }.tips`,
  125. defaultMessage: '确认禁用?',
  126. }),
  127. onConfirm: () => {
  128. if (record.state.value !== 'notActive') {
  129. service.undeployDevice(record.id).then((resp: any) => {
  130. if (resp.status === 200) {
  131. onlyMessage(
  132. intl.formatMessage({
  133. id: 'pages.data.option.success',
  134. defaultMessage: '操作成功!',
  135. }),
  136. );
  137. actionRef.current?.reload();
  138. }
  139. });
  140. } else {
  141. service.deployDevice(record.id).then((resp: any) => {
  142. if (resp.status === 200) {
  143. onlyMessage(
  144. intl.formatMessage({
  145. id: 'pages.data.option.success',
  146. defaultMessage: '操作成功!',
  147. }),
  148. );
  149. actionRef.current?.reload();
  150. }
  151. });
  152. }
  153. },
  154. }}
  155. isPermission={permission.action}
  156. tooltip={{
  157. title: intl.formatMessage({
  158. id: `pages.data.option.${record.state.value !== 'notActive' ? 'disabled' : 'enabled'}`,
  159. defaultMessage: record.state.value !== 'notActive' ? '禁用' : '启用',
  160. }),
  161. }}
  162. >
  163. {record.state.value !== 'notActive' ? <StopOutlined /> : <PlayCircleOutlined />}
  164. {record.state.value !== 'notActive'
  165. ? type === 'list'
  166. ? ''
  167. : '禁用'
  168. : type === 'list'
  169. ? ''
  170. : '启用'}
  171. </PermissionButton>,
  172. <PermissionButton
  173. type={'link'}
  174. key={'delete'}
  175. style={{ padding: 0 }}
  176. isPermission={permission.delete}
  177. tooltip={
  178. record.state.value !== 'notActive'
  179. ? { title: intl.formatMessage({ id: 'pages.device.instance.deleteTip' }) }
  180. : undefined
  181. }
  182. disabled={record.state.value !== 'notActive'}
  183. popConfirm={{
  184. title: intl.formatMessage({
  185. id: 'pages.data.option.remove.tips',
  186. }),
  187. disabled: record.state.value !== 'notActive',
  188. onConfirm: async () => {
  189. if (record.state.value === 'notActive') {
  190. await service.remove(record.id);
  191. onlyMessage(
  192. intl.formatMessage({
  193. id: 'pages.data.option.success',
  194. defaultMessage: '操作成功!',
  195. }),
  196. );
  197. actionRef.current?.reload();
  198. } else {
  199. onlyMessage(intl.formatMessage({ id: 'pages.device.instance.deleteTip' }), 'error');
  200. }
  201. },
  202. }}
  203. >
  204. <DeleteOutlined />
  205. </PermissionButton>,
  206. ];
  207. const columns: ProColumns<DeviceInstance>[] = [
  208. {
  209. title: 'ID',
  210. dataIndex: 'id',
  211. width: 200,
  212. ellipsis: true,
  213. fixed: 'left',
  214. },
  215. {
  216. title: intl.formatMessage({
  217. id: 'pages.table.deviceName',
  218. defaultMessage: '设备名称',
  219. }),
  220. dataIndex: 'name',
  221. ellipsis: true,
  222. width: 200,
  223. },
  224. {
  225. title: intl.formatMessage({
  226. id: 'pages.table.productName',
  227. defaultMessage: '产品名称',
  228. }),
  229. dataIndex: 'productId',
  230. width: 200,
  231. ellipsis: true,
  232. valueType: 'select',
  233. request: async () => {
  234. const res = await service.getProductList();
  235. if (res.status === 200) {
  236. return res.result.map((pItem: any) => ({ label: pItem.name, value: pItem.id }));
  237. }
  238. return [];
  239. },
  240. render: (_, row) => row.productName,
  241. filterMultiple: true,
  242. },
  243. {
  244. title: intl.formatMessage({
  245. id: 'pages.device.instance.registrationTime',
  246. defaultMessage: '注册时间',
  247. }),
  248. dataIndex: 'registryTime',
  249. width: '200px',
  250. valueType: 'dateTime',
  251. render: (_: any, row) => {
  252. return row.registryTime ? moment(row.registryTime).format('YYYY-MM-DD HH:mm:ss') : '';
  253. },
  254. sorter: true,
  255. },
  256. {
  257. title: intl.formatMessage({
  258. id: 'pages.searchTable.titleStatus',
  259. defaultMessage: '状态',
  260. }),
  261. dataIndex: 'state',
  262. width: '90px',
  263. valueType: 'select',
  264. renderText: (record) =>
  265. record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
  266. valueEnum: {
  267. notActive: {
  268. text: intl.formatMessage({
  269. id: 'pages.device.instance.status.notActive',
  270. defaultMessage: '禁用',
  271. }),
  272. status: 'notActive',
  273. },
  274. offline: {
  275. text: intl.formatMessage({
  276. id: 'pages.device.instance.status.offLine',
  277. defaultMessage: '离线',
  278. }),
  279. status: 'offline',
  280. },
  281. online: {
  282. text: intl.formatMessage({
  283. id: 'pages.device.instance.status.onLine',
  284. defaultMessage: '在线',
  285. }),
  286. status: 'online',
  287. },
  288. },
  289. filterMultiple: false,
  290. },
  291. {
  292. dataIndex: 'classifiedId',
  293. title: '产品分类',
  294. valueType: 'treeSelect',
  295. hideInTable: true,
  296. fieldProps: {
  297. fieldNames: {
  298. label: 'name',
  299. value: 'id',
  300. },
  301. },
  302. request: () =>
  303. categoryService
  304. .queryTree({
  305. paging: false,
  306. })
  307. .then((resp: any) => resp.result),
  308. },
  309. {
  310. dataIndex: 'productId$product-info',
  311. title: '接入方式',
  312. valueType: 'select',
  313. hideInTable: true,
  314. request: () =>
  315. service.queryGatewayList().then((resp: any) =>
  316. resp.result.map((item: any) => ({
  317. label: item.name,
  318. value: `accessId is ${item.id}`,
  319. })),
  320. ),
  321. },
  322. {
  323. dataIndex: 'deviceType',
  324. title: '设备类型',
  325. valueType: 'select',
  326. hideInTable: true,
  327. valueEnum: {
  328. device: {
  329. text: '直连设备',
  330. status: 'device',
  331. },
  332. childrenDevice: {
  333. text: '网关子设备',
  334. status: 'childrenDevice',
  335. },
  336. gateway: {
  337. text: '网关设备',
  338. status: 'gateway',
  339. },
  340. },
  341. },
  342. {
  343. title: intl.formatMessage({
  344. id: 'pages.table.description',
  345. defaultMessage: '说明',
  346. }),
  347. dataIndex: 'describe',
  348. width: '15%',
  349. ellipsis: true,
  350. hideInSearch: true,
  351. },
  352. {
  353. title: intl.formatMessage({
  354. id: 'pages.data.option',
  355. defaultMessage: '操作',
  356. }),
  357. valueType: 'option',
  358. width: 250,
  359. fixed: 'right',
  360. render: (text, record) => tools(record, 'list'),
  361. },
  362. ];
  363. return (
  364. <PageContainer>
  365. <SearchComponent<DeviceInstance>
  366. field={columns}
  367. target="edge-device"
  368. onSearch={(data) => {
  369. actionRef.current?.reset?.();
  370. setSearchParams(data);
  371. }}
  372. />
  373. <ProTableCard<DeviceInstance>
  374. columns={columns}
  375. scroll={{ x: 1366 }}
  376. actionRef={actionRef}
  377. params={searchParams}
  378. options={{ fullScreen: true }}
  379. columnEmptyText={''}
  380. request={(params) =>
  381. service.query({
  382. ...params,
  383. terms: [
  384. ...(params?.terms || []),
  385. {
  386. terms: [
  387. {
  388. column: 'productId$product-info',
  389. value: 'accessProvider is official-edge-gateway',
  390. },
  391. ],
  392. type: 'and',
  393. },
  394. ],
  395. sorts: [
  396. {
  397. name: 'createTime',
  398. order: 'desc',
  399. },
  400. ],
  401. })
  402. }
  403. rowKey="id"
  404. search={false}
  405. pagination={{ pageSize: 10 }}
  406. headerTitle={[
  407. <PermissionButton
  408. onClick={() => {
  409. setVisible(true);
  410. setCurrent({});
  411. }}
  412. style={{ marginRight: 12 }}
  413. isPermission={permission.add}
  414. key="button"
  415. icon={<PlusOutlined />}
  416. type="primary"
  417. >
  418. {intl.formatMessage({
  419. id: 'pages.data.option.add',
  420. defaultMessage: '新增',
  421. })}
  422. </PermissionButton>,
  423. <PermissionButton
  424. isPermission={permission.import}
  425. icon={<ImportOutlined />}
  426. onClick={() => {
  427. setImportVisible(true);
  428. }}
  429. >
  430. 导入
  431. </PermissionButton>,
  432. ]}
  433. cardRender={(record) => (
  434. <DeviceCard
  435. {...record}
  436. detail={
  437. <div
  438. style={{ padding: 8, fontSize: 24 }}
  439. onClick={() => {
  440. InstanceModel.current = record;
  441. const url = getMenuPathByParams(MENUS_CODE['device/Instance/Detail'], record.id);
  442. history.push(url);
  443. }}
  444. >
  445. <EyeOutlined />
  446. </div>
  447. }
  448. actions={tools(record, 'card')}
  449. />
  450. )}
  451. />
  452. {visible && (
  453. <Save
  454. data={current}
  455. model={!Object.keys(current).length ? 'add' : 'edit'}
  456. close={() => {
  457. setVisible(false);
  458. }}
  459. reload={() => {
  460. actionRef.current?.reload();
  461. }}
  462. />
  463. )}
  464. <Import
  465. // data={current}
  466. type={'official-edge-gateway'}
  467. close={() => {
  468. setImportVisible(false);
  469. actionRef.current?.reload();
  470. }}
  471. visible={importVisible}
  472. />
  473. </PageContainer>
  474. );
  475. };