From 5672f352d0ba114e2ae96c8cefad6c74ae6d2934 Mon Sep 17 00:00:00 2001 From: unknown <280848880@qq.com> Date: 星期一, 23 十月 2023 10:38:28 +0800 Subject: [PATCH] 苏州-web:智能安全帽提交 --- web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue | 728 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 725 insertions(+), 3 deletions(-) diff --git a/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue b/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue index 668cc49..9c82ec0 100644 --- a/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue +++ b/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue @@ -1,3 +1,725 @@ -<template> - <div>报警记录</div> -</template> \ No newline at end of file +<template> + <!-- 安全管理 ==> 智能安全帽 => 报警记录 --> + <div class="main"> + <!-- main_left --> + <div class="main_left" v-show="!isRenderDetail"> + <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"> + </cpnTable> + </div> + </div> + <!-- main_right --> + <div class="main_right" v-show="!isRenderDetail"> + <div class="echart_options"> + <el-radio-group v-model="chart.radioGroupVal" @change="loadChart"> + <el-radio-button label="7">近7天报警数</el-radio-button> + <el-radio-button label="31">近31天报警数</el-radio-button> + </el-radio-group> + </div> + <div ref="echart_bar" class="echart_bar"></div> + <div class="echart_drop"> + <div ref="echart_drop_1" :class="{ show: chart.isShowDrop1, hide: !chart.isShowDrop1 }"></div> + <div ref="echart_drop_2" :class="{ show: chart.isShowDrop2, hide: !chart.isShowDrop2 }"></div> + </div> + </div> + <!-- detail --> + <div class="detail" v-if="isRenderDetail"> + <div class="detail_option"> + <div class="search_item"> + <span>日期:</span> + <el-date-picker class="elDatePicker" type="daterange" v-model="detail.date" value-format="yyyy-MM-dd" + start-placeholder="开始日期" end-placeholder="结束日期" clearable></el-date-picker> + </div> + <el-button icon="el-icon-search" @click="queryDetail">查询</el-button> + <el-button style="float:right" @click="hideDetail">返回</el-button> + </div> + <div class="detail_botbox"> + <div class="detail_table"> + <cpnTable :table-data="detail.dataList" :table-columns="detail.tableColumns"> + </cpnTable> + </div> + <div class="detail_map"> + <div id="container"></div> + <div class="detail_map_info_l" v-show="detail.isShowInfo"> + <h2>静默报警</h2> + <p>原因: <span>{{ detail.info.cause }}</span></p> + <p>姓名: <span>{{ detail.info.name }}</span></p> + <p>设备: <span>{{ detail.info.device }}</span></p> + <p>位置: <span>{{ detail.info.position }}</span></p> + <p>时间: <span>{{ detail.info.time }}</span></p> + <el-button style="float: right;" @click="() => { detail.isShowInfo = false }">确定</el-button> + </div> + <div class="detail_map_info_r" v-show="detail.isShowInfo"> + <h2>报警设备信息</h2> + <p>姓名: <span>{{ detail.info.name }}</span></p> + <p>时间: <span>{{ detail.info.time }}</span></p> + </div> + </div> + </div> + </div> + </div> +</template> + +<script> +import AMapLoader from '@amap/amap-jsapi-loader'; +import cpnTable from '@/components/element/Table' +import 'echarts-liquidfill' // 水滴图形 +export default { + data() { + return { + loading: false, + isRenderDetail: false, // 是否查看轨迹地图 + queryInfo: { + pageNum: 1, + pageSize: 9999, + userName: '', + deviceNum: '', + }, + dataList: [], + tableColumns: [], + userId: '', // 用户id + userName: '', // 用户名称 + deviceNum: '', // 设备编号 + // 图表数据 + chart: { + date: '', + radioGroupVal: '7', // (7 31)默认近7天 + isShowDrop1: false, + isShowDrop2: false, + chartInstanceLine: null, // 柱状图实例 + chartInstanceDrop1: null, // 水滴图实例 + chartInstanceDrop2: null, + }, + detail: { // 详情 + date: [], // 日期查询 + dataList: [], + tableColumns: [], + isShowInfo: false, + // 报警信息 + info: { + cause: '', + name: '', + device: '', + position: '', + time: '', + }, + // 报警类型原因 + sosTypes: { + 1: '长时间静止' + }, + }, + $http: '', // 接口api路径 + } + }, + components: { + cpnTable + }, + created() { + // 添加密钥,获取高德地址使用 + window._AMapSecurityConfig = { + securityJsCode: "aa90b00e27025d1f57286675cc8b232e", + } + this.$http = this.$api.Safety.SmartHelmet.warning + this.setTableColumn() + this.getLists() + }, + mounted() { + this.initChart() + window.addEventListener("resize", this.screenAdapter) + }, + beforeDestroy() { + this.clearMap() + this.clearChart() + window.removeEventListener("resize", this.screenAdapter) + }, + methods: { + setTableColumn() { + this.tableColumns = [ + {name: "用户ID", key: "userId"}, + {name: "姓名", key: "userName"}, + {name: "设备编号", key: "deviceNum"}, + { + operation: true, name: "操作", width: 140, value: [ + {name: "查看", class: "table_btn", handleRow: this.warchRow}, + {name: "详情", class: "table_btn", handleRow: this.detailRow}, + ] + }, + ] + }, + // 获取报警信息 + async getWarning() { + let out = {} + const params = { + userId: this.userId, + strTime: this.chart.date?.[0], + endTime: this.chart.date?.[1], + } + const {data, statusMsg} = await this.$http.getWarning(params) + if (statusMsg === 'ok') { + out = data + } + return out + }, + // 获取table数据 + 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 + }) + }, + // table-详情按钮 + detailRow(row) { + this.userId = row.userId + this.deviceNum = row.deviceNum + this.renderDetail() + }, + // table-查看按钮 + warchRow(row) { + if (this.userId === row.userId) return + this.userId = row.userId + this.userName = row.userName + console.log(row) + this.chart.isShowDrop1 = false + this.chart.isShowDrop2 = false + this.loadChart() + }, + // 加载图表 + loadChart() { + this.changeDay() + this.showDrop() + this.renderChart() + }, + // 水滴图表根据 7/31 切换显示 + showDrop() { + if (this.chart.radioGroupVal === '7') { + this.chart.isShowDrop1 = true + } else if (this.chart.radioGroupVal === '31') { + this.chart.isShowDrop2 = true + } + }, + // 近 7/31 天切换 + changeDay() { + const nowDate = new Date().format() + const beforeDate7 = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).format() + const beforeDate31 = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000).format() + const beforeDate = this.chart.radioGroupVal === '7' ? beforeDate7 : (this.chart.radioGroupVal === '31' ? beforeDate31 : '') + + this.chart.date = [beforeDate, nowDate] + }, + // 初始化图表 + initChart() { + this.chart.chartInstanceLine = this.$echarts.init(this.$refs.echart_bar) + this.chart.chartInstanceDrop1 = this.$echarts.init(this.$refs.echart_drop_1) + this.chart.chartInstanceDrop2 = this.$echarts.init(this.$refs.echart_drop_2) + this.renderBaseChart() + }, + // 渲染chart基础配置 + renderBaseChart() { + const baseBarOptions = { + animationDuration: 1500, + tooltip: { + trigger: "axis", + formatter: "日期: {b} <div></div> 在线时长: {c}分", + }, + 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: 'line', + smooth: true, + barWidth: 14, + color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [ + {offset: 0, color: "#C15B3C"}, + {offset: 1, color: "#FACD91"}, + ]), + }], + } + const baseDropOptions1 = { + title: { + text: '近一周报警天数', + left: '32%', + top: 30, + textStyle: { + color: "#fff", + }, + }, + series: [{ + type: 'liquidFill', + data: [0], + radius: '60%', //图表的大小 值是圆的直径 + 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, 用于表示位置 + ) + }, + }] + } + const baseDropOptions2 = { + title: { + text: '近一个月(31天)报警天数', + right: '24%', + top: 30, + textStyle: { + color: "#fff", + }, + }, + series: [{ + type: 'liquidFill', + data: [0], + radius: '60%', //图表的大小 值是圆的直径 + 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.chartInstanceLine.setOption(baseBarOptions) + this.chart.chartInstanceDrop1.setOption(baseDropOptions1) + this.chart.chartInstanceDrop2.setOption(baseDropOptions2) + }, + // 渲染chart内容数据(x,y,series) + async renderChart() { + const obj = await this.getWarning() + const day = obj.day // 报警天数 + const percentDay = (day / this.chart.radioGroupVal).toFixed(2) //百分比 + const x = [] // x轴数据 + const y = [] // y轴数据 + obj.reportNumDtos.forEach(item => { + x.push(item.smTime) + y.push(item.num) + }) + const lineOptions = { + title: { + text: `${this.userName} 近${this.chart.radioGroupVal}天报警天数`, + left: '42%', + textStyle: { + color: "#E2E5EA", + fontSize: 12, + }, + }, + xAxis: { + index: 0, + data: x + }, + series: { + index: 0, + data: y + } + } + const dropOptions = { + series: { + index: 0, + data: [percentDay], + label: { + color: '#fff', + formatter: () => day, + } + } + } + this.chart.chartInstanceLine.setOption(lineOptions) + if (this.chart.radioGroupVal === '7') { + this.chart.chartInstanceDrop1.setOption(dropOptions) + + } else if (this.chart.radioGroupVal === '31') { + this.chart.chartInstanceDrop2.setOption(dropOptions) + } + }, + screenAdapter() { + this.chart.chartInstanceLine?.resize() + this.chart.chartInstanceDrop1?.resize() + this.chart.chartInstanceDrop2?.resize() + }, + queryTable() { + this.getLists() + }, + /* --------详情页面-s */ + renderDetail() { + this.isRenderDetail = true + this.setTableDetailColumn() + this.getDetailLists() + this.renderMap() + }, + setTableDetailColumn() { + if (this.detail.tableColumns.length) return + this.detail.tableColumns = [ + {name: "时间", key: "smTime"}, + {name: "姓名", key: "userName", width: 70}, + {name: "原因", key: "sosType", width: 80, formatter: (row) => this.detail.sosTypes[row.sosType] || ''}, + { + operation: true, name: "操作", width: 100, value: [ + {name: "查看", class: "table_btn", handleRow: this.warchDetailRow}, + ] + }, + ] + }, + getDetailLists() { + const params = { + userId: this.userId, + strTime: this.detail.date?.[0], + endTime: this.detail.date?.[1], + } + this.detail.dataList = [] + this.$http.getDetailWarning(params).then(res => { + if (res.statusMsg === 'ok') { + this.detail.dataList = res.data + } + }) + }, + // 查看具体报警信息 + async warchDetailRow(row) { + const _position = await this.getAdress(row.xpoint, row.ypoint) + const info = { + cause: this.detail.sosTypes[row.sosType], + name: row.userName, + device: this.deviceNum, + position: `${_position}(经度:${row.ypoint}, 纬度${row.xpoint})`, + time: row.smTime, + } + this.detail.info = info + this.detail.isShowInfo = true + }, + renderMap() { + AMapLoader.load({ + key: "1dc3895771d98745fa27bbcc3280464f", // 申请好的Web端开发者Key,首次调用 load 时必填 + version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 + }).then(AMap => { + this.detail.AMap = AMap + this.detail.map = new AMap.Map("container", { + resizeEnable: true, + viewMode: "2D", // 是否为3D地图模式 + zoom: 13, // 初始化地图级别 + }) + }) + }, + createMapMarker(lnglatXY) { + if (!this.detail.marker) { + const AMap = this.detail.AMap + this.detail.marker = new AMap.Marker() + } + const marker = this.detail.marker + marker.setMap(null) + marker.setPosition(lnglatXY) + this.detail.map.add(marker) + this.detail.map.setFitView() + }, + // 获取地址 + getAdress(x, y) { + return new Promise(resolve => { + let address = '' + const AMap = this.detail.AMap + const lnglatXY = [y, x] // 位置的经纬度 + this.createMapMarker(lnglatXY) + AMap.plugin('AMap.Geocoder', () => { + const geocoder = new AMap.Geocoder({ + radius: 1000, + extensions: "all" + }) + geocoder.getAddress(lnglatXY, function (status, result) { + if (status === 'complete' && result.info === 'OK') { + address = result.regeocode.formattedAddress + } + resolve(address) + }) + }) + }) + }, + hideDetail() { + this.clearMap() + this.detail.date = [] + this.detail.isShowInfo = false + this.isRenderDetail = !this.isRenderDetail + }, + queryDetail() { + this.getDetailLists() + }, + /* --------详情页面-e */ + clearMap() { + this.detail.map?.destroy() + }, + clearChart() { + this.chart.chartInstanceLine?.clear() + this.chart.chartInstanceDrop1?.clear() + this.chart.chartInstanceDrop2?.clear() + }, + } +} +</script> + +<style lang="scss" scoped> +@import '@/style/layout-main.scss'; + +::v-deep .el-radio-button__inner { + border: 1px solid #39B5FE; + color: #fff; + background: transparent; +} + +.hide { + opacity: 0; +} + +.show { + opacity: 1; +} + +#container { + width: 100%; + height: 100%; +} + +.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 { + display: flex; + flex-direction: column; + + .el-table__body-wrapper { + overflow-y: auto; + flex: 1; + } + } + } + } + + .main_right { + 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 { + margin-bottom: 10px; + + + } + + .echart_bar { + flex: 1; + } + + .echart_drop { + flex: 1.1; + + > div { + float: left; + width: 50%; + height: 100%; + } + } + } + + .detail { + display: flex; + flex-direction: column; + position: absolute; + width: 100%; + height: 100%; + padding: 20px; + + .detail_option { + .search_item { + float: left; + + span { + display: inline-block; + margin-right: 6px; + } + + .el-input { + width: 220px; + margin: 0 28px 16px 0; + } + + .elDatePicker { + margin: 0 10px 14px 0; + } + } + } + + .detail_botbox { + overflow: hidden; + flex: 1; + } + + ::v-deep.detail_table { + float: left; + width: 400px; + height: 100%; + margin-right: 20px; + + .el-table { + display: flex; + flex-direction: column; + + .el-table__body-wrapper { + overflow-y: auto; + flex: 1; + } + } + } + + .detail_map { + position: relative; + overflow: hidden; + min-height: 200px; + height: 100%; + + p { + display: flex; + width: 300px; + padding-left: 10px; + line-height: 32px; + word-break: break-all; + border-radius: 3px; + background: #143164; + + span { + padding: 0 6px; + color: #39B5FE; + flex: 1; + } + + } + + .detail_map_info_l { + position: absolute; + left: 44px; + top: 160px; + padding: 0 20px 10px 20px; + border-radius: 5px; + background: #0D1846; + + h2 { + margin-bottom: 30px; + color: #F94550; + } + } + + .detail_map_info_r { + position: absolute; + right: 36px; + top: 260px; + padding: 0 20px; + border-radius: 5px; + background: #0D1846; + + h2 { + margin-bottom: 30px; + color: #39B5FE; + } + } + } + + } +} +</style> \ No newline at end of file -- Gitblit v1.9.3