MeasureManager.js
import * as Cesium from 'cesium'
export default class MeasureManager {
constructor(viewer) {
this.viewer = viewer
this.entityCollection = []
}
measurePolygon() {
var positions = [];
var clickStatus = false;
var labelEntity = null;
this.viewer.screenSpaceEventHandler.setInputAction((clickEvent) => {
clickStatus = true;
var cartesian = this.viewer.scene.globe.pick(this.viewer.camera.getPickRay(clickEvent.position), this.viewer.scene);
if (!cartesian) {
return false
}
if (positions.length == 0) {
positions.push(cartesian.clone()); //鼠标左击 添加第1个点
this.addPoint(cartesian);
this.viewer.screenSpaceEventHandler.setInputAction((moveEvent) => {
var movePosition = this.viewer.scene.globe.pick(this.viewer.camera.getPickRay(moveEvent.endPosition), this.viewer.scene);
if (!movePosition) {
return false;
}
if (positions.length == 1) {
positions.push(movePosition);
this.addLine(positions);
} else {
if (clickStatus) {
positions.push(movePosition);
} else {
positions.pop();
positions.push(movePosition);
}
}
if (positions.length >= 3) {
// 绘制label
if (labelEntity) {
this.viewer.entities.remove(labelEntity);
this.entityCollection.splice(this.entityCollection.indexOf(labelEntity), 1);
}
var text = "面积:" + this.getArea(positions);
var centerPoint = this.getCenterOfGravityPoint(positions);
labelEntity = this.addLabel(centerPoint, text);
this.entityCollection.push(labelEntity);
}
clickStatus = false;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
} else if (positions.length == 2) {
if (!cartesian) {
return false
}
positions.pop();
positions.push(cartesian.clone()); // 鼠标左击 添加第2个点
this.addPoint(cartesian);
this.addPolyGon(positions);
// 右击结束
this.viewer.screenSpaceEventHandler.setInputAction((clickEvent) => {
var clickPosition = this.viewer.scene.globe.pick(this.viewer.camera.getPickRay(clickEvent.position), this.viewer.scene);
if (!clickPosition) {
return false;
}
positions.pop();
positions.push(clickPosition);
positions.push(positions[0]); // 闭合
this.addPoint(clickPosition);
this.viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
} else if (positions.length >= 3) {
if (!cartesian) {
return false
}
positions.pop();
positions.push(cartesian.clone()); // 鼠标左击 添加第3个点
this.addPoint(cartesian);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 添加点
addPoint(position) {
this.entityCollection.push(this.viewer.entities.add(new Cesium.Entity({
position: position,
point: {
color: Cesium.Color.BLUE,
pixelSize: 5,
heightReference: Cesium.HeightReference.NONE
}
})));
};
// 添加线
addLine(positions) {
this.viewer.entities.add(
new Cesium.Entity({
polyline: {
positions: new Cesium.CallbackProperty(() => positions, false),
width: 2,
material: Cesium.Color.YELLOW,
clampToGround: true,
}
})
)
}
// 添加面
addPolyGon(positions) {
this.entityCollection.push(this.viewer.entities.add(new Cesium.Entity({
polygon: {
hierarchy: new Cesium.CallbackProperty(() => {
return new Cesium.PolygonHierarchy(positions);
}, false),
material: Cesium.Color.RED.withAlpha(0.6),
classificationType: Cesium.ClassificationType.BOTH // 贴地表和贴模型,如果设置了,这不能使用挤出高度
}
})));
};
// 添加标签
addLabel(position, text) {
return this.viewer.entities.add(
new Cesium.Entity({
position: position,
label: {
text: text,
font: '14px sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE, //FILL FILL_AND_OUTLINE OUTLINE
fillColor: Cesium.Color.YELLOW,
showBackground: true, //指定标签后面背景的可见性
backgroundColor: new Cesium.Color(0.165, 0.165, 0.165, 0.8), // 背景颜色
backgroundPadding: new Cesium.Cartesian2(6, 6), //指定以像素为单位的水平和垂直背景填充padding
pixelOffset: new Cesium.Cartesian2(0, -25),
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
})
)
}
/*方向*/
Bearing(from, to) {
from = Cesium.Cartographic.fromCartesian(from);
to = Cesium.Cartographic.fromCartesian(to);
var lat1 = from.latitude;
var lon1 = from.longitude;
var lat2 = to.latitude;
var lon2 = to.longitude;
var angle = -Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2));
if (angle < 0) {
angle += Math.PI * 2.0;
}
var degreesPerRadian = 180.0 / Math.PI; //弧度转化为角度
angle = angle * degreesPerRadian; //角度
return angle;
}
/*角度*/
Angle(p1, p2, p3) {
var bearing21 = this.Bearing(p2, p1);
var bearing23 = this.Bearing(p2, p3);
var angle = bearing21 - bearing23;
if (angle < 0) {
angle += 360;
}
return angle;
}
distance(point1, point2) {
var point1cartographic = Cesium.Cartographic.fromCartesian(point1);
var point2cartographic = Cesium.Cartographic.fromCartesian(point2);
/**根据经纬度计算出距离**/
var geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
var s = geodesic.surfaceDistance;
//console.log(Math.sqrt(Math.pow(distance, 2) + Math.pow(endheight, 2)));
//返回两点之间的距离
s = Math.sqrt(Math.pow(s, 2) + Math.pow(point2cartographic.height - point1cartographic.height, 2));
return s;
}
//计算多边形面积
getArea(points) {
var res = 0;
//拆分三角曲面
for (var i = 0; i < points.length - 2; i++) {
var j = (i + 1) % points.length;
var k = (i + 2) % points.length;
var totalAngle = this.Angle(points[i], points[j], points[k]);
var dis_temp1 = this.distance(points[j], points[0]);
var dis_temp2 = this.distance(points[k], points[0]);
res += dis_temp1 * dis_temp2 * Math.sin(totalAngle) / 2;
}
if (res < 1000000) {
res = Math.abs(res).toFixed(4) + " 平方米";
} else {
res = Math.abs((res / 1000000.0).toFixed(4)) + " 平方公里";
}
return res;
};
/**
* 计算多边形的中心(简单的处理)
* @param mPoints
* @returns {*[]}
*/
getCenterOfGravityPoint(mPoints) {
var centerPoint = mPoints[0];
for (var i = 1; i < mPoints.length; i++) {
centerPoint = Cesium.Cartesian3.midpoint(centerPoint, mPoints[i], new Cesium.Cartesian3());
}
return centerPoint;
}
}
测试:
<template>
<div class="main">
<div id="cesiumContainer"></div>
</div>
</template>
<script setup>
import * as Cesium from 'cesium';
import MeasureManager from './MeasureManager.js';
onMounted(() => {
const cesiumViewer = new Cesium.Viewer("cesiumContainer", {
terrainProvider: Cesium.createWorldTerrain(),
infoBox: false, //信息面板
timeline: false, //底部的时间轴
fullscreenButton: false, //右下角的全屏按钮
});
cesiumViewer._cesiumWidget._creditContainer.style.display = "none"; //隐藏logo版权
const measureManager = new MeasureManager(cesiumViewer)
measureManager.measurePolygon()
})
</script>
<style lang="scss" scoped>
.main {
position: relative;
height: 100vh;
width: 100vw;
#cesiumContainer {
position: absolute;
height: 100vh;
width: 100vw;
}
}
</style>
方式2:
<template>
<div id="app">
<div id="cesiumContainer"></div>
<div class="btnContainer">
<button @click="draw('Polyline')">标点测距</button>
<button @click="draw('Polygon')">标点测面</button>
<button @click="clearAllDrawn()">清空数据</button>
<div class="tip">
<p>点击按钮后,在场景内单击左键标点,单击右键结束测量。</p>
<p>点击“标点测距”按钮后在场景内单击鼠标左键加点,在两点间显示距离,单击右键结束测量。</p>
<p>点击“标点测面”按钮后在场景内单击鼠标左键加点,单击右键在勾出的范围中显示面积。</p>
<p>点击“清空数据”按钮删除所有数据。</p>
<p>剩下的我慢慢写</p>
</div>
</div>
</div>
</template>
<script>
import * as Cesium from 'cesium'
export default {
name: 'App',
data() {
return {
viewer: undefined,
tempEntities: [],
pointNum: 0,
floatingPoint: undefined,
activeShape: undefined
}
},
mounted() {
this.Init()
},
methods: {
/* 初始化 */
Init() {
// 引入个人token
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYmJkNWQ3Mi0zOGVkLTQ5N2YtYTBmMy0wMDAyODZiMDMyZWYiLCJpZCI6ODQ2NzQsImlhdCI6MTY0NjQ0NTYxNX0.XkHX3rdysM4uUe5VTKDVEV3W2An33zyh4qAkFUac2fk'
// 设置取景器
this.viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain(),
selectionIndicator: false, // 不显示指示器小部件
infoBox: false, // 不显示信息框
sceneModePicker: false, // 不显示模式切换选项
baseLayerPicker: false,
navigationHelpButton: false,
animation: false,
shouldAnimate: false,
timeline: false,
geocoder: false,
homeButton: false,
// 添加ArcGIS在线影像底图
imageryProvider: new Cesium.UrlTemplateImageryProvider({
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
subdomains: ['0', '1', '2', '3'],
tilingScheme: new Cesium.WebMercatorTilingScheme()
})
})
// 若浏览器不支持pickPosition,显示报错信息
if (!this.viewer.scene.pickPositionSupported) {
window.alert('This browser does not support pickPosition.')
}
// 载入OSM建筑物
// const osmBuildings = this.viewer.scene.primitives.add(Cesium.createOsmBuildings()) // eslint-disable-line no-unused-vars
// 初始化镜头
this.viewer.camera.lookAt(
Cesium.Cartesian3.fromDegrees(-122.2058, 46.1955, 1000.0),
new Cesium.Cartesian3(5000.0, 5000.0, 5000.0)
)
this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
},
/* 空间两点距离计算函数 */
getLength(start, end) {
// 将起点与终点位置信息从笛卡尔坐标形式转换为Cartographic形式
let startCartographic = Cesium.Cartographic.fromCartesian(start)
let endCartographic = Cesium.Cartographic.fromCartesian(end)
// 初始化测地线
let geodesic = new Cesium.EllipsoidGeodesic()
// 设置测地线起点和终点,EllipsoidGeodesic中setEndPoints常与surfaceDistance搭配使用
geodesic.setEndPoints(startCartographic, endCartographic)
// 获取起点和终点之间的表面距离,单位为km,规定四舍五入保留两位小数
// surfaceDistance返回number 单位为m,带小数
// console.log((geodesic.surfaceDistance / 1000).toFixed(2))
return (geodesic.surfaceDistance / 1000).toFixed(2)
},
/* 空间两点计算中点函数 */
getMidpoint(start, end) {
let startPoint = Cesium.Cartographic.fromCartesian(start)
let endPoint = Cesium.Cartographic.fromCartesian(end)
let geodesic = new Cesium.EllipsoidGeodesic()
geodesic.setEndPoints(startPoint, endPoint)
let geoPoint = geodesic.interpolateUsingFraction(0.5)
console.log(Cesium.Ellipsoid.WGS84.cartographicToCartesian(geoPoint))
return Cesium.Ellipsoid.WGS84.cartographicToCartesian(geoPoint)
},
addLabel(midPoint, labelLength) {
let viewer = this.viewer
return viewer.entities.add({
name: '中点',
position: midPoint,
label: {
text: labelLength + 'km',
font: '20px sans-serif',
fillColor: Cesium.Color.WHITE,
outlineWidth: 2,
backgroundColor: Cesium.Color.BLACK,
showBackground: true,
style: Cesium.LabelStyle.FILL,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
})
},
/* 测量空间面积 */
// 方向
Bearing(from, to) {
let fromCartographic = Cesium.Cartographic.fromCartesian(from)
let toCartographic = Cesium.Cartographic.fromCartesian(to)
let lat1 = fromCartographic.latitude
let lon1 = fromCartographic.longitude
let lat2 = toCartographic.latitude
let lon2 = toCartographic.longitude
let angle = -Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2))
if (angle < 0) {
angle += Math.PI * 2.0
}
return angle
},
// 角度
pointAngle(point1, point2, point3) {
let bearing21 = this.Bearing(point2, point1)
let bearing23 = this.Bearing(point2, point3)
let angle = bearing21 - bearing23
if (angle < 0) {
angle += Math.PI * 2.0
}
return angle
},
getArea(positions) {
let res = 0
for (let i = 0; i < positions.length - 2; i++) {
let j = (i + 1) % positions.length
let k = (i + 2) % positions.length
let totalAngle = this.pointAngle(positions[i], positions[j], positions[k])
let tempLength1 = this.getLength(positions[j], positions[0])
let tempLength2 = this.getLength(positions[k], positions[0])
res += tempLength1 * tempLength2 * Math.sin(totalAngle) / 2
}
res = res.toFixed(2)
// console.log(res)
res = parseFloat(res)
// console.log(Math.abs(res))
return Math.abs(res)
},
addArea(area, positions) {
let viewer = this.viewer
return viewer.entities.add({
name: '多边形面积',
position: positions[positions.length - 1],
label: {
text: area + '平方公里',
font: '20px sans-serif',
fillColor: Cesium.Color.WHITE,
outlineWidth: 2,
backgroundColor: Cesium.Color.BLACK,
showBackground: true,
style: Cesium.LabelStyle.FILL,
pixelOffset: new Cesium.Cartesian2(60, -60),
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
})
},
/* 绘制函数 */
drawPointLabel(position, pointNum) {
let viewer = this.viewer
// 本质上就是添加一个点的实体
return viewer.entities.add({
name: '点几何对象',
position: position,
point: {
color: Cesium.Color.WHEAT,
pixelSize: 5,
outlineWidth: 3,
disableDepthTestDistance: Number.POSITIVE_INFINITY, //
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 规定贴地
},
label: {
text: pointNum,
font: '30px sans-serif',
fillColor: Cesium.Color.WHITE,
outlineWidth: 2,
backgroundColor: Cesium.Color.BLACK,
showBackground: true,
style: Cesium.LabelStyle.FILL,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER
}
})
},
drawPoint(position) {
let viewer = this.viewer
// 本质上就是添加一个点的实体
return viewer.entities.add({
position: position,
point: {
color: Cesium.Color.WHEAT,
pixelSize: 5,
outlineWidth: 3,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 规定贴地
}
})
},
drawPolyline(positions) {
let viewer = this.viewer
if (positions.length < 1) return
return viewer.entities.add({
name: '线几何对象',
polyline: {
positions: positions,
width: 5.0,
material: new Cesium.PolylineGlowMaterialProperty({
// eslint-disable-next-line new-cap
color: Cesium.Color.WHEAT
}),
depthFailMaterial: new Cesium.PolylineGlowMaterialProperty({
// eslint-disable-next-line new-cap
color: Cesium.Color.WHEAT
}),
clampToGround: true
}
})
},
drawPolygon(positions) {
let viewer = this.viewer
if (positions.length < 2) return
return viewer.entities.add({
name: '面几何对象',
polygon: {
hierarchy: positions,
// eslint-disable-next-line new-cap
material: new Cesium.ColorMaterialProperty(
Cesium.Color.WHEAT.withAlpha(0.4)
)
}
})
},
/* 清除实体 */
clearAllDrawn() {
let viewer = this.viewer
this.tempEntities = []
this.pointNum = 0
viewer.entities.removeAll()
},
created() {
},
/* 根据类型绘制对象
* @param type point polyline polygon */
draw(type) {
let that = this
let viewer = this.viewer
// let pointNum = this.pointNum
// console.log(pointNum)
let tempEntities = this.tempEntities
let floatingPoint = this.floatingPoint
let activeShape = this.activeShape
let position = []
let tempPoints = []
let activeShapePoints = []
// 开启深度检测
viewer.scene.globe.depthTestAgainstTerrain = true
// 创建场景的HTML canvas元素
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
switch (type) {
// 绘制线
case 'Polyline':
// 取消鼠标双击事件
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
// 监听鼠标移动
handler.setInputAction(function (movement) {
if (Cesium.defined(floatingPoint)) {
let newPosition = viewer.scene.pickPosition(movement.endPosition)
if (Cesium.defined(newPosition)) {
floatingPoint.position.setValue(newPosition)
activeShapePoints.pop()
activeShapePoints.push(newPosition)
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
// 左键单击开始画线
handler.setInputAction(function (click) {
let earthPosition = viewer.scene.pickPosition(click.position)
if (Cesium.defined(earthPosition)) {
floatingPoint = that.drawPoint(earthPosition)
}
// 获取位置信息
// 从相机位置创建一条射线,这条射线通过世界中movement.position像素所在的坐标,返回Cartesian3坐标
let ray = viewer.camera.getPickRay(click.position)
// 找到射线与渲染的地球表面之间的交点。射线必须以世界坐标给出。返回Cartesian3坐标
position = viewer.scene.globe.pick(ray, viewer.scene)
tempPoints.push(position) // 记录点位
that.pointNum += 1
let tempLength = tempPoints.length // 记录点数
// 调用绘制点的接口
let point = that.drawPointLabel(tempPoints[tempPoints.length - 1], JSON.stringify(that.pointNum))
tempEntities.push(point)
// 存在超过一个点时
if (tempLength > 1) {
// 绘制线
let pointLength = that.getLength(tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1])
let midPosition = that.getMidpoint(tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1])
let pointline = that.drawPolyline([tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
let pointLabel = that.addLabel(midPosition, pointLength)
tempEntities.push(pointline) // 保存记录
tempEntities.push(pointLabel)
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
// 右键单击结束画线
handler.setInputAction(function (click) {
console.log(that.pointNum)
activeShapePoints.pop()
viewer.entities.remove(activeShapePoints)
viewer.entities.remove(floatingPoint)
tempPoints = [] // 清空点位记录
handler.destroy()
handler = null
floatingPoint = undefined
activeShape = undefined
activeShapePoints = []
console.log(that.pointNum)
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
break
// 绘制面
case 'Polygon':
// 取消鼠标双击事件
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
// 监听鼠标移动
handler.setInputAction(function (movement) {
if (Cesium.defined(floatingPoint)) {
let newPosition = viewer.scene.pickPosition(movement.endPosition)
if (Cesium.defined(newPosition)) {
floatingPoint.position.setValue(newPosition)
activeShapePoints.pop()
activeShapePoints.push(newPosition)
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
// 左键单击开始画线
handler.setInputAction(function (click) {
let earthPosition = viewer.scene.pickPosition(click.position)
if (Cesium.defined(earthPosition)) {
if (activeShapePoints.length === 0) {
floatingPoint = that.drawPoint(earthPosition)
activeShapePoints.push(earthPosition)
const dynamicPositions = new Cesium.CallbackProperty(function () {
return new Cesium.PolygonHierarchy(activeShapePoints)
}, false)
activeShape = that.drawPolygon(dynamicPositions)
}
activeShapePoints.push(earthPosition)
}
// 获取位置信息
let ray = viewer.camera.getPickRay(click.position)
position = viewer.scene.globe.pick(ray, viewer.scene)
tempPoints.push(position) // 记录点位
let tempLength = tempPoints.length // 记录点数
that.pointNum += 1
// 调用绘制点的接口
let point = that.drawPointLabel(tempPoints[tempPoints.length - 1], JSON.stringify(that.pointNum))
tempEntities.push(point)
// 存在超过一个点时
if (tempLength > 1) {
// 绘制线
let pointline = that.drawPolyline([tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
tempEntities.push(pointline) // 保存记录
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
// 右键单击结束画面
handler.setInputAction(function (click) {
// 选择一个椭球或地图
let cartesian = viewer.camera.pickEllipsoid(click.position, viewer.scene.globe.ellipsoid)
if (cartesian) {
let tempLength = tempPoints.length
if (tempLength < 3) {
alert('闭合操作需要至少3个点嗷')
} else {
// 闭合最后一条线
let pointline = that.drawPolyline([tempPoints[0], tempPoints[tempPoints.length - 1]])
tempEntities.push(pointline)
that.drawPolygon(tempPoints)
let pointArea = that.getArea(tempPoints)
that.addArea(JSON.stringify(pointArea), tempPoints)
tempEntities.push(tempPoints)
handler.destroy()
handler = null
}
}
activeShapePoints.pop()
viewer.entities.remove(activeShapePoints)
viewer.entities.remove(floatingPoint)
floatingPoint = undefined
activeShapePoints = []
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
break
}
}
}
}
</script>
<style>
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#app,
#cesiumContainer {
font-family: "Avenir", Helvetica, Arial, sans-serif;
width: 100%;
height: 100%;
overflow: hidden;
}
.btnContainer {
position: absolute;
left: 15px;
top: 80px;
padding: 10px 15px;
/*添加圆角边框*/
border-radius: 5px;
border: 1px solid rgba(128, 128, 128, 0.5);
color: #ffffff;
background: rgba(0, 0, 0, 0.4);
box-shadow: 0 4px 8px rgb(128 128 128 / 50%);
max-width: 300px;
}
button {
background: transparent;
border: 1px solid #00d0ffb8;
color: white;
padding: 7px 9px;
border-radius: 3px;
cursor: pointer;
}
.tip p {
margin: 2px 0px;
padding: 5px 1px;
}
</style>