Commit af2e92c033abafef873cbe779ab743548b4a2fd1
1 parent
39e29489
add:车辆管理系统:完成整个运维管理模块
Showing
14 changed files
with
599 additions
and
77 deletions
src/pages/vms/OperationAdministration/api.ts
0 → 100644
1 | +import { http } from '@/typing/http'; | |
2 | +import request from '@/utils/request'; | |
3 | +import { HOST_VMS } from '@/utils/host'; | |
4 | + | |
5 | +type PromiseResp<T> = Promise<http.Response<T>>; | |
6 | +type PromiseRespArr<T> = Promise<http.Response<T[]>>; | |
7 | + | |
8 | +export function getPointsDataMock(params:any):PromiseResp<string> { | |
9 | + return request.get(`https://tsapi.amap.com/v1/track/terminal/trsearch`, {params}); | |
10 | +} | |
11 | +// 查询GPS设备信息分页列表 | |
12 | +export function getGpsTableData(params?:any):PromiseResp<any> { | |
13 | + return request.get(`${HOST_VMS}/erp/operation/gps/page`, {params}); | |
14 | +} | |
15 | +//查询外勤设备信息分页列表 | |
16 | +export function getTripTableData(params?:any):PromiseResp<any> { | |
17 | + return request.get(`${HOST_VMS}/erp/operation/trip/page`, {params}); | |
18 | +} | |
19 | +//运维轨迹查询 | |
20 | +export function getPointsData(params:any):PromiseResp<any> { | |
21 | + return request.get(`${HOST_VMS}/erp/operation/track/message/query`, {params}); | |
22 | +} | |
0 | 23 | \ No newline at end of file | ... | ... |
src/pages/vms/OperationAdministration/components/MapContent/index.tsx
1 | 1 | import React, { useState, useEffect, useRef } from 'react'; |
2 | +import {message, Input, Button} from 'antd'; | |
2 | 3 | import AMapLoader from "@amap/amap-jsapi-loader"; |
4 | +import {getPointsData, getPointsDataMock} from '../../api'; | |
5 | +import useUpdateEffect from '../useUpdateEffect'; | |
6 | +import PlayBackPicker from '../PlayBackPicker'; | |
3 | 7 | import './style.less'; |
4 | 8 | |
9 | +interface searchParams<T>{ | |
10 | + trid?:number | undefined; | |
11 | + date:Array<T> | |
12 | +} | |
13 | +interface DateParams { | |
14 | + startTime?: number; | |
15 | + endTime?:number; | |
16 | + trid?:number; | |
17 | +} | |
5 | 18 | function MapContent(props:any) { |
6 | - console.log('MapContentprops', props); | |
19 | + const { trackParams, id} = props; | |
20 | + const trid = useRef<number | undefined>(undefined); | |
21 | + const date = useRef<Array<number | null>>([]); | |
22 | + const [searchParams, setSearchParams] = useState<searchParams<number | null>>({trid: trid.current, date: date.current}); | |
23 | + //地图 | |
7 | 24 | const map = useRef<any>(null); |
25 | + const amapCopy = useRef<any>(null); | |
26 | + const marker = useRef<Array<any>>([]); | |
27 | + const [tracksFilter, setTracksFilter] = useState<Array<any>>([]); | |
28 | + const lineArr= [ | |
29 | + [116.478935, 39.997761], | |
30 | + [116.478939, 39.997825], | |
31 | + [116.478912, 39.998549], | |
32 | + [116.478912, 39.998549], | |
33 | + [116.478998, 39.998555], | |
34 | + [116.478998, 39.998555], | |
35 | + [116.479282, 39.99856], | |
36 | + [116.479658, 39.998528], | |
37 | + [116.480151, 39.998453], | |
38 | + [116.480784, 39.998302], | |
39 | + [116.480784, 39.998302], | |
40 | + [116.481149, 39.998184], [116.481573, 39.997997], [116.481863, 39.997846], [116.482072, 39.997718], [116.482362, 39.997718], [116.483633, 39.998935], [116.48367, 39.998968], [116.484648, 39.999861]]; | |
8 | 41 | useEffect(() => { |
9 | - initMap(); | |
10 | - return () => { | |
11 | - | |
12 | - }; | |
42 | + initMap(); | |
43 | + return () => { | |
44 | + }; | |
13 | 45 | }, []); |
14 | - | |
46 | + useEffect(() => { | |
47 | + if (!trackParams) return; | |
48 | + let dateParams ={} as DateParams; | |
49 | + const {trid, date} = searchParams; | |
50 | + if (trid) { | |
51 | + dateParams.trid = trid; | |
52 | + } | |
53 | + if (date[0] && date[1]) { | |
54 | + dateParams.startTime = date[0]; | |
55 | + dateParams.endTime = date[1]; | |
56 | + } | |
57 | + getPointsData({...trackParams, ...dateParams}).then(res => { | |
58 | + const {data: {data: tracksData}}=res; | |
59 | + if (res.data.errcode === 20000) { | |
60 | + message.warning('请选择24小时以内的时间段'); | |
61 | + return; | |
62 | + } | |
63 | + const { tracks }= tracksData; | |
64 | + let tracksFilterData:Array<any> = []; | |
65 | + tracks?.forEach((item:any, index:any) => { | |
66 | + let tracksArr:Array<Array<number>> = []; | |
67 | + item.startPoint && tracksArr.unshift(item?.startPoint?.location.split(',').map(Number)); | |
68 | + const points = filterPoint(item?.points); | |
69 | + tracksArr.push(...points); | |
70 | + item.endPoint && tracksArr.push(item?.endPoint?.location.split(',').map(Number)); | |
71 | + tracksArr.length && tracksFilterData.push(tracksArr); | |
72 | + }); | |
73 | + tracksFilterData.push(lineArr); | |
74 | + setTracksFilter(tracksFilterData); | |
75 | + mapPlayBack(tracksFilterData); | |
76 | + }).catch((e) => { | |
77 | + message.error('此时间段没有你想要的数据!'); | |
78 | + }); | |
79 | + }, [searchParams, trackParams]); | |
80 | + | |
81 | + /** | |
82 | + * @description: 地图轨迹展示 | |
83 | + * @param {*} | |
84 | + * @return {*} | |
85 | + */ | |
86 | + const mapPlayBack = (tracksData:Array<any>) => { | |
87 | + amapCopy.current.plugin('AMap.MoveAnimation', () => { | |
88 | + map.current = new amapCopy.current.Map(id, { //设置地图容器id | |
89 | + viewMode: "3D", //是否为3D地图模式 | |
90 | + resizeEnable: true, | |
91 | + zoom: 8, //初始化地图级别 | |
92 | + // center: tracksData[0][0], //初始化地图中心点位置 | |
93 | + }); | |
94 | + tracksData.forEach(tracks => { | |
95 | + // | |
96 | + const markerItem = new amapCopy.current.Marker({ | |
97 | + map: map.current, | |
98 | + position: tracks[0], //轨迹起点 | |
99 | + icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png", | |
100 | + offset: new amapCopy.current.Pixel(-13, -26), | |
101 | + }); | |
102 | + const polyline = new amapCopy.current.Polyline({ | |
103 | + map: map.current, | |
104 | + path: tracks, | |
105 | + showDir: true, | |
106 | + strokeColor: "#28F", //线颜色 | |
107 | + // strokeOpacity: 1, //线透明度 | |
108 | + strokeWeight: 8, //线宽 | |
109 | + lineJoin: 'round', | |
110 | + strokeStyle: "solid" //线样式 | |
111 | + }); | |
112 | + marker.current.push(markerItem); | |
113 | + map.current.add(markerItem); | |
114 | + map.current.add(polyline); | |
115 | + const passedPolyline = new amapCopy.current.Polyline({ | |
116 | + map: map.current, | |
117 | + strokeColor: "#AF5", //线颜色 | |
118 | + strokeWeight: 6, //线宽 | |
119 | + }); | |
120 | + markerItem.on('moving', (e:any) => { | |
121 | + passedPolyline.setPath(e.passedPath); | |
122 | + map.current.setCenter(e.target.getPosition(), true); | |
123 | + }); | |
124 | + }); | |
125 | + map.current.setFitView(); | |
126 | + }); | |
127 | + }; | |
128 | + /** | |
129 | + * @description: 过滤坐标点 | |
130 | + * @param {Array} points | |
131 | + * @return {*} | |
132 | + */ | |
133 | + const filterPoint= (points:Array<any>):Array<any> => { | |
134 | + if (!points.length) return []; | |
135 | + const arr = points.map((item) => { | |
136 | + const {location }= item; | |
137 | + const locationarr = location.split(',').map(Number); | |
138 | + return locationarr; | |
139 | + }); | |
140 | + return arr; | |
141 | + }; | |
142 | + | |
143 | + /** | |
144 | + * @description: 开始回放 | |
145 | + * @param {*} | |
146 | + * @return {*} | |
147 | + */ | |
148 | + const startAnimation = () => { | |
149 | + console.log(1); | |
150 | + tracksFilter.forEach((item, index) => { | |
151 | + marker.current[index].moveAlong(item, { | |
152 | + // 每一段的时长 | |
153 | + duration: 200, //可根据实际采集时间间隔设置 | |
154 | + // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置 | |
155 | + autoRotation: true, | |
156 | + }); | |
157 | + }); | |
158 | + }; | |
159 | + | |
160 | + /** | |
161 | + * @description: 暂停动画 | |
162 | + * @param {*} | |
163 | + * @return {*} | |
164 | + */ | |
165 | + const pauseAnimation = () => { | |
166 | + const {current} = marker; | |
167 | + current.forEach((item, index) => { | |
168 | + current[index].pauseMove(); | |
169 | + }); | |
170 | + }; | |
171 | + /** | |
172 | + * @description: 继续动画 | |
173 | + * @param {*} | |
174 | + * @return {*} | |
175 | + */ | |
176 | + const resumeAnimation = () => { | |
177 | + const {current} = marker; | |
178 | + current.length && current.forEach((item, index) => { | |
179 | + current[index].resumeMove(); | |
180 | + }); | |
181 | + }; | |
182 | + /** | |
183 | + * @description: 停止动画 | |
184 | + * @param {*} | |
185 | + * @return {*} | |
186 | + */ | |
187 | + const stopAnimation = () => { | |
188 | + const {current} = marker; | |
189 | + current.forEach((item, index) => { | |
190 | + current[index].stopMove(); | |
191 | + }); | |
192 | + }; | |
15 | 193 | /** |
16 | 194 | * @description: 初始化地图 |
17 | 195 | */ |
... | ... | @@ -21,18 +199,70 @@ function MapContent(props:any) { |
21 | 199 | version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 |
22 | 200 | plugins: ["AMap.Geolocation", "AMap.AutoComplete", "AMap.PlaceSearch"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等 |
23 | 201 | }).then((AMap) => { |
24 | - map.current = new AMap.Map("container", { //设置地图容器id | |
202 | + amapCopy.current = AMap; | |
203 | + map.current = new AMap.Map(id, { //设置地图容器id | |
25 | 204 | viewMode: "3D", //是否为3D地图模式 |
26 | - zoom: 5, //初始化地图级别 | |
27 | - center: [105.602725, 37.076636], //初始化地图中心点位置 | |
205 | + resizeEnable: true, | |
206 | + zoom: 10, //初始化地图级别 | |
207 | + center: [104.06, 30.67], //初始化地图中心点位置 | |
28 | 208 | }); |
29 | 209 | }).catch(e => { |
30 | 210 | console.log('e', e); |
31 | 211 | }); |
32 | 212 | }; |
33 | 213 | |
214 | + /** | |
215 | + * @description: 手动获取时间 | |
216 | + * @param {*} | |
217 | + * @return {*} | |
218 | + */ | |
219 | + const handelDate = (val:Array<any>) => { | |
220 | + date.current = val; | |
221 | + }; | |
34 | 222 | return ( |
35 | - <div id="container" className="map_main" /> | |
223 | + <div className="map_box"> | |
224 | + <div className="right_top"> | |
225 | + <div className="top_input"> | |
226 | + <Input | |
227 | + placeholder="轨迹ID" | |
228 | + onBlur={(event) => { | |
229 | + trid.current = parseInt(event.target.value, 10); | |
230 | + }} | |
231 | + /> | |
232 | + </div> | |
233 | + <PlayBackPicker | |
234 | + handelDate={handelDate} | |
235 | + /> | |
236 | + <div> | |
237 | + <Button | |
238 | + className="back_btn" | |
239 | + type="primary" | |
240 | + onClick={() => { | |
241 | + setSearchParams({trid: trid.current, date: date.current}); | |
242 | + if (!trackParams)message.warning('请在列表中选择终端!'); | |
243 | + }} | |
244 | + > | |
245 | + 查询 | |
246 | + </Button> | |
247 | + </div> | |
248 | + </div> | |
249 | + <div id={id} className="map_main" /> | |
250 | + { | |
251 | + tracksFilter.length && ( | |
252 | + <div className="play_back_btn_box"> | |
253 | + <div className="btn_item"> | |
254 | + <Button type="primary" className="play_back_btn" onClick={startAnimation}>开始回放</Button> | |
255 | + <Button type="primary" className="play_back_btn" onClick={pauseAnimation}>暂停回放</Button> | |
256 | + </div> | |
257 | + <div className="btn_item"> | |
258 | + <Button type="primary" className="play_back_btn" onClick={resumeAnimation}>继续回放</Button> | |
259 | + <Button type="primary" className="play_back_btn" onClick={stopAnimation}>停止回放</Button> | |
260 | + </div> | |
261 | + </div> | |
262 | + ) | |
263 | + } | |
264 | + | |
265 | + </div> | |
36 | 266 | ); |
37 | 267 | } |
38 | 268 | export default MapContent; | ... | ... |
src/pages/vms/OperationAdministration/components/MapContent/style.css
1 | -#container { | |
1 | +.map_box { | |
2 | + position: relative; | |
3 | +} | |
4 | +.map_box .right_top { | |
5 | + margin-bottom: 10px; | |
6 | + display: flex; | |
7 | + justify-content: flex-start; | |
8 | + align-items: center; | |
9 | +} | |
10 | +.map_box .right_top .top_input { | |
11 | + margin-right: 25px; | |
12 | +} | |
13 | +.map_box .right_top .back_btn { | |
14 | + margin-left: 30px; | |
15 | +} | |
16 | +.map_box .map_main { | |
2 | 17 | padding: 0px; |
3 | 18 | margin: 0px; |
4 | 19 | width: 100%; |
20 | + height: 750px; | |
21 | +} | |
22 | +.map_box .play_back_btn_box { | |
23 | + position: absolute; | |
24 | + display: flex; | |
25 | + flex-direction: column; | |
26 | + z-index: 9999; | |
27 | + background-color: #fff; | |
28 | + padding: 20px; | |
29 | + border-radius: 8px; | |
30 | + right: 20px; | |
31 | + bottom: 20px; | |
32 | + box-shadow: 0 2px 6px 0 #FAAD14; | |
33 | +} | |
34 | +.map_box .play_back_btn_box .btn_item { | |
35 | + display: flex; | |
36 | + align-items: center; | |
37 | + justify-content: space-around; | |
38 | + width: 100%; | |
39 | +} | |
40 | +.map_box .play_back_btn_box .btn_item:first-child { | |
41 | + margin-bottom: 20px; | |
5 | 42 | } |
6 | -.map_main { | |
7 | - height: 700px; | |
43 | +.map_box .play_back_btn_box .btn_item .play_back_btn:first-child { | |
44 | + margin-right: 25px; | |
8 | 45 | } | ... | ... |
src/pages/vms/OperationAdministration/components/MapContent/style.less
1 | -#container{ | |
2 | - padding: 0px; | |
3 | - margin: 0px; | |
4 | - width: 100%; | |
5 | -} | |
6 | -.map_main{ | |
7 | - height: 700px; | |
1 | +.map_box{ | |
2 | + position: relative; | |
3 | + .right_top{ | |
4 | + margin-bottom: 10px; | |
5 | + display:flex; | |
6 | + justify-content: flex-start; | |
7 | + align-items: center; | |
8 | + .top_input{ | |
9 | + margin-right: 25px; | |
10 | + } | |
11 | + .back_btn{ | |
12 | + margin-left: 30px; | |
13 | + } | |
14 | + } | |
15 | + #container{ | |
16 | + } | |
17 | + .map_main{ | |
18 | + padding: 0px; | |
19 | + margin: 0px; | |
20 | + width: 100%; | |
21 | + height: 750px; | |
22 | + } | |
23 | + .play_back_btn_box{ | |
24 | + position: absolute; | |
25 | + display: flex; | |
26 | + flex-direction: column; | |
27 | + z-index: 9999; | |
28 | + background-color: #fff; | |
29 | + padding: 20px; | |
30 | + border-radius: 8px; | |
31 | + right: 20px; | |
32 | + bottom: 20px; | |
33 | + box-shadow: 0 2px 6px 0 #FAAD14; | |
34 | + .btn_item{ | |
35 | + display: flex; | |
36 | + align-items: center; | |
37 | + justify-content: space-around; | |
38 | + width: 100%; | |
39 | + &:first-child{ | |
40 | + margin-bottom: 20px; | |
41 | + } | |
42 | + .play_back_btn{ | |
43 | + &:first-child{ | |
44 | + margin-right: 25px; | |
45 | + } | |
46 | + } | |
47 | + } | |
48 | + } | |
8 | 49 | } |
9 | 50 | \ No newline at end of file | ... | ... |
src/pages/vms/OperationAdministration/components/PlayBack/index.tsx
1 | -import React, { useState } from 'react'; | |
1 | +import React, { useState, useEffect, useRef } from 'react'; | |
2 | 2 | import PlayBackTable from '../PlayBackTable'; |
3 | 3 | import MapContent from '../MapContent'; |
4 | -import moment from 'moment'; | |
5 | -import { Input, DatePicker as Picker, Space } from 'antd'; | |
6 | 4 | import './style.less'; |
7 | 5 | |
8 | -const DatePicker:any = Picker; | |
9 | -const {RangePicker} = DatePicker; | |
10 | -function PlayBack() { | |
11 | - const [tid, setTid] = useState<number | undefined>(); | |
6 | +interface TrackParams { | |
7 | + tid: number; | |
8 | + entityName:string; | |
9 | +} | |
10 | + | |
11 | +function PlayBack(props:any) { | |
12 | + const {name, type} = props; | |
13 | + const [trackParams, setTrackParams] = useState<TrackParams | undefined>(undefined); | |
14 | + useEffect(() => { | |
15 | + }, []); | |
12 | 16 | /** |
13 | 17 | * @description: 点击行 |
14 | 18 | * @param {*} |
15 | 19 | * @return {*} |
16 | 20 | */ |
17 | - const handelRow = (tid:number | undefined) => { | |
18 | - console.log('tid==>', tid); | |
19 | - setTid(tid); | |
21 | + const handelRow = (params:TrackParams) => { | |
22 | + setTrackParams(params); | |
20 | 23 | }; |
21 | 24 | return ( |
22 | 25 | <div className="play_back"> |
23 | 26 | <div className="left"> |
24 | 27 | <PlayBackTable |
25 | 28 | handelRow={handelRow} |
29 | + type={type} | |
26 | 30 | /> |
27 | 31 | </div> |
28 | - <div className="right"> | |
29 | - <div className="right_top"> | |
30 | - <div className="top_input"> | |
31 | - <Input | |
32 | - placeholder="轨迹ID" | |
33 | - /> | |
34 | - </div> | |
35 | - <div> | |
36 | - <RangePicker | |
37 | - showTime | |
38 | - /> | |
39 | - </div> | |
40 | - </div> | |
41 | - | |
42 | - <MapContent /> | |
32 | + <div className="right"> | |
33 | + <MapContent | |
34 | + trackParams={trackParams} | |
35 | + id={name} | |
36 | + /> | |
43 | 37 | </div> |
44 | 38 | </div> |
45 | 39 | ); | ... | ... |
src/pages/vms/OperationAdministration/components/PlayBack/style.css
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 | padding: 0 10px; |
9 | 9 | } |
10 | 10 | .play_back .right { |
11 | - flex: 1 ; | |
11 | + flex: 1 1 ; | |
12 | 12 | height: 100%; |
13 | 13 | } |
14 | 14 | .play_back .right .right_top { |
... | ... | @@ -20,3 +20,6 @@ |
20 | 20 | .play_back .right .right_top .top_input { |
21 | 21 | margin-right: 25px; |
22 | 22 | } |
23 | +.play_back .right .right_top .back_btn { | |
24 | + margin-left: 30px; | |
25 | +} | ... | ... |
src/pages/vms/OperationAdministration/components/PlayBack/style.less
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | padding: 0 10px; |
8 | 8 | } |
9 | 9 | .right { |
10 | - flex: 1 ; | |
10 | + flex: 1 1 ; | |
11 | 11 | height:100%; |
12 | 12 | .right_top{ |
13 | 13 | margin-bottom: 10px; |
... | ... | @@ -17,6 +17,9 @@ |
17 | 17 | .top_input{ |
18 | 18 | margin-right: 25px; |
19 | 19 | } |
20 | + .back_btn{ | |
21 | + margin-left: 30px; | |
22 | + } | |
20 | 23 | } |
21 | 24 | } |
22 | 25 | } |
23 | 26 | \ No newline at end of file | ... | ... |
src/pages/vms/OperationAdministration/components/PlayBackPicker/index.tsx
0 → 100644
1 | +import React, { useState, useEffect, useRef } from 'react'; | |
2 | +import { DatePicker as Picker } from 'antd'; | |
3 | +import moment from 'moment'; | |
4 | +import type { Moment } from 'moment'; | |
5 | +import './style.less'; | |
6 | + | |
7 | +interface searchParams<T>{ | |
8 | + trid?:number | undefined; | |
9 | + date?:Array<T> | |
10 | +} | |
11 | +const DatePicker:any = Picker; | |
12 | +const {RangePicker} = DatePicker; | |
13 | +type RangeValue = [Moment | undefined, Moment | undefined]; | |
14 | + | |
15 | +function PlayBackPicker(props:any) { | |
16 | + const {handelDate} = props; | |
17 | + | |
18 | + const startTime = moment(new Date()).subtract(1, "days").startOf('days').valueOf(); | |
19 | + const endTime = moment(new Date()).startOf('days').valueOf(); | |
20 | + const date = useRef<Array<number | null>>([startTime, endTime]); | |
21 | + const [dateFlag, setDateFlag] = useState<RangeValue>([moment(new Date()).subtract(1, "days").startOf('days'), moment(new Date(), 'YYYY-MM-DD HH:mm:ss').startOf('days')]); | |
22 | + const [flag, setFlag] = useState<boolean>(true); | |
23 | + | |
24 | + useEffect(() => { | |
25 | + handelDate(date.current); | |
26 | + }, []); | |
27 | + | |
28 | + /** | |
29 | + * @description: 年月日时间选择 | |
30 | + * @param {*} | |
31 | + * @return {*} | |
32 | + */ | |
33 | + const disabledDate = (current: Moment) => { | |
34 | + if (flag) { | |
35 | + //禁用当天之后的时间 | |
36 | + return current > moment().endOf('day'); | |
37 | + } | |
38 | + //限制选择开始时间后的取值范围 | |
39 | + const startHour = dateFlag[0] && dateFlag[0].hour(); | |
40 | + const startMinute = dateFlag[0] && dateFlag[0].minute(); | |
41 | + const startSecond = dateFlag[0] && dateFlag[0].second(); | |
42 | + const beforeDate = dateFlag[0] && (current > moment().endOf('day') || moment(current).set({'hour': startHour, 'minute': startMinute, "second": startSecond}).diff(dateFlag[0], 'days') > 1); | |
43 | + //限制选择结束时间后的取值范围 | |
44 | + const afterHour = dateFlag[1] && dateFlag[1].hour(); | |
45 | + const afterMinute = dateFlag[1] && dateFlag[1].minute(); | |
46 | + const afterSecond = dateFlag[1] && dateFlag[1].second(); | |
47 | + const afterDate = dateFlag[1] && dateFlag[1].diff(moment(current).set({'hour': afterHour, 'minute': afterMinute, "second": afterSecond}), 'days')>1; | |
48 | + return !!beforeDate || !!afterDate; | |
49 | + }; | |
50 | + | |
51 | + /** | |
52 | + * @description: 日期时分秒时间范围 | |
53 | + * @param {any} date | |
54 | + * @param {any} type | |
55 | + * @return {*} | |
56 | + */ | |
57 | + const disabledTracksTime = (date:any, type:any) => { | |
58 | + if (!date) return; | |
59 | + const {_d: checkDate, _i: nowDate} = date; | |
60 | + const checkYear = moment(checkDate).format('YYYY-MM-DD'); | |
61 | + const nowYear = moment(nowDate).format('YYYY-MM-DD'); | |
62 | + if (checkYear === nowYear) { | |
63 | + const nowHour = moment(nowDate).hour(); | |
64 | + const nowMinute = moment(nowDate).minute(); | |
65 | + const nowSecond = moment(nowDate).second(); | |
66 | + return { | |
67 | + disabledHours: () => { | |
68 | + const disabledArr = timeArr(24).splice(nowHour, timeArr(24).length - nowHour); | |
69 | + return disabledArr; | |
70 | + }, | |
71 | + disabledMinutes: () => { | |
72 | + const disabledArr = timeArr(60).splice(nowMinute, timeArr(60).length - nowMinute); | |
73 | + return disabledArr; | |
74 | + }, | |
75 | + disabledSeconds: () => { | |
76 | + const disabledArr = timeArr(60).splice(nowSecond, timeArr(60).length - nowSecond); | |
77 | + return disabledArr; | |
78 | + }, | |
79 | + }; | |
80 | + } | |
81 | + }; | |
82 | + /** | |
83 | + * @description: 时分秒时间数组 | |
84 | + * @param {number} length | |
85 | + * @return {*} | |
86 | + */ | |
87 | + const timeArr = (length:number):Array<number> => { | |
88 | + const arr = Array.from(Array(length), (value, index) => index); | |
89 | + return arr; | |
90 | + }; | |
91 | + const onOpenChange = (type:boolean) => { | |
92 | + if (type) { | |
93 | + setDateFlag([undefined, undefined]); | |
94 | + if (date.current[0]!==null && date.current[1]!==null) { | |
95 | + date.current =[null, null]; | |
96 | + handelDate([null, null]); | |
97 | + } | |
98 | + } else { | |
99 | + setFlag(true); | |
100 | + } | |
101 | + }; | |
102 | + return ( | |
103 | + <div> | |
104 | + | |
105 | + <RangePicker | |
106 | + format="YYYY-MM-DD HH:mm:ss" | |
107 | + allowClear={false} | |
108 | + value={dateFlag} | |
109 | + showTime={{ | |
110 | + hideDisabledOptions: true, | |
111 | + }} | |
112 | + disabledDate={disabledDate} | |
113 | + disabledTime={disabledTracksTime} | |
114 | + onCalendarChange={ | |
115 | + (value:any) => { | |
116 | + setDateFlag(value); | |
117 | + setFlag(false); | |
118 | + } | |
119 | + } | |
120 | + onOpenChange={onOpenChange} | |
121 | + onChange={(dateVal:Object, dateString:Array<string>) => { | |
122 | + const dateTimestamp = dateString.map(item => { | |
123 | + return moment(item).valueOf(); | |
124 | + }); | |
125 | + date.current = dateTimestamp; | |
126 | + handelDate(dateTimestamp); | |
127 | + }} | |
128 | + /> | |
129 | + </div> | |
130 | + ); | |
131 | +} | |
132 | +export default PlayBackPicker; | ... | ... |
src/pages/vms/OperationAdministration/components/PlayBackPicker/style.less
0 → 100644
src/pages/vms/OperationAdministration/components/PlayBackTable/index.tsx
1 | -import React, { useState } from 'react'; | |
2 | -import { Table } from 'antd'; | |
1 | +import React, { useState, useEffect } from 'react'; | |
2 | +import { Table, message} from 'antd'; | |
3 | +import type { ColumnsType } from 'antd/es/table'; | |
4 | +import {getGpsTableData, getTripTableData} from '../../api'; | |
3 | 5 | |
4 | 6 | import './style.less'; |
5 | 7 | |
6 | -const data:Array<any> = []; | |
7 | -for (let i=0; i<100; i++) { | |
8 | - const item = { | |
9 | - key: i, | |
10 | - name: '胡彦斌', | |
11 | - tid: i, | |
12 | - address: '西湖区湖底公园1号', | |
13 | - }; | |
14 | - data.push(item); | |
8 | +interface DataType { | |
9 | + tid: number; | |
10 | + trid: number; | |
11 | + entityName:string; | |
12 | + enable:boolean; | |
15 | 13 | } |
16 | - | |
17 | -const dataSource = data; | |
18 | 14 | |
19 | - const columns = [ | |
20 | - { | |
21 | - title: '姓名', | |
22 | - dataIndex: 'name', | |
23 | - key: 'name', | |
24 | - }, | |
15 | + const columns:ColumnsType<DataType> = [ | |
25 | 16 | { |
26 | - title: '设备id', | |
17 | + title: '终端id', | |
27 | 18 | dataIndex: 'tid', |
28 | 19 | key: 'tid', |
20 | + width: 100 | |
21 | + }, | |
22 | + { | |
23 | + title: '终端名称', | |
24 | + dataIndex: 'entityName', | |
25 | + key: 'entityName', | |
26 | + width: 100 | |
27 | + | |
29 | 28 | }, |
30 | 29 | { |
31 | - title: '住址', | |
32 | - dataIndex: 'address', | |
33 | - key: 'address', | |
30 | + title: '轨迹id', | |
31 | + dataIndex: 'trid', | |
32 | + key: 'trid', | |
33 | + width: 100 | |
34 | + | |
34 | 35 | }, |
36 | + { | |
37 | + title: '设备状态', | |
38 | + dataIndex: 'enable', | |
39 | + key: 'enable', | |
40 | + width: 100 | |
41 | + | |
42 | + } | |
35 | 43 | ]; |
44 | + | |
36 | 45 | function PlayBackTable(props:any) { |
37 | - console.log('props', props); | |
38 | - const {handelRow} = props; | |
46 | + const {handelRow, type} = props; | |
47 | + const [tableData, setTableData] = useState<any>([]); | |
48 | + useEffect(() => { | |
49 | + //获取table数据 | |
50 | + if (type === 1) { | |
51 | + getGpsTableData().then(res => { | |
52 | + const data = res?.data?.data.map((item:any) => { | |
53 | + if (item?.enable) item.enable = '启用'; else item.enable = '禁用'; | |
54 | + return item; | |
55 | + }); | |
56 | + setTableData(data); | |
57 | + }).catch((e) => { | |
58 | + message.error('查询数据失败!'); | |
59 | + }); | |
60 | + } else if (type === 2) { | |
61 | + getTripTableData().then(res => { | |
62 | + const data = res?.data?.data.map((item:any) => { | |
63 | + if (item?.enable) item.enable = '启用'; else item.enable = '禁用'; | |
64 | + return item; | |
65 | + }); | |
66 | + setTableData(data); | |
67 | + }).catch((e) => { | |
68 | + message.error('查询数据失败!'); | |
69 | + }); | |
70 | + } | |
71 | + }, []); | |
72 | + | |
39 | 73 | return ( |
40 | 74 | <div className="play_back_table"> |
41 | 75 | <Table |
42 | - dataSource={dataSource} | |
76 | + dataSource={tableData} | |
43 | 77 | columns={columns} |
78 | + className="table_back" | |
79 | + rowKey={row => row.tid} | |
44 | 80 | pagination={{ |
45 | 81 | showQuickJumper: false, |
46 | 82 | showSizeChanger: true, |
... | ... | @@ -50,7 +86,7 @@ function PlayBackTable(props:any) { |
50 | 86 | return { |
51 | 87 | //行点击 |
52 | 88 | onClick: () => { |
53 | - handelRow(record.tid); | |
89 | + handelRow({tid: record?.tid, entityName: record?.entityName}); | |
54 | 90 | }, |
55 | 91 | }; |
56 | 92 | } | ... | ... |
src/pages/vms/OperationAdministration/components/PlayBackTable/style.css
src/pages/vms/OperationAdministration/components/PlayBackTable/style.less
src/pages/vms/OperationAdministration/components/useUpdateEffect.tsx
0 → 100644
1 | +import { useEffect, useRef } from "react"; | |
2 | + | |
3 | +//模拟didupdate | |
4 | +const useUpdateEffect =(effect:Function, deps?:[any | null]) => { | |
5 | + const updateFlag = useRef(true); | |
6 | + useEffect(() => { | |
7 | + if (updateFlag.current) { | |
8 | + updateFlag.current =false; | |
9 | + } else { | |
10 | + effect(); | |
11 | + } | |
12 | + }, deps); | |
13 | +}; | |
14 | +export default useUpdateEffect; | |
0 | 15 | \ No newline at end of file | ... | ... |
src/pages/vms/OperationAdministration/index.tsx
... | ... | @@ -5,18 +5,22 @@ import PlayBack from './components/PlayBack'; |
5 | 5 | |
6 | 6 | const TabPane = Tabs.TabPane; |
7 | 7 | |
8 | +const gpsName = 'container1'; | |
9 | +const tripName = 'container2'; | |
10 | +const type=[1, 2]; | |
8 | 11 | function OperationAdministration() { |
9 | - // const [first, setfirst] = useState(1); | |
10 | 12 | const items = [ |
11 | 13 | {label: 'GPS轨迹回放', key: '1', children: PlayBack} |
12 | 14 | ]; |
13 | 15 | return ( |
14 | 16 | <PageHeaderWrapper title="轨迹回放"> |
15 | 17 | <Card> |
16 | - {/* <Tabs items={items} /> */} | |
17 | 18 | <Tabs> |
18 | 19 | <TabPane tab="GPS轨迹回放" key="1" className="tabsPanelScroll"> |
19 | - <PlayBack /> | |
20 | + <PlayBack name={gpsName} type={type[0]} /> | |
21 | + </TabPane> | |
22 | + <TabPane tab="霏微出行" key="2" className="tabsPanelScroll"> | |
23 | + <PlayBack name={tripName} type={type[1]} /> | |
20 | 24 | </TabPane> |
21 | 25 | </Tabs> |
22 | 26 | </Card> | ... | ... |