From 1237f377b9ec9238135eefb966cd384d0a91f343 Mon Sep 17 00:00:00 2001 From: "gao.zhiyuan" Date: Wed, 2 Aug 2023 17:43:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E4=BA=A4GIS=E5=B8=83=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/lib/controllers/overview/operation.js | 33 ++++ api/app/lib/index.js | 3 + api/app/lib/routes/overview/index.js | 3 + api/app/lib/schedule/zhidiao.js | 44 +++++ api/package.json | 1 + web/client/assets/images/gis/bus.png | Bin 0 -> 2215 bytes web/client/src/layout/index.js | 1 - .../src/sections/quanju/actions/example.js | 24 ++- .../containers/footer/operation/index.js | 12 +- .../containers/footer/operation/left.js | 51 +++-- .../quanju/containers/public/olMap.js | 175 ++++++++++++++++-- web/client/src/utils/webapi.js | 2 + 12 files changed, 313 insertions(+), 36 deletions(-) create mode 100644 api/app/lib/schedule/zhidiao.js create mode 100644 web/client/assets/images/gis/bus.png diff --git a/api/app/lib/controllers/overview/operation.js b/api/app/lib/controllers/overview/operation.js index 03f9414e..7e06102d 100644 --- a/api/app/lib/controllers/overview/operation.js +++ b/api/app/lib/controllers/overview/operation.js @@ -144,6 +144,12 @@ async function busRunRealTime (ctx) { } }) + app.fs.zhidiaoData ? + app.fs.zhidiaoData.busRun = busRunRes : + app.fs.zhidiaoData = { + busRun: busRunRes + } + let now = moment() let hourBefour = now.subtract(1, 'h') ctx.status = 200; @@ -157,8 +163,35 @@ async function busRunRealTime (ctx) { } } +async function busLine (ctx) { + try { + const { models } = ctx.fs.dc; + const { app } = ctx + const { busLicence } = ctx.query + + const zhidiaoData = app.fs.zhidiaoData + let rslt = {} + if (zhidiaoData) { + let corBus = (zhidiaoData.busRun || []).find(b => b.busNoChar == busLicence) + if (corBus) { + rslt = (zhidiaoData.busLine || {})[corBus.lineNo] || {} + } + } + + ctx.status = 200; + ctx.body = rslt + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + module.exports = { busCarLevelList, vehicleStatistic, busRunRealTime, + busLine, }; \ No newline at end of file diff --git a/api/app/lib/index.js b/api/app/lib/index.js index bf0334d8..d6b9be07 100644 --- a/api/app/lib/index.js +++ b/api/app/lib/index.js @@ -6,6 +6,7 @@ const path = require('path'); const authenticator = require('./middlewares/authenticator'); // const apiLog = require('./middlewares/api-log'); const paasRequest = require('./service/paasRequest'); +const schedule = require('./schedule') module.exports.entry = function (app, router, opts) { app.fs.logger.log('info', '[FS-AUTH]', 'Inject auth and api mv into router.'); @@ -18,6 +19,8 @@ module.exports.entry = function (app, router, opts) { // router.use(apiLog(app, opts)); // 实例其他平台请求方法 paasRequest(app, opts) + // 定时任务 + schedule(app, opts) router = routes(app, router, opts); }; diff --git a/api/app/lib/routes/overview/index.js b/api/app/lib/routes/overview/index.js index 9bd23c45..da38a270 100644 --- a/api/app/lib/routes/overview/index.js +++ b/api/app/lib/routes/overview/index.js @@ -26,4 +26,7 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/bus/run_realtime'] = { content: '获取公交实时运行信息', visible: false }; router.get('/bus/run_realtime', operation.busRunRealTime); + + app.fs.api.logAttr['GET/bus/run/line'] = { content: '获取公交运行路线', visible: false }; + router.get('/bus/run/line', operation.busLine); } \ No newline at end of file diff --git a/api/app/lib/schedule/zhidiao.js b/api/app/lib/schedule/zhidiao.js new file mode 100644 index 00000000..1245e3fe --- /dev/null +++ b/api/app/lib/schedule/zhidiao.js @@ -0,0 +1,44 @@ +const zhidiaoSystem = require('../../../utils/zhidiaoSystem.js'); +const moment = require('moment') + +let isDev = false +isDev = true + +module.exports = function (app, opts) { + const busLines = app.fs.scheduleInit( + { + interval: '24 0 */1 * * *', + immediate: true, + proRun: false, + disabled: false + }, + + async () => { + try { + const { models, ORM: sequelize } = app.fs.dc + const Authorization = zhidiaoSystem.createAuthorization() + const busRunRes = await app.fs.zhidiaoRequest.get(`getBusRunList`, { + header: { + Authorization: Authorization + } + }) + const busLineRes = await app.fs.zhidiaoRequest.get(`getLinePointList`, { + header: { + Authorization: Authorization + } + }) + console.log(`定时获取BUS路线信息`); + app.fs.zhidiaoData = { + busLine: busLineRes, + busRun: busRunRes + } + } catch (error) { + console.error(error); + } + } + ) + + return { + busLines, + } +} \ No newline at end of file diff --git a/api/package.json b/api/package.json index dabf4b39..4e66560a 100644 --- a/api/package.json +++ b/api/package.json @@ -28,6 +28,7 @@ "koa2-swagger-ui": "^5.3.0", "md5-node": "^1.0.1", "moment": "^2.24.0", + "node-schedule": "^2.1.1", "path": "^0.12.7", "path-to-regexp": "^3.0.0", "pg": "^7.9.0", diff --git a/web/client/assets/images/gis/bus.png b/web/client/assets/images/gis/bus.png new file mode 100644 index 0000000000000000000000000000000000000000..7d9320da192b31a2e544c78353eafabb5bf11cf0 GIT binary patch literal 2215 zcmWlbc{CIX8^#C861U~1?iG3Kgm)wwmY{(g^x&~xUO)m^%>d97Ol1{Lll}6 zX3$KgT$C-kk$qwcW4U9Ugqi2lbI$WV=e&RX&ikJ8{_);8P9n-m%S#go1X)W9GuzeF zTD7jE*lJZwtIk;skpNrb5dyVMflDBWP8_$fGhb~IfK`(Ld%&Xwmy7XR3+%^n=x-eC zgGW02crbhqlOBZ9;1-LcTm;p?D;k!7Q9rzD;d}*_(=h3WeH2F-%8SUS#JNAxT#hQ9N!(O|>IxeB%}()YC>!4B>nk+{fTjh>KNlEroLqyhagJ1vd)(S@>fW zn-6ae!kb{y0-rMYGjKi|;lm)eB8Z7|>4@t_L_OjXWBwhHii0<>vp zWXyC4QNDuRlw|+{fG+@V1zbJ`=FuoXoe-V#Xqm-7Q^LAwyqH1003|#;oI#=h88fIE zT27zFohjswAd!cJNfh-gXAFWoj+hZVdOw@lB?$Y1fDr_8kou3{G8-NoTpYr^mW7lz zGcJ8dsGkmhBXH`%sV6Yb2fD0>ZW>C(0L9WD)tn^ zoHC|b3avuu=FMqQgqk_u6!Wm_!Gc!ClzPUj`u$1uyWFkmi)y#OsHG0e-{Gm=WN*1O zshZTkJ$ZO%Vy{a4M}_!-jfvb1H-qn_F7jK6Li6KYtnFPE1Vlcysd${I82kOH0ek%PXrAtV$>p_E>s(VY+8}Dw5gFetpQO$;8W^=N+8+LtX}D~s7O5;LeAK(y8~j>+ul*YG^v5@j z`669oXSk=OrG~}Vm=?(WM2M%1?e3^jD*WKDw9)>(>&b_w`Rl5APl{KD=G5K1{q64H%iYlBEsovfny{!F8giG_7qL2n!Z!c{NIerZF#vc|3Vy+&3I2W|3lUYt=q)z$R1svYxuSL3P(v$1|>R|J& zxZ>IV4=;Yv?=}ipSMS=Wph9;{x0bvh9^}K$Ovb-77pDx_k)`|%hZrndA8DdPf>GNwNLf4&ZpVtfrr&t zn=@>5d*8OdVQoIOO_o1t#Xgd8J5weudaDg#*FpJtbhOLiJNpzWcdC&Bt9 ziM-*NSk&L$L&vhu=w4r>Igoz0S}(EbvdPnHG7kAk&kEb6R9Z`mt1n0@zYJ(Unqjf+ zp&NlC@@~*Ng;4GNX>Ytinfi6fuf2jxPru5U=8|ihl_Fejv?VuMC4GM~EX_$~)FZCf F{tumX*8u { flat(routes); //将路由信息全部放在session,方便后面拿了用(权限控制的时候) - //console.log('combineRoutes1', combineRoutes) sessionStorage.setItem('allRoutes', JSON.stringify(combineRoutes)); return combineRoutes; } diff --git a/web/client/src/sections/quanju/actions/example.js b/web/client/src/sections/quanju/actions/example.js index dd5c1ed5..180e5f1e 100644 --- a/web/client/src/sections/quanju/actions/example.js +++ b/web/client/src/sections/quanju/actions/example.js @@ -1,6 +1,6 @@ 'use strict'; -import { basicAction } from '@peace/utils' +import { basicAction, clearData } from '@peace/utils' import { ApiTable } from '$utils' export function getMembers (orgId) { @@ -180,13 +180,25 @@ export function getVideoCenterList () { }); } -export function getBusRuntTime () { +export function getBusRunTime () { return dispatch => basicAction({ type: 'get', dispatch: dispatch, - actionType: 'GET_VIDEO_CENTER_LIST', - url: ApiTable.videoCenterList, - msg: { error: '获取视频中心列表失败' }, - reducer: { name: 'videoCenterList' } + actionType: 'GET_BUS_REALTIME_LIST', + url: ApiTable.busRealtimeList, + msg: { error: '获取公交实时信息失败' }, + reducer: { name: 'busRunTime', params: { noClear: true } } + }); +} + +export const getBuslineActionType = 'GET_BUS_LINE' +export function getBusline (busLicence) { + return dispatch => basicAction({ + type: 'get', + dispatch: dispatch, + actionType: getBuslineActionType, + url: ApiTable.busLine + `?busLicence=${busLicence}`, + msg: { error: '获取公交路线信息失败' }, + reducer: { name: 'busLine', params: { noClear: true } } }); } \ No newline at end of file diff --git a/web/client/src/sections/quanju/containers/footer/operation/index.js b/web/client/src/sections/quanju/containers/footer/operation/index.js index 1f723da9..92438b65 100644 --- a/web/client/src/sections/quanju/containers/footer/operation/index.js +++ b/web/client/src/sections/quanju/containers/footer/operation/index.js @@ -2,8 +2,9 @@ import React, { useEffect, useState } from 'react' import Left from './left' import Right from './right' import { connect } from 'react-redux' -import { getBusTierList } from '../../../actions/example' +import { getBusTierList, getBusRunTime } from '../../../actions/example' +let busRealtimeInterval = null const Operation = (props) => { const [roadData, setRoadData] = useState() @@ -14,6 +15,13 @@ const Operation = (props) => { setLoading(false) setRoadData(res.payload.data || {}) }) + busRealtimeInterval = setInterval(() => { + dispatch(getBusRunTime()) + }, 1000 * 16) + dispatch(getBusRunTime()) + return () => { + clearInterval(busRealtimeInterval) + } }, []) return ( @@ -23,7 +31,7 @@ const Operation = (props) => { ) } -function mapStateToProps(state) { +function mapStateToProps (state) { return { diff --git a/web/client/src/sections/quanju/containers/footer/operation/left.js b/web/client/src/sections/quanju/containers/footer/operation/left.js index a00da638..2b063202 100644 --- a/web/client/src/sections/quanju/containers/footer/operation/left.js +++ b/web/client/src/sections/quanju/containers/footer/operation/left.js @@ -1,11 +1,15 @@ import React, { useState, useEffect } from 'react'; import { connect } from 'react-redux'; -import { Input, Tree } from 'antd' +import { Input, Tree, message } from 'antd' import Module from '../../public/module' -import { getBusTierList } from '../../../actions/example' +import { getBusTierList, getBusline, getBuslineActionType } from '../../../actions/example' +import { basicAction, clearData } from '@peace/utils' import './style.less' const Left = (props) => { + const { busTier, dispatch } = props + const style = { height: "97%", marginTop: "3%" } + const [treeData, setTreeData] = useState([]) const [treeDataList, setTreeDataList] = useState([]) const [expandedKeys, setExpandedKeys] = useState([]); @@ -20,10 +24,7 @@ const Left = (props) => { useEffect(() => { const { dispatch } = props dispatch(getBusTierList()) - }, []) - const { busTier } = props - const style = { height: "97%", marginTop: "3%" } const onChange = (e) => { const { value } = e.target; @@ -38,7 +39,6 @@ const Left = (props) => { }; const loop = (treeData) => treeData.map((item) => { - // console.log(item,'===> item -----'); const index = item.title != null && item.title.indexOf(searchValue); const beforeStr = item.title != null && item.title.substr(0, index); const afterStr = item.title != null && item.title.substr(index + searchValue.length); @@ -91,15 +91,17 @@ const Left = (props) => { title: e.name, children: e.child.map(s => { return { - key: s.name == '--' || s.name == null ? Math.ceil(Math.random() * 100) : s.name, + key: s.name == '--' || s.name == null ? Math.ceil(Math.random() * 100) : s.name, title: s.name, children: s.child.map(i => { return { - key: i.name == '--' || i.name == null ? Math.ceil(Math.random() * 100) : i.name, + key: + i.name == '--' || i.name == null ? Math.ceil(Math.random() * 100) : i.name, title: i.name, children: i.child.map(x => { return { - key: x.name == '--' || x.name == null ? Math.ceil(Math.random() * 100) : x.name, + key: 'bus_' + x.id + '_' + x.name || '--', + // key: x.name == '--' || x.name == null ? Math.ceil(Math.random() * 100) : x.name, title: x.name == null ? '--' : x.name, } }) @@ -128,7 +130,7 @@ const Left = (props) => { setTreeDataList(dataList) }, [busTier]) return ( -
+
@@ -160,6 +162,31 @@ const Left = (props) => { expandedKeys={expandedKeys} autoExpandParent={autoExpandParent} treeData={loop(treeData)} + onSelect={(selectedKeys, { selected, selectedNodes, node, event }) => { + let isClear = true + if (selectedKeys.length) { + if (selected) { + let k = selectedKeys[0] + let kArr = k.split('_') + let busLicence = kArr[2] + if (busLicence && busLicence != '--') { + dispatch(getBusline(busLicence)).then(res => { + if (res.success) { + let data = res.payload.data + + if (!data || !data.length) { + message.warning(`没有查询到${busLicence}的路线信息`) + } + } + }) + isClear = false + } + } + } + if (isClear) { + clearData(dispatch, { actionType: getBuslineActionType }) + } + }} /> :
暂无数据
} @@ -169,8 +196,8 @@ const Left = (props) => {
); }; -function mapStateToProps(state) { - const { busTier } = state +function mapStateToProps (state) { + const { busTier, } = state return { busTier: busTier } diff --git a/web/client/src/sections/quanju/containers/public/olMap.js b/web/client/src/sections/quanju/containers/public/olMap.js index 85e7d53c..5a34110c 100644 --- a/web/client/src/sections/quanju/containers/public/olMap.js +++ b/web/client/src/sections/quanju/containers/public/olMap.js @@ -6,10 +6,12 @@ import moment from 'moment'; import { OlMapRequest } from '$utils' const OlMap = (props) => { - const { dispatch, actions, user, olMapArcgisHost, olMapGeoDataHost, patrolList, roadProjectList, tab } = props + const { dispatch, actions, user, olMapArcgisHost, olMapGeoDataHost, patrolList, roadProjectList, tab, busRunTime, busLine } = props const [olMapOpenData, setOlMapOpenData] = useState([]) const [olMap, setOlMap] = useState() const [pointItem, setPointItem] = useState({}) + // + const [busRunData, setBusRunData] = useState([]) useEffect(() => { setOlMapOpenData([]) @@ -158,9 +160,11 @@ const OlMap = (props) => { roadProjectList.forEach((d, index) => { olMap.removeGeometryLayer('geometry_road_' + index) }) + busRunData.forEach((d, index) => { + olMap.removeGeometryLayer('geometry_bus_' + index) + }) patrolList.forEach((d, index) => { - console.log(d, index); olMap.addGeometryLayer({ features: [ { @@ -221,6 +225,9 @@ const OlMap = (props) => { patrolList.forEach((d, index) => { olMap.removeGeometryLayer('geometry_patrol_' + index) }) + busRunData.forEach((d, index) => { + olMap.removeGeometryLayer('geometry_bus_' + index) + }) roadProjectList.forEach((d, index) => { olMap.addGeometryLayer({ @@ -268,7 +275,141 @@ const OlMap = (props) => { } }, [roadProjectList, olMap, tab]) + useEffect(() => { + if (busRunTime && busRunTime.length) { + setBusRunData(busRunTime) + } + }, [busRunTime]) + + const drawBusRunPoint = () => { + patrolList.forEach((d, index) => { + olMap.removeGeometryLayer('geometry_patrol_' + index) + }) + roadProjectList.forEach((d, index) => { + olMap.removeGeometryLayer('geometry_road_' + index) + }) + + busRunData.forEach((d, index) => { + olMap.addGeometryLayer({ + features: [ + { + name: 'busPoint', + attributes: { + callbackParams: { + type: 'busPoint', + busNo: d.busNo, + }, + callback: (p) => { + setPointItem(p) + setOlMapOpenData([{ + n: '车牌号', + v: d.busNoChar + }, { + n: '司机', + v: d.employeeName + }, { + n: '工号', + v: d.opNo + }, { + n: '调度状态', + v: d.willRun, + map: [ + { text: '非运营', value: '0' }, + { text: '运营', value: '1' }, + { text: '维修', value: '2' }, + { text: '保养', value: '3' }, + { text: '专车', value: '4' }, + { text: '机动车', value: '5' }, + { text: '包车', value: '6' }, + { text: '班车', value: '7' }, + { text: '检车', value: '8' }, + ], + }, { + n: '实际发车时间', + v: d.lastDepTime + }, { + n: '定位时间', + v: d.siteTime + },]) + olMap.addOverlay('clickOpen', { + id: 'clickOpen', + offset: [0, 4], // 偏移 + position: [d.lng, d.lat], // 坐标 + // position: [115.944220000000, 28.545380000000], + autoPan: true, + autoPanMargin: 100, + positioning: 'top-right' + }) + } + }, + geometry: [d.lng, d.lat], + // geometry: [115.944220000000, 28.545380000000], + geometryType: 'Point', + }, + ], + style: { + icon: { + src: '/assets/images/gis/bus.png', + scale: 0.7, // 图标引用 + }, + }, + selectStyle: { + icon: { + src: '/assets/images/gis/bus.png', + scale: 0.8, // 图标引用 + }, + }, + layerName: 'geometry_bus_' + index + }); + }); + } + useEffect(() => { + if (busRunData.length && olMap && tab == 'operation') { + if (!busLine.length) { + drawBusRunPoint() + } + } + }, [busRunData, olMap, tab]) + + useEffect(() => { + if (busLine && olMap && tab == 'operation') { + olMap.addGeometryLayer({ + features: [ + { + name: 'busLine', + attributes: { + }, + geometry: busLine.map(b => { + return [b.pointLng, b.pointLat] + }), + // geometry: [115.944220000000, 28.545380000000], + geometryType: 'LineString', + style: { stroke: { width: 5, color: '#9933FF' } }, + selectStyle: { stroke: { width: 8, color: '#9933FF' } }, + }, + ], + style: { stroke: { width: 5, color: '#9933FF' } }, + selectStyle: { stroke: { width: 8, color: '#9933FF' } }, + layerName: 'geometry_bus_line' + }); + if (busLine.length) { + olMap.setCenter([ + (busLine[0].pointLng + busLine[busLine.length - 1].pointLng) / 2, + (busLine[0].pointLat + busLine[busLine.length - 1].pointLat) / 2 + ]) + busRunData.forEach((d, index) => { + olMap.removeGeometryLayer('geometry_bus_' + index) + }) + } else { + drawBusRunPoint() + } + } else if (tab != 'operation' && olMap) { + olMap.removeGeometryLayer('geometry_bus_line') + } + }, [busLine, olMap, tab]) + const isRoadProject = pointItem.report_type == 'road' + // const isBusPoint = pointItem.busNoChar return (
@@ -278,25 +419,27 @@ const OlMap = (props) => { width: 340, backgroundPosition: 'center', backgroundSize: '100% 100%', - minHeight: 240, + minHeight: 180, padding: '24px', backgroundRepeat: 'no-repeat', color: '#fff' }}> { olMapOpenData.map(s => { - return
-
{s.n}
-
: - { - s.map ? - s.map.find(sm => sm.value == s.v)?.text - : s.v || s.v == 0 ? - s.v - : '-' - } + return ( +
+
{s.n}
+
: + { + s.map ? + s.map.find(sm => sm.value == s.v)?.text + : s.v || s.v == 0 ? + s.v + : '-' + } +
-
+ ) }) }
@@ -425,12 +568,14 @@ const OlMap = (props) => { } function mapStateToProps (state) { - const { auth, global } = state; + const { auth, global, busRunTime, busLine } = state; return { user: auth.user, actions: global.actions, olMapArcgisHost: global.olMapArcgisHost, olMapGeoDataHost: global.olMapGeoDataHost, + busRunTime: busRunTime.data || [], + busLine: busLine.data || [], }; } diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index c45ebbad..181f500b 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -200,6 +200,8 @@ export const ApiTable = { //大屏运营 —— 公交车辆层级信息 getBusTier: '/operation/car_level', + busRealtimeList:'bus/run_realtime', + busLine:'bus/run/line', //获取路政列表 getHighways: 'road_manage', putHighways: 'road_manage',