From a8c870fc1cefb1a9cd651f46f759a146df661f60 Mon Sep 17 00:00:00 2001
From: 叶松 <2217086471@qq.com>
Date: 星期一, 27 十一月 2023 11:23:40 +0800
Subject: [PATCH] 供应商字段修改
---
web/src/views/SecureManage/SmartHelmet/TrackBack.vue | 737 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 734 insertions(+), 3 deletions(-)
diff --git a/web/src/views/SecureManage/SmartHelmet/TrackBack.vue b/web/src/views/SecureManage/SmartHelmet/TrackBack.vue
index 1939462..f058bec 100644
--- a/web/src/views/SecureManage/SmartHelmet/TrackBack.vue
+++ b/web/src/views/SecureManage/SmartHelmet/TrackBack.vue
@@ -1,3 +1,734 @@
-<template>
- <div>轨迹回放</div>
-</template>
\ No newline at end of file
+<template>
+ <!-- 安全管理 ==> 智能安全帽 => 轨迹回放 -->
+ <div class="main">
+ <!-- table -->
+ <div class="main_left">
+ <div class="main_left_search">
+ <div class="search_item">
+ <span>姓名:</span>
+ <el-input size="mini" v-model="queryInfo.userName" placeholder="请输入姓名" clearable></el-input>
+ </div>
+ <div class="search_item">
+ <span>设备编号:</span>
+ <el-input size="mini" v-model="queryInfo.deviceNum" placeholder="请输入设备编号" clearable></el-input>
+ <el-button icon="el-icon-search" @click="queryTable">查询</el-button>
+ </div>
+ </div>
+ <div class="main_left_table">
+ <cpnTable :table-loading="loading" :table-data="dataList" :table-columns="tableColumns"
+ :row-click="rowClick">
+ </cpnTable>
+ </div>
+ </div>
+ <!-- chart -->
+ <div class="main_right_echart" v-show="!isShowMap">
+ <div class="echart_options">
+ <span>日期:</span>
+ <el-date-picker class="elDatePicker" type="daterange" v-model="chart.date" value-format="yyyy-MM-dd"
+ start-placeholder="开始日期" end-placeholder="结束日期" clearable></el-date-picker>
+ <el-button icon="el-icon-search" @click="queryEchart">查询</el-button>
+ <el-button style="float:right" @click="watchMap">查看</el-button>
+ </div>
+ <div ref="echart_bar" class="echart_bar"></div>
+ <div ref="echart_drop" class="echart_drop"></div>
+ </div>
+ <!-- map -->
+ <div class="main_right_map" v-show="isShowMap">
+ <div class="map_option">
+ <span>日期:</span>
+ <el-date-picker class="elDatePicker" type="daterange" v-model="chart.date" value-format="yyyy-MM-dd"
+ start-placeholder="开始日期" end-placeholder="结束日期" clearable></el-date-picker>
+ <el-button icon="el-icon-search" @click="queryEchart">查询</el-button>
+ <el-button style="float:right" @click="hideMap">返回</el-button>
+ </div>
+ <div class="map_view">
+ <div class="map_view_main">
+ <div id="container"></div>
+ <div id="trackDetail" v-show="isShowTrackDetail">
+ <h2>位置信息统计</h2>
+ <div class="ul_box">
+ <ul>
+ <li :class="{ active: index === maps.trackListIndex }"
+ v-for="(val, index) in maps.trackLists " :key="index" @click="trackListClick(index)">
+ {{ val }}
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="map_view_btn">
+ <el-button @click="startAnimation">开始动画</el-button>
+ <el-button @click="pauseAnimation">停止动画</el-button>
+ <el-button @click="resumeAnimation">继续动画</el-button>
+ <el-button @click="stopAnimation">停止动画</el-button>
+ <el-button @click="showTrackDetail">轨迹详情</el-button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import AMapLoader from '@amap/amap-jsapi-loader';
+import cpnTable from '@/components/element/Table'
+import icon from '@/assets/markerIcon.png'
+import 'echarts-liquidfill' // 水滴图形
+export default {
+ data() {
+ return {
+ loading: false,
+ isShowMap: false, // 是否查看轨迹地图
+ isShowTrackDetail: false, // 是否渲染轨迹详情
+ queryInfo: {
+ pageNum: 1,
+ pageSize: 9999,
+ userName: '',
+ deviceNum: '',
+ },
+ dataList: [],
+ tableColumns: [],
+ userId: '', // 用户id
+ // 图表数据
+ chart: {
+ date: [new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).format(), new Date().format()], // 轨迹图表日期.默认近一个月
+ totaltime: '', // 安全帽在线总时长
+ },
+ maps: { // 地图数据
+ date: '',
+ trackLists: [], // 轨迹详情
+ trackListIndex: '',
+ },
+ }
+ },
+ components: {
+ cpnTable
+ },
+ created() {
+ // 添加密钥,获取高德地址使用
+ window._AMapSecurityConfig = {
+ securityJsCode: "aa90b00e27025d1f57286675cc8b232e",
+ }
+ // 存储全局变量.(不响应式)
+ this.maps.times = []
+ this.maps.lineArr = []
+ this.maps.AMap = null
+ this.maps.map = null
+ this.maps.mapMarker = null
+ this.chart.chartInstanceBar = null
+ this.chart.chartInstanceDrop = null
+ this.$http = this.$api.Safety.SmartHelmet.trackBack // 接口api路径
+
+ this.setTableColumn()
+ this.getLists()
+ },
+ mounted() {
+ window.addEventListener("resize", this.screenAdapter)
+ },
+ beforeDestroy() {
+ this.clearMap()
+ this.clearChart()
+ window.removeEventListener("resize", this.screenAdapter)
+ },
+ methods: {
+ setTableColumn() {
+ this.tableColumns = [
+ {selection: true},
+ {name: "用户ID", key: "userId"},
+ {name: "姓名", key: "userName"},
+ {name: "设备编号", key: "deviceNum"},
+ ]
+ },
+ getLists() {
+ this.loading = true
+ this.$api.Safety.SmartHelmet.getLists(this.queryInfo).then(res => {
+ if (res.statusMsg === 'ok') {
+ this.dataList = res.data.list
+ }
+ this.loading = false
+ })
+ },
+ // 获取用户在线时长
+ async getOnlineTime() {
+ let out = {}
+ const params = {
+ userId: this.userId,
+ strTime: this.chart.date?.[0],
+ endTime: this.chart.date?.[1],
+ }
+ const {data, statusMsg} = await this.$http.getOnlineTime(params)
+ if (statusMsg === 'ok') {
+ out = {
+ list: data.tHelmetTrajectory,
+ total: data.total,
+ }
+ }
+ return out
+ },
+ // 获取用户轨迹
+ async getTrackLists() {
+ let out = {
+ positions: [], // 经纬度
+ times: [], // 时间
+ }
+ const params = {
+ userId: this.userId,
+ strTime: this.chart.date?.[0],
+ endTime: this.chart.date?.[1],
+ }
+ const {data, statusMsg} = await this.$http.getTrackLists(params)
+ if (statusMsg === 'ok') {
+ data.forEach(item => {
+ out.positions.push([+item.ypoint, +item.xpoint])
+ out.times.push(item.smTime)
+ })
+ }
+ return out
+ },
+ // 初始化图表
+ initChart() {
+ this.chart.chartInstanceBar = this.$echarts.init(this.$refs.echart_bar)
+ this.chart.chartInstanceDrop = this.$echarts.init(this.$refs.echart_drop)
+ this.renderBaseChart()
+ },
+ // 渲染chart基础配置
+ renderBaseChart() {
+ const baseBarOptions = {
+ animationDuration: 1500,
+ tooltip: {
+ trigger: "axis",
+ },
+ legend: {
+ itemWidth: 35,
+ itemHeight: 12,
+ x: 'right',
+ y: 'top',
+ textStyle: {
+ color: "#fff",
+ },
+ },
+ grid: {
+ top: "10%",
+ right: "0",
+ left: "2%",
+ bottom: "14",
+ containLabel: true,
+ },
+ xAxis: [{
+ type: 'category',
+ axisLine: {
+ lineStyle: {
+ width: 2,
+ color: "#B7E4F7",
+ },
+ },
+ axisTick: {
+ show: false,
+ },
+ }],
+ yAxis: [{
+ type: 'value',
+ axisLabel: {
+ formatter: "{value}",
+ color: "#CAD3E0",
+ },
+ splitLine: {
+ lineStyle: {
+ width: 2,
+ type: "dashed",
+ color: "#5A7E9D",
+ },
+ },
+ }],
+ series: [{
+ type: 'bar',
+ name: "在线时长",
+ barWidth: 14,
+ color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ {offset: 0, color: "#C15B3C"},
+ {offset: 1, color: "#FACD91"},
+ ]),
+ }],
+ }
+ const baseDropOptions = {
+ series: [{
+ type: 'liquidFill',
+ data: [0],
+ radius: '70%', //图表的大小 值是圆的直径
+ shape: 'circle', //水填充图的形状 circle默认圆形 rect圆角矩形 triangle三角形 diamond菱形 pin水滴状 arrow箭头状 还可以是svg的path
+ center: ['50%', '50%'], //图表相对于盒子的位置
+ outline: {
+ show: false
+ },
+ backgroundStyle: { // 背景配置
+ color: 'transparent', //背景颜色
+ borderWidth: '2',//边框大小
+ borderColor: 'rgba(17, 94, 176, 0.4)', // 边框颜色
+ },
+ itemStyle: {
+ color: new this.$echarts.graphic.LinearGradient(
+ 0, 0, 0, 1, //4个参数用于配置渐变色的起止位置, 这4个参数依次对应右/下/左/上四个方位. 而0 0 0 1则代表渐变色从正上方开始
+ [
+ {offset: 0, color: '#2790F4'},
+ {offset: 1, color: '#165190'}
+ ] //数组, 用于配置颜色的渐变过程. 每一项为一个对象, 包含offset和color两个参数. offset的范围是0 ~ 1, 用于表示位置
+ )
+ },
+ }]
+ }
+ this.chart.chartInstanceBar.setOption(baseBarOptions)
+ this.chart.chartInstanceDrop.setOption(baseDropOptions)
+ },
+ // 渲染chart内容数据(x,y,series)
+ async renderChart() {
+ const {list, total} = await this.getOnlineTime()
+ const x = [] // x轴数据
+ const y = [] // y轴数据
+ list.forEach(item => {
+ x.push(item.ctime)
+ y.push(item.ltime)
+ })
+ const baseBarOptions = {
+ xAxis: {
+ index: 0,
+ data: x
+ },
+ series: {
+ index: 0,
+ data: y
+ }
+ }
+ const baseDropOptions = {
+ series: {
+ index: 0,
+ data: [0.3],
+ label: {
+ color: '#fff',
+ fontSize: 28,
+ formatter: () => this.handleTime(total),
+ }
+ }
+ }
+ this.chart.chartInstanceBar.setOption(baseBarOptions)
+ this.chart.chartInstanceDrop.setOption(baseDropOptions)
+ },
+ // 处理秒=>天时分
+ handleTime(num) {
+ let second = num * 60
+ let day = 0
+ let hour = 0
+ let minutes = 0
+ if (second < 86400) {
+ day = 0;
+ if (second < 3600) {
+ hour = 0;
+ if (second < 60) {
+ minutes = 0;
+ } else {
+ minutes = Math.floor(second / 60);
+ }
+ } else {
+ hour = Math.floor(second / 3600);
+ if (second - hour * 3600 < 60) {
+ minutes = 0;
+ } else {
+ minutes = Math.floor((second - hour * 3600) / 60);
+ }
+ }
+ } else {
+ day = Math.floor(second / 86400);
+ if (second - 86400 * day < 3600) {
+ hour = 0;
+ minutes = Math.floor((second - 86400 * day) / 60);
+ } else {
+ hour = Math.floor((second - 86400 * day) / 3600);
+ if (second - 86400 * day - 3600 * hour < 60) {
+ minutes = 0;
+ } else {
+ minutes = Math.floor((second - 86400 * day - 3600 * hour) / 60);
+ }
+ }
+ }
+ day = day ? day + "天" : ''
+ hour = hour ? hour + "小时" : ''
+ minutes = minutes && minutes + "分"
+ return day + hour + minutes
+ },
+ rowClick(row) {
+ this.userId = row.userId
+ if (!this.chart.chartInstanceBar) {
+ this.initChart()
+ }
+ this.hideMap()
+ this.renderChart()
+ },
+ queryTable() {
+ this.getLists()
+ },
+ queryEchart() {
+ this.renderChart()
+ },
+ queryMap() {
+
+ },
+ // chart 查看按钮
+ watchMap() {
+ if (!this.userId) {
+ this.$message.warning('请先选择用户');
+ return
+ }
+ this.renderMap()
+ },
+ async renderMap() {
+ this.isShowMap = !this.isShowMap
+ let {positions, times} = await this.getTrackLists()
+ this.maps.times = times
+ this.maps.lineArr = positions
+ AMapLoader.load({
+ key: "1dc3895771d98745fa27bbcc3280464f", // 申请好的Web端开发者Key,首次调用 load 时必填
+ version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
+ }).then(AMap => {
+ this.maps.AMap = AMap
+ AMap.plugin('AMap.MoveAnimation', () => {
+ // 创建地图
+ this.maps.map = new AMap.Map("container", {
+ resizeEnable: true,
+ viewMode: "2D", // 是否为3D地图模式
+ zoom: 17, // 初始化地图级别
+ })
+ if (positions.length) {
+ // 创建一个 (小人) Icon
+ var personIcon = new AMap.Icon({
+ image: icon, // 图标的取图地址
+ imageSize: new AMap.Size(40, 40), // 图标所用图片大小
+ })
+ // 创建marker实例
+ let mapMarker = this.maps.mapMarker = new AMap.Marker({
+ map: this.maps.map,
+ position: positions[0],
+ icon: personIcon,
+ zIndex: 10,
+ offset: new AMap.Pixel(-20, -36),
+ })
+ // marker提示框
+ let infoWindow = new AMap.InfoWindow({offset: new AMap.Pixel(0, -30)});
+ let markerMouseover = async (e) => {
+ let index = e.target.content
+ let adress = await this.getAdress(positions[index])
+ let time = times[index]
+ let el = `
+ <p><t>地址:</t><span>${adress}</span></p>
+ <p><t>时间:</t><span>${time}</span></p>
+ <p><t>经纬度:</t><span>${positions[index][0]}, ${positions[index][1]}</span></p>
+ `
+ infoWindow.setContent(el)
+ infoWindow.open(this.maps.map, e.target.getPosition())
+ }
+ // 给折线图添加拐点和绑定事件
+ for (var i = 0; i < positions.length; i += 1) {
+ let center = positions[i]
+ let icon = new AMap.Icon({
+ size: new AMap.Size(6, 11), // 图标尺寸
+ image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png', // Icon的图像
+ imageSize: new AMap.Size(6, 11)
+ })
+ let marker = new AMap.Marker({
+ map: this.maps.map,
+ position: center,
+ icon: icon,
+ offset: new AMap.Pixel(-3, -10)
+ })
+ marker.content = i
+ marker.on("mouseover", markerMouseover)
+ }
+ // 绘制轨迹
+ new AMap.Polyline({
+ map: this.maps.map,
+ path: positions,
+ // showDir: true,
+ strokeColor: "#28F", //线颜色
+ strokeWeight: 3, //线宽
+ })
+ // 已通过轨迹
+ let passedPolyline = new AMap.Polyline({
+ map: this.maps.map,
+ strokeColor: "red", //线颜色
+ strokeWeight: 3, //线宽
+ })
+ mapMarker.on('moving', (e) => {
+ passedPolyline.setPath(e.passedPath);
+ this.maps.map.setCenter(e.target.getPosition(), true)
+ })
+ this.maps.map.setFitView()
+ }
+ })
+ })
+ },
+ // 获取地址
+ getAdress(lnglats) {
+ return new Promise(resolve => {
+ const AMap = this.maps.AMap
+ AMap.plugin('AMap.Geocoder', () => {
+ const geocoder = new AMap.Geocoder({
+ radius: 1000,
+ extensions: "all"
+ })
+ geocoder.getAddress(lnglats, (status, result) => {
+ let address = ''
+ if (status === 'complete' && result.info === 'OK') {
+ address = result.regeocodes ? result.regeocodes : result.regeocode.formattedAddress
+ }
+ resolve(address)
+ })
+ })
+ })
+ },
+ hideMap() {
+ this.clearMap()
+ this.isShowMap = false
+ this.maps.trackLists = []
+ this.maps.trackListIndex = ''
+ },
+ startAnimation() {
+ this.maps.mapMarker?.moveAlong(this.maps.lineArr, {
+ // 每一段的时长
+ duration: 500,//可根据实际采集时间间隔设置
+ // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
+ autoRotation: true,
+ })
+ },
+ pauseAnimation() {
+ this.maps.mapMarker?.pauseMove()
+ },
+ resumeAnimation() {
+ this.maps.mapMarker?.resumeMove()
+ },
+ stopAnimation() {
+ this.maps.mapMarker?.stopMove()
+ },
+ // 显示轨迹详情
+ async showTrackDetail() {
+ this.isShowTrackDetail = !this.isShowTrackDetail
+ if (this.maps.trackLists.length) {
+ return
+ }
+ if (this.maps.times.length && this.maps.lineArr.length) {
+ let tracks = []
+ let adress = await this.getAdress([...this.maps.lineArr])
+ adress.forEach((item, index) => {
+ tracks.push(`位置: ${this.maps.times[index]}(${item.formattedAddress})`)
+ })
+ this.maps.trackLists = tracks
+ }
+ },
+ trackListClick(index) {
+ this.maps.trackListIndex = index
+ },
+ screenAdapter() {
+ this.chart.chartInstanceBar?.resize()
+ this.chart.chartInstanceDrop?.resize()
+ },
+ clearMap() {
+ this.maps.mapMarker?.stopMove()
+ this.maps.map?.destroy()
+ },
+ clearChart() {
+ this.chart.chartInstanceBar?.clear()
+ this.chart.chartInstanceDrop?.clear()
+ },
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/style/layout-main.scss';
+
+::v-deep .amap-info-content {
+ flex-direction: row;
+ width: 220px;
+ padding: 14px;
+ line-height: 20px;
+ border-radius: 5px;
+ background: #0D1846;
+
+ p {
+ display: flex;
+ width: 100%;
+ }
+
+ t {
+ width: 40px;
+ }
+
+ span {
+ flex: 1;
+ color: #39B5FE;
+ }
+}
+
+::v-deep .bottom-center .amap-info-sharp {
+ border-top: 8px solid #0D1846;
+}
+
+.active {
+ color: #39B5FE;
+}
+
+#container {
+ min-height: 400px;
+ flex: 1.6;
+}
+
+#trackDetail {
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+
+ h2 {
+ text-indent: 20px;
+ }
+
+ .ul_box {
+ overflow-y: auto;
+ flex: 1;
+
+ ul {
+ margin: 0;
+ padding: 0 16px;
+ }
+
+ li {
+ padding: 10px 0;
+ font-size: 15px;
+ line-height: 24px;
+ list-style: none;
+ border-bottom: 1px dashed #fff;
+ cursor: pointer;
+ }
+ }
+}
+
+.main {
+ display: flex;
+ flex-direction: row;
+ color: #EBEBEB;
+
+ .main_left {
+ display: flex;
+ flex-direction: column;
+ width: 380px;
+ padding: 14px;
+ margin-right: 18px;
+ border: 1px solid #39B5FE;
+ border-radius: 10px;
+ box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset;
+ box-sizing: content-box;
+
+ .search_item {
+ span {
+ display: inline-block;
+ min-width: 54px;
+ margin-right: 6px;
+ text-align: right;
+ }
+
+ .el-input {
+ width: 220px;
+ margin: 0 28px 16px 0;
+ }
+ }
+
+ ::v-deep.main_left_table {
+ overflow: hidden;
+
+ .el-table__row {
+ cursor: pointer
+ }
+
+ .el-table {
+ display: flex;
+ flex-direction: column;
+
+ .el-table__body-wrapper {
+ overflow-y: auto;
+ flex: 1;
+ }
+ }
+ }
+ }
+
+ .main_right_echart {
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ padding: 20px;
+ border: 1px solid #39B5FE;
+ background-color: rgba(0, 52, 121, 0.3);
+ border-radius: 10px;
+ box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset;
+ box-sizing: content-box;
+
+ .echart_options {
+ span {
+ display: inline-block;
+ margin-right: 6px;
+ text-align: right;
+ }
+
+ .elDatePicker {
+ width: 260px;
+ margin: 0 10px 14px 0;
+ }
+ }
+
+ .echart_bar {
+ flex: 1;
+ }
+
+ .echart_drop {
+ flex: 1.1;
+ }
+ }
+
+ .main_right_map {
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ right: 0;
+ left: 430px;
+ top: 0;
+ bottom: 0;
+ padding: 20px;
+
+ .map_option {
+ span {
+ display: inline-block;
+ margin-right: 6px;
+ }
+
+ .elDatePicker {
+ width: 260px;
+ margin: 0 10px 14px 0;
+ }
+ }
+
+ .map_view {
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+
+ .map_view_main {
+ overflow: hidden;
+ display: flex;
+ flex: 1;
+ }
+
+ .map_view_btn {
+ margin-top: 10px;
+ }
+ }
+ }
+}
+</style>
\ No newline at end of file
--
Gitblit v1.9.3