ソースを参照

feat: head-mast table list

touchitvoid 3 年 前
コミット
655a0ac962

+ 11 - 11
.idea/workspace.xml

@@ -2,18 +2,14 @@
 <project version="4">
   <component name="ChangeListManager">
     <list default="true" id="e134edf7-cd39-4d8a-9fa6-b64c2a732b8a" name="Default Changelist" comment="">
-      <change afterPath="$PROJECT_DIR$/src/pages/hard/components/3d-component.tsx" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/pages/head-mast/components/bindChanle/index.tsx" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/pages/head-mast/data.d.ts" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/src/pages/head-mast/hook.ts" afterDir="false" />
       <change afterPath="$PROJECT_DIR$/src/pages/head-mast/index.tsx" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" />
-      <change beforePath="$PROJECT_DIR$/config/config.js" beforeDir="false" afterPath="$PROJECT_DIR$/config/config.js" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/src/config/menuConfig.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/config/menuConfig.tsx" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/global.d.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/global.d.ts" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.tsx" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/pages/hard/index.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/hard/index.scss" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/pages/hard/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/hard/index.tsx" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/reset.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/reset.scss" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/yarn.lock" beforeDir="false" afterPath="$PROJECT_DIR$/yarn.lock" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/router/config.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/router/config.tsx" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/src/router/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/router/index.tsx" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -49,12 +45,16 @@
     <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
     <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
     <property name="WebServerToolWindowFactoryState" value="false" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/src/pages/head-mast" />
     <property name="nodejs_package_manager_path" value="yarn" />
     <property name="settings.editor.selected.configurable" value="MTHome" />
     <property name="ts.external.directory.path" value="$PROJECT_DIR$/node_modules/typescript/lib" />
     <property name="vue.rearranger.settings.migration" value="true" />
   </component>
   <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/src/pages/head-mast" />
+    </key>
     <key name="MoveFile.RECENT_KEYS">
       <recent name="$PROJECT_DIR$/src/assets/3d" />
       <recent name="$PROJECT_DIR$/src" />
@@ -80,7 +80,7 @@
       <workItem from="1624518277506" duration="7545000" />
       <workItem from="1624585683521" duration="9000" />
       <workItem from="1624956776578" duration="1834000" />
-      <workItem from="1625035060479" duration="1932000" />
+      <workItem from="1625035060479" duration="4147000" />
     </task>
     <servers />
   </component>

+ 6 - 1
src/config/menuConfig.tsx

@@ -240,11 +240,16 @@ const adminMenuConfig = [
 
         icon: '',
       },
+      {
+        title: '塔吊设备',
+        key: '/device/headMast',
+        path: '/admin/device/headMast',
+        icon: '',
+      },
       {
         title: '视频监控',
         key: '/device/videos',
         path: '/admin/device/videos',
-
         icon: '',
       },
       {

+ 65 - 0
src/pages/head-mast/components/bindChanle/index.tsx

@@ -0,0 +1,65 @@
+import { useEffect, useCallback } from 'react';
+import { Modal, Form, Select } from 'antd';
+import { useUnBindChanles } from '../../../../hooks/video';
+
+interface Iprops {
+	onOk?: (values: { channel_id: number }) => void;
+	visible?: boolean;
+	onCancle?: () => void;
+	loading?: boolean;
+}
+const bindChanleModal: React.FC<Iprops> = ({
+	onOk,
+	visible,
+	onCancle,
+	loading,
+}) => {
+	const [form] = Form.useForm();
+	const {
+		state: chanleListState,
+		request: requestChanleList,
+	} = useUnBindChanles();
+	useEffect(() => {
+		requestChanleList();
+	}, []);
+	useEffect(() => {
+		form && form.resetFields();
+	}, [visible]);
+	const handleOK = useCallback(() => {
+		form
+			.validateFields()
+			.then((data: any) => {
+				onOk && onOk(data);
+			})
+			.catch((error) => {});
+	}, [onOk, form]);
+	return (
+		<Modal
+			title='绑定摄像头'
+			visible={visible}
+			onOk={handleOK}
+			okButtonProps={{ loading }}
+			onCancel={onCancle}>
+			<Form form={form}>
+				<Form.Item
+					name='channel_id'
+					required
+					rules={[{ required: true, message: '请选择摄像头' }]}
+					label='摄像头'
+					children={
+						<Select
+							loading={chanleListState.loading}
+							showSearch={false}
+							placeholder='请输入摄像头'
+							options={chanleListState.dataSource.map((element) => {
+								return {
+									label: element.name,
+									value: element.id,
+								};
+							})}></Select>
+					}></Form.Item>
+			</Form>
+		</Modal>
+	);
+};
+export default bindChanleModal;

+ 0 - 0
src/pages/head-mast/data.d.ts


+ 65 - 0
src/pages/head-mast/hook.ts

@@ -0,0 +1,65 @@
+import { useState } from 'react';
+import Request from '../../utils/request';
+export interface DustRecordProps {
+	id: number;
+	name: string;
+	batch: string;
+	provider_name: string;
+	created_time: string;
+	approve_time: string;
+	state: keyof typeof DustState;
+	channel_id: number;
+	status: keyof typeof DustStatusConfis;
+	type_code: number;
+}
+export const DustState = {
+	0: '离线',
+	1: '在线',
+};
+
+export enum DustStatus {
+	'wait',
+	'agree',
+	'Refuse',
+}
+export const DustStatusConfis = {
+	[DustStatus.wait]: '待确认',
+	[DustStatus.agree]: '已确认',
+	[DustStatus.Refuse]: '非本项目',
+};
+/**
+ * @description 扬尘设备列表
+ */
+export const useDustList = () => {
+	const [state, setState] = useState<{
+		loading: boolean;
+		dataSource: DustRecordProps[];
+		total: number;
+		current: number;
+	}>({
+		loading: false,
+		dataSource: [],
+		total: 0,
+		current: 1,
+	});
+	const request = (params: { page: number }) => {
+		setState((preState) => ({ ...preState, loading: true }));
+		return Request.sendRequest({
+			url: '/v1/device/tower_list',
+			params,
+		})
+			.then((data: any) => {
+				setState((preState) => ({
+					...preState,
+					...data,
+					loading: false,
+					dataSource: data.list,
+				}));
+			})
+			.catch((error) => {
+				setState((preState) => ({ ...preState, loading: false }));
+				return Promise.reject(error);
+			});
+	};
+	return { state, request } as const;
+};

+ 279 - 0
src/pages/head-mast/index.tsx

@@ -0,0 +1,279 @@
+import { useEffect, useCallback, useState, useRef } from 'react';
+import {
+	Button,
+	Table,
+	Modal,
+	Radio,
+	Space,
+	PageHeader,
+	message,
+	Badge,
+	Typography,
+} from 'antd';
+import { ColumnProps } from 'antd/lib/table';
+import {
+	useDustList,
+	DustRecordProps,
+	DustState,
+	DustStatus,
+	DustStatusConfis,
+} from './hook';
+import {
+	useBindChanle,
+	useUnBindChanle,
+	usPutDevice,
+} from '../../hooks/device';
+
+const { Text } = Typography;
+
+import BindChanleModal from './components/bindChanle';
+const device: React.FC = () => {
+	const ApplyStatus = useRef(true);
+	const [modalState, setModalState] = useState<{
+		visible: boolean;
+		values?: DustRecordProps;
+	}>({
+		visible: false,
+	});
+	const [searchState, changeSearchState] = useState({ page: 1 });
+	const { state: dustListState, request: requestDustList } = useDustList();
+	const { state: bindState, request: requestBindChanle } = useBindChanle();
+	const { state: revokeState, request: requestRevoke } = useUnBindChanle();
+	const { state: putState, request: requestPutDevice } = usPutDevice();
+	const [state, setState] = useState<{ reviewStatue: boolean }>({
+		reviewStatue: true,
+	});
+	useEffect(() => {
+		requestDustList(searchState);
+	}, [searchState]);
+
+	const colums: ColumnProps<DustRecordProps>[] = [
+		{
+			title: '供应商',
+			dataIndex: 'provider_name',
+			key: 'provider_name',
+		},
+		{
+			title: '设备名',
+			dataIndex: 'name',
+			key: 'name',
+		},
+		{
+			title: '批次',
+			dataIndex: 'batch',
+			key: 'batch',
+		},
+		{
+			title: '设备唯一编码',
+			dataIndex: 'sn',
+			key: 'sn',
+		},
+		/*{
+			title: '设备密钥',
+			width: 130,
+			dataIndex: 'key',
+			key: 'key',
+		},*/
+		{
+			title: '申请时间',
+			dataIndex: 'created_time',
+			key: 'created_time',
+		},
+		{
+			title: '审批时间',
+			dataIndex: 'approve_time',
+			key: 'approve_time',
+		},
+		{
+			title: '状态',
+			dataIndex: 'state',
+			key: 'state',
+			render: (dataIndex) => {
+				const online = dataIndex === 1;
+				return (
+					<>
+						<Badge status={online ? 'success' : 'error'}></Badge>
+						<Text type={online ? 'success' : 'secondary'}>
+							{DustState[dataIndex as 0 | 1]}
+						</Text>
+					</>
+				);
+			},
+			//render: (_, record) => DustState[record.state],
+		},
+		{
+			title: '确认状态',
+			dataIndex: 'status',
+			key: 'status',
+			render: (_, record) => DustStatusConfis[record.status],
+		},
+		{
+			title: '操作',
+			render: (record) => {
+				return (
+					<Space>
+						<Button
+							type={record.status === DustStatus.wait ? 'primary' : 'default'}
+							disabled={record.status !== DustStatus.wait}
+							size='small'
+							onClick={() => handleConfirm(record)}>
+							审核
+						</Button>
+						{/*{!!record.channel_id ? (*/}
+						{/*	<Button*/}
+						{/*		size='small'*/}
+						{/*		danger*/}
+						{/*		onClick={() => handleRevokeChanle(record)}>*/}
+						{/*		解绑摄像头*/}
+						{/*	</Button>*/}
+						{/*) : (*/}
+						{/*	<Button*/}
+						{/*		size='small'*/}
+						{/*		onClick={() => {*/}
+						{/*			setModalState((preState) => ({*/}
+						{/*				...preState,*/}
+						{/*				visible: true,*/}
+						{/*				values: record,*/}
+						{/*			}));*/}
+						{/*		}}>*/}
+						{/*		绑定摄像头*/}
+						{/*	</Button>*/}
+						{/*)}*/}
+					</Space>
+				);
+			},
+		},
+	];
+	/**
+	 * @description 审批设备
+	 * @param record
+	 */
+	const handleConfirm = useCallback(
+		(record: DustRecordProps) => {
+			Modal.confirm({
+				title: '审核',
+				content: (
+					<div>
+						<Space>
+							确认是否为当前项目设备:
+							<Radio.Group
+								defaultValue={true}
+								onChange={(event) => {
+									console.log(event.target.value);
+									ApplyStatus.current = event.target.value;
+								}}>
+								<Radio value={true}>通过</Radio>
+								<Radio value={false}>拒绝</Radio>
+							</Radio.Group>
+						</Space>
+					</div>
+				),
+				okText: '确认',
+				cancelText: '取消',
+				onOk: () => {
+					return requestPutDevice({
+						id: record.id,
+						device_code: record.type_code,
+						status: ApplyStatus.current,
+					})
+						.then(() => {
+							requestDustList(searchState);
+							message.success('操作成功');
+						})
+						.catch((error) => {
+							message.error(error.message);
+						})
+						.finally(() => {
+							ApplyStatus.current = true;
+						});
+				},
+				onCancel() {
+					ApplyStatus.current = true;
+				},
+			});
+		},
+		[Modal]
+	);
+
+	/**
+	 * @description 绑定摄像头
+	 */
+	const handleBindChanle = useCallback(
+		(data) => {
+			requestBindChanle({
+				...data,
+				device_id: modalState.values?.id,
+				device_code: modalState.values?.type_code,
+			})
+				.then(() => {
+					message.success('绑定摄像头成功');
+					requestDustList(searchState);
+					setModalState((preState) => ({
+						...preState,
+						visible: false,
+						values: undefined,
+					}));
+				})
+				.catch((error) => {
+					message.error(error.message);
+				});
+		},
+		[modalState.values, message, requestDustList]
+	);
+	/**
+	 * @description 解绑摄像头
+	 */
+	const handleRevokeChanle = useCallback(
+		(record: DustRecordProps) => {
+			Modal.confirm({
+				title: '确认解绑摄像头',
+				okText: '确认',
+				cancelText: '取消',
+				okButtonProps: {
+					loading: revokeState.loading,
+				},
+				onOk: () => {
+					return requestRevoke({
+						device_id: record.id,
+						device_code: record.type_code,
+					})
+						.then(() => {
+							requestDustList(searchState);
+							message.success('解绑成功');
+						})
+						.catch((error) => {
+							message.error(error.message);
+						});
+				},
+			});
+		},
+		[requestDustList, message]
+	);
+	return (
+		<div>
+			<PageHeader title='塔吊设备'/>
+			<Table
+				rowKey={(record) => record.id}
+				columns={colums}
+				pagination={{
+					current: dustListState.current,
+					total: dustListState.total,
+					showQuickJumper: false,
+					showSizeChanger: false,
+					hideOnSinglePage: true,
+					onChange: (page) =>
+						changeSearchState((preState) => ({ ...preState, page })),
+				}}
+				loading={dustListState.loading}
+				dataSource={dustListState.dataSource}/>
+			<BindChanleModal
+				loading={bindState.loading}
+				visible={modalState.visible}
+				onCancle={() => {
+					setModalState((preState) => ({ ...preState, visible: false }));
+				}}
+				onOk={handleBindChanle}/>
+		</div>
+	);
+};
+export default device;

+ 6 - 0
src/router/config.tsx

@@ -18,6 +18,7 @@ const projectPlan = lazy(() => import('../pages/projectPlan'));
 const projectRender = lazy(() => import('../pages/admin-renderings'));
 
 const dust = lazy(() => import('../pages/dust'));
+const headMast = lazy(() => import('../pages/head-mast'));
 
 const videoDevice = lazy(() => import('../pages/device-video'));
 
@@ -127,6 +128,11 @@ export const routerConfig = [
 				exact: true,
 				component: wrapperLoading(dust),
 			},
+      {
+				path: '/admin/device/headMast',
+				exact: true,
+				component: wrapperLoading(headMast),
+			},
 			{
 				path: '/admin/device/accessController',
 				exact: true,

+ 8 - 7
src/router/index.tsx

@@ -24,8 +24,8 @@ const router: React.FC<Iprops> = ({ isAdmin = false }) => {
 									path={route.path}
 									exact={!!route.exact}
 									render={() => {
-										return <Redirect to='/'></Redirect>;
-									}}></Route>
+										return <Redirect to='/'/>;
+									}}/>
 							);
 						}
 						return (
@@ -40,11 +40,11 @@ const router: React.FC<Iprops> = ({ isAdmin = false }) => {
 										return (
 											<route.component
 												{...props}
-												routes={route.routes}></route.component>
+												routes={route.routes}/>
 										);
 									}
-									return <Redirect to='/login'></Redirect>;
-								}}></Route>
+									return <Redirect to='/login'/>;
+								}}/>
 						);
 					}
 					return (
@@ -52,11 +52,12 @@ const router: React.FC<Iprops> = ({ isAdmin = false }) => {
 							key={route.path}
 							path={route.path}
 							exact={route.exact}
-							component={route.component}></Route>
+							component={route.component}/>
 					);
 				})}
 			</Switch>
 		</Router>
 	);
 };
-export default router;
+export default router
+