Support the functionality of the implementation
- Limit the time span to 3 days
- The car changes speed in motion
- Trolley removement
- Small car movement point position support to open details
This was the first feature I implemented when I cut from the back end to the front end. At that time, I had not been exposed to the development mode of front and back end separation, let alone react, and I did it with an attitude of learning by doing….. , may have the need of the students, do a reference.
import React, {Component} from 'react';
import {DatePicker, Input, Icon, Button, Table, Progress, Slider, notification} from 'antd';
import ol from 'openlayers';
import moment from 'moment';
import axios from 'axios';
import playback from '.. /.. /Images/playback.png';
import speedcar from '.. /.. /Images/car.png';
import move from '.. /.. /Images/move.png';
import direction from '.. /.. /Images/direction.png';
import stop from '.. /.. /Images/stop.png';
import daemarker from '.. /.. /Images/direction.png';
import './CarMove.less';
import 'openlayers/css/ol.css'; const RangePicker = DatePicker.RangePicker; // The original vector on the mapletprevVector; // Determine the map scale levellet zoomSize = [100, 200, 500, 1000, 2000, 8000, 14000, 20000, 25000, 50000, 100000, 200000, 500000, 1000000, 2000000];
letzoomlevel = [17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3]; class CarMove extends Component { constructor(props) { super(props); This. State = {columns: [], // Table header DAENumber:' ', // The default number is DAEStatus:' ', // The default state DEAAddress:' ', // Default address queryKeyWord: this.props. Keyword === undefined?' '// scrollPathTableHeight: [], // scrollPathTableHeight:' 'UserChooseStartTime: this.props. Starttime === undefined?' 'UserChooseEndTime: this.props. Endtime === undefined?' ': this.props. Endtime, // the endtime selected by the user defaultShowBtnFlag: this.props.false: this.props. ShowBtnFlag, // If the user passes in the return button defaultModeFlag: this.props. ModeFlag === undefined?false[], // AdjustSpeed: adjustspeed: adjustspeed:false, // The default progress bar for adjusting the car's driving does not display carProgerss: 0, // The default progress of the car on the track canRefreshAnimation:trueMapCenterLongitude: 28.871718 mapCenterLatitude: 28.871718 // The default map shows the latitude of the center'http://www.google.cn/maps/vt/pb=! 1m4! 1m3! 1i{z}! 2i{x}! 3i{y}! 2m3! 1e0! 2sm! 3i345013117! 3m8! 2szh-CN! 3scn! 5e1105! 12m4! 1e68! 2m2! 1sset! 2sRoadmap! 4e0'} this.map = null; // Define map container handle this.marker = null; // This. Index = 0; // The default car position in the track this.defaultCarSpeed = 12; } // eslint-disable-next-line react/sort-compgenerateTableHeaderData() {
const columns = [
{title: 'Acquisition time', dataIndex: 'column0', key: '1', width: 150, align: 'center'},
{title: 'Collector Serial Number', dataIndex: 'column1', key: '2', width: 150, align: 'center'},
{title: 'location', dataIndex: 'column2', key: '3', width: 180, align: 'center'}]; This.setstate ({columns: columns})} userInputKeyWord = (e) => {this.setState({queryKeyWord: E.target.value})} clearUserKeyWord = () => {this.setState({queryKeyWord:' '}} userChangeQueryTime = (date, dateString) => {this.setState({userChooseStartTime: DateString [0], userChooseEndTime: dateString[1]})}let mapData = [], tableData = [];
letqueryKey = this.state.queryKeyWord; // The user will be prompted if the interval is longer than three dayslet startTime = this.state.userChooseStartTime;
letendTime = this.state.userChooseEndTime; // Convert the time to milliseconds and calculate the differencelettime = new Date(endTime).getTime() - new Date(startTime).getTime(); // Check the required itemsif (queryKey === "" || queryKey == null) {
notification.warn({
message: 'No blank plate number or tag number entered'Duration: 3, Placement:'bottomRight'
});
return;
}
if (startTime === "" || endTime === "") {
notification.warn({
message: 'Please select a time period for query', description: 'Query period cannot be empty, please select a new time to query', Duration: 3, Placement:'bottomRight'
});
return;
}
if (time > 259200000) {
notification.warn({
message: 'The interval shall not exceed three days', description: 'Query duration cannot exceed 3 days, please reselect time to query', Duration: 3, Placement:'bottomRight'
});
return; } // The data request method var url ="http://localhost:8080/getAllDaeInfo";
axios.get(url).then(function (response) {
console.log("response-------------->", response.data);
}).catch(function (error) {
console.log(error);
});
let enterTime, daeStatus, longitude, latitude, maxDistance = 0, distance = 0;
letcarPathData = new Array(); // Generate simulated datafor (let i = 0; i < 20; i++) {
let time = Date.now() + i * 1000;
let carPathDataItem = {};
carPathDataItem.collectTime = time;
carPathDataItem.devStatus = 0;
carPathDataItem.devId = 'ts10001' + i;
carPathDataItem.devAddr = 'XX City Test Area Test Street '+ i; CarPathDataItem. Longitude = 107.4425790689 + Math. The random () * (I + 1) / 500; Carpathdataitem.latitude = 29.8717183035 + math.random () * (I + 1) / 500; carPathData.push(carPathDataItem); }for (let i = 0; i < carPathData.length; i++) {
enterTime = moment(carPathData[i].collectTime).format("YYYY-MM-DD HH:mm:ss");
if (carPathData[i].devStatus === 0) {
daeStatus = "Offline";
} else {
daeStatus = "Online"; } // Calculate the maximum distance between latitude and longitudeif (i < carPathData.length - 1) {
distance = this.getDistance(carPathData[i].latitude, carPathData[i].longitude, carPathData[i + 1].latitude, carPathData[i + 1].longitude);
if(distance > maxDistance) { maxDistance = distance; }} // TableData.push ({key: '${i}`, column0: enterTime, column1: carPathData[i].devId, column2: carPathData[i].devAddr }); longitude = carPathData[i].longitude; latitude = carPathData[i].latitude; Mapdata.push ([longitude, latitude, carPathData[I].devid, daeStatus, carPathData[I].devaddr]); }if (mapData.length > 0) {
letlevel = 13; // Determine the scale of the mapfor (let i = 0; i < zoomSize.length; i++) {
if (maxDistance > 100) {
if (maxDistance < zoomSize[i]) {
level = zoomlevel[i - 1];
break; }}else {
if (maxDistance === 0) {
level = 5;
break;
} else {
level = 17;
break;
}
}
}
this.setState({
tableBodyData: tableData,
canRefreshAnimation: true})} this.map.removelayer (prevVector) {this.map.removelayer (prevVector); // Empty the car to move this.map.un('postcompose', this.moveFeature); This.closemapmarker (); this.closemapmarker (); this.map.getView().setZoom(level); This.map.getview ().setCenter(ol.proj.transform([mapData[0][0], mapData[0][1]],'EPSG:4326'.'EPSG:3857'));
this.generateCarMoveOnMap(mapData);
} else {
this.setState({
tableBodyData: tableData,
carProgerss: 0,
canRefreshAnimation: false})} this.map.removelayer (prevVector) {this.map.removelayer (prevVector); // Empty the car to move this.map.un('postcompose', this.moveFeature); This.closemapmarker (); this.closemapmarker (); / / reset the center of the map (it is not set up the car moving error). This map. The getView (). The setCenter (ol. Proj. The transform ([this. State. MapCenterLongitude, this.state.mapCenterLatitude],'EPSG:4326'.'EPSG:3857'));
this.generateCarMoveOnMap(mapData);
notification.info({
message: 'No trace information found'Duration: 3}); }} getDistance = (lat1, lng1, lat2, lng2) => {letRadLat1 = lat1 * math.pi / 180.0;letRadLat2 = lat2 * math.pi / 180.0;let a = radLat1 - radLat2;
letB = lng1 * math.pi / 180.0 - lng2 * math.pi / 180.0;lets = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))); S = s * 6378.137; // EARTH_RADIUS; s = Math.round(s * 10000) / 10;return s;
}
componentDidMount() {/ / the default Settings form rolling area enclosing setState ({scrollPathTableHeight: this. TableContrainer. ClientHeight - 60}); Window. onresize = () => {let tableContainer = this.tableContrainer;
if (tableContainer) {
lettableHeight = tableContainer.clientHeight - 60; Enclosing setState ({scrollPathTableHeight: tableHeight})}} / / call the structure table header information function enclosing generateTableHeaderData (); // Generate a map layerlet raster = new ol.layer.Tile({source: new ol.source.XYZ({url: this.state.mapUrl})}); This. map = new ol. map ({target:'pathMapContainer', layers: [raster],
view: new ol.View({
center: ol.proj.transform([this.state.mapCenterLongitude, this.state.mapCenterLatitude], 'EPSG:4326'.'EPSG:3857'// Specify map projection type projection:'EPSG:3857'// Define the map display hierarchy zoom: 13, maxZoom: 18, minZoom: 5})}); // The user opens the coordinates to display this.openMapMarker(this.map); GenerateCarMoveOnMap = (coordinate) => {letthat = this; / / markup layerlet layer = new ol.layer.Vector({
source: new ol.source.Vector()
});
letStyles = {// Line styles'route': new ol.style.Style({stroke: new ol.style.Stroke({width: 5, color: "#66ACED"})}), // The starting point style'start'Style({image: new ol.style.icon ({scale: 0.75, SRC: direction})}), // Start the style'end'Style({image: new ol.style.icon ({scale: 0.8, Anchor: [0.5, 0.82], SRC: stop})}), // Car style'geoMarker'Style({image: new ol.style.icon ({scale: 0.65, Anchor: [0.5, 0.8], SRC: move})}), // True point style'point': new ol.style.Style({image: new ol.style.Icon({scale: 1, src: daemarker})})
}
let animating = false, now, speed;
let routeCoords, routeLength, geoMarker;
let startButton = document.getElementById('start-animation');
lettraversed = 0; // The distance traveledletelapsedTime = 0; // Time spentletretime = 0; // Save the last movement timeif (coordinate.length > 2) {
let geometry = new ol.geom.LineString();
let anchor;
letMinScale = 0.001;let lngValue, latValue, times;
letlnglatArray = new Array(); // Construct a line between coordinate pointsfor (leti = 0; i < coordinate.length; I++) {// construct the path when constructing coordinate pointsif(I > 0) {// Construct the path between coordinate points lngValue = coordinate[I][0] -coordinate [i-1][0]; latValue = coordinate[i][1] - coordinate[i - 1][1]; // There is a special case when two coordinate points coincide, or when two coordinate points are very closelet zLength = Math.sqrt(lngValue * lngValue + latValue * latValue);
if(zLength === 0 || Math.round(zLength / minScale) === 0) { geometry.appendCoordinate(ol.proj.transform([coordinate[i][0], coordinate[i][1]],'EPSG:4326'.'EPSG:3857'));
} else {
times = Math.round(zLength / minScale);
let xminScale = lngValue / times;
let yminScale = latValue / times;
for (let j = 0; j < times; j++) {
lnglatArray[0] = coordinate[i - 1][0] + j * xminScale;
lnglatArray[1] = coordinate[i - 1][1] + j * yminScale;
// eslint-disable-next-line no-use-before-define
anchor = setAnchorStyle(geometry, lnglatArray); layer.getSource().addFeature(anchor); }}}} // Set the coordinate between coordinate points // eslint-disable-next-line no-inner-declarationsfunction setAnchorStyle(geometry, lnglatArray) {
geometry.appendCoordinate(ol.proj.transform(lnglatArray, 'EPSG:4326'.'EPSG:3857'));
let anchor = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform(lnglatArray, 'EPSG:4326'.'EPSG:3857'))});returnanchor; } // Mark the real coordinate points on the car trajectoryletreallyGeometry = new ol.geom.LineString(); // From the second to the penultimate pointfor (let i = 1; i < coordinate.length - 1; i++) {
reallyGeometry.appendCoordinate(ol.proj.transform([coordinate[i][0], coordinate[i][1]], 'EPSG:4326'.'EPSG:3857'));
}
let reallyCoords = reallyGeometry.getCoordinates();
let reallyPoint = new Array();
for (leti = 0; i < reallyCoords.length; ReallyPoint. Push (new ol.feature ({i++)) {// reallyPoint. Push (new ol.feature ({type: 'point', state: 0, num: coordinate[i + 1][2], status: coordinate[i + 1][3], address: coordinate[i + 1][4], geometry: new ol.geom.Point(reallyCoords[i]) })); } routeCoords = geometry.getCoordinates(); routeLength = routeCoords.length; // The car goes along the linelet routeFeature = new ol.Feature({
type: 'route', state: 1, geometry: geometry }); // geoMarker = new ol.Feature({geoMarker = new ol.Feature({type: 'geoMarker', state: 1, geometry: new ol.geom.Point(routeCoords[0]) }); // Start coordinates of the trajectorylet startMarker = new ol.Feature({
type: 'start', state: 0, num: coordinate[0][2], status: coordinate[0][3], address: coordinate[0][4], geometry: new ol.geom.Point(routeCoords[0]) }); // The trajectory terminates coordinateslet endMarker = new ol.Feature({
type: 'end', state: 0, num: coordinate[coordinate.length - 1][2], status: coordinate[coordinate.length - 1][3], address: coordinate[coordinate.length - 1][4], geometry: new ol.geom.Point(routeCoords[routeLength - 1]) }); // The set of all coordinates on the trajectorylet vector = new ol.layer.Vector({
source: new ol.source.Vector({
features: [routeFeature, ...reallyPoint, geoMarker, startMarker, endMarker]
}),
style: function (feature) {
if (animating && feature.get('type') = = ='geoMarker') {
return null;
}
return styles[feature.get('type')]; }}); // Save the previous layer prevVector = vector; // Add a set of coordinates on the map that.map.addlayer (vector); // eslint-disable-next-line no-use-before-define startButton.addEventListener('click', startAnimation, false);
// eslint-disable-next-line no-use-before-define
setTimeout(() => startAnimation(), 50);
} else if(coordinate. Length > 0 && coordinate. Length < 3) {// when punctuation is less than 3 and greater than 0let geometry = new ol.geom.LineString();
for (let i = 0; i < coordinate.length; i++) {
geometry.appendCoordinate(ol.proj.transform([coordinate[i][0], coordinate[i][1]], 'EPSG:4326'.'EPSG:3857')); } routeCoords = geometry.getCoordinates(); routeLength = routeCoords.length; // The car goes along the linelet routeFeature = new ol.Feature({
type: 'route', state: 1, geometry: geometry }); // geoMarker = new ol.Feature({geoMarker = new ol.Feature({type: 'geoMarker', state: 1, geometry: new ol.geom.Point(routeCoords[0]) }); // Start coordinates of the trajectorylet startMarker = new ol.Feature({
type: 'start', state: 0, num: coordinate[0][2], status: coordinate[0][3], address: coordinate[0][4], geometry: new ol.geom.Point(routeCoords[0]) }); // The trajectory terminates coordinateslet endMarker = new ol.Feature({
type: 'end', state: 0, num: coordinate[coordinate.length - 1][2], status: coordinate[coordinate.length - 1][3], address: coordinate[coordinate.length - 1][4], geometry: new ol.geom.Point(routeCoords[routeLength - 1]) }); // The set of all coordinates on the trajectorylet vector = new ol.layer.Vector({
source: new ol.source.Vector({
features: [routeFeature, geoMarker, startMarker, endMarker]
}),
style: function (feature) {
if (animating && feature.get('type') = = ='geoMarker') {
return null;
}
return styles[feature.get('type')]; }}); // Save the previous layer prevVector = vector; // Add a set of coordinates on the map that.map.addlayer (vector); // eslint-disable-next-line no-use-before-define startButton.addEventListener('click', startAnimation, false);
// eslint-disable-next-line no-use-before-define
setTimeout(() => startAnimation(), 50); } // This. MoveFeature =function (event) {
let vectorContext = event.vectorContext;
let frameState = event.frameState;
if (animating) {
if (retime === 0) {
elapsedTime = frameState.time - now;
} else {
elapsedTime = frameState.time - retime;
}
retime = frameState.time;
let index = Math.round(that.defaultCarSpeed * elapsedTime / 1000);
traversed += index;
that.index = traversed;
if(traversed >= routeLength) {// eslint-disable-next-line no-use-before-define moveEnd(traversed >= routeLength) {// Eslint-disable-next-line no-use-before-define moveEnd(true);
return;
}
let currentPoint = new ol.geom.Point(routeCoords[traversed]);
letfeature = new ol.Feature(currentPoint); vectorContext.drawFeature(feature, styles.geoMarker); SetState ({carProgerss: math.round ((that.index + 1)/routeLength * 100)}) that.map.render(); }; // Start the motion of the carfunction startAnimation() { traversed = 0; ElapsedTime = 0; Retime = 0; // Save the last movement timeif (animating) {
// eslint-disable-next-line no-use-before-define
refreshAnimation();
} else {
animating = true;
now = new Date().getTime();
//speed = that.defaultCarSpeed;
geoMarker.setStyle(null);
that.map.on('postcompose', that.moveFeature); that.map.render(); }} // When the car moves to the endfunction moveEnd(isend) {
animating = false;
letcoord = isend ? routeCoords[routeLength - 1] : routeCoords[0]; (geoMarker.getGeometry()).setCoordinates(coord); SetState ({carProgerss: 100}) that.map.un({carProgerss: 100}) setState({carProgerss: 100}) that.map.un('postcompose', that.moveFeature); } // The car moves againfunction refreshAnimation() {
if (that.state.carProgerss === 100) {
that.setState({
carProgerss: 0
})
}
animating = false;
if(that.state.canRefreshAnimation) { startAnimation(); }} adjustSpeed = () => {this.setState((prevState) => ({showAdjustSpeed:! PrevState. ShowAdjustSpeed})} / / change small convoy of speedsetNewSpeed = (value) => { this.defaultCarSpeed = value + 4; } closeMapMarker = () => {this.marker.setPosition(undefined);return false; } openMapMarker = (map) => {let that = this;
let element = that.alertContainer;
that.marker = new ol.Overlay({
element: element,
positioning: 'bottom-center',
stopEvent: true,
offset: [-5, -24],
autoPan: true,
autoPanAnimation: {
duration: 250
}
});
map.addOverlay(that.marker);
let dom = document.createElement('div');
dom.setAttribute('id'.'closeAlert');
that.alertContainer.appendChild(dom);
map.on('click'.function (event) {
let feature = map.forEachFeatureAtPixel(event.pixel, function (feature) {
return feature;
})
if(feature) {// A pop-up box appears when clicking a large coordinate point on the mapif (feature.get("state") = = = 0) {letcoordinatePoint = feature.getGeometry().getCoordinates(); that.marker.setPosition(coordinatePoint); // The popup displays element.style.display ="block";
dom.onclick = that.closeMapMarker;
that.setState({
DAENumber: feature.get("num"),
DAEStatus: feature.get("status"),
DEAAddress: feature.get("address")})}}}); } / / return the higher level menu goBackUpperLevel = () = > {this. Props. BackPrevLavel (this) state) defaultModeFlag); }render() {
let {queryKeyWord} = this.state;
const suffix = queryKeyWord ? <Icon type="close-circle" onClick={this.clearUserKeyWord}/> : null;
return (
<div className="pathContainer">
<div className="headerContainer">
<Input
placeholder="Please enter the license plate number for enquiry"
suffix={suffix}
onChange={this.userInputKeyWord}
value={queryKeyWord}
className="userInputKeyWord"
/>
<RangePicker
onChange={this.userChangeQueryTime}
format="YYYY-MM-DD HH:mm"
showTime
className="userSelectQueryTime"
value={this.state.userChooseStartTime ? [moment(this.state.userChooseStartTime), moment(this.state.userChooseEndTime)] : null}
/>
<Button onClick={this.getPathDatas} icon="search" className="queryButton"</Button> </div> <div className="displayContainer">
<div className="displayLeftContainer">
<div id="pathMapContainer" className="pathMapContainer">
{this.state.defaultShowBtnFlag &&
<Button onClick={this.goBackUpperLevel} icon="left" className="goBack"</Button>} <div className="alertContainer" ref={(box) => {
this.alertContainer = box
}}
><br/>
<div className="alertItem">
<span className="alertSpan"> Collector number :</span><span className="alertSpanValue"
>{this.state.DAENumber}</span>
</div>
<div className="alertItem">
<span className="alertSpan"> Collector status :</span><span className="alertSpanValue"
>{this.state.DAEStatus}</span>
</div>
<div className="alertItem">
<span className="alertSpan"> Collector address :</span><span className="alertSpanValue"
>{this.state.DEAAddress}</span>
</div>
<div className="triangleDown"></div>
</div>
<div className="adjustSpeed">
<img src={playback} className="imgSize" id="start-animation"/>
<Progress percent={this.state.carProgerss} status="active" className="progress"/>
<img src={speedcar} className="carImgSize" onClick={this.adjustSpeed}/>
{this.state.showAdjustSpeed && <div className="adjustSlider">
<Slider vertical min={1} defaultValue={this.defaultCarSpeed}
className="sliderChoose" onChange={this.setNewSpeed}
/>
</div>}
</div>
</div>
</div>
<div className="displayRightContainer">
<div className="pathTitle">
<span className="spanTitle"</span> </div> <div className="pathTable" ref={(box) => {
this.tableContrainer = box
}}
>
<Table
pagination={false}
columns={this.state.columns}
dataSource={this.state.tableBodyData}
scroll={{y: this.state.scrollPathTableHeight}}
/>
</div>
</div>
</div>
</div>
)
}
}
export default CarMove;
Copy the code
.pathContainer{
width: 100%;
height: calc(~'100% - 60px');
margin-top: 60px;
}
.headerContainer{
width: 100%;
height: 40px;
padding-top: 4px;
}
.displayContainer{
width:100%;
height: calc(~'100% - 40px');
}
.userInputKeyWord{
width: 280px;
margin-left: 10px;
}
.userSelectQueryTime{
width: 280px;
margin-left: 4px;
}
.queryButton{
margin-left: 4px;
width: 100px;
}
.displayLeftContainer{
width: calc(~'100% - 480px');
height: 100%;
float: left;
}
.displayRightContainer{
width: 480px;
height: 100%;
z-index: 1;
float: right;
background-color: rgb(236, 236, 236);
}
.pathTitle{
width: 100%;
height: 35px;
padding: 7px;
}
.spanTitle{
font-size: 14px;
color: #4291E9;
margin-left: 5px;
}
.pathTable{
width: 100%;
height: calc(~'100% - 35px');
}
.pathMapContainer{
height: 100%;
position: relative;
.ol-zoom{
left: calc(~'100% - 46px');
top: calc(~'100% - 72px'); padding: 0px; Box-shadow: 0px 2px 2px 0px Rgba (0, 0, 0, 0.15); .ol-zoom-in{ height: 26px; width: 26px; background-color: rgb(255, 255, 255); font-size: 20px; color: rgb(150, 150, 150); cursor: pointer; overflow: hidden; ::selection{ color: white ! important; } } .ol-zoom-out{ cursor: pointer; height: 26px; width: 26px; background-color: rgb(255, 255, 255); font-size: 20px; color: rgb(150, 150, 150); overflow: hidden; } } .ol-attribution{ display: none; } } .goBack{ position: absolute; top: 17px; left: 35px; z-index: 1; color: rgb(100, 173, 255); } .adjustSpeed{ width: 500px; height: 40px; position: absolute; z-index: 1; Bottom: 2.7%; border-radius: 5px; background-color: rgb(254, 254, 254); left: 50%; margin-left: -250px; } .imgSize{ width: 35px; height: 35px; margin-top: 4px; margin-left: 5px; cursor: pointer; } .progress{ width: 400px; margin-left: 4px; position: absolute; margin-top: 11px; } .carImgSize{ width: 35px; height: 35px; position: absolute; cursor: pointer; margin-left: 414px; margin-top: 2px; } .adjustSlider{ background-color: snow; width: 28px; height: 181px; position: absolute; margin-top: -229px; border-radius: 6px; margin-left: 456px; padding-bottom: 16px; padding-top: 5px; } .alertContainer{ width: 190px; height: 120px; background-color:white; border-radius: 5px; display: none; border: 1px solid lightgrey; -moz-user-select: text; user-select: text; }#closeAlert{
position: absolute;
right: 5%;
top: 3%;
width: 15px;
height: 15px;
cursor: pointer;
background: url('.. /.. /Images/loginout.png') no-repeat ;
}
.alertItem{
width: 93.6%;
white-space: normal;
word-break: break-all;
margin-left: 8px;
}
.alertSpan{
font-family: 'Microsoft YaHei';
font-size: 12px;
color: # 111111;
}
.alertSpanValue{
font-family: 'Microsoft YaHei';
font-size: 12px;
margin-left: 7px;
}
.triangleDown{
position: absolute;
width: 0;
height: 0;
border-left: 15px solid transparent ;
border-right: 15px solid transparent ;
border-top: 20px solid white;
left: 50%;
margin-left: -10px;
bottom: -13px;
}
Copy the code
Effect: