unknown
2023-10-23 5672f352d0ba114e2ae96c8cefad6c74ae6d2934
web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue
@@ -1,3 +1,725 @@
<template>
    <div>报警记录</div>
</template>
<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>