Vue、OpenLayers结合天地图实现Gis
程序员文章站
2022-07-02 09:49:01
...
安装依赖:
npm install ol
主要代码:
<template>
<div class="command-center">
<div id="mapContainer" class="mapContainer"></div>
<div class="customDiv">
<div class="HeadDiv">
<el-popover
placement="bottom"
width="500"
trigger="manual"
:offset="-50"
v-model="visibleSearch">
<div style="max-height: 460px; overflow-y: auto;">
<div v-for="(item, index) in poiList" :key="index" @click="selectOne(item)" style="line-height: 24px;cursor: pointer">
<el-row>
<el-col :span="16">{{item.name}}</el-col>
<el-col :span="8">{{item.lng}} {{item.lat}}</el-col>
</el-row>
</div>
</div>
<el-input class="inputAddress" placeholder="请输入地点" v-model="address" slot="reference" clearable>
<el-button slot="append" icon="el-icon-search" @click="searchPlace(address)" >查询</el-button>
</el-input>
</el-popover>
<!-- <el-select-->
<!-- v-model="address"-->
<!-- :multiple="false"-->
<!-- :reserve-keyword="false"-->
<!-- filterable-->
<!-- remote-->
<!-- placeholder="请输入地点"-->
<!-- :remote-method="searchPlace"-->
<!-- @change="selectOne" clearable-->
<!-- :loading="loading">-->
<!-- <el-option-->
<!-- v-for="(item, index) in poiList"-->
<!-- :key="index"-->
<!-- :label="item.name"-->
<!-- :value="item">-->
<!-- </el-option>-->
<!-- </el-select>-->
<!-- <el-button type="primary" @click="searchPlace">查询</el-button>-->
</div>
<div class="mapType">
<el-radio v-model="radioMap" label="1" border size="small" @change="radioMapChange">地图(道路)</el-radio>
<el-radio v-model="radioMap" label="2" border size="small" @change="radioMapChange">地图(卫星)</el-radio>
</div>
<div class="optionDiv">
<!-- <el-row class="btn_gis">-->
<!-- <el-button size="small" type="primary" class="el-icon-map-location" @click="addMapClick"></el-button>-->
<!-- </el-row>-->
<!-- <el-row class="btn_gis">-->
<!-- <el-button size="small" type="primary" class="el-icon-coin" @click="getLayers"></el-button>-->
<!-- </el-row>-->
<el-row class="btn_gis">
<el-tooltip content="清空" placement="left">
<el-button size="small" type="primary" class="el-icon-brush" @click="cleanMap"></el-button>
</el-tooltip>
</el-row>
<!-- <el-row class="btn_gis">-->
<!-- <el-button size="small" type="primary" class="el-icon-brush" @click="addPopup"></el-button>-->
<!-- </el-row>-->
</div>
<div class="InfoListDiv">
<el-tooltip content="单位装备信息列表" placement="left">
<el-button v-show="drawerShowBtn" style="position: absolute;top: 60px;right: -5px;" size="small" type="primary" class="el-icon-caret-left" @click="handleOpen"></el-button>
</el-tooltip>
<el-drawer
title="单位装备信息列表"
:visible.sync="drawer" :size="550" :append-to-body="false"
direction="rtl" :modal="false" :wrapperClosable="false"
:before-close="handleClose">
<el-row>
<el-row>
事件类型
<el-select v-model="currentScenarioType" placeholder="请选择">
<el-option
v-for="item in scenarioTypes"
:key="item.id"
:label="item.name"
:value="item.code">
</el-option>
</el-select>
</el-row>
<el-row>
<el-tabs v-model="activeOrgType" @tab-click="changeOrgByDistance">
<el-tab-pane :label="'0~5KM-可增援单位' + orgList5KM.length +'个'" name="first"></el-tab-pane>
<el-tab-pane :label="'5~10KM-可增援单位' + orgList10KM.length +'个'" name="second"></el-tab-pane>
<el-tab-pane :label="'10~20KM-可增援单位' + orgList20KM.length +'个'" name="third"></el-tab-pane>
</el-tabs>
</el-row>
<el-row>
<el-table :data.sync="orgListTable" border stripe style="width: 100%">
<el-table-column type="index" label="序号" width="50"></el-table-column>
<el-table-column prop="name" label="单位名称"></el-table-column>
<el-table-column prop="distance" label="距离" width="80"></el-table-column>
<el-table-column prop="areaName" label="地址"></el-table-column>
</el-table>
</el-row>
</el-row>
</el-drawer>
</div>
</div>
</div>
</template>
<script>
import * as Gis from '@/api/modules/gis/gis' // 接口
import $ from 'jquery'
// openLayer
import 'ol/ol.css'
import Map from 'ol/Map'
import View from 'ol/View'
import {Vector as VectorSource, XYZ} from 'ol/source'
import {createStringXY} from 'ol/coordinate'
import {defaults as defaultControls, ScaleLine, ZoomSlider, FullScreen, MousePosition} from 'ol/control'
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer'
import Feature from 'ol/Feature'
import {Point, LineString } from 'ol/geom'
import { Style, Icon, Stroke, Circle as CircleStyle, Fill} from 'ol/style'
import {unByKey} from 'ol/Observable'
import {easeOut} from 'ol/easing'
import {getVectorContext} from 'ol/render'
import Overlay from 'ol/Overlay'
export default {
components: {
},
data () {
return {
CommonUrl: window.gisCommonUrl,
// 查询
address: '',
poiList: [],
visibleSearch: false,
loading: false,
drawer: false,
drawerShowBtn: false,
currentAddress: null,
scenarioTypes: null, // 事件类型
currentScenarioType: [], // 事件类型
orgList: [], // 增援单位
orgList5KM: [],
orgList10KM: [],
orgList20KM: [],
orgListTable: [],
activeOrgTypeOld: 'first',
activeOrgType: 'first',
// gis 地图
map: null,
startIcon: require("@/assets/images/start.png"), //起点图标
endIcon: require("@/assets/images/end.png"), //终点图标
// openLayer
view: null,
TileLayerCommon: null, // 普通地图
TileLayerSatellite: null, // 卫星地图
TileLayer: null,
radioMap: '1',
vectorLayer: null,
animateSourceLayer: null,
animateVectorLayer: null,
iconFeature: null,
feature: null, // 水波纹
routerColor: [[18, 109, 4, 0.7], [237, 212, 0, 0.7]], // 轨迹路线颜色
speed: 8, // 轨迹速度
animatingRouter: false,
startTimeRouter: null,
currentCoordinates: []
}
},
mounted () {
this.TileLayerCommon = new TileLayer({
source: new XYZ({
name: "地图(道路)",
url: window.gisCommonUrl, // 地图服务地址或在线地图
}),
visible: true
})
this.TileLayerSatellite = new TileLayer({
source: new XYZ({
name: "地图(卫星)",
url: window.gisSatelliteUrl // 地图服务地址或在线地图
}),
visible: false
})
this.init()
this.getScenarioType()
// 每当触发添加要素的事件时,执行flash()水波纹动画 --- 使用第一种时放开
// this.addfeatureKey = this.animateSourceLayer.on("addfeature", (e) => {
// // this.flash(e.feature)
// this.flashOverlay()
// })
},
destroyed() {
// 水波纹动画 --- 使用第一种时放开
// unByKey(this.addfeatureKey)
this.removeMapClick()
},
methods: {
// 初始化地图
init () {
this.TileLayer = [this.TileLayerCommon, this.TileLayerSatellite]
this.map = new Map({
target: 'mapContainer',
layers: this.TileLayer,
view: new View({
center: [117.276828, 31.77611],
projection: 'EPSG:4326',
zoom: 10,
minZoom: 2,
controls: defaultControls({ zoom: true }).extend([])
})
})
// 水波纹动画图层
this.animateSourceLayer = new VectorSource({
wrapX: false
})
this.animateVectorLayer = new VectorLayer({
name: "点位标注(水波纹动画)",
source: this.animateSourceLayer
})
this.map.addLayer(this.animateVectorLayer)
// 轨迹图层
this.routerVectorLayer = new VectorLayer({
id: '20210526',
source: '',
style: new Style({
stroke: new Stroke({
width: 6,
color: this.routerColor[0]
})
})
})
this.map.addLayer(this.routerVectorLayer)
// 添加点位图层
// this.vectorLayer = new VectorLayer({
// name: "点位标注",
// source: new VectorSource({
// features: [],
// }),
// zIndex: 100,
// visible: true,
// })
// this.map.addLayer(this.vectorLayer)
// 向地图添加 MousePosition
let mousePositionControl = new MousePosition({
//坐标格式
coordinateFormat: createStringXY(6),
//地图投影坐标系(若未设置则输出为默认投影坐标系下的坐标)
projection: "EPSG:4326",
//坐标信息显示样式类名,默认是'ol-mouse-position'custom-mouse-position
className: "ol-mouse-position",
//显示鼠标位置信息的目标容器
target: document.getElementById("mouse-position"),
//未定义坐标的标记
undefinedHTML: " "
})
this.map.addControl(mousePositionControl)
// 比例尺
this.map.addControl(new ScaleLine())
// 缩放条
this.map.addControl(new ZoomSlider())
// 全屏
this.map.addControl(new FullScreen({
tipLabel: "全屏"
}))
// let bern = fromLonLat([117.276828, 31.77611]);
// this.flyTo(bern, function () {})
},
// 查询目标地
searchPlace (address) {
this.loading = true
let params = {
'name': address
}
Gis.getPoiList(params).then(({data}) => {
this.poiList = data.data
this.visibleSearch = true
this.loading = false
})
},
selectOne (item) {
this.cleanMap()
// 切换定位跳转方式动画
this.flyTo([item.lng, item.lat], function () {})
// 普通定位
// this.map.getView().setCenter([item.lng, item.lat])
// this.map.getView().setZoom(12)
this.addRandomFeature(item, false)
this.address = ''
this.poiList = []
this.visibleSearch = false
this.activeOrgType = "first"
this.activeOrgTypeOld = "first"
this.currentAddress = JSON.parse(JSON.stringify(item))
this.getOrgAll(item)
},
// 获取增援单位信息列表
getOrgAll (point) {
Gis.getOrgAll().then((res) => {
this.orgList = res.data.data
console.log(this.orgList)
this.groupOrg(point)
this.drawerShowBtn = false
this.drawer = true
})
},
// 增援单位根据距离分组
groupOrg(point) {
this.orgList5KM = []
this.orgList10KM = []
this.orgList20KM = []
this.orgList.forEach((item) => {
if (item.lat && item.lng) {
let distance = this.distanceTo(item, point)
item.distance = distance
if (distance > 0 && distance <= 5) {
this.orgList5KM.push(item)
this.addRandomFeature(item, true)
} else if (distance > 5 && distance <= 10) {
this.orgList10KM.push(item)
} else if (distance > 10 && distance <= 20) {
this.orgList20KM.push(item)
}
}
})
this.orgListTable = this.orgList5KM
this.addRouterLine()
},
// 计算距离 单位KM
distanceTo (point1, point2) {
let R = 6371; // km
let dLon = (point1.lng - point2.lng) * Math.PI/180,
lat1 = point1.lat * Math.PI/180,
lat2 = point2.lat * Math.PI/180,
d = Math.acos(Math.sin(lat1)*Math.sin(lat2) + Math.cos(lat1)*Math.cos(lat2) * Math.cos(dLon)) * R
return d.toFixed(2)
// let distance = 0.0;
// if ((point1.lng != null) && (point1.lat != null) && (point2 != null) && (point2.lng != null) && (point2.lat != null)) {
// let dx2 = Math.pow(point1.lng - point2.lng, 2)*1000
// let dy2 = Math.pow(point1.lat - point2.lat, 2)*1000
// distance = Math.sqrt(dx2 + dy2)
// }
// return distance
},
// 切换展示不同距离增援单位
changeOrgByDistance (tab) {
switch (tab.name) {
case 'first':
this.orgListTable = this.orgList5KM;
this.map.getView()
this.map.getView().setZoom(14)
break;
case 'second':
this.orgListTable = this.orgList10KM
this.map.getView().setZoom(13)
break;
case 'third':
this.orgListTable = this.orgList20KM;
this.map.getView().setZoom(12)
break;
}
if(this.activeOrgTypeOld !== tab.name) {
this.map.getView().setCenter([this.currentAddress.lng, this.currentAddress.lat])
this.changeOrgByDistancePoint ()
this.activeOrgTypeOld = tab.name
this.addRouterLine()
}
},
// 单位装备信息列表
handleOpen () {
this.drawerShowBtn = false
this.drawer = true
},
handleClose() {
this.drawer = false
this.drawerShowBtn = true
// this.$confirm('确认关闭?')
// .then( () => {
// this.drawer = false
// })
// .catch( () => {})
},
// 获取事件类型
getScenarioType() {
Gis.scenarioType().then((res) => {
this.scenarioTypes = res.data.data
})
},
// 切换定位跳转方式动画
flyTo (location, done) {
let duration = 2000
let zoom = this.map.getView().getZoom()
let parts = 2
let called = false
function callback(complete) {
--parts;
if (called) {
return;
}
if (parts === 0 || !complete) {
called = true;
done(complete);
}
}
this.map.getView().animate(
{
center: location,
duration: duration,
},
callback
)
this.map.getView().animate(
{
zoom: zoom - 1,
duration: duration / 2,
},
{
zoom: 14,
duration: duration / 2,
},
callback
)
},
// 切换地图
radioMapChange (val) {
switch (val) {
case '1':
this.TileLayerCommon.setVisible(true)
this.TileLayerSatellite.setVisible(false)
this.routerVectorLayer.getStyle().getStroke().setColor(this.routerColor[0])
break;
case '2':
this.TileLayerSatellite.setVisible(true)
this.TileLayerCommon.setVisible(false)
this.routerVectorLayer.getStyle().getStroke().setColor(this.routerColor[1])
break;
}
this.addRouterLine()
},
// 图层
getLayers() {
let layers = this.map.getLayers()
console.log(layers)
// this.vectorLayer.setVisible(!this.vectorLayer.getVisible())
},
// 移除监听
removeMapClick() {
unByKey(this.mapClickKey)
unByKey(this.animateKey)
},
// 地图点击事件 -- 添加点
addMapClick () {
this.mapClickKey = this.map.on('singleclick', (e) => {
// 获取当前点位坐标
let point = e.coordinate
// 打点,水纹动画
this.addRandomFeature(point[0], point[1], true)
unByKey(this.mapClickKey)
})
},
// 清空地图
cleanMap () {
this.animateVectorLayer.getSource().clear()
this.map.getOverlays().clear()
this.removeMapClick()
this.currentAddress = ''
this.drawer = false
this.drawerShowBtn = false
this.routerVectorLayer.setSource(null)
},
// 切换展示不同距离增援单位 --- 显示增援地点信息
changeOrgByDistancePoint() {
let overlays = this.map.getOverlays().getArray()
for(let i = 0; i < overlays.length; i++) {
if(overlays[i].getId() == 'organ' || overlays[i].getId() == 'organPopup') {
this.map.removeOverlay(overlays[i])
i--
}
}
this.orgListTable.forEach((item) => {
this.addRandomFeature(item, true)
})
},
// 打点
addRandomFeature (item, flag) {
// 点图标
if(!flag) {
this.iconFeature = new Feature({
geometry: new Point([item.lng, item.lat]),
name: '地点',
// population: 4000,
// rainfall: 500,
})
let iconStyle = new Style({
image: new Icon({
anchor: [0.5, 1],
src: this.startIcon, // 图标
}),
})
this.iconFeature.setStyle(iconStyle)
this.animateSourceLayer.addFeatures([this.iconFeature])
// 水波纹动画
this.flashOverlay(item.lng, item.lat, 'flagOrgan')
}
if(flag) {
// 水波纹动画
this.flashOverlay(item.lng, item.lat, 'organ')
// 弹窗
this.addPopup(item)
}
// // 增加可移动点位元素
// let iconTranslate = new PointerTranslate({
// features: new Collection([feature, this.iconFeature])
// })
// this.map.addInteraction(iconTranslate)
// // 为元素增加点击事件
// unByKey(this.featureClickKey)
// this.featureClickKey = iconTranslate.on("translateend", () => {
// let clickPoint = this.iconFeature.getGeometry().flatCoordinates
// // 将点位转成EPSG:4326坐标系
// clickPoint = transform(clickPoint, 'EPSG:3857', 'EPSG:4326')
// alert(clickPoint)
// })
},
// 水纹动画--第一种
flash (feature) {
let duration = 3000
let start = new Date().getTime()
//进行地图水波渲染
let that = this
that.animateKey = this.TileLayer.on('postrender', function animate(event) {
let vectorContext = getVectorContext(event)
let frameState = event.frameState
let flashGeom = feature.getGeometry().clone()
let elapsed = frameState.time - start
let elapsedRatio = elapsed / duration
let radius = easeOut(elapsedRatio) * 25 + 15
let opacityFill = easeOut(1 - elapsedRatio) - 0.3
let opacity = easeOut(1 - elapsedRatio)
let style = new Style({
image: new CircleStyle({
radius: radius,
fill: new Fill({
color: 'rgba(255, 0, 0, ' + opacityFill + ')',
width: 0.5 + opacity,
}),
stroke: new Stroke({
color: 'rgba(255, 0, 0, ' + opacity + ')',
width: 0.5 + opacity,
}),
})
})
vectorContext.setStyle(style)
vectorContext.drawGeometry(flashGeom)
// 当持续时间超过了duration后,取消绑定的postrender事件
if (elapsed > duration) {
unByKey(that.animateKey)
return;
}
that.map.render()
})
},
// 水纹动画--第二种
flashOverlay (x, y, id) {
let element = document.createElement("div")
element.className =(id == "flagOrgan" ? "point_animation_flag" : "point_animation_com")
let p = document.createElement("p")
let span = document.createElement("span")
element.appendChild(p)
element.appendChild(span)
let point_overlay = new Overlay({
id: id,
element: element,
positioning: 'center-center',
})
point_overlay.setPosition([x, y])
this.map.addOverlay(point_overlay)
},
// 弹窗
addPopup (item) {
let element = document.createElement("div")
element.className = "popup"
element.innerHTML = "<div class='popupInfoMap'>" +
"<div class='el-header'><div class='name'>"+ item.name +"</div><div class='optionBtn' id='detail'>装备详情</div></div>" +
"<div class='el-main'>" +
"<div class='el-row numDiv'><div class='el-col-6'>20</div><div class='el-col-6'>241</div>" +
"<div class='el-col-6'>90</div><div class='el-col-6'>"+ item.distance +"KM</div></div>" +
"<div class='el-row typeDiv'><div class='el-col-6'>装备分类</div><div class='el-col-6'>装备总数</div>" +
"<div class='el-col-6'>人员</div><div class='el-col-6'>距离</div></div>" +
"<div class='el-row'><button class='btn' id='taskSend'>下发任务</button></div>" +
// "<div class='el-row'>经纬度:"+ item.lng +","+ item.lat + "</div>" +
"</div></div>"
let popup_overlay = new Overlay({
id: 'organPopup',
element: element,
positioning: 'center-center',
})
popup_overlay.setPosition([item.lng, item.lat])
this.map.addOverlay(popup_overlay)
$('.popup').parent().css('z-index', 9999)
// 添加点击事件
$("#taskSend").on("click",()=>{
this.taskSend(item);
})
$("#detail").on("click",()=>{
this.detail(popup_overlay, item);
})
},
// 发布任务
taskSend(item) {
let params = {
'startPoint': item.lat + "," + item.lng,
'endPoint': this.currentAddress.lat + "," + this.currentAddress.lng
}
Gis.getRouterGis(params).then((res) => {
this.currentCoordinates = res.data.data.paths[0].points.coordinates
this.geoMarker = new Feature({
type: 'geoMarker',
geometry: new Point([item.lng, item.lat]),
});
this.animatingRouter = false
this.startAnimation()
})
},
// 装备详情
detail(overlay, item) {
let elementNode = overlay.element.childNodes[0]
if (elementNode.children.length > 1) {
elementNode.removeChild(elementNode.children[1])
}else {
let params = {
'orgId': item.id
}
Gis.getEquipStrengthList(params).then((res) => {
let equipList = res.data.data
let addHtml = document.createElement('div')
addHtml.innerHTML = ''
let str = "<div id='detailEquip'><table><thead><tr><td>序号</td><td>单位名称</td><td>装备数量</td></tr></thead>"
for(let i = 0; i < equipList.length; i++){
str += "<tr><td>"+ (i+1) +"</td><td>"+ equipList[i].equipName +"</td><td>" + equipList[i].total + "</td></tr>"
}
addHtml.innerHTML = (str + "</table></div>")
elementNode.appendChild(addHtml)
})
}
},
// 轨迹路线===============================start
// 添加静态路线
addRouterLine() {
let routeFeatureArr = []
for(let i = 0; i < this.orgListTable.length; i++) {
let params = {
'startPoint': this.orgListTable[i].lat + "," + this.orgListTable[i].lng,
'endPoint': this.currentAddress.lat + "," + this.currentAddress.lng
}
Gis.getRouterGis(params).then((res) => {
let routeFeature = new Feature({
type: "route",
geometry: new LineString(res.data.data.paths[0].points.coordinates)
})
routeFeatureArr.push(routeFeature)
if(i === this.orgListTable.length - 1) {
this.routerVectorLayer.setSource(new VectorSource({
features: routeFeatureArr
}))
}
})
}
},
// 轨迹跟踪动效
moveFeature(event) {
let vectorContext = getVectorContext(event);
let frameState = event.frameState;
if (this.animatingRouter) {
let elapsedTime = frameState.time - this.startTimeRouter;
let distance = Math.round(this.speed * elapsedTime/1000)
if (distance >= this.currentCoordinates.length) {
this.geoMarker.getGeometry().setCoordinates(this.currentCoordinates[0]);
this.routerVectorLayer.un("postrender", this.moveFeature);
return;
}
let currentPoint = new Point(this.currentCoordinates[distance])
let feature = new Feature(currentPoint);
vectorContext.drawFeature(feature, new Style({
image: new CircleStyle({
radius: 7,
fill: new Fill({color: 'black'}),
stroke: new Stroke({
color: 'white',
width: 6,
}),
}),
}));
}
this.map.render()
},
startAnimation() {
if (this.animatingRouter) {
this.geoMarker.getGeometry().setCoordinates(this.currentCoordinates[this.currentCoordinates.length-1]);
this.routerVectorLayer.un("postrender", this.moveFeature);
} else {
this.animatingRouter = true;
this.startTimeRouter = new Date().getTime();
this.geoMarker.changed();
this.routerVectorLayer.on("postrender", this.moveFeature);
this.map.render()
}
}
// 轨迹=========================end
}
}
</script>
<style lang="scss" scoped>
.command-center {
width: 100%;
height: 100%;
.customDiv {
z-index: 999;
.HeadDiv {
position: absolute;
top: 50px;
right: 700px;
width: 600px;
background: #409eff;
padding: 10px;
border-radius: 4px;
/*.el-select {*/
/* width: 80%;*/
/*}*/
}
.mapType {
position: absolute;
top: 10px;
right: 50px;
.el-radio {
background: #fff;
margin-right: 0;
}
}
.optionDiv {
position: absolute;
bottom: 50px;
right: 10px;
width: 50px;
.btn_gis {
margin-bottom: 5px;
}
}
.InfoListDiv {
/deep/.el-drawer__wrapper {
margin: 10px;
top: 50px;
left: auto;
width: 550px;
height: 600px;
.el-drawer__header {
/*padding: 10px 10px 0;*/
font-size: 18px;
font-weight: 600;
}
.el-drawer.rtl {
background: rgba(255, 255, 255, 0.9);
height: 600px;
.orgType {
cursor: pointer;
}
.el-drawer__body {
padding: 0px 20px;
}
.el-table__body-wrapper {
height: 360px;
overflow-y: auto;
}
}
}
}
}
/**map*/
.mapContainer {
width: 100vw;
height: 100vh;
/**openLayer样式**/
/deep/.ol-control {
background-color: rgba(255, 255, 255, 0.8);
}
/**坐标信息*/
/deep/.ol-mouse-position {
bottom: 8px;
left: 150px;
top: unset;
right: unset;
}
/**点扩散闪烁样式*/
/deep/.point_animation_flag{
background: #ff0000;
width: 10px;
height: 10px;
border: 2px #ff0000 solid;
border-radius: 50%;
position: absolute;
p, span{
position: absolute;
top: -25px;
left: -25px;
width: 60px;
height: 60px;
animation: point_animation_flag 1.5s infinite;
box-shadow: 0px 0px 1px #ff0000;
background: rgba(255, 0, 0, 0.25);
margin: 0px;
border-radius: 50%;
}
span{
animation-delay: 0.8s;
}
}
@keyframes point_animation_flag{
10% {
transform: scale(1);
}
100% {
transform: scale(8);
}
}
/deep/.point_animation_com{
background: #367ae2;
width: 10px;
height: 10px;
border: 2px #367ae2 solid;
border-radius: 50%;
position: absolute;
p, span{
position: absolute;
top: -8px;
left: -8px;
width: 20px;
height: 20px;
animation: point_animation_com 1.5s infinite;
box-shadow: 0px 0px 1px #0059e0;
background: rgba(54, 122, 226, 0.25);
margin: 0px;
border-radius: 50%;
}
span{
animation-delay: 0.8s;
}
}
@keyframes point_animation_com{
10% {
transform: scale(1);
}
100% {
transform: scale(8);
}
}
/**弹窗样式*/
/deep/.popup {
position: absolute;
background-color: rgba(255, 255, 255, 0.9);
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
padding: 15px;
border-radius: 10px;
border: 1px solid #409eff;
bottom: 12px;
left: -50px;
width: 335px;
.el-header {
color: #409eff;
.name {
width: calc(100% - 100px);
float: left;
}
.optionBtn {
width: 100px;
float: right;
text-align: right;
cursor: pointer;
}
}
.el-main {
padding: 3px 20px;
.numDiv {
font-size: 18px;
font-weight: 600;
text-align: center;
}
.typeDiv {
text-align: center;
}
.btn {
background: #fbc500;
border: 1px solid #fbc500;
border-radius: 3px;
cursor: pointer;
float: right;
}
}
#detailEquip {
table {
width: 100%;
border: 1px solid #66b1ff;
tbody {
color: #888;
}
}
}
}
/deep/.popup:after, /deep/.popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
/deep/.popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
/deep/.popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
}
}
</style>