he wei
2025-05-08 84ff051d5c6bbf10e71f6f8d1d57740c26619d9e
U 苏州地铁首页
6个文件已修改
12个文件已添加
8808 ■■■■■ 已修改文件
public/mapJson/subway/suzhou1.json 6793 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/js/config.module.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/myCharts/MapChart.vue 185 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/permission.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/SubwayView.vue 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/home_card.vue 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/marqueeTop.vue 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/pie-chart1.vue 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/svgLine.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/svgStation.vue 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/test.vue 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/home-szdt.vue 610 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/images/bg-card.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/images/bg-info.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/images/bg-map-sz.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/images/bg-side.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/test.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/mapJson/subway/suzhou1.json
New file
Diff too large
src/assets/js/config.module.js
@@ -96,9 +96,9 @@
     */
    // name: "",
    // name: "cqdz",
    name: "sxty", // 请查看alarmPopup是否开启,logo是否开启且为gjdw
    // name: "sxty", // 请查看alarmPopup是否开启,logo是否开启且为gjdw
    // name: "tydc",
    // name: 'szdt',
    name: 'szdt',
    // name: 'njck',
    // name: "ynkm",    // 云南昆明定制了设备实时告警和设备历史告警名称 打包时注意设置为isChangeAlarm=true,其他设置为false
    //value: false,
