diff --git a/package.json b/package.json index 437fe99..4f81fce 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,10 @@ "@amap/amap-jsapi-loader": "^1.0.1", "axios": "^1.6.2", "dayjs": "^1.11.10", + "mitt": "^3.0.1", "path": "^0.12.7", "prettier": "^3.1.0", + "qs": "^6.11.2", "vue": "^3.3.4", "vue-router": "^4.2.5", "vue3-seamless-scroll": "^2.0.1" diff --git a/src/api/request.ts b/src/api/request.ts index 58390ed..0360c96 100644 --- a/src/api/request.ts +++ b/src/api/request.ts @@ -1,5 +1,4 @@ import axios from 'axios'; -import requestURL from './url'; import { getAppEnvConfig } from '../utils/env.ts'; const { VITE_GLOB_API_URL } = getAppEnvConfig(); @@ -29,4 +28,15 @@ export const get = (url, params) => { reject(err.data); }); }); -}; \ No newline at end of file +}; +export const post = (url: string, params: any) => + new Promise((resolve, reject) => { + service + .post(url, params) + .then((res: any) => { + resolve(res.data); + }) + .catch((err: any) => { + reject(err); + }); + }); \ No newline at end of file diff --git a/src/assets/img/alarm-icon.png b/src/assets/img/alarm-icon.png new file mode 100644 index 0000000..4433361 Binary files /dev/null and b/src/assets/img/alarm-icon.png differ diff --git a/src/assets/img/helicopter.png b/src/assets/img/helicopter.png index ad29083..cbd2166 100644 Binary files a/src/assets/img/helicopter.png and b/src/assets/img/helicopter.png differ diff --git a/src/assets/img/position.png b/src/assets/img/position.png new file mode 100644 index 0000000..b719a87 Binary files /dev/null and b/src/assets/img/position.png differ diff --git a/src/assets/less/amap-item.less b/src/assets/less/amap-item.less new file mode 100644 index 0000000..e8aee68 --- /dev/null +++ b/src/assets/less/amap-item.less @@ -0,0 +1,59 @@ +//高德信息弹窗 +.amap-info-contentContainer { + .amap-content-body { + background: #00152B; + border: 1px solid #129BFF; + + .amap-lib-infowindow { + background: #00152B; + + .amap-lib-infowindow-title { + color: #fff; + border-bottom: 1px solid #129BFF; + padding: 4px 0; + } + + .amap-lib-infowindow-content { + color: #fff; + padding: 4px 0; + } + } + } +} + + +/*地图元素样式---start*/ +//marker +.amap-marker-label { + padding: 0; +} + +.alarm-content { + background-color: #263d5e; + color: red; + font-size: 14px; + padding: 7px 10px; + border: 1px solid #1b7ef2; + position: relative; + + .close-btn { + position: absolute; + top: -6px; + right: -8px; + width: 15px; + height: 15px; + font-size: 12px; + background: #ccc; + border-radius: 50%; + color: #fff; + text-align: center; + line-height: 15px; + box-shadow: -1px 1px 1px rgba(10, 10, 10, 0.2); + } + + .close-btn:hover { + background: #666; + } +} + +/*地图元素样式---end*/ \ No newline at end of file diff --git a/src/assets/less/index.less b/src/assets/less/index.less index 17d572a..43ff20b 100644 --- a/src/assets/less/index.less +++ b/src/assets/less/index.less @@ -1,63 +1,68 @@ +@import "./amap-item"; + .type-time { - padding-right: 4%; - padding-top: 3%; - font-size: 16px; - display: flex; - justify-content: right; + padding-right: 4%; + padding-top: 3%; + font-size: 16px; + display: flex; + justify-content: right; - span { - padding: 0 2%; - cursor: pointer; + span { + padding: 0 2%; + cursor: pointer; + } + + .select { + color: #45a2ff; + position: relative; + + &:after { + content: ''; + position: absolute; + display: block; + width: 72%; + left: 6px; + bottom: -12px; + height: 6px; + background: url('../images/light.png') no-repeat; + background-size: 100% 100%; } + } - .select { - color: #45a2ff; - position: relative; - - &:after { - content: ''; - position: absolute; - display: block; - width: 72%; - left: 6px; - bottom: -12px; - height: 6px; - background: url('../images/light.png') no-repeat; - background-size: 100% 100%; - } - } - - .unSelect { - color: #a3a4a4; - } + .unSelect { + color: #a3a4a4; + } } *::-webkit-scrollbar { - width: 12px; - height: 12px; + width: 12px; + height: 12px; } *::-webkit-scrollbar-button { - width: 0; - height: 0; - display: none; + width: 0; + height: 0; + display: none; } *::-webkit-scrollbar-corner { - background-color: transparent; + background-color: transparent; } *::-webkit-scrollbar-thumb { - border: 4px solid rgba(0, 0, 0, 0); - height: 6px; - border-radius: 25px; - background-clip: padding-box; - background-color: rgba(0, 0, 0, 0.3); + border: 4px solid rgba(0, 0, 0, 0); + height: 6px; + border-radius: 25px; + background-clip: padding-box; + background-color: rgba(0, 0, 0, 0.3); } +//直升机 .helicopter { - width: 60px; - height: 60px; - background: url('../img/helicopter.png') no-repeat; - background-size: 100% 100%; + width: 60px; + height: 37px; + background: url("../img/helicopter.png") no-repeat; + background-size: 100% 100%; + margin-bottom: 4px; } + diff --git a/src/assets/mapUtils/commonFun.ts b/src/assets/mapUtils/commonFun.ts index 8d05918..6891e80 100644 --- a/src/assets/mapUtils/commonFun.ts +++ b/src/assets/mapUtils/commonFun.ts @@ -6,7 +6,7 @@ import { mapIcon1, mapIcon2, mapIcon3, mapIcon4 } from '/@/components/chinaMap/c export function createIcon({ icon, iconSize, imageSize }) { return new AMap.Icon({ // 图标尺寸 - size: new AMap.Size(21, 44), + size: new AMap.Size(...iconSize), // 图标的取图地址 image: icon, // 图标所用图片大小 @@ -19,8 +19,8 @@ export function createIcon({ icon, iconSize, imageSize }) { * @type 1:油田医院,2:合作医院,3:医疗点 */ export function typeToIcon(type: string) { - const iconSize = [21, 44]; - const imageSize = [21, 44]; + const iconSize = [18, 44]; + const imageSize = [18, 44]; if (type == '1') { return createIcon({ icon: mapIcon1, iconSize, imageSize }); } @@ -33,4 +33,26 @@ export function typeToIcon(type: string) { if (type == '4') { return createIcon({ icon: mapIcon3, iconSize, imageSize }); } -} \ No newline at end of file +} + +/** + * + */ +export function createCircle(option) { + const { lon, lat } = option; + return new AMap.Circle({ + center: [lon, lat], + radius: 100000, //半径 + borderWeight: 3, + strokeColor: '#FF33FF', + // strokeWeight: 6, + strokeOpacity: 0.2, + fillOpacity: 0.4, + strokeStyle: 'dashed', + strokeDasharray: [10, 10], + // 线样式还支持 'dashed' + fillColor: '#1791fc', + zIndex: 50, + ...option, + }); +} diff --git a/src/assets/mapUtils/map.ts b/src/assets/mapUtils/map.ts index a130506..1f0fb05 100644 --- a/src/assets/mapUtils/map.ts +++ b/src/assets/mapUtils/map.ts @@ -1,19 +1,27 @@ import china from '/@/assets/lngLat/china.ts'; import chinaInner from '/@/assets/lngLat/chinaInner.ts'; -import { typeToIcon } from '/@/assets/mapUtils/commonFun.ts'; -import { helicopter, mapbg } from '/@/components/chinaMap/chinaHooks.ts'; +import { createCircle, createIcon, typeToIcon } from '/@/assets/mapUtils/commonFun.ts'; +import { aircraftContent, alarmIcon } from '/@/components/chinaMap/chinaHooks.ts'; export const defaultOption = { center: [106.3, 37.87], //地图中心点 zoom: 4.3, //地图显示的缩放级别 mapStyle: 'darkblue', //地图样式 }; -export const mapPlugin = ['AMap.ToolBar', 'AMap.Driving', 'AMap.GeoJSON', 'AMap.MarkerCluster']; +export const mapPlugin = ['AMap.ToolBar', 'AMap.Driving', 'AMap.GeoJSON', 'AMap.MarkerCluster', 'AMap.PlaceSearch']; export const Map = { map: null, overlayGroup: null, // 地图中marker - aircraftGroup: null, //直升机marker - cluster: null, + circleGroup: null, //直升机背景圆 + cluster: null, //直升机marker点聚合图层 + airMarkerList: [], //直升机markerList + placeSearch: null, //地点查询 + marker: null, // 健康终端报警 + /** + * @desc 地图舒适化 + * @param el dom元素 id选择器 + * @param option 地图配置 + */ initMap({ el, option }) { const { zoom, center, mapStyle } = { ...defaultOption, ...option }; //基本地图加载 @@ -28,8 +36,9 @@ export const Map = { this.drawInnerLine(); this.drawOutLine(); }); - }, - // 中国外层边界线 + } /** + * @desc 中国外层边界线 + */, drawOutLine() { let geojson = new AMap.GeoJSON({ geoJSON: china, @@ -46,8 +55,9 @@ export const Map = { }, }); this.map.add(geojson); - }, - // 中国内层边界线 + } /** + * @desc 中国内层边界线 + */, drawInnerLine() { let geojson = new AMap.GeoJSON({ geoJSON: chinaInner, @@ -60,25 +70,39 @@ export const Map = { }, }); this.map.add(geojson); - }, - // 创建覆盖物组,用于批量处理点 (控制显隐,点击事件等) - createOverlay(result) { + }, // 清空当前地图上除了边界线的覆盖物 + removeOverlayGroup() { this.clearAircraft(); this.clearOverlay(); + this.clearAirRange(); + this.clearMarker(); // + if (this.airMarkerList.length > 0) { + for (let i = 0; i < this.airMarkerList.length; i++) { + this.airMarkerList[i].setMap(null); + } + } + // this.map.remove(this.airMarkerList); + }, // + /** + * @desc 创建覆盖物组,用于批量处理点 (控制显隐,点击事件等) + * @param result + */ + createOverlay(result) { + this.removeOverlayGroup(); this.overlayGroup = new AMap.OverlayGroup(); for (let i = 0; i < result.length; i++) { let { type, lon, lat } = result[i]; + if (!lon || !lat || !type) { + continue; + } // 创建一个 Icon let startIcon = typeToIcon(type); // 将 icon 传入 marker let startMarker = new AMap.Marker({ // 点的坐标 - position: [lon, lat], - // 指定点的icon,也可以是个url ,但这样就不能对图片进行设置了 - icon: startIcon, - //启用点击事件 如果需要点击事件,必须开启 - clickable: true, - //点额外携带的数据,用户自定义属性,支持JavaScript API任意数据类型 + position: [lon, lat], // 指定点的icon,也可以是个url ,但这样就不能对图片进行设置了 + icon: startIcon, //启用点击事件 如果需要点击事件,必须开启 + clickable: true, //点额外携带的数据,用户自定义属性,支持JavaScript API任意数据类型 extData: result[i], }); // 可以调用setExtData()设置自定义属性 @@ -100,130 +124,123 @@ export const Map = { clearOverlay() { if (this.overlayGroup) { this.map.remove(this.overlayGroup); + this.overlayGroup.setMap(null); this.overlayGroup = null; } }, - //创建直升机marker + /** + * @desc 创建直升机marker + * @param result + */ createAircraft(result) { - this.clearAircraft(); - this.clearOverlay(); - // this.aircraftGroup = new AMap.OverlayGroup(); + this.removeOverlayGroup(); + this.clearActiveHospital(); if (!Array.isArray(result) && result.length == 0) return; - const content = ` -
-
-
-
-
-
-
- 4009-120-999 -
-
-
- `; - const markers = []; - for (let i = 0; i < result.length; i++) { - let { lon, lat } = result[i]; - const marker = new AMap.Marker({ - // 点的坐标 - position: [lon, lat], - //启用点击事件 如果需要点击事件,必须开启 - clickable: true, - title: `4009-120-999`, - content: content, - offset: new AMap.Pixel(-16, -30), - }); - markers.push(marker); - } - - // //将组 挂载到地图上 - // this.aircraftGroup.setMap(this.map); - // //将需要批量控制的点放到一个组中,通过控制组,就可以操作组内所有所有覆盖物 - // this.aircraftGroup.addOverlay(markers); - - if (this.cluster) { - // this.cluster.setMap(null); - this.map.remove(this.cluster); - // this.cluster = null; - this.cluster.setMap(null); - } - //使用renderCluserMarker属性实现聚合点的完全自定义绘制 - let sts = [ - { - url: mapbg, - size: new AMap.Size(32, 32), - offset: new AMap.Pixel(-16, -30), - textColor: '#fff', - textSize: 20, - }, - ]; + this.createAirRange(result); let that = this; - let arr = result.map((item) => ({ weight: 4, lnglat: [item.lon, item.lat] })); + let arr = result.map((item) => ({ weight: 4, lnglat: [item.lon, item.lat], params: item.name })); let count = 4; this.cluster = new AMap.MarkerClusterer( that.map, // 地图实例 arr, { renderMarker: (context) => { - var factor = Math.pow(context.count / count, 1 / 18); - var div = document.createElement('div'); - var Hue = 180 - factor * 180; - var bgColor = 'hsla(' + Hue + ',100%,50%,0.7)'; - var fontColor = 'hsla(' + Hue + ',100%,20%,1)'; - var borderColor = 'hsla(' + Hue + ',100%,40%,1)'; - var shadowColor = 'hsla(' + Hue + ',100%,50%,1)'; - var size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20); + let factor = Math.pow(context.count / count, 1 / 18); + let div = document.createElement('div'); + div.className = 'render-air'; + let Hue = 180 - factor * 180; + let size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20); div.style.width = div.style.height = size + 'px'; - div.innerHTML = content || context.count; - div.style.lineHeight = size + 'px'; - div.style.color = fontColor; + div.innerHTML = aircraftContent() || context.count; + div.style.color = '#fff'; div.style.fontSize = '14px'; - div.style.textAlign = 'center'; context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2)); context.marker.setContent(div); + this.airMarkerList.push(context.marker); + context.marker.on('click', () => { + console.log('data', context); + }); }, } ); - console.log('xx', this.cluster); this.cluster.setMap(this.map); - this.getRange(result); }, clearAircraft() { - if (this.aircraftGroup) { - this.map.remove(this.aircraftGroup); - this.aircraftGroup = null; + if (this.cluster) { + this.map.remove(this.cluster); + this.cluster = null; + } + } /** + * @desc 飞机范围 + * @param list + */, + createAirRange(list) { + this.removeOverlayGroup(); + this.circleGroup = new AMap.OverlayGroup(); + for (let i = 0; i < list.length; i++) { + let circle = createCircle({ lon: list[i].lon, lat: list[i].lat }); + this.circleGroup.setMap(this.map); + this.circleGroup.addOverlay(circle); } }, - getRange(list) { - //绘制飞机的范围图 - var array = new Array(list.length); - for (var i = 0; i < list.length; i++) { - var circle = new AMap.Circle({ - center: [list[i].lon, list[i].lat], - radius: 100000, //半径 - borderWeight: 3, - strokeColor: '#FF33FF', - strokeOpacity: 1, - strokeWeight: 6, - strokeOpacity: 0.2, - fillOpacity: 0.4, - strokeStyle: 'dashed', - strokeDasharray: [10, 10], - // 线样式还支持 'dashed' - fillColor: '#1791fc', - zIndex: 50, - }); - circle.setMap(this.map); - array.push(circle); + clearAirRange() { + if (this.circleGroup) { + this.map.remove(this.circleGroup); + this.circleGroup = null; + } + } /** + * @desc 地方医院医疗点显示 + * @param cityCode 城市code + * @param keywords 搜索名称 + */, + createActiveHospital(cityCode: string, keywords = '医院') { + this.removeOverlayGroup(); + this.clearActiveHospital(); + if (!cityCode) return; + this.placeSearch = new AMap.PlaceSearch({ + pageSize: 30, // 单页显示结果条数 + pageIndex: 1, // 页码 + city: cityCode, // 兴趣点城市 + citylimit: true, //是否强制限制在设置的城市内搜索 + map: this.map, // 展现结果的地图实例 + // panel: 'map_hospital', // 结果列表将在此容器中进行展示。 + autoFitView: false, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围 + }); + //关键字查询 + this.placeSearch.search(keywords); + }, + clearActiveHospital() { + if (this.placeSearch) { + // debugger; + this.placeSearch.clear(); + this.placeSearch = null; + } + }, + createMarker(option, callbck: () => {}) { + this.clearMarker(); + let { lon, lat, zoom = 12 } = option; + const icon = createIcon({ icon: alarmIcon, iconSize: [36, 42], imageSize: [36, 42] }); + this.marker = new AMap.Marker({ + position: [lon, lat], + icon: icon, + offset: new AMap.Pixel(0, 0), //设置偏移量 + label: { + content: `
+
${option?.realName},${option?.eventType_dictText}
+
`, + offset: new AMap.Pixel(0, -9), + direction: 'top', + }, + }); + this.map.add(this.marker); + this.map.setZoomAndCenter(zoom, [lon, lat]); + this.marker.on('click', callbck); + }, + clearMarker() { + if (this.marker) { + this.marker?.setMap(null); + this.marker = null; } - return array; }, }; diff --git a/src/components/body-d-left/bodyLeftHooks.ts b/src/components/body-d-left/bodyLeftHooks.ts index 27833ff..b3a0649 100644 --- a/src/components/body-d-left/bodyLeftHooks.ts +++ b/src/components/body-d-left/bodyLeftHooks.ts @@ -1,6 +1,43 @@ import { ref } from 'vue'; import * as echarts from 'echarts'; +export function useStatistics() { + const statistics = ref([ + { + name: '最新呼入电话', + value: '', + key: 'gross', + }, + { + name: '最新受理电话', + value: '', + key: 'alerdyreceive', + }, + { + name: '突发重大伤病应急', + value: '', + key: 'jqqz', + }, + { + name: '重大伤病应急就医', + value: '', + key: 'dbjy', + }, + ]); + + function setStatistics(data = {}) { + statistics.value.forEach((item) => { + const { key } = item; + item.value = data[key]; + }); + } + + return { + statistics, + setStatistics, + }; +} + export const timeTab = [ { name: '全部', diff --git a/src/components/body-d-left/oneL.vue b/src/components/body-d-left/oneL.vue index 8457a17..d83b558 100644 --- a/src/components/body-d-left/oneL.vue +++ b/src/components/body-d-left/oneL.vue @@ -12,9 +12,9 @@ >
-
- {{ item?.title }} - {{ item?.gross }} +
+ {{ item?.name }} + {{ item?.value }}
@@ -23,12 +23,13 @@ + \ No newline at end of file diff --git a/src/utils/bus.ts b/src/utils/bus.ts new file mode 100644 index 0000000..5dc098c --- /dev/null +++ b/src/utils/bus.ts @@ -0,0 +1,5 @@ +// 引入mitt插件 index.ts +import mitt from "mitt" + +const $bus = mitt(); +export default $bus \ No newline at end of file diff --git a/src/utils/socket.ts b/src/utils/socket.ts new file mode 100644 index 0000000..3f67c53 --- /dev/null +++ b/src/utils/socket.ts @@ -0,0 +1,56 @@ +import { onMounted, onUnmounted, ref } from 'vue'; +//健康监测工具报警 +export const basicPath = 'ws://192.168.1.16:7098/websocket/watchMonitor'; +//应急求助 +export const emergencyPath = 'ws://192.168.1.16:7011/websocket/emergency'; +const token = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWRlbnRpZmllciI6ImU5Y2EyM2Q2OGQ4ODRkNGViYjE5ZDA3ODg5NzI3ZGFlIiwiZXhwIjoxNzAyNjA0MDI3fQ.MYf9yUnXO0RTFL4u96pN1Z2mBwtXU5W-QlxDgoAsoOw'; + +export function useSocket(path = basicPath) { + const socket = ref(); + let socketData = ref({}); + + function init() { + if (typeof WebSocket === 'undefined') { + alert('您的浏览器不支持socket'); + } else { + // 实例化socket + socket.value = new WebSocket(path, [token]); + // 监听socket连接 + socket.value.onopen = open; + // 监听socket错误信息 + socket.value.onerror = error; + // 监听socket消息 + socket.value.onmessage = getMessage; + } + } + + function open() { + console.log('socket连接成功'); + } + + function error() { + console.log('socket连接错误'); + } + + function getMessage(msg) { + console.log('y', msg.data); + socketData.value = JSON.parse(msg.data); + } + + function setSocketData(data) { + socketData.value = data; + } + + function close() { + console.log('socket已经关闭'); + } + + onMounted(() => { + init(); + }); + onUnmounted(() => { + socket.value.onclose = close; + }); + return { socket, socketData, setSocketData }; +} \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 0000000..3a539b8 --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,25 @@ +import * as dayjs from 'dayjs'; + +export function getZero(num: any) { + if (num < 10) { + return '0' + num; + } + return num; +} + +export function useTopTime() { + const timeLeft = dayjs().format('YYYY年MM月DD日 HH时mm分ss秒'); + const timeRight = + dayjs().diff(dayjs('2018-10-01'), 'day') + + '天' + + getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD') + ' 00:00:00'), 'hours')) + + '时' + + getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD HH') + ':00:00'), 'minutes')) + + '分' + + getZero(dayjs().diff(dayjs(dayjs().format('YYYY-MM-DD HH:mm') + ':00'), 'seconds')) + + '秒'; + return { + timeLeft, + timeRight, + }; +} \ No newline at end of file diff --git a/src/views/components/dialogHooks.ts b/src/views/components/dialogHooks.ts new file mode 100644 index 0000000..8d5d3d7 --- /dev/null +++ b/src/views/components/dialogHooks.ts @@ -0,0 +1,20 @@ +import { ref } from 'vue'; + +export function useDialog({ title }) { + const visible = ref(false); + + function openDialog() { + visible.value = true; + } + + function closeDialog() { + visible.value = false; + } + + return { + visible, + title, + openDialog, + closeDialog, + }; +} \ No newline at end of file diff --git a/src/views/components/emergencyDialog.vue b/src/views/components/emergencyDialog.vue new file mode 100644 index 0000000..b8647fe --- /dev/null +++ b/src/views/components/emergencyDialog.vue @@ -0,0 +1,86 @@ + + + \ No newline at end of file diff --git a/src/views/components/employeeDialog.vue b/src/views/components/employeeDialog.vue new file mode 100644 index 0000000..b8647fe --- /dev/null +++ b/src/views/components/employeeDialog.vue @@ -0,0 +1,86 @@ + + + \ No newline at end of file diff --git a/src/views/index.api.ts b/src/views/index.api.ts new file mode 100644 index 0000000..f864e36 --- /dev/null +++ b/src/views/index.api.ts @@ -0,0 +1,8 @@ +import { get } from '/@/api/request'; + +export enum Api { + deleteWatchNo = 'health-watch/watch/watchMonitorData/delete', +} + +//获取全部部门 +export const deleteWatchNo = (params: any) => get(Api.deleteWatchNo, params); \ No newline at end of file diff --git a/src/views/index.vue b/src/views/index.vue index e4e3c35..c551182 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -22,11 +22,13 @@
-
+
+ +