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