src/components/myCharts/MapChart.vue
@@ -289,101 +289,102 @@
        },
        series: [
          // 图片底色
          {
            name: "",
            type: "scatter",
            coordinateSystem: "geo",
            symbol: "circle",
            symbolSize: [24, 24],
            label: {
              formatter: "{b}",
              position: "inside",
              show: true,
              color: "#FFFFFF",
              fontWeight: "bold",
            },
            itemStyle: {
              color(params) {
                return params.data.color;
              },
            },
            data: convertData(data),
            z: 3,
          },
          {
            //图片
            name: "图片",
            type: "scatter",
            coordinateSystem: "geo",
            symbol: function (value, params) {
              let img;
              let color = params.data.color;
              // let color = "#ff6b6c";
              switch (color) {
                case "#0081ff": // 浮充
                  img = HomeChargeImage;
                  break;
                case "#ff6b6c": // 放电
                  img = HomeDischargeImage;
                  break;
                case "#66f842": // 充电
                  img = HomeNormalImage;
                  break;
                case "#7668f9": // 停电
                  img = HomeTingDianImage;
                  break;
                default:
                  // img = HomeChargeImage;
                  img = HomeTransImage;
                  break;
              }
              return params.data.name ? "circle" : "image://" + img;
            },
            symbolSize: [24, 24],
            label: {
              formatter: "{b}",
              position: "inside",
              show: true,
              color: "#FFFFFF",
              fontWeight: "bold",
            },
            itemStyle: {
              color(params) {
                return params.data.color;
              },
            },
            data: convertData(data),
            showEffectOn: "render",
            rippleEffect: {
              brushType: "stroke",
            },
            hoverAnimation: true,
            // zlevel: 3
            z: 4,
          },
          {
            name: "波纹",
            type: "effectScatter",
            coordinateSystem: "geo",
            rippleEffect: {
              scale: 3,
            },
            symbolSize: [18, 18],
            data: convertData(data),
            itemStyle: {
              color(params) {
                return params.data.color;
              },
            },
            tooltip: {
              show: true,
            },
            // zlevel: 2
            z: 2,
          },
          // {
          //   name: "",
          //   type: "scatter",
          //   coordinateSystem: "geo",
          //   symbol: "circle",
          //   symbolSize: [24, 24],
          //   label: {
          //     formatter: "{b}",
          //     position: "inside",
          //     show: true,
          //     color: "#FFFFFF",
          //     fontWeight: "bold",
          //   },
          //   itemStyle: {
          //     color(params) {
          //       return params.data.color;
          //     },
          //   },
          //   data: convertData(data),
          //   z: 3,
          // },
          // {
          //   //图片
          //   name: "图片",
          //   type: "scatter",
          //   coordinateSystem: "geo",
          //   symbol: function (value, params) {
          //     let img;
          //     let color = params.data.color;
          //     // let color = "#ff6b6c";
          //     switch (color) {
          //       case "#0081ff": // 浮充
          //         img = HomeChargeImage;
          //         break;
          //       case "#ff6b6c": // 放电
          //         img = HomeDischargeImage;
          //         break;
          //       case "#66f842": // 充电
          //         img = HomeNormalImage;
          //         break;
          //       case "#7668f9": // 停电
          //         img = HomeTingDianImage;
          //         break;
          //       default:
          //         // img = HomeChargeImage;
          //         img = HomeTransImage;
          //         break;
          //     }
          //     return params.data.name ? "circle" : "image://" + img;
          //   },
          //   symbolSize: [24, 24],
          //   label: {
          //     formatter: "{b}",
          //     position: "inside",
          //     show: true,
          //     color: "#FFFFFF",
          //     fontWeight: "bold",
          //   },
          //   itemStyle: {
          //     color(params) {
          //       return params.data.color;
          //     },
          //   },
          //   data: convertData(data),
          //   showEffectOn: "render",
          //   rippleEffect: {
          //     brushType: "stroke",
          //   },
          //   hoverAnimation: true,
          //   // zlevel: 3
          //   z: 4,
          // },
          // {
          //   name: "波纹",
          //   type: "effectScatter",
          //   coordinateSystem: "geo",
          //   rippleEffect: {
          //     scale: 3,
          //   },
          //   symbolSize: [18, 18],
          //   data: convertData(data),
          //   itemStyle: {
          //     color(params) {
          //       return params.data.color;
          //     },
          //   },
          //   tooltip: {
          //     show: true,
          //   },
          //   // zlevel: 2
          //   z: 2,
          // },
          {
            type: "map",
            map: mapName,
            show: false,
            geoIndex: 0,
            aspectScale: 0.75, //长宽比
            showLegendSymbol: false, // 存在legend时显示
src/permission.js
@@ -6,7 +6,7 @@
NProgress.configure({ showSpinner: false }) // NProgress Configuration
// /frame-jy 为山西晋源的iframe 统计页面
const whiteList = ['/login', '/frame-jy'] // no redirect whitelist
const whiteList = ['/login', '/frame-jy', '/test'] // no redirect whitelist
router.beforeEach(async (to, from, next) => {
  // determine whether the user has logged in
  let username = sessionStorage.getItem('username');
src/router/index.js
@@ -1278,6 +1278,10 @@
    path: "/redirect/:path*",
    component: () => import("@/views/404/redirect"),
  },
  {
    path: '/test',
    component: () => import('@/views/test.vue'),
  },
  // 404 页面
  {
    path: "*",
src/views/home/components/SubwayView.vue
New file
@@ -0,0 +1,232 @@
<template>
  <div id="metroChart" ref="chart" style="height: 100%"></div>
</template>
<script>
import echarts from "echarts";
let myChart;
export default {
  props: {
    lines: {
      type: Array,
      default() {
        return []
      }
    },
    status: {
      type: Object,
      default() {
        return {}
      }
    }
  },
  data() {
    return {
      jsonData: {},
    }
  },
  computed: {
    lineList() {
      // console.log('line', this.lines)
      return this.lines.map(v => v + '号线');
    }
  },
  watch: {
    lines() {
      // console.log('line', this.lines, '=============');
      this.updateMap();
    },
    status() {
      this.updateMap();
    }
  },
  methods: {
    async getJson() {
      const dynamicPath = `mapJson/subway/suzhou1.json`;
      try {
        const response = await fetch(dynamicPath);
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        // console.log("data", data, "=============");
        // this.defaultData = data.l;
        return data;
      } catch (error) {
        console.error("加载 JSON 出错:", error);
      }
    },
    async getSubwayJson() {
      const { l } = this.jsonData;
      let stationList = this.lineList;
      const subwayList = l.filter((item) => {
        return stationList.includes(item.ln);
      });
      // 站点状态
      let statusList = this.status;
      const position = {
        0: "top",
        1: "topRight",
        2: "right",
        3: "bottomRight",
        4: "bottom",
        5: "bottomLeft",
        6: "left",
        7: "topLeft",
      };
      const list = {
        xPeak: {
          min: 0,
          max: 0,
        },
        yPeak: {
          min: 0,
          max: 0,
        },
        names: [],
        x: [],
        y: [],
        nodes: [],
        lines: [],
      };
      for (let i = 0; i < subwayList.length; i++) {
        const { cl, st, ln } = subwayList[i];
        let lineNum = ln.replace('号线', '');
        for (let k = 0; k < st.length; k++) {
          const { n, p, lg } = st[k];
          const point = p.split(" ");
          const x = Number(point[0]);
          const y = Number(point[1]);
          list.x.push(x);
          list.y.push(y);
          if (!list.names.includes(n)) {
            list.names.push(n);
            const names = n.split("");
            let p = "";
            let offset = [0, 0];
            if (position[lg] === "topLeft") {
              p = "left";
              offset = [0, -8];
            } else if (position[lg] === "topRight") {
              p = "right";
              offset = [0, -8];
            } else if (position[lg] === "bottomLeft") {
              p = "left";
              offset = [0, 12];
            } else if (position[lg] === "bottomRight") {
              p = "right";
              offset = [0, 12];
            } else {
              p = position[lg];
            }
            let stations = statusList[lineNum].filter(v => v.stationName8 == k + 1);
            let status = stations.length ? stations[0].note == 1 : false;
            list.nodes.push({
              category: i,
              name: n,
              x,
              status,
              y,
              color: "#" + cl,
              p,
              offset,
            });
          }
          if (k !== st.length - 1) {
            list.lines.push({
              source: n,
              target: st[k + 1].n,
              lineStyle: {
                normal: {
                  color: "#" + cl,
                },
              },
            });
          }
        }
      }
      list.xPeak = {
        min: Math.min(...list.x),
        max: Math.max(...list.x),
      };
      list.yPeak = {
        min: Math.min(...list.y),
        max: Math.max(...list.y),
      };
      return list;
    },
    async updateMap() {
      let metroData = await this.getSubwayJson();
      const option = {
        xAxis: {
          show: false,
          type: "value",
          min: metroData.xPeak.min,
          max: metroData.xPeak.max,
          position: "top",
        },
        yAxis: {
          show: false,
          type: "value",
          min: metroData.yPeak.min,
          max: metroData.yPeak.max,
        },
        legend: [
          {
            data: this.lineList,
            textStyle: {
              color: '#fff'
            }
          }
        ],
        series: [
          {
            type: "graph",
            layout: "none",
            roam: true,
            labelLayout: {
              hideOverlap: true,
            },
            categories: this.lineList.map(v=>({name: v})),
            data: metroData.nodes.map((node) => ({
              name: node.name,
              symbol: "circle",
              symbolSize: 6,
              category: node.category,
              x: node.x,
              y: node.y,
              itemStyle: {
                color: node.status ? '#0f0' : "#f00",
                // borderColor: node.color,
              },
              label: {
                show: true,
                position: node.p,
                color: '#fff',
                offset: node.offset,
              },
            })),
            links: metroData.lines,
          },
        ],
      };
      myChart.setOption(option);
    },
  },
  async mounted() {
    this.jsonData = await this.getJson();
    myChart = echarts.init(this.$refs.chart);
    this.updateMap();
    myChart.on('legendselectchanged', (param) => {
      this.$emit('legend-change', param.selected);
    });
  },
};
</script>
src/views/home/components/home_card.vue
New file
@@ -0,0 +1,85 @@
<template>
  <div class="home-card">
    <div class="home-card-container">
      <div class="home-card-header">
        <span class="home-card-title" :class="{'red': isRedTitle}">{{ title }}</span>
        <div class="tools">
          <slot name="tools"></slot>
        </div>
      </div>
      <div :class="['home-card-body', contentClass]">
        <slot></slot>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "home_card",
  props: {
    title: {
      type: String,
      default: ""
    },
    contentClass: {
      type: [String, Array],
      default() {
        return []
      }
    },
    isRedTitle: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {};
  },
  methods: {}
}
</script>
<style scoped>
.home-card {
  height: 100%;
  box-sizing: border-box;
  padding-bottom: 8px;
}
.home-card-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  /* background-color: #023752; */
}
.home-card-header {
  padding: 8px;
  position: relative;
}
.tools {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
}
.home-card-header img {
  height: 16px;
  vertical-align: middle;
}
.home-card-title {
  margin-left: 8px;
  color: #00feff;
  font-size: 20px;
  font-weight: bold;
}
.home-card-title.red {
  color: #ff0000;
}
.home-card-body {
  flex: 1;
  position: relative;
}
.home-card-title-sub {
  font-size: 16px;
}
</style>
src/views/home/components/marqueeTop.vue
New file
@@ -0,0 +1,187 @@
<template>
    <vue-seamless-scroll :data="listData" :class-option="classOption" ref="seamlessScroll" class="my-outbox">
        <div class="my-inbox">
            <div v-for="(item,index) in listData" :key='index' ref='list' @click="clickItem(item)" class="my-list">
                <div v-if="!item.isEmpty" class="item">
                    <div class="station-name" :title="item.stationName3">{{item.stationName3}}</div>  <div class="alarm-time">{{item.almStartTime}}</div>  <div class="alarm-name">{{item.almSignalIdName ||'未知'}}</div>
                </div>
                <div class="item empty" v-else :style="{'width':item.width+'px'}"></div>
            </div>
        </div>
        <!-- <div class="my-inbox" slot="copy">
            <div v-for="(item,index) in listData" :key='index' ref='list' @click="clickItem(item)" class="my-list">
                <div v-if="!item.isEmpty">
                    {{item.stationName}}在{{item.almStartTime}}出现了{{item.alarmName||'未知'}}
                    <img src="../../assets/images/new.png" class="img" v-if="item.isNew">
                </div>
                <div v-else :style="{'width':item.width+'px'}"></div>
            </div>
        </div> -->
    </vue-seamless-scroll>
</template>
<script>
import vueSeamlessScroll from '@/layout/components/FooterScroll.vue';
export default {
    name: 'marquee-left',
    components: {
        vueSeamlessScroll
    },
    props: {
        sendVal: {
            type: Array,
            default() {
                return [];
            }
        },
    },
    data() {
        return {
            listData: [],
            classOption: {
                limitMoveNum: 0,
                direction: 1,
                step: 1,
            },
            startDis: window.screen.width,
            sstationObj: {},
            alarmId: "",
            emptyIndex: null,
            distanceWid: null,
            // 每一个内容的宽度
            disArr: [],
        }
    },
    watch: {
        'sendVal': {
            handler(val) {
                this.$nextTick(() => {
                    this.listData = JSON.parse(JSON.stringify(val))
                    var item = this.$refs.list
                    var arr = []
                    // 因为设置的margin值一样,所以取第一个就行。
                    var margin = 0
                    if (item && item.length > 0) {
                        margin = this.getMargin(item[0])
                    }
                    if (this.listData.length > 0) {
                        this.listData.map((jtem, i) => {
                            if (item && item.length > 0) {
                                arr.push(item[i]?.clientWidth + margin) // 把宽度和 margin 加起来就是每一个元素需要移动的距离
                            }
                        })
                    }
                    this.disArr = arr
                    // listData length无变化,仅仅是属性变更,手动调用下组件内部的reset方法
                    this.$refs.seamlessScroll.reset()
                })
            },
            immediate: true,
        },
    },
    mounted() {
    },
    beforeDestroy() {
    },
    computed: {
        search() {
            let list = this.stationObj;
      let id = this.alarmId;
      return '?province=' + list.stationName1 + '&city=' + list.stationName2 + '&county=' + list.stationName5 +
        '&home=' + list.stationName3 + '&alamId=' + id + "&stationName=" + list.stationName
    },
    },
    methods: {
        clickItem(val) {
            this.stationObj = val.stationObj;
            this.alarmId = val.almId;
      let pageFlag = Math.random();
            if (val.alarmType == '1') {
                //设备实时告警
                this.$router.push('/alarmMager/deviceTimequery' + this.search + '&fromType=fromIndex&'+"pageFlag="+pageFlag)
            }else if(val.alarmType=='2') {
        let strNum = RegExp('^1150');
        // 通信电源设备告警
        if(strNum.test(val.battGroupId)) {
          this.$router.push('/alarmMager/powerBoxAlarm' + this.search + '&fromType=fromIndex&'+"pageFlag="+pageFlag)
        }else {
          //电源实时告警-国网页面
          this.$router.push('/alarmMager/powerRealtimeInfo' + this.search + '&fromType=fromIndex&'+"pageFlag="+pageFlag)
        }
      } else {
                //电池实时告警
                this.$router.push('/alarmMager/batteryrTimequery' + this.search + '&fromType=fromIndex&'+"pageFlag="+pageFlag)
            }
        },
        // 获取margin属性
        getMargin(obj) {
            var marg = window.getComputedStyle(obj, null)['margin-right']
            marg = marg.replace('px', '')
            return Number(marg) // 强制转化成数字
        },
    }
}
</script>
<style scoped lang="less">
.my-outbox {
    /*color: #D7BC8D;*/
    overflow: hidden;
    color: #ffffff;
    /* height: 35px; */
    /*background: #422b02;*/
}
.my-inbox {
    white-space: nowrap;
    display: flex;
  flex-direction: column;
    align-items: center;
}
.my-list {
    margin-right: 25px;
    display: inline-block;
    font-size: 0.6rem;
    height: 40px;
    line-height: 40px;
    cursor: pointer;
    position: relative;
}
.my-list .img {
    width: 32px;
    height: 14px;
}
.item {
  display: flex;
  .station-name {
    display: inline-block;
    color: #fff;
    max-width: 14em;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    margin-right: 0.4em;
  }
  .alarm-time {
    display: inline-block;
    color: #ebf006;
    margin-left: 0.4em;
    margin-right: 0.4em;
  }
  .alarm-name {
    display: inline-block;
    color: #fc0303;
    margin-left: 0.4em;
  }
}
.my-uname {
    /*color: #FF8900;*/
    color: #ffffff;
}
</style>
src/views/home/components/pie-chart1.vue
New file
@@ -0,0 +1,170 @@
<script>
import ECharts from 'echarts';
import BaseChart from '@/components/myCharts/BaseChart.vue';
import imgSrc from '../images/batt.png';
const colorList = ['#afa3f5', '#00d488', '#3feed4', '#3bafff', '#f1bb4c', '#1fed08', '#f04d0d'];
export default {
  extends: BaseChart,
  props: {
    name: {
      type: String,
      default: "",
    },
    unit: {
      type: String,
      default: ""
    },
  },
  data() {
    return {}
  },
  methods: {
    fullScreen() {
      return false;
    },
    setData(data) {
      let option = this.getOption(data);
      this.setOption(option);
    },
    getOption(data) {
      let sData = data;
      let width = this.$options.chart.getWidth(),
        height = this.$options.chart.getHeight();
      let imgW = width > height ? height * .25 : width * .25;
      // console.log(this.$options.chart.getWidth(), this.$options.chart.getHeight(), '=====get', imgW);
      return {
        grid: {
          bottom: 150,
          left: 0,
          // right: '10%'
          right: 0
        },
        graphic: {
          elements: [{
            type: "image",
            z: 6,
            // x: 0,
            y: -100,
            style: {
              image: imgSrc,
              width: imgW / 2,
              // shadowBlur: 0,
              // shadowColor: '#000',
              // shadowOffsetX: '1',
              // shadowOffsetY: '1',
            },
            left: 'center',
            // left: '48%',
            // right: '52%',
            top: "44%",
            // top: 'middle'
          }]
        },
        legend: {
          show: false,
          orient: 'vertical',
          top: "middle",
          right: "5%",
          textStyle: {
            color: '#f2f2f2',
            fontSize: 25,
          },
          icon: 'roundRect'
        },
        series: [
          // 主要展示层的
          {
            radius: ['25%', '45%'],
            center: ['50%', '50%'],
            type: 'pie',
            itemStyle: {
              normal: {
                color: function (params) {
                  return colorList[params.dataIndex % colorList.length]
                },
                borderWidth: 0,
              },
            },
            // data: [{
            //         value:17,
            //         name:'体育技能',
            //     },]
            data: sData.map((v, i) => {
              v.label = {
                color: colorList[i % colorList.length],
                normal: {
                  formatter: '{nameStyle|{b}} \n {rate|{d}%  ({c})}',
                  // padding: [0, -110],
                  // height: 165,
                  rich: {
                    nameStyle: {
                      fontSize: 12,
                      color: colorList[i % colorList.length],
                      align: 'left'
                    },
                    rate: {
                      fontSize: 14,
                      color: colorList[i % colorList.length],
                      align: 'left'
                    }
                  }
                }
              };
              return v;
            })
          },
          // 边框的设置
          {
            radius: ['43%', '45%'],
            center: ['50%', '50%'],
            type: 'pie',
            label: {
              normal: {
                show: false
              },
              emphasis: {
                show: false
              }
            },
            labelLine: {
              normal: {
                show: false
              },
              emphasis: {
                show: false
              }
            },
            animation: false,
            tooltip: {
              show: false
            },
            itemStyle: {
              normal: {
                color: 'rgba(250,250,250,0.5)'
              }
            },
            data: [{
              value: 1,
            }],
          }
        ],
      }
    }
  },
  computed: {
    yName() {
      let unit = this.unit;
      return unit ? "Y(" + unit + ")" : "Y";
    },
  },
  mounted() {
    this.setData([]);
  }
}
</script>
src/views/home/components/svgLine.vue
New file
@@ -0,0 +1,61 @@
<script >
export default {
  props: {
    offset: {
      type: Array,
      default: () => [0, 0],
    },
    points: {
      type: Array,
      default: () => [
        [0, 0],
        [10, 10],
      ],
    },
    name: {
      type: String,
      default: '',
    },
    color: {
      type: String,
      default: "#804000",
    },
    lineWidth: {
      type: Number,
      default: 4,
    },
    currColor: {
      type: String,
      default: "#0f0",
    },
  },
  computed: {
    path() {
      let points = this.points;
      let path = `M ${points[0].join(",")} `;
      for (let i = 1, len = points.length; i < len; i++) {
        path += ` L ${points[i].join(",")} `;
      }
      return path;
    }
  },
}
</script>
<template>
  <g ref="g" :transform="'translate(' + offset.join(',') + ')'">
    <path :d="path" ref="path" fill="none" :stroke-width="lineWidth" :stroke="color"></path>
    <text :x="points[1][0] + 10" :y="points[1][1]+10"
        font-family="Verdana"
        fill="#fff"
        font-size="8">
      {{ name }}
    </text>
  </g>
</template>
<style scoped lang="less"></style>
src/views/home/components/svgStation.vue
New file
@@ -0,0 +1,134 @@
<script >
export default {
  props: {
    offset: {
      type: Array,
      default: () => [0, 0],
    },
    points: {
      type: Array,
      default: () => [0, 0],
    },
    r: {
      type: [Number, String],
      default: 12,
    },
    name: {
      type: String,
      default: '',
    },
    color: {
      type: String,
      default: "#804000",
    },
    lineWidth: {
      type: Number,
      default: 2,
    },
    currColor: {
      type: String,
      default: "#0f0",
    },
    pos: {
      type: [String, Number],
      default: 0
    },
  },
  computed: {
    x() {
      return this.points[0] * 1;
    },
    y() {
      return this.points[1] * 1;
    },
    dx() {
      let res = 0;
      switch(this.pos * 1) {
        case 0:
        case 4:
          res = 0;
          break;
        case 1:
        case 2:
        case 3:
          res = 16;
          break;
        case 5:
        case 6:
        case 7:
          res = -16;
          break;
        default:
          res = 0;
          break;
      }
      return res;
    },
    dy() {
      let res = 0;
      switch(this.pos * 1) {
        case 2:
        case 6:
          res = 0;
          break;
        case 0:
        case 1:
        case 7:
          res = -16;
          break;
        case 3:
        case 4:
        case 5:
          res = 32;
          break;
        default:
          res = 0;
          break;
      }
      return res;
    },
    textAnchor() {
      let res = '';
      switch(this.pos * 1) {
        case 0:
        case 4:
          res = 'middle';
          break;
        case 1:
        case 2:
        case 3:
          res = 'start';
          break;
        case 5:
        case 6:
        case 7:
          res = 'end';
          break;
        default:
          res = 'start';
          break;
      }
      return res;
    },
  }
}
</script>
<template>
  <g ref="g" :transform="'translate(' + offset.join(',') + ')'">
    <circle :cx="x" :cy="y" :r="r" :stroke="color" fill="#aaa" />
    <text :x="x" :y="y"
        :dx="dx"
        :dy="dy"
        font-family="Verdana"
        fill="#fff"
        :text-anchor="textAnchor"
        font-size="34">
      {{ name }}
    </text>
  </g>
</template>
<style scoped lang="less"></style>
src/views/home/components/test.vue
New file
@@ -0,0 +1,329 @@
<script >
import svgLine from './svgLine.vue';
import svgStation from './svgStation.vue';
const colors = [
  '#0f0',
  '#f00',
  '#00f',
  '#ff0',
  '#0ff',
  '#f0f',
  '#018AE8',
  '#FF713B',
  '#0AE88E',
];
export default {
  components: {
    svgLine,
    svgStation,
  },
  data() {
    return {
      colors,
      svgWidth: 1400,
      svgHeight: 712,
      padding: 100,
      minLat: Infinity,
      minLng: Infinity,
      maxLat: -Infinity,
      maxLng: -Infinity,
      lines: [],
      allLines: [3, 5, 6, 7, 8],
      disableList: [],
      viewBox: [0, 0, 100, 100],
      lineObjs: [],
      defaultData: [],
      infoVisible: false,
      info: {
        name: '',
        lineName: '',
        status: 0,
      },
      left: 0,
      top: 0,
      transLeft: false,
      transTop: false,
    }
  },
  computed: {
    filterLines() {
      return this.allLines.filter(v => this.disableList.every(vv => vv != v));
    },
    resData() {
      return this.defaultData.filter(v => this.filterLines.some(vv=>vv == v.ln.substr(0, 1))).map(v => {
        return {
          name: v.ln,
          list: v.st.map(vv => ({
            name: vv.n,
            // points: vv.sl.split(',')
            points: vv.p.split(' '),
            p: vv.lg
          }))
        }
      })
    },
    subWays() {
      return this.defaultData.filter(v => this.allLines.some(vv=>vv == v.ln.substr(0, 1))).map(v => v.ln)
    },
    viewBoxArr() {
      if (
        this.minLat == Infinity
        || this.maxLat == -Infinity
        || this.minLng == Infinity
        || this.maxLng == -Infinity
      ) {
        return [0, 0, this.svgWidth, this.svgHeight];
      } else {
        return [this.minLng - this.padding, this.minLat - this.padding, this.maxLng - this.minLng + this.padding * 2, this.maxLat - this.minLat + this.padding * 2];
      }
    },
  },
  methods: {
     async getJson () {
      const dynamicPath = `mapJson/subway/suzhou1.json`;
      try {
        const response = await fetch(dynamicPath);
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        console.log('data', data, '=============');
        this.defaultData = data.l;
      } catch (error) {
        console.error('加载 JSON 出错:', error);
      }
    },
    getSize(points) {
      points.forEach(point => {
        let [lng, lat] = point;
        lng *= 1;
        lat *= 1;
        if (lat < this.minLat) this.minLat = lat;
        if (lat > this.maxLat) this.maxLat = lat;
        if (lng < this.minLng) this.minLng = lng;
        if (lng > this.maxLng) this.maxLng = lng;
      });
    },
    async getPoints() {
      let gpsPoints = this.resData;
      let arr = [];
      for (let i = 0, len = gpsPoints.length; i < len; i++) {
        let item = gpsPoints[i];
        item.list.forEach(v => {
          arr.push(v.points);
        })
      }
      console.log('arr', arr, '=============');
      this.getSize(arr);
      this.lines = gpsPoints.map((v, i) => ({ name: v.name, points: v.list.map(vv => vv.points) }));
      this.lineObjs = gpsPoints.map((v, i) => ({ name: v.name, list: v.list.map(vv => ({ name: vv.name, points: vv.points, p: vv.p })) }));
    },
    toggleLine(idx) {
      let _idx = this.subWays[idx].substr(0,1);
      let arr = this.disableList;
      // 判断是否是不显示的
      let _i = arr.indexOf(_idx);
      if(_i > -1) {
        arr.splice(_i, 1);
      } else {
        arr.push(_idx);
      }
      this.disableList = arr;
      // nextTick()
      this.getPoints();
    },
    showInfo(e, data, lineName) {
      this.infoVisible = true;
      console.log('e', e, data, '=============');
      this.left = e.clientX + 'px';
      this.top = e.clientY + 'px';
      let w = window.innerWidth;
      let h = window.innerHeight;
      if (e.clientX > w / 2) {
        this.transLeft = true;
      } else {
        this.transLeft = false;
      }
      if (e.clientY > h / 2) {
        this.transTop  = true;
      } else {
        this.transTop = false;
      }
      this.info.name = data.name;
      this.info.lineName = lineName;
    },
    hideInfo () {
      this.infoVisible = false;
    },
  },
  async mounted() {
    await this.getJson();
    this.getPoints();
  },
}
</script>
<template>
  <div class="main">
    <div class="lines">
      <div :class="['item', `level${item.substr(0, 1)}`, {disabled: disableList.indexOf(item.substr(0, 1)) > -1}]" v-for="(item, idx) in subWays" :key="idx" @click="toggleLine(idx)">
        {{ item }}
      </div>
    </div>
    <svg
      class="map"
      width="100%"
      height="100%"
      :viewBox="viewBoxArr.join(' ')"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      style="position: relative;"
      :transform="'scale(' + [1, 1].join(',') + ')'"
      ref="map"
    >
      <svg-line
        v-for="(item, idx) in lines"
        :key="idx"
        :points="item.points"
        :color="colors[item.name.substr(0,1)]"
        :name="subWays[idx]"
      ></svg-line>
      <!-- 站点 -->
      <template v-for="(item, i) in lineObjs">
        <svg-station
          v-for="(point, idx) in item.list"
          :key="'line_' + i + '_point' + idx"
          :points="point.points"
          :name="point.name"
          :color="colors[item.name.substr(0,1)]"
          :pos="point.p"
          @mouseenter.capture="(e) => showInfo(e, point, item.name)"
        ></svg-station>
      </template>
    </svg>
    <teleport to='body' v-if="infoVisible">
      <div :class="['info', {'trans-left': transLeft, 'trans-top': transTop}]" v-if="infoVisible" :style="{ left, top }">
        <div class="info-item station-name">
          <div class="label">站名:</div>
          <div class="value">{{ info.name }}</div>
        </div>
        <div class="info-item line-name">
          <div class="label">线路:</div>
          <div class="value">{{ info.lineName }}</div>
        </div>
        <div class="info-item status">
          <div class="label">状态:</div>
          <div class="value">{{ info.status ? '在线' : '离线' }}</div>
        </div>
      </div>
      <div class="mask" @mouseenter="hideInfo"></div>
    </teleport>
  </div>
</template>
<style scoped lang="less">
.item {
  background: #0ff;
  border-radius: 4px;
  color: #000;
  padding: 8px;
  &.level3 {
    background: #ff0;
  }
  &.level5 {
    background: #f0f;
  }
  &.level6 {
    background: #018AE8;
  }
  &.level7 {
    background: #FF713B;
  }
  &.level8 {
    background: #0AE88E;
  }
  &.disabled {
    background: #ccc;
  }
}
.main {
  background: #333;
  height: 100%;
  display: flex;
  flex-direction: column;
  .lines {
    display: flex;
    flex-direction: row;
    .item {
      margin-right: 20px;
      cursor: pointer;
    }
  }
  .map {
    flex: 1;
  }
}
.info {
  position: absolute;
  background: #0ff;
  z-index: 99;
  color: #000;
  padding: 12px;
  border-radius: 8px;
  white-space: nowrap;
  &.trans-left {
    transform: translateX(-100%);
  }
  &.trans-top {
    transform: translateY(-100%);
  }
  &.trans-left.trans-top {
    transform: translate(-100%, -100%);
  }
  &::after {
    content: '';
    position: absolute;
    left: -6px;
    top: -6px;
    right: -6px;
    bottom: -6px;
    z-index: -1;
  }
}
.info-item {
  display: flex;
  .label {
    margin-right: 0.4em;
  }
}
.mask {
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background: transparent;
  z-index: 98;
}
</style>
src/views/home/home-szdt.vue
@@ -1,162 +1,94 @@
<template>
  <div class="p-main">
    <div class="row">
      <card>
        <big-screen-card title="告警统计">
          <template #tools>
            <el-radio-group size="mini" v-model="alarmType" @input="alarmTypeChangede">
              <el-radio-button :label="0">告警类型</el-radio-button>
              <el-radio-button :label="1">告警等级</el-radio-button>
            </el-radio-group>
          </template>
          <div class="chart-wrap">
            <div class="inner">
              <bar-chart-alarm v-if="alarmType == 1" ref="chart_alarm"></bar-chart-alarm>
              <pie-chart ref="alarmPieChart" :values="alarms" v-else></pie-chart>
            </div>
          </div>
        </big-screen-card>
      </card>
    </div>
    <div class="row">
      <card>
        <big-screen-card title="设备工作状态">
          <div class="dev-wrap">
            <circle-box class="item" :type="0" :value="workState[0]" @click.native="goDevWorkState(4)"></circle-box>
            <circle-box ref="devWorkState1" class="item" :type="1" :value="workState[1]"
              @click.native="goDevWorkState(2)"></circle-box>
            <circle-box ref="devWorkState2" class="item" :type="2" :value="workState[2]"
              @click.native="goDevWorkState(0)"></circle-box>
            <circle-box ref="devWorkState3" class="item" :type="3" :value="workState[3]"
              @click.native="goDevAlarm"></circle-box>
          </div>
        </big-screen-card>
      </card>
    </div>
    <div class="row">
      <card>
        <big-screen-card title="整组容量">
          <div class="mon-box-container">
            <div class="mon-info-box">
              <div class="mon-info-title">大于95%比例:</div>
              <div class="mon-info-value">{{ cap.level0 }}</div>
            </div>
            <div class="mon-info-box box2">
              <div class="mon-info-title">95%~85%比例:</div>
              <div class="mon-info-value">{{ cap.level1 }}</div>
            </div>
            <div class="mon-info-box box3">
              <div class="mon-info-title">85%~60%比例:</div>
              <div class="mon-info-value">{{ cap.level2 }}</div>
            </div>
          </div>
          <div class="mon-box-container mgt16">
            <div class="mon-info-box box2">
              <div class="mon-info-title">60%~50%比例:</div>
              <div class="mon-info-value">{{ cap.level3 }}</div>
            </div>
            <div class="mon-info-box box3">
              <div class="mon-info-title">小于50%:</div>
              <div class="mon-info-value">{{ cap.level4 }}</div>
            </div>
          </div>
        </big-screen-card>
      </card>
    <div class="p-side p-left">
      <div class="row">
          <home-card title="统计电池告警">
            <template #tools>
              <el-select v-model="lineSelect0" size="mini" @change="testLevelChange(0)">
                <el-option v-for="(item, idx) in lineList" :key="'select_' + idx" :label="item == 0 ? '全部' : item + '号线'" :value="item"></el-option>
              </el-select>
            </template>
            <!-- <div class="chart-wrap">
              <div class="inner">
                <bar-chart-alarm v-if="alarmType == 1" ref="chart_alarm"></bar-chart-alarm>
                <pie-chart ref="alarmPieChart" :values="alarms" v-else></pie-chart>
              </div>
            </div> -->
            <bar3d ref="bar3d0"></bar3d>
          </home-card>
      </div>
      <div class="row">
          <home-card title="故障类型统计">
            <template #tools>
              <el-select v-model="lineSelect1" size="mini" @change="testLevelChange(1)">
                <el-option v-for="(item, idx) in lineList" :key="'select_' + idx" :label="item == 0 ? '全部' : item + '号线'" :value="item"></el-option>
              </el-select>
            </template>
            <pie-chart ref="pie"></pie-chart>
          </home-card>
      </div>
    </div>
    <div class="wraper">
      <div class="row row1">
        <card :miniCor="true">
          <div class="info">
            <div class="item" @click="goStations">
              <div class="name">站点</div>
              <div class="name">机房个数</div>
              <div class="num">
                <led-num color="#f00" :num="stationNum"></led-num>
                {{ stationNum }}
              </div>
            </div>
            <div class="item">
              <div class="name">设备</div>
              <div class="name">设备个数</div>
              <div class="num">
                <led-num color="#f00" :num="devCount"></led-num>
                {{ devCount }}
              </div>
            </div>
            <div class="item" @click="goBatt">
              <div class="name">电池节数</div>
              <div class="num bits5">
                <led-num color="#f00" :bits='5' :num="battGroupMonCount"></led-num>
              </div>
            </div>
            <div class="item" @click="goBatt">
              <div class="name">电池组</div>
              <div class="name">电池组个数</div>
              <div class="num">
                <led-num color="#f00" :num="battGroupCount"></led-num>
                {{ battGroupCount }}
              </div>
            </div>
          </div>
        </card>
      </div>
      <div class="row row2">
        <card :inside="false">
          <div class="flex-map-contain">
            <div class="inner">
              <map-chart ref="map" subway="suzhou">
                <map-mark-list slot="tools"></map-mark-list>
              </map-chart>
              <svg-subway :lines="lineList.filter(v=> v != 0)" :status="statusList" @legend-change="legendChange"></svg-subway>
            </div>
          </div>
        </card>
      </div>
    </div>
    <div class="row alarm">
      <card>
        <big-screen-card title="实时告警信息">
          <div class="scroller-wrap">
            <div class="inner">
              <el-table ref="elTbl" stripe size="small" :data="tbl.data" height="100%" :row-class-name="
                    (row) =>'cursor-pointer'
                  " @row-dblclick="goRealTime" class="tableCent">
                <el-table-column v-for="header in tbl.headers" :key="header.prop" :prop="header.prop"
                  :label="header.label" :width="header.width" :min-width="header.minWidth" :sortable="header.sortable"
                  align="center"></el-table-column>
                <el-table-column label="操作" width="110" align="center">
                  <template slot-scope="scope">
                    <el-button type="primary" size="mini" @click="goRealTime(scope.row)">实时</el-button>
                  </template>
                </el-table-column>
              </el-table>
              <!-- <div class="empty-msg" v-else>最近一周无记录</div> -->
    <div class="p-side p-right">
      <div class="row alarm">
          <home-card title="电池状态">
            <template #tools>
              <el-select v-model="lineSelect2" size="mini" @change="testLevelChange(2)">
                <el-option v-for="(item, idx) in lineList" :key="'select_' + idx" :label="item == 0 ? '全部' : item + '号线'" :value="item"></el-option>
              </el-select>
            </template>
            <bar3d ref="bar3d1"></bar3d>
          </home-card>
      </div>
      <div class="row test-info">
          <home-card title="实时电池告警滚动">
            <template #tools>
              <el-select v-model="lineSelect3" size="mini" @change="testLevelChange(3)">
                <el-option v-for="(item, idx) in lineList" :key="'select_' + idx" :label="item == 0 ? '全部' : item + '号线'" :value="item"></el-option>
              </el-select>
            </template>
            <div class="scroller-wraper">
              <marquee-top :sendVal="newItems" v-if="showMarquee"></marquee-top>
              <div class="no-warning-msg" v-else>最近24小时内无告警</div>
            </div>
          </div>
        </big-screen-card>
      </card>
    </div>
    <div class="row test-info">
      <card>
        <big-screen-card title="线路告警">
          <template #tools>
            <!-- :disabled="!chartData.test.length" -->
            <!-- <el-radio-group
                size="mini"
                @input="testLevelChange"
                v-model="testLevel"
              >
                <el-radio-button :label="0">总线路</el-radio-button>
                <el-radio-button :label="1">1号线</el-radio-button>
                <el-radio-button :label="2">2号线</el-radio-button>
              </el-radio-group> -->
            <el-select v-model="testLevel" size="small" @change="testLevelChange">
              <el-option v-for="(item, idx) in lineList" :key="'select_' + idx" :label="item" :value="item"></el-option>
            </el-select>
          </template>
          <bar3d ref="bar3d"></bar3d>
        </big-screen-card>
      </card>
          </home-card>
      </div>
    </div>
  </div>
</template>
<script>
    import card from "./components/card-corner";
    import BigScreenCard from "@/components/bigScreenPage/big_screen_card.vue";
    import homeCard from "./components/home_card.vue";
    import BatteryPieChart from "@/components/myCharts/BatteryPieChart.vue";
    import MapChart from "@/components/myCharts/MapChart.vue";
    import MapMarkList from "@/components/mapMarkList.vue";
@@ -165,28 +97,42 @@
    import circleBox from "./components/circle.vue";
    import BarChart from "@/components/myCharts/BarChart.vue";
    import LedNum from "@/components/ledNum";
    import pieChart from "./components/pieCharts";
    // import pieChart from "./components/pieCharts";
  import pieChart from "./components/pie-chart1";
    import BarChartAlarm from "./components/batt-chart-alarm";
    import PowerPieChart from "@/components/myCharts/PieRoseChart.vue";
    import { const_alarm_level } from "@/assets/js/const";
    import { getLabelByValue } from "@/assets/js/tools";
    import getNowStamp from "@/assets/js/tools/getNowStamp";
  import marqueeTop from './components/marqueeTop.vue';
  // import svgSubway from './components/test.vue';
  import svgSubway from './components/SubwayView.vue';
    import createWs from "@/assets/js/websocket/plus";
    import { sethoubeiTime, formatSeconds } from "@/assets/js/tools";
    const WSMixin = createWs("screen_sz", 'battAlarmFoot');
    const WSMixin = createWs("screen_sz2", 'battAlarmFoot');
    export default {
        name: "",
        mixins: [WSMixin],
        data() {
            return {
        nums: {},
        selectFlag: false,
        newItems: [],
        lineSelect0: '',
        lineSelect1: '',
        lineSelect2: '',
        lineSelect3: '',
                lineList: [],
                otherPwrProd: [],
                testData: {},
        faultData: {},
                workState: {},
        alarmRt: {},
        statusList: {},
                alarmData: [[], [], []],
                testLevel: '',
                workState: [0, 0, 0, 0],
                nxType: 0,
                alarmType: 0,
                alarms: [0, 0, 0],
@@ -238,19 +184,13 @@
                            label: "告警开始时间",
                            minWidth: 110,
                        },
                        // {
                        //   prop: "xuhang",
                        //   label: "预估续航时长",
                        //   minWidth: 110,
                        // },
                    ],
                    data: [],
                },
            };
        },
        components: {
            card,
            BigScreenCard,
            homeCard,
            BatteryPieChart,
            MapChart,
            MapMarkList,
@@ -262,6 +202,8 @@
            BarChartAlarm,
            bar3d,
            PowerPieChart,
      svgSubway,
      marqueeTop,
        },
        methods: {
            alarmTypeChangede(value) {
@@ -279,30 +221,19 @@
                res = JSON.parse(res.data);
                // console.log(res, "=====111data");
                let {
                    devStates,
                    stationCount,
                    battGroupInfo,
                    alarmsLevelWithStationName5,
                    devCountMap,
                    resTestdataInfAnalysis,
                    groupCap,
                    alarmsLevel,
                    dischargingBattery,
                    res_inf,
          res_battState,
          res_battAlarm,
          res_station,
                } = res.data;
                // 站点数量统计更新
                this.updateSiteDev(devCountMap);
                // 更新设备工作状态
                this.updateWorkState(devStates);
                // 更新整组容量
                this.updateGroupCap(groupCap);
                this.updateSiteDev(res_inf);
                // 测试信息 改为告警信息
                this.updateTestData(alarmsLevelWithStationName5);
                // 更新地图
                this.updateMap(devCountMap);
                // 更新告警统计
                this.updateAlarms(alarmsLevel);
                // // 实时放电信息
                // this.updateDischargeInfo(dischargingBattery);
                this.updateTestData(res_battAlarm);
                // 更新电池状态
                this.updateWorkState(res_battState);
                // // 更新地图
                this.updateMap(res_station);
            },
            onWSOpen2() {
                this.sendMessage2();
@@ -353,21 +284,56 @@
                // this.updateFlag = Math.random();
            },
      legendChange(data) {
        console.log('data, ', data, '=============');
        this.selectFlag = true;
        let stionNum = 0;
        let battNum = 0;
        let devNum = 0;
        let nums = this.nums;
        Object.keys(data).forEach(v => {
          if (data[v]) {
            stionNum += nums[v].stionNum;
            devNum += nums[v].devNum;
            battNum += nums[v].battNum;
          }
        });
        this.stationNum = stionNum;
        this.devCount = devNum;
        this.battGroupCount = battNum;
      },
            updateSiteDev(data) {
                const { devCount, stationNum, battGroupCount, battGroupMonCount } = data.data2;
                this.devCount = devCount;
                this.stationNum = stationNum;
                this.battGroupCount = battGroupCount;
                this.battGroupMonCount = battGroupMonCount;
                const { devNum, stionNum, battNum } = data.data2?.allmap || {};
        if (!this.selectFlag) {
          this.devCount = devNum || 0;
          this.stationNum = stionNum || 0;
          this.battGroupCount = battNum || 0;
        }
        let _data = data.data2;
        let res = {};
        Object.keys(_data).forEach(v => {
          if (v != 'allmap') {
            res[v + '号线'] = _data[v];
          }
        });
        this.nums = res;
            },
            updateWorkState(data) {
                data = data.data2;
                this.workState = [
                    data["内阻测试数量"],
                    data["核容测试数量"],
                    data["直连充电数量"],
                    data["通讯故障数量"],
                ];
        let workList = {};
        this.lineList.forEach(v => {
          let item = v == 0 ? data['allmap'] : data[v];
          let xLabel = [];
          let sData = [];
          Object.keys(item).forEach(vv => {
            xLabel.push(vv);
            sData.push(item[vv]);
          });
          workList[v] = {xLabel, sData};
        });
                this.workState = workList;
        this.$refs.bar3d1.setData(this.workState[this.lineSelect2]);
            },
            updateGroupCap(obj) {
                const { code, data, data3 } = obj;
@@ -389,123 +355,90 @@
                this.cap.level3 = level3;
                this.cap.level4 = level4;
            },
            // 电池统计
            updateBattInfo(obj) {
                // let { code, data, data2 } = obj;
                // let vol2 = 0,
                //   vol12 = 0,
                //   producer = [{ name: "电池品牌", value: 0 }];
                // if (code && data) {
                //   vol2 = data2.monVol["2.0"];
                //   vol12 = data2.monVol["12.0"];
                //   let arr = Object.keys(data2.producer)
                //     .map((v) => ({ name: v, value: data2.producer[v] }))
                //     .sort((a, b) => b.value - a.value);
                //   if (arr.length <= 5) {
                //     producer = arr;
                //   } else {
                //     let name = "其他";
                //     let value = 0;
                //     arr.splice(4).forEach((v) => {
                //       value += v.value * 1;
                //     });
                //     arr.push({ name, value });
                //     producer = arr;
                //   }
                // }
                // this.$refs.batteryChart.setData(vol2, vol12, producer);
            },
            updateTestData(obj) {
                let list_obj = obj.data2.data;
                let list_obj = obj.data2.level;
                let lineList = Object.keys(list_obj);
        lineList.unshift(0);
                if (!this.lineList.length) {
                    this.lineList = lineList;
                    this.testLevel = lineList[0];
                    this.lineSelect0 = 0;
                    this.lineSelect1 = 0;
                    this.lineSelect2 = 0;
                    this.lineSelect3 = 0;
                }
                let test = {};
                lineList.forEach((item) => {
                    let _item = list_obj[item];
                    let _item = item == 0 ? obj.data2.allLevel : list_obj[item];
                    const {
                        level1,
                        level2,
                        level3,
                        level4,
                        1: level1,
                        2: level2,
                        3: level3,
                        4: level4,
                    } = _item;
                    test[item] = {
                        xLabel: ["一级告警", "二级告警", "三级告警", "四级告警"],
                        sData: [
                            level1.battAlarmCount + level1.powerAlarmCount + level1.deviceAlarmCount,
                            level2.battAlarmCount + level2.powerAlarmCount + level2.deviceAlarmCount,
                            level3.battAlarmCount + level3.powerAlarmCount + level3.deviceAlarmCount,
                            level4.battAlarmCount + level4.powerAlarmCount + level4.deviceAlarmCount
                            level1,
                            level2,
                            level3,
                            level4
                        ],
                    };
                });
                this.testData = test;
                this.$refs.bar3d.setData(test[this.testLevel]);
        // 故障
        let fault = {};
        let _fault = obj.data2.type;
        lineList.forEach(v => {
          let _item = v == 0 ? obj.data2.allType : _fault[v];
          let arr = [];
          Object.keys(_item).forEach(vv => {
            arr.push({
              name: vv,
              value: _item[vv]
            });
          });
          fault[v] = arr;
        });
        this.faultData = fault;
                this.$refs.bar3d0.setData(test[this.lineSelect0]);
        this.$refs.pie.setData(fault[this.lineSelect1]);
        // 告警列表
        let alarmRt = {};
        let alarmList = obj.data2.list;
        lineList.forEach(v => {
          let _list = v == 0 ? alarmList['allList'] : alarmList[v];
          alarmRt[v] = _list;
        });
        this.alarmRt = alarmRt;
        this.newItems = alarmRt[this.lineSelect3];
            },
            testLevelChange(value) {
                this.$refs.bar3d.setData(this.testData[value]);
        // TODO
        switch(value) {
          case 0:
            this.$refs.bar3d0.setData(this.testData[this.lineSelect0]);
            break;
          case 1:
            this.$refs.pie.setData(this.faultData[this.lineSelect1]);
            break;
          case 2:
            this.$refs.bar3d1.setData(this.workState[this.lineSelect2]);
            break;
          case 3:
            this.newItems = this.alarmRt[this.lineSelect3];
            break;
        }
            },
            // 更新地图
            updateMap(obj) {
                let data = obj.data2.stationInfList;
                const getColor = (o) => {
                    let res = 0;
                    res = o.devWorkState <= 3 ? o.devWorkState : 0;
                    return ["#0081ff", "#66f842", "#ff6b6c", "#7668f9"][res];
                };
                this.$refs.map.setData(
                    data.map((v) => {
                        return {
                            ...v,
                            label: v.stationName3,
                            color: getColor(v),
                            points: [v.stationLongitude, v.stationLatitude],
                            // 无实际意义
                            value: 100,
                        };
                    })
                );
            },
            // 更新告警统计
            updateAlarms(obj) {
                let { level1, level2, level3, level4 } = obj.data2.data;
                this.alarms = [
                    level1.powerAlarmCount,
                    level1.deviceAlarmCount,
                    level1.battAlarmCount,
                ];
                this.alarmData = [
                    [
                        level1.powerAlarmCount,
                        level2.powerAlarmCount,
                        level3.powerAlarmCount,
                        level4.powerAlarmCount,
                    ],
                    [
                        level1.deviceAlarmCount,
                        level2.deviceAlarmCount,
                        level3.deviceAlarmCount,
                        level4.deviceAlarmCount,
                    ],
                    [
                        level1.battAlarmCount,
                        level2.battAlarmCount,
                        level3.battAlarmCount,
                        level4.battAlarmCount,
                    ],
                ];
                if (this.$refs.alarmPieChart) {
                    this.$refs.alarmPieChart.update();
                }
                if (this.$refs.chart_alarm) {
                    this.$refs.chart_alarm.setData(this.alarmData);
                }
                this.statusList = obj.data2 || {};
            },
            goDevWorkState(num) {
                let status = num;
@@ -526,29 +459,6 @@
                    },
                });
            },
            // updateDischargeInfo(obj) {
            //   const { code, data, data2, msg } = obj;
            //   let list = [];
            //   if (code && data) {
            //     let outTime = 2 * 60; //设备超时时间(2分钟)
            //     let nowTime = new Date(msg).getTime(); //当前时间
            //     list = data2
            //       .filter((v) => {
            //         // 判断是否超时
            //         let record = new Date(v.battTestRecordtime).getTime();
            //         return Math.abs(nowTime - record) / 1000 <= outTime;
            //       })
            //       .map((v) => {
            //         // v.stationArea =
            //         //   v.stationName1 + " " + v.stationName2 + " " + v.stationName5;
            //         v.battTestTimelong = formatSeconds(v.battTestTlong);
            //         let xuhang = v.loadCurr ? v.battRealCap / v.loadCurr : 0;
            //         v.xuhang = xuhang ? sethoubeiTime(xuhang) : "---";
            //         return v;
            //       });
            //   }
            //   this.tbl.data = list;
            // },
            goDevAlarm() {
                this.$router.push({
                    path: "/alarmMager/deviceTimequery",
@@ -585,12 +495,20 @@
                this.$router.push("/dataMager/battGroupMager");
            },
            initEvent() {
                this.$refs.map.getChart().on("click", (e) => {
                    console.log('e', e, '=======123======');
                // this.$refs.map.getChart().on("click", (e) => {
                //     console.log('e', e, '=======123======');
                });
                // });
            },
        },
    computed: {
      showMarquee() {
        return typeof this.newItems == "undefined" || this.newItems.length == 0
          ? false
          : true;
      },
    },
        mounted() {
            if (this.$refs.alarmPieChart) {
@@ -604,18 +522,61 @@
<style scoped lang="less">
.p-main {
  height: 100%;
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: 1fr 1fr 1fr;
  grid-template-columns: 1fr 1.5fr 1fr;
  grid-gap: 8px;
  // display: grid;
  // grid-auto-flow: column;
  // grid-template-rows: 1fr;
  // grid-template-columns: 1fr 1.95fr 1fr;
  // grid-gap: 8px;
  display: flex;
  /deep/ .big-screen-card {
  .p-side {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding-left: 36px;
    padding-right: 58px;
    position: relative;
    &::before {
      content: '';
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background: url(./images/bg-side.png) left center / auto 100% no-repeat,
                url(./images/bg-card.png) left center / 100% 100% no-repeat;
    }
    &.p-right {
      padding-left:  68px;
      padding-right: 36px;
      &::before {
        transform: scaleX(-1);
      }
    }
    .row {
      margin-top: 50px;
      margin-bottom: 50px;
    }
    .row~.row {
      margin-top: 8px;
    }
  }
    .row {
      flex: 1;
      :deep(.el-select) {
        width: 8em;
      }
    }
  /deep/ .home-card {
    padding: 0;
  }
  .wraper {
    grid-row-start: span 2;
    flex: 2.4;
    // grid-row-start: span 2;
    display: flex;
    flex-direction: column;
@@ -626,15 +587,26 @@
    .row2 {
      flex: 1.8;
      margin-top: 8px;
      position: relative;
      &::before {
        content: '';
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        background: url(./images/bg-map-sz.jpg) center center / cover no-repeat;
        opacity: 0.6;
      }
    }
  }
  .row.alarm {
    grid-column-start: span 2;
    // grid-column-start: span 2;
  }
  .row.test-info {
    grid-row-start: span 2;
    // grid-row-start: span 2;
  }
}
@@ -731,26 +703,36 @@
  .item {
    cursor: pointer;
    display: flex;
    flex-direction: column;
    // border-radius: 6px;
    align-items: center;
    justify-content: center;
    min-width: 10em;
    min-height: 4em;
    background: url(./images/bg-info.png) center center / contain no-repeat;
    .name {
      background: #0ff;
      border-radius: 6px;
      color: #333;
      font-size: 18px;
      font-weight: bold;
      padding: 0 10px;
      // background: #0ff;
      // border-radius: 6px;
      // color: #333;
      // font-size: 18px;
      // font-weight: bold;
      // padding: 0 10px;
      font-size: 14px;
      margin-top: 10px;
      color: #0ff;
      &::after {
        content: ':';
      }
    }
    .num {
      width: 3em;
      height: 1.5em;
      padding: 0 10px;
      &.bits5 {
        width: 5em;
      }
      // width: 3em;
      // height: 1.5em;
      // padding: 0 10px;
      color: #fbfc0d;
      font-weight: bold;
      font-size: 22px;
    }
  }
}
@@ -767,6 +749,14 @@
    bottom: 0;
  }
}
.scroller-wraper {
  position: absolute;
  left: 0;
  top: 30px;
  right: 0;
  bottom: 30px;
  overflow: hidden;
}
/deep/ .mark-list-container {
  bottom: 0.4rem;
src/views/home/images/bg-card.png
src/views/home/images/bg-info.png
src/views/home/images/bg-map-sz.jpg
src/views/home/images/bg-side.png
src/views/test.vue
@@ -1,11 +1,11 @@
<template>
  <div class="">
    <svg-dyhr style="background: gray;"></svg-dyhr>
  <div class="test">
    <test ></test>
  </div>
</template>
<script>
import SvgDyhr from '@/views/home/components/svg/svgDyhr.vue';
import test from '@/views/home/home-szdt.vue';
export default {
  name: '',
@@ -15,7 +15,7 @@
    }
  },
  components: {
    SvgDyhr
    test
  },
  methods: {
@@ -29,5 +29,7 @@
</script>
<style scoped>
.test {
  height: 100%;
}
</style>