大屏可视化背景是构建整个数据大屏视觉体验和氛围的基石。它远不止是一张“好看的图片”,而是承载数据、引导视觉、传达主题的关键元素。

以下是对大屏可视化背景的全面描述,涵盖其核心作用、主要类型、设计原则和未来趋势。这是目前通过柱状、饼状、折现、列表等多种展示方式表示比对、占比、趋势等全方位的数据分析。
1、渐变柱状图
image.png

dom元素

<div :class="['card', 'cursor', selectedCardKey == 'headcount' ? 'selected' : '']" @click="selectedCard('headcount')">
        <div class="card-title">
          人员统计
          <span class="dataTotal"
            >{{ JSK.formatNumberWithCommas(populationTotal) || 0 }}
            <el-icon v-if="selectedCardKey == 'headcount'" class="CaretRight"><CaretRight /></el-icon>
          </span>
        </div>
        <v-chart ref="headcountChartRef" class="schart" :option="headcountChartOptions" :style="{ width: '296px', height: '125px' }"></v-chart>
      </div>

options参数

 headcountChartRef.value.setOption({
        xAxis: { data: headcountChartList.value.map((item) => item.year) },
        series: [
          {
            data: headcountChartList.value.map((item, index) => {
              let itemStyle;
              if (index == 0) {
                itemStyle = {
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    { offset: 0, color: "#00BAC5" },
                    { offset: 1, color: "rgba(0, 186, 197, 0)" },
                  ]),
                };
              } else if (index == 1) {
                itemStyle = {
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    { offset: 0, color: "#5C7CEA" },
                    { offset: 1, color: "rgba(92, 124, 234, 0)" },
                  ]),
                };
              } else if (index == 2) {
                itemStyle = {
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    { offset: 0, color: "#A2A1DA" },
                    { offset: 1, color: "rgba(162, 161, 218, 0)" },
                  ]),
                };
              } else if (index == 3) {
                itemStyle = {
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    { offset: 0, color: "#D098CB" },
                    { offset: 1, color: "rgba(208, 152, 203, 0)" },
                  ]),
                };
              } else {
                itemStyle = {
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    { offset: 0, color: "#CD6C27" },
                    { offset: 1, color: "rgba(205, 108, 39, 0)" },
                  ]),
                };
              }
              return {
                value: item.total,
                itemStyle: itemStyle,
              };
            }),
          },
        ],
      });

2、横向柱状图比例展示
image.png

dom展示

 <!-- 居所管理 -->
      <div :class="['card', 'cursor', selectedCardKey == 'domicile' ? 'selected' : '']" @click="selectedCard('domicile')">
        <div class="card-title">
          居所管理<span class="dataTotal"
            >{{ JSK.formatNumberWithCommas(domicileTotal) || 0 }} <el-icon v-if="selectedCardKey == 'domicile'" class="CaretRight"><CaretRight /></el-icon
          ></span>
        </div>
        <div v-for="(item, index) in residenceList" class="residence-item" :key="index" :title="item.dictName + ':' + JSK.formatNumberWithCommas(item.total)">
          <span class="lable" style="margin-right: 7px">{{ item.dictName }}</span>
          <div class="myProgress" style="width: 110px; min-width: 136px">
            <span :class="['inner', 'inner_color' + (index + 1)]" :style="{ width: getProportion(item.total, domicileTotal) }"></span>
          </div>
          <span :class="['proportionValue', 'color' + (index + 1)]"> {{ JSK.formatNumberWithCommas(item.total) }}</span>
        </div>
        <div v-if="!residenceList || residenceList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
      </div>

css

.residence-item {
  display: flex;
  align-items: center;
  white-space: nowrap;
  line-height: 31px;
  .lable {
    display: inline-block;
    width: 80px;
    min-width: 80px;
  }
}

.myProgress {
  display: inline-block;
  height: 10px;
  background: rgba(255, 255, 255, 0.1);
  position: relative;
  .inner {
    height: 4px;
    position: absolute;
    top: 3px;
  }
  .inner_color1 {
    background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
  }
  .inner_color2 {
    background: linear-gradient(270deg, #cd92c8 0%, rgba(205, 146, 200, 0.2) 100%);
  }
  .inner_color3 {
    background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
  }
  .inner_color4 {
    background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
  }
}

.proportionValue {
    display: inline-block;
    font-size: 14px;
    width: 50px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-left: 14px;
  }

3、折线图
image.png

dom

 <div :class="['card', 'cursor', selectedCardKey == 'caseEvent' ? 'selected' : '']" @click="selectedCard('caseEvent')">
        <div class="card-title">
          案事件统计<span class="dataTotal">
            <el-icon v-if="selectedCardKey == 'caseEvent'" class="CaretRight"><CaretRight /></el-icon
          ></span>
        </div>
        <div class="flex justify-end" style="margin-top: 13px">
          <el-select v-model="dateClassVal_caseEvent" style="width: 80px" class="sidebar_select" @change="getCaseStatistics">
            <el-option v-for="item in dateClass_caseEvent" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
        </div>

        <v-chart ref="caseEventChartRef" class="schart" :option="caseEventOptions" :style="{ width: '296px', height: '170px' }"></v-chart>

options

 let dataZoom;
    if (res.data.length > 12) {
      dataZoom = [
        {
          type: "slider",
          show: true,
          left: "9%",
          bottom: 8,
          startValue: 0,
          endValue: 12,
          handleSize: 0, //两侧手柄大小
          showDetail: false,
          height: 0,
          borderColor: "transparent",
          showDataShadow: false,
          minValueSpan: 12,
          maxValueSpan: 12,
        },
        // { type: "inside", show: false, disabled: false },
      ];
    } else {
      dataZoom = [
        { type: "slider", show: false, disabled: true, endValue: res.data.length, bottom: 0 },
        // { type: "inside", show: false, disabled: true },
      ];
    }

    caseEventChartRef.value &&
      caseEventChartRef.value.setOption({
        dataZoom,
        xAxis: { data: res.data.map((item) => item.month || item.year || item.date) },
        series: {
          data: res.data.map((item) => item.num),
        },
      });
  });

4、饼状图、滚动图、联动
image.png

<!-- 人员统计详情 -->
<template>
  <div class="sidebar_right_headcount" v-if="sidebarType == 'headcount'">
    <!-- 基础人员 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>基础人员
      </div>

      <div class="flex" style="align-items: center; margin-top: 12px; position: relative">
        <div style="position: absolute; left: 88px; top: -29px">
          总数:<span class="color2" style="font-weight: 600">{{ JSK.formatNumberWithCommas(basicPersonnelTotal) }}</span>
        </div>
        <v-chart
          ref="populationChartRef"
          class="schart"
          :option="populationChartOptions"
          :style="{ width: '74px', height: '74px' }"
          @mousemove="populationChart_mousemove"
          @mouseout="populationChart_mouseout"
        ></v-chart>
        <ul class="ageGroup">
          <li v-for="(item, index) in residentialPersonnelList" :key="item.name">
            <span :class="['colorBlock', 'bg' + (index + 1)]"></span
            ><span style="width: 48px; margin-right: 10px" :class="['text_overflow_ellipsis', populationChartSelectIndex == index ? 'color' + (index + 1) : '']">{{ item.name }}</span>
            <span :class="['color' + (index + 1)]" style="width: 48px; margin-right: 13px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
            <span :class="['color' + (index + 1)]" style="width: 52px" class="text_overflow_ellipsis">{{ JSK.formatNumberWithCommas(item.num) }}</span>
          </li>
        </ul>
      </div>
      <div class="flex justify-between" style="margin-top: 20px">
        <el-radio-group v-model="dateClassKey" size="small" class="sidebar_radio_group" @change="getGtreetClassPersonnelList_basic">
          <el-radio-button v-for="item in dateClass" :key="item" :label="item.value">{{ item.label }}</el-radio-button>
        </el-radio-group>
        <el-select v-model="residentialClassKey" style="width: 80px" class="sidebar_select" @change="getGtreetClassPersonnelList_basic">
          <el-option v-for="item in residentialClass" :key="item.enums" :label="item.name" :value="item.enums" />
        </el-select>
      </div>
      <!-- <el-scrollbar height="106px" style="margin-top: 20px">
        <ul style="width: 100%" class="barChart">
          <li class="row" v-for="(item, index) in streetClassPersonnelList_basic" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
            <span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
            <div class="myProgress myProgress1 color2" style="width: 128px; min-width: 128px; margin-right: 8px">
              <span class="inner inner_color4" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
            </div>
            <span class="color2" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
            <span class="proportionValue color2 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
          </li>
        </ul>
        <div v-if="!streetClassPersonnelList_basic || streetClassPersonnelList_basic.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
          暂无数据
        </div>
      </el-scrollbar> -->

      <ul
        style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
        :class="['barChart', streetClassPersonnelList_basic.length == 1 ? 'oneRow' : streetClassPersonnelList_basic.length < 5 ? 'static' : '']"
      >
        <Vue3SeamlessScroll hover-stop="true" :list="streetClassPersonnelList_basic" hover limitScrollNum="5" :step="0.1">
          <li class="row" v-for="(item, index) in streetClassPersonnelList_basic" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
            <span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
            <div class="myProgress myProgress1 color2" style="width: 128px; min-width: 128px; margin-right: 8px">
              <span class="inner inner_color4" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
            </div>
            <span class="color2" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
            <span class="proportionValue color2 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
          </li>
        </Vue3SeamlessScroll>
      </ul>
      <div v-if="!streetClassPersonnelList_basic || streetClassPersonnelList_basic.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
        暂无数据
      </div>
    </div>
    <!-- 重点人员 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>重点人员
      </div>
      <div class="flex" style="margin-top: 20px">
        <p style="margin-right: 20px">
          在管人员:<span class="color2" style="font-weight: 600">{{ JSK.formatNumberWithCommas(inManagementPersonnel) }}</span>
        </p>
        <p class="flex items-center">
          总数本周新增<i
            :class="['iconfont', addPersonnel >= 0 ? 'icon-shangzhang' : 'icon-xiajiang']"
            :style="{ fontSize: '10px', margin: ' 0 2px 0 6px', color: addPersonnel >= 0 ? '#1FC61E' : 'red' }"
          /><span :style="{ color: addPersonnel >= 0 ? '#1FC61E' : 'red' }">{{ Math.abs(addPersonnel) }}</span>
        </p>
      </div>
      <div class="flex justify-between" style="margin-top: 20px">
        <el-radio-group v-model="dateClassKeyVal" size="small" class="sidebar_radio_group" @change="getLocationAggregationStatisticsFocus">
          <el-radio-button v-for="item in dateClass" :key="item" :label="item.value">{{ item.label }}</el-radio-button>
        </el-radio-group>
        <el-select v-model="keyPersonnelVal" style="width: 80px" class="sidebar_select" @change="getLocationAggregationStatisticsFocus">
          <el-option v-for="item in keyPersonnelClass" :key="item.enums" :label="item.name" :value="item.enums" />
        </el-select>
      </div>
      <!-- <el-scrollbar height="106px" style="margin-top: 20px">
        <ul style="width: 100%" class="barChart">
          <li class="row" v-for="(item, index) in streetClassPersonnelList_key" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
            <span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
            <div class="myProgress myProgress1 color3" style="width: 128px; min-width: 128px; margin-right: 8px">
              <span class="inner inner_color2" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
            </div>
            <span class="color3" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
            <span class="proportionValue color3 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
          </li>
        </ul>
        <div v-if="!streetClassPersonnelList_key || streetClassPersonnelList_key.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
          暂无数据
        </div>
      </el-scrollbar> -->
      <ul
        style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
        :class="['barChart', streetClassPersonnelList_key.length == 1 ? 'oneRow' : streetClassPersonnelList_key.length < 5 ? 'static' : '']"
      >
        <Vue3SeamlessScroll hover-stop="true" :list="streetClassPersonnelList_key" hover limitScrollNum="5" :step="0.1">
          <li class="row" v-for="(item, index) in streetClassPersonnelList_key" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
            <span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
            <div class="myProgress myProgress1 color3" style="width: 128px; min-width: 128px; margin-right: 8px">
              <span class="inner inner_color2" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
            </div>
            <span class="color3" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
            <span class="proportionValue color3 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
          </li>
        </Vue3SeamlessScroll>
      </ul>
      <div v-if="!streetClassPersonnelList_key || streetClassPersonnelList_key.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
        暂无数据
      </div>
    </div>

    <!-- 重点人员占比统计 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>重点人员占比统计
      </div>
      <div class="flex" style="padding: 12px 0">
        <v-chart
          ref="keyPersonnelStatisticsRef"
          :class="['schart', 'keyPersonnelStatisticsChart', actionIndex < 0 ? 'noAfter' : '']"
          @mouseover="keyPersonnelStatistics_mouseover"
          :option="keyPersonnelStatisticsOptions"
          :style="{ width: '94px', height: '74px', minWidth: '94px', marginLeft: '-4px' }"
        ></v-chart>
        <div style="margin-left: 4px">
          <p :class="['color' + ((actionIndex % 6) + 1)]" style="margin-bottom: 2px">{{ keyPersonnelStatistics_crrentData.name }}</p>
          <p style="margin-bottom: 2px">
            总数:<span :class="['color' + ((actionIndex % 6) + 1)]">{{ JSK.formatNumberWithCommas(keyPersonnelStatistics_crrentData.num) }}</span>
          </p>
          <p style="margin-bottom: 2px; white-space: nowrap">
            本市占比:<span :class="['color' + ((actionIndex % 6) + 1)]" style="margin-right: 8px; letter-spacing: -0.5px">{{
              (keyPersonnelStatistics_crrentData.cityPercent * 100).toFixed(2) + "%"
            }}</span
            >外市占比:<span :class="['color' + ((actionIndex % 6) + 1)]" style="letter-spacing: -0.5px">{{ (keyPersonnelStatistics_crrentData.noCityPercent * 100).toFixed(2) + "%" }}</span>
          </p>
          <p class="flex items-center">
            总数本周新增
            <i
              :class="['iconfont', keyPersonnelStatistics_crrentData.weekIncrNum >= 0 ? 'icon-shangzhang' : 'icon-xiajiang']"
              :style="{ fontSize: '10px', margin: ' 0 2px 0 6px', color: keyPersonnelStatistics_crrentData.weekIncrNum >= 0 ? '#1FC61E' : 'red' }"
            /><span :style="{ color: keyPersonnelStatistics_crrentData.weekIncrNum >= 0 ? '#1FC61E' : 'red' }">{{ Math.abs(keyPersonnelStatistics_crrentData.weekIncrNum) }}</span>
          </p>
        </div>
      </div>

      <ul class="ageGroup">
        <li style="cursor: pointer" v-for="(item, index) in keyPersonnelStatistics" :key="item.name" @mouseover="keyPersonnelStatisticsList_mouseover(index)">
          <span :class="['colorBlock', 'bg' + ((index % 6) + 1)]"></span
          ><span style="width: 130px; margin-right: 10px" :class="['text_overflow_ellipsis', actionIndex == index ? 'color' + ((index % 6) + 1) : '']">{{ item.name }}</span>
          <span :class="['color' + ((index % 6) + 1)]" style="width: 48px; margin-right: 13px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
          <span :class="['color' + ((index % 6) + 1)]" style="width: 52px" class="text_overflow_ellipsis">{{ JSK.formatNumberWithCommas(item.num) }}</span>
        </li>
      </ul>
    </div>
    <!-- 严重精神患者数据统计分析 -->
    <div>
      <div style="margin-top: 20px">严重精神患者数据统计分析</div>
      <div class="flex justify-between" style="margin-top: 13px">
        <el-radio-group v-model="dateClassKey_serious" size="small" class="sidebar_radio_group" @change="getPsychosisAggregationStatistics">
          <el-radio-button v-for="item in dateClass" :key="item" :label="item.value">{{ item.label }}</el-radio-button>
        </el-radio-group>
        <el-select v-model="keyPersonnelVal_serious" style="width: 80px" class="sidebar_select" @change="getPsychosisAggregationStatistics">
          <el-option v-for="item in keyPersonnelClass_serious" :key="item.enums" :label="item.name" :value="item.enums" />
        </el-select>
      </div>
      <v-chart ref="seriousPersonnelChartRef" class="schart" :option="seriousPersonnelOptions" :style="{ width: '296px', height: '170px' }"></v-chart>
    </div>
    <div style="height: 20px"></div>
  </div>
</template>
<script setup>
import { onMounted, reactive, ref, nextTick, watch, defineProps, defineExpose, computed } from "vue";
import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
import { CaretRight } from "@element-plus/icons-vue";
import JSK from "@/utils/JSK";
import { categoryStatisticsPeople, locationAggregationStatistics, locationAggregationStatisticsFocus, percentAggregationStatistics, psychosisAggregationStatistics } from "@/api/map_sidebar.js";

const props = defineProps({
  sidebarType: "",
  data: {
    type: Object,
    default: {},
  },
  orgData: {
    type: Object,
    default: {},
  },
});
const dateClass = [
  { label: "近一周", value: "week" },
  { label: "近一月", value: "month" },
  { label: "近一季", value: "quarter" },
  { label: "近一年", value: "year" },
];

const residentialClass = window.$dicts.getEnums("ResidentialClass");

const keyPersonnelClass = window.$dicts.getEnums("KeyPersonnelClass");

const keyPersonnelClass_serious = window.$dicts.getEnums("KeyPersonnelClassSerious");

const populationChartRef = ref(null);
const keyPersonnelStatisticsRef = ref(null);
const seriousPersonnelChartRef = ref(null);
const dateClassKey = ref("week"); //基础人员 周月季年关键字
const residentialClassKey = ref("01"); //基础人员 居住分类关键字
// const streetClassPersonnel_total = ref(0); //基础人员 按街道分类总数

const keyPersonnelVal = ref("01"); //重点人员 分类关键字
const dateClassKeyVal = ref("week"); //重点人员 周月季年关键字
// const keyPersonne_total = ref(0); //重点人员 按街道分类总数
// let keyPersonnelStatisticsData = []; //重点人员占比统计 饼图数据

const dateClassKey_serious = ref("week"); //严重精神患者人员 日周月年关键字
const keyPersonnelVal_serious = ref("01"); //严重精神患者人员  分类关键字

// 基础人员 - 按街道分类人员列表
// const streetClassPersonnelList_basic = computed(() => {
//   let res = props.data.basicPersonnel.streetClassPersonnelList
//     .filter((item) => item.type == residentialClassKey.value)
//     .map((item) => {
//       if (dateClassKey.value == 1) {
//         return {
//           label: item.label,
//           num: item.weekNum,
//         };
//       } else if (dateClassKey.value == 2) {
//         return {
//           label: item.label,
//           num: item.monthNum,
//         };
//       } else if (dateClassKey.value == 3) {
//         return {
//           label: item.label,
//           num: item.quarterNum,
//         };
//       } else {
//         return {
//           label: item.label,
//           num: item.yearNum,
//         };
//       }
//     });
//   // 基础人员总数
//   streetClassPersonnel_total.value = res.reduce((prev, cur) => {
//     return prev + cur.num;
//   }, 0);
//   return res;
// });
// 重点人员 - 按街道分类人员列表
// const streetClassPersonnelList_key = computed(() => {
//   let res = props.data.keyPersonnel.streetClassPersonnelList
//     .filter((item) => item.type == keyPersonnelVal.value)
//     .map((item) => {
//       if (dateClassKeyVal.value == 1) {
//         return {
//           label: item.label,
//           num: item.weekNum,
//         };
//       } else if (dateClassKeyVal.value == 2) {
//         return {
//           label: item.label,
//           num: item.monthNum,
//         };
//       } else if (dateClassKeyVal.value == 3) {
//         return {
//           label: item.label,
//           num: item.quarterNum,
//         };
//       } else {
//         return {
//           label: item.label,
//           num: item.yearNum,
//         };
//       }
//     });
//   // 管控人员总数
//   keyPersonne_total.value = res.reduce((prev, cur) => {
//     return prev + cur.num;
//   }, 0);
//   return res;
// });
// 重点人员占比统计-总数
// const keyPersonnelStatistics_total = computed(() => {
//   return props.data.keyPersonnelStatistics.reduce((prev, cur) => {
//     return prev + cur.num;
//   }, 0);
// });
// 严重精神患者数据统计分析-折线图数据
// const setOption_seriousPersonnelChart = () => {
//   seriousPersonnelChartRef.value &&
//     seriousPersonnelChartRef.value.setOption({
//       xAxis: { data: props.data.seriousPersonnel[dateClassKey_serious.value].filter((item) => item.type == keyPersonnelVal_serious.value).map((item) => item.time) },
//       series: {
//         data: props.data.seriousPersonnel[dateClassKey_serious.value].filter((item) => item.type == keyPersonnelVal_serious.value).map((item) => item.num),
//       },
//     });
// };
const basicPersonnelTotal = ref(0); //基础人员 总数
const residentialPersonnelList = ref([]); //基础人员 饼图数据
const streetClassPersonnelList_basic = ref([]); //基础人员 按人员流动分类获取列表数据

// 基础人员-按人员流动分类获取列表数据
const getGtreetClassPersonnelList_basic = () => {
  locationAggregationStatistics(props.orgData.key, dateClassKey.value, residentialClassKey.value).then((res) => {
    streetClassPersonnelList_basic.value = res.data;
  });
};
const streetClassPersonnelList_key = ref([]); //重点人员列表数据
const inManagementPersonnel = ref(0); //重点人员-在管人员
const addPersonnel = ref(0); //重点人员-总数本周新增

// 获取重点人员列表数据
const getLocationAggregationStatisticsFocus = () => {
  locationAggregationStatisticsFocus(props.orgData.key, dateClassKeyVal.value, keyPersonnelVal.value).then((res) => {
    streetClassPersonnelList_key.value = res.data.data || [];
    inManagementPersonnel.value = res.data.inChargeNum || 0;
    addPersonnel.value = res.data.weekIncrNum || 0;
  });
};
// 重点人员占比统计
const keyPersonnelStatistics = ref([]); //重点人员列表数据
// 严重精神患者数据统计
const getPsychosisAggregationStatistics = () => {
  psychosisAggregationStatistics(props.orgData.key, dateClassKey_serious.value, keyPersonnelVal_serious.value).then((res) => {
    let dataZoom;
    if (res.data.length > 12) {
      dataZoom = [
        {
          type: "slider",
          show: true,
          left: "9%",
          bottom: 8,
          startValue: 0,
          endValue: 12,
          handleSize: 0, //两侧手柄大小
          showDetail: false,
          height: 0,
          borderColor: "transparent",
          showDataShadow: false,
          minValueSpan: 12,
          maxValueSpan: 12,
        },
        // { type: "inside", show: false, disabled: false },
      ];
    } else {
      dataZoom = [
        { type: "slider", show: false, disabled: true, endValue: res.data.length, bottom: 0 },
        // { type: "inside", show: false, disabled: true },
      ];
    }

    seriousPersonnelChartRef.value &&
      seriousPersonnelChartRef.value.setOption({
        dataZoom,
        xAxis: { data: res.data.map((item) => item.hour || item.date || item.month) },
        series: {
          data: res.data.map((item) => item.num),
        },
      });
  });
};
props.orgData.key;
watch(
  () => props.orgData,
  (val) => {
    if (val && props.sidebarType == "headcount") {
      // 基础人员-饼图数据
      categoryStatisticsPeople(props.orgData.key).then((res) => {
        basicPersonnelTotal.value = res.data.total;
        residentialPersonnelList.value = res.data.detail;
        populationChartRef.value &&
          populationChartRef.value.setOption({
            series: [
              {
                data: residentialPersonnelList.value.map((item) => {
                  return { value: item.num };
                }),
              },
            ],
          });
      });
      // 基础人员-按人员流动分类获取列表数据
      getGtreetClassPersonnelList_basic();
      // 获取重点人员列表数据
      getLocationAggregationStatisticsFocus();
      // 重点人员占比统计
      percentAggregationStatistics(props.orgData.key).then((res) => {
        if (res.data && res.data.length > 0) {
          actionIndex.value = 0;
          keyPersonnelStatistics.value = res.data.map((item) => Object.assign(item, { value: item.num }));

          keyPersonnelStatisticsRef.value &&
            keyPersonnelStatisticsRef.value.setOption({
              series: [
                {
                  data: keyPersonnelStatistics.value,
                },
              ],
            });

          keyPersonnelStatisticsRef.value.dispatchAction({
            type: "highlight",
            seriesIndex: 0,
            dataIndex: actionIndex.value,
          });

          keyPersonnelStatistics_crrentData.value = keyPersonnelStatistics.value[0];
          drawLinesFromCenterToTarget();
        }
      });
      // 严重精神患者数据统计
      getPsychosisAggregationStatistics();
    }
  },
  // immediate: true
  { deep: true, immediate: true }
);
watch(
  () => props.data,
  (val) => {
    if (val && props.sidebarType == "headcount") {
      nextTick(() => {
        // 基础人员-饼图数据
        // populationChartRef.value &&
        //   populationChartRef.value.setOption({
        //     series: [
        //       {
        //         data: val.basicPersonnel.residentialPersonnelList.map((item) => {
        //           return { value: item.value };
        //         }),
        //       },
        //     ],
        //   });
        //重点人员占比统计-饼图数据
        // keyPersonnelStatisticsData = val.keyPersonnelStatistics.map((item) => {
        //   return { ...item, value: item.num, name: item.label };
        // });
        // keyPersonnelStatisticsRef.value &&
        //   keyPersonnelStatisticsRef.value.setOption({
        //     series: [
        //       {
        //         data: keyPersonnelStatisticsData,
        //       },
        //     ],
        //   });
        // keyPersonnelStatisticsRef.value.dispatchAction({
        //   type: "highlight",
        //   seriesIndex: 0,
        //   dataIndex: actionIndex.value,
        // });
        // drawLinesFromCenterToTarget();
        // keyPersonnelStatistics_crrentData.value = keyPersonnelStatisticsData[0];
        // 严重精神患者数据统计分析-折线图数据
        // setOption_seriousPersonnelChart();
      });
    }
  },
  // immediate: true
  { deep: true, immediate: true }
);

// 基础人员-饼图配置
const populationChartOptions = ref({
  tooltip: {
    show: false,
    trigger: "item",
  },
  color: ["#5C7CEA", "#00bac5", "#d098cb", "#fd742f", "#ffaa39"],
  series: [
    {
      type: "pie",
      radius: ["54%", "86%"],
      center: ["50%", "50%"],
      itemStyle: {
        borderRadius: 0,
        borderColor: "#fff",
        borderWidth: 0,
      },
      label: {
        show: false,
      },
      emphasis: {
        // scale: false, //禁止触摸缩放
      },
      labelLine: {
        show: false,
      },
      data: [],
    },
  ],
});
const populationChartSelectIndex = ref(-1); //基础人员-饼图被选中index
// 基础人员-饼图-鼠标进入事件
const populationChart_mousemove = (param) => {
  console.log(param);
  populationChartSelectIndex.value = param.dataIndex;
};
// 基础人员-饼图-鼠标离开事件
const populationChart_mouseout = () => {
  populationChartSelectIndex.value = -1;
};
// 重点人员占比统计-饼图配置
const keyPersonnelStatisticsOptions = ref({
  tooltip: {
    show: false,
  },
  color: ["#5c7cea", "#00bac5", "#d098cb", "#fd742f", "#ffaa39", "#405ae9"],
  series: [
    {
      type: "pie",
      radius: ["54%", "86%"],
      center: ["40%", "50%"],
      avoidLabelOverlap: false,
      label: {
        show: false,
      },
      // 禁用默认引导线
      labelLine: {
        show: false,
      },

      data: [],
    },
  ],
});

// 严重精神患者数据统计分析-折线图
const seriousPersonnelOptions = ref({
  grid: {
    top: "20%",
    left: "5%",
    right: "1%",
    bottom: "1%",
    containLabel: true,
  },
  tooltip: {
    trigger: "axis",
    extraCssText: "width:auto;height:auto",
    valueFormatter: function (value) {
      return value + "人";
    },
  },
  xAxis: {
    type: "category",
    // boundaryGap: false,
    axisLabel: { color: "#fff", fontSize: 9, interval: 0, rotate: 40 },

    axisLine: {
      show: true,
      lineStyle: {
        // 使用深浅的间隔色
        color: "#6C8097",
      },
    },
    axisTick: {
      alignWithLabel: true,
    },
    data: [],
  },
  yAxis: {
    name: "单位(人)",
    nameTextStyle: {
      fontSize: 10,
      padding: [2, -5, -2, 5],
    },
    // axisLine: {
    //   show: false,
    // },
    // axisTick: {
    //   show: false,
    // },
    splitLine: {
      show: true, // 先隐藏默认的分割线
      lineStyle: {
        // 使用深浅的间隔色
        color: "rgba(108,128,151,0.3)",
      },
    },
    axisLabel: {
      color: "#999",
      fontSize: 10,
      margin: 3,
      padding: [0, 0, 10, 0],
    },
  },

  series: {
    data: [],
    type: "line",
    symbolSize: 0,
    itemStyle: {
      color: "#FFAA39",
    },
    emphasis: {
      focus: "series",
    },
    areaStyle: {
      color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        {
          offset: 0,
          color: "#FFAA39",
        },
        {
          offset: 0.9,
          color: "rgba(255,170,57,0)",
        },
      ]),
    },
  },
});

/*
   监听高亮事件
   重点人员占比统计
*/
const keyPersonnelStatistics_mouseover = (param) => {
  console.log(param);
  console.log(keyPersonnelStatisticsRef.value);
  if (actionIndex.value == param.dataIndex) return;
  //   取消高亮模块
  keyPersonnelStatisticsRef.value.dispatchAction({
    type: "downplay",
    seriesIndex: 0,
    dataIndex: actionIndex.value,
  });

  //   设置高亮模块
  actionIndex.value = param.dataIndex;
  keyPersonnelStatisticsRef.value.dispatchAction({
    type: "highlight",
    seriesIndex: 0,
    dataIndex: actionIndex.value,
  });

  drawLinesFromCenterToTarget();
  keyPersonnelStatistics_crrentData.value = param.data;
};
const keyPersonnelStatisticsList_mouseover = (index) => {
  if (actionIndex.value == index) return;
  //   取消高亮模块
  keyPersonnelStatisticsRef.value.dispatchAction({
    type: "downplay",
    seriesIndex: 0,
    dataIndex: actionIndex.value,
  });

  //   设置高亮模块
  actionIndex.value = index;
  keyPersonnelStatisticsRef.value.dispatchAction({
    type: "highlight",
    seriesIndex: 0,
    dataIndex: actionIndex.value,
  });

  drawLinesFromCenterToTarget();
  keyPersonnelStatistics_crrentData.value = keyPersonnelStatistics.value[index];
};

//重点人员占比统计 绘制引导线  开始------------------------------------------------------

let actionIndex = ref(-1); //高亮模块的index
let keyPersonnelStatistics_crrentData = ref({}); //重点人员占比统计 当前选中模块的数据
const targetXSlider = 90; //x位置
const targetYSlider = 10; //y位置
const startOffsetSlider = 45; //起点偏移量
const lineColorPicker = "#fff"; //线条颜色
const lineWidthSlider = 1; //线条宽度
const lineTypeSelect = "solid"; //线条类型

// 绘制从扇区中心指向同一目标点的引导线
function drawLinesFromCenterToTarget() {
  // 获取饼图系列
  const series = keyPersonnelStatisticsRef.value.getOption().series[0];
  const center = series.center || ["40%", "50%"];
  const radius = series.radius;

  // 转换百分比为像素值
  const chartWidth = keyPersonnelStatisticsRef.value.getWidth();
  const chartHeight = keyPersonnelStatisticsRef.value.getHeight();

  let centerX, centerY;
  if (typeof center[0] === "string") {
    centerX = (parseFloat(center[0]) / 100) * chartWidth;
  } else {
    centerX = center[0];
  }

  if (typeof center[1] === "string") {
    centerY = (parseFloat(center[1]) / 100) * chartHeight;
  } else {
    centerY = center[1];
  }

  let outerRadius;
  if (typeof radius === "string") {
    outerRadius = (Math.min(chartWidth, chartHeight) * parseFloat(radius)) / 100;
  } else if (Array.isArray(radius)) {
    outerRadius = (Math.min(chartWidth, chartHeight) * parseFloat(radius[1])) / 100;
  } else {
    outerRadius = (Math.min(chartWidth, chartHeight) * parseFloat(radius)) / 200;
  }

  // 计算每个扇区的角度
  const total = keyPersonnelStatistics.value.reduce((sum, item) => sum + item.value, 0);
  let startAngle = -Math.PI / 2; // 从顶部开始
  const angles = [];
  let isAllZero = false;
  // 判断数据是否都为0
  let arr = keyPersonnelStatistics.value.filter((item) => item.value == 0);
  if (arr.length == keyPersonnelStatistics.value.length) {
    isAllZero = true;
  }
  if (isAllZero) {
    keyPersonnelStatistics.value.forEach((item) => {
      const angle = (1 / keyPersonnelStatistics.value.length) * Math.PI * 2;
      angles.push({
        start: startAngle,
        end: startAngle + angle,
        mid: startAngle + angle / 2,
      });
      startAngle += angle;
    });
  } else {
    keyPersonnelStatistics.value.forEach((item) => {
      const angle = (item.value / total) * Math.PI * 2;
      angles.push({
        start: startAngle,
        end: startAngle + angle,
        mid: startAngle + angle / 2,
      });
      startAngle += angle;
    });
  }

  // 计算目标点位置
  const targetX = (parseFloat(targetXSlider) / 100) * chartWidth;
  const targetY = (parseFloat(targetYSlider) / 100) * chartHeight;
  const startOffset = parseFloat(startOffsetSlider);

  // 创建图形元素
  const graphicElements = [];

  // 为每个扇区添加引导线
  angles.forEach((angle, index) => {
    if (index != actionIndex.value) return;
    // 计算起点(扇区内部,根据偏移量)
    const startRadius = outerRadius * (startOffset / 100);
    const startX = centerX + Math.cos(angle.mid) * startRadius;
    const startY = centerY + Math.sin(angle.mid) * startRadius;
    // 添加线条
    graphicElements.push({
      type: "line",
      shape: {
        x1: startX,
        y1: startY,
        x2: targetX,
        y2: targetY,
      },
      style: {
        stroke: lineColorPicker,
        lineWidth: parseFloat(lineWidthSlider),
        lineDash: lineTypeSelect === "dashed" ? [5, 5] : lineTypeSelect === "dotted" ? [2, 2] : null,
      },
      zlevel: 5,
    });

    // 添加起点标记
    graphicElements.push({
      type: "circle",
      shape: {
        cx: startX,
        cy: startY,
        r: 2,
      },
      style: {
        fill: lineColorPicker,
      },
      zlevel: 999,
    });
  });
  // 更新图表
  keyPersonnelStatisticsRef.value.setOption({
    graphic: graphicElements,
  });
}

//重点人员占比统计 绘制引导线  结束------------------------------------------------------

// 获取比例
const getProportion = (val, total) => {
  if (val && total) {
    return ((val / total) * 100).toFixed(2) + "%";
  } else {
    return 0 + "%";
  }
};

defineExpose({});
</script>
<style lang="scss" scoped>
.sidebar_right_headcount {
  padding: 0 12px;
}
.title {
  font-size: 12px;
  color: #00bac5;
  line-height: 17px;
  display: flex;
  align-items: center;
  margin-top: 20px;
  margin-left: -5px;
}
ul.barChart {
  li {
    display: flex;
    align-items: center;
    line-height: 24px;
    height: 24px;
    overflow: hidden;
  }
  &.static li {
    &:first-child {
      line-height: 12px;
      height: 17px;
      align-items: flex-start;
    }
    &:last-child {
      line-height: 12px;
      height: 17px;
      align-items: flex-end;
    }
  }
}

.ageGroup {
  flex: 1;
  li {
    height: 18px;
    // width: 208px;
    background: rgba(255, 255, 255, 0.05);
    position: relative;
    display: flex;
    align-items: center;

    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 3px;
      height: 3px;
      border: 1px solid rgba(255, 255, 255, 0.5);
      border-right: 0;
      border-bottom: 0;
    }
  }
  li + li {
    margin-top: 4px;
  }
}
.colorBlock {
  width: 8px;
  height: 8px;
  margin-left: 15px;
  margin-right: 8px;
}
.bg1 {
  background: #5c7cea;
}
.bg2 {
  background: #00bac5;
}
.bg3 {
  background: #d098cb;
}
.bg4 {
  background: #fd742f;
}
.bg5 {
  background: #ffaa39;
}
.bg6 {
  background: #405ae9;
}
.color1 {
  color: #5c7cea;
}
.color2 {
  color: #00bac5;
}
.color3 {
  color: #d098cb;
}
.color4 {
  color: #fd742f;
}
.color5 {
  color: #ffaa39;
}
.color6 {
  color: #405ae9;
}
.myProgress {
  display: inline-block;
  height: 10px;
  background: rgba(255, 255, 255, 0.1);
  position: relative;
  .inner {
    height: 4px;
    position: absolute;
    top: 3px;
  }
  .inner_color1 {
    background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
  }
  .inner_color2 {
    background: linear-gradient(270deg, #d098cb 0%, rgba(205, 146, 200, 0.2) 100%);
  }
  .inner_color3 {
    background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
  }
  .inner_color4 {
    background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
  }
}
.myProgress1 {
  &::before {
    content: "";
    position: absolute;
    top: -14px;
    height: 38px;
    border-left: 1px solid #00bac5;
    opacity: 0.5;
  }
}
.myProgress1.color2::before {
  border-left-color: #00bac5;
}
.myProgress1.color3::before {
  border-left: 1px solid #d098cb;
}
.keyPersonnelStatisticsChart {
  position: relative;
  &::after {
    content: "";
    position: absolute;
    width: 20px;
    height: 1px;
    background: #fff;
    top: 7px;
    left: 85px;
  }
  &.noAfter::after {
    opacity: 0;
  }
}
</style>
<style lang="scss">
.sidebar_radio_group {
  .el-radio-button__original-radio:checked + .el-radio-button__inner {
    background: rgba(0, 186, 197, 0.2);
    border-color: #00bac5 !important;
    color: #3ff5ff;
  }
  .el-radio-button__inner {
    padding: 2px 6px;
    border-radius: 0px !important;
    background: transparent;
    border-color: #417174 !important;
    color: #417174;
    line-height: 12.5px;
  }
}
.sidebar_select {
  .el-input .el-select__caret {
    color: #417174;
  }
  .el-input__inner,
  .el-input,
  .el-input__wrapper {
    background-color: transparent !important;
    border-radius: 0px !important;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .el-input__wrapper {
    padding-left: 6px;
    padding-right: 6px;
    // background-color: transparent !important;
    box-shadow: 0 0 0 1px #417174 inset !important;
    &:hover {
      box-shadow: 0 0px 0px 0px !important;
    }
  }
  .el-input__inner {
    height: 16px;
    font-size: 11px;
    color: #3ff5ff !important;
  }
}
</style>

5、折现、柱状融合图
image.png

<!-- 案事件统计详情 -->
<template>
  <div class="sidebar_right_caseEvent" v-if="sidebarType == 'caseEvent'">
    <!-- 社会治安 -->
    <div style="position: relative">
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>社会治安
      </div>
      <p class="color2 dataTotal" style="position: absolute; top: 0; right: 10px; font-size: 18px">{{ JSK.formatNumberWithCommas(caseEvent_total) }}</p>
      <el-table
        :data="caseEventTypeList"
        style="width: 100%; margin-top: 16px"
        :header-cell-style="{
          padding: 0,
          height: '24px',
          lineHeight: '24px',
          color: '#3FF5FF',
          background: '#205767',
          fontSize: '12px',
          fontWeight: 'normal',
          borderBottom: '2px solid #000',
          borderRight: '2px solid #000',
        }"
        :cell-style="{
          padding: 0,
          height: '24px',
          lineHeight: '24px',
          fontSize: '12px',
          color: '#fff',
          borderBottom: '2px solid #000',
          borderRight: '2px solid #000',
        }"
        :row-style="setRowStyle"
        class="orgDataTable"
      >
        <el-table-column prop="policeSituation" label="类型" show-overflow-tooltip
          ><template #default="scope">
            <span>{{ scope.row.policeSituation == 1 ? "警情" : "非警情" }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="total" label="数量" align="center" show-overflow-tooltip />
        <el-table-column prop="increment" label="本月新增数" align="center" show-overflow-tooltip />
      </el-table>
    </div>
    <!-- 矛盾纠纷数据分析 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷数据分析
      </div>

      <v-chart style="margin-top: 13px" ref="caseEventCompareChartRef" class="schart" :option="caseEventCompareChartOptions" :style="{ width: '290px', height: '188px' }"></v-chart>
    </div>

    <!-- 矛盾纠纷化解情况 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷化解情况
      </div>
      <div class="flex justify-between" style="margin-top: 14px">
        <el-radio-group v-model="dateClassKey_conflictResolution" size="small" class="sidebar_radio_group" @change="setOption_conflictResolutionChart">
          <el-radio-button v-for="item in dateClass" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
        </el-radio-group>
        <el-select v-model="contradictionTypeVal" style="width: 80px" class="sidebar_select" @change="setOption_conflictResolutionChart">
          <el-option v-for="item in contradictionType" :key="item.enums" :label="item.name" :value="item.enums" />
        </el-select>
      </div>
      <v-chart style="margin-top: 13px" ref="conflictResolutionChartRef" class="schart" :option="conflictResolutionChartOptions" :style="{ width: '290px', height: '188px' }"></v-chart>
    </div>

    <!-- 矛盾纠纷占比及下级分类占比 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷占比及下级分类占比
      </div>
      <div class="flex" style="align-items: center; margin-top: 18px; position: relative">
        <el-popover :offset="0" effect="dark" :visible="popoverVisible" :width="290" placement="bottom-start" :show-arrow="false" trigger="hover" popper-class="subContradictionRatioPopper">
          <template #reference>
            <!-- 矛盾纠纷占比 饼图 -->
            <v-chart
              ref="contradictionRatioChartRef"
              class="schart"
              :option="contradictionRatioChartOptions"
              :style="{ width: '74px', height: '74px' }"
              @mousemove="contradictionRatioChart_mousemove"
              @globalout="contradictionRatioChart_globalout"
            ></v-chart>
          </template>
          <!-- 下级分类占比 饼图 -->
          <div>
            <div class="title" style="color: #fff; margin-top: 0">
              <el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷占比及下级分类占比
            </div>
            <v-chart ref="subContradictionRatioChartRef" class="schart" :option="subContradictionRatioChartOptions" :style="{ width: '266px', height: '137px' }"></v-chart>
          </div>
        </el-popover>
        <ul class="ageGroup">
          <li v-for="(item, index) in contradictionRatioList" :key="item.name">
            <span :class="['colorBlock', 'bg' + ((index % 6) + 1)]"></span
            ><span style="width: 72px; margin-right: 10px" :class="['text_overflow_ellipsis', index == contradictionRatioChartSelectIndex ? 'color' + ((index % 6) + 1) : '']">{{ item.name }}</span>
            <span :class="['color' + ((index % 6) + 1)]" style="width: 48px; margin-right: 13px">{{ (item.proportion * 100).toFixed(2) + "%" }}</span>
            <span :class="['color' + ((index % 6) + 1)]" style="width: 48px" class="text_overflow_ellipsis">{{ JSK.formatNumberWithCommas(item.total) }}</span>
          </li>
        </ul>
      </div>
    </div>
    <!-- 次生、升级、反复 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>次生、升级、反复
      </div>
      <p style="margin-top: 16px">
        次生、升级、反复占比: <span class="color5">{{ caseUp_proportion }}</span>
      </p>
      <p style="margin: 6px 0 12px 0">
        总数本周新增
        <i
          :class="['iconfont', caseUp_WeekAdd >= 0 ? 'icon-shangzhang' : 'icon-xiajiang']"
          :style="{ fontSize: '10px', margin: ' 0 2px 0 6px', color: caseUp_WeekAdd >= 0 ? '#1FC61E' : 'red' }"
        /><span :style="{ color: caseUp_WeekAdd >= 0 ? '#1FC61E' : 'red' }">{{ Math.abs(caseUp_WeekAdd) }}</span>
      </p>

      <el-row :gutter="2" class="scroll-title">
        <el-col v-for="item in caseUp_columns" :key="item.prop" :span="item.span"
          ><span :style="{ textAlign: item.align }">{{ item.label }}</span>
        </el-col>
      </el-row>
      <div style="width: 100%; max-height: 125px; overflow: hidden; margin-top: 2px">
        <Vue3SeamlessScroll hover-stop="true" :list="caseUp_List" hover limitScrollNum="5" :step="0.1">
          <el-row v-for="item in caseUp_List" :key="item.uuid" :gutter="2" class="scroll-item">
            <el-col v-for="item1 in caseUp_columns" :key="item1.prop" :span="item1.span"
              ><span class="text-overflow" :style="{ textAlign: item1.align }" :title="item1.prop == 'eventTime' ? $filters.time(item[item1.prop], 'yyyy.MM.dd') : item[item1.prop]">{{
                item1.prop == "eventTime" ? $filters.time(item[item1.prop], "yyyy.MM.dd") : item[item1.prop] || "-"
              }}</span>
            </el-col>
          </el-row>
        </Vue3SeamlessScroll>
        <div v-if="!caseUp_List || caseUp_List.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>

        <!-- <el-table
            :data="caseUp_List"
            style="width: 100%"
            :header-cell-style="{
              padding: 0,
              height: '24px',
              lineHeight: '24px',
              color: '#3FF5FF',
              background: '#205767',
              fontSize: '12px',
              fontWeight: 'normal',
              borderBottom: '2px solid #000',
              borderRight: '2px solid #000',
            }"
            :cell-style="{
              padding: 0,
              height: '24px',
              lineHeight: '24px',
              fontSize: '12px',
              color: '#fff',
              borderBottom: '2px solid #000',
              borderRight: '2px solid #000',
            }"
            :row-style="setRowStyle"
            :show-header="false"
            class="orgDataTable"
          >
            <el-table-column prop="eventTime" label="日期" show-overflow-tooltip>
              <template #default="scope">
                <span>{{ $filters.time(scope.row.eventTime, "yyyy.MM.dd") || "-" }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="eventStage" label="事件阶段" align="center" show-overflow-tooltip width="53px">
              <template #default="scope">
                <span>{{ scope.row.eventStage || "-" }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="directExpression" label="直接表现" align="center" show-overflow-tooltip width="53px">
              <template #default="scope">
                <span>{{ scope.row.directExpression || "-" }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="secondaryImpact" label="次生影响" align="center" show-overflow-tooltip width="53px">
              <template #default="scope">
                <span>{{ scope.row.secondaryImpact || "-" }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="responseMeasure" label="应对措施" align="center" show-overflow-tooltip width="53px">
              <template #default="scope">
                <span>{{ scope.row.responseMeasure || "-" }}</span>
              </template>
            </el-table-column>
          </el-table> -->
      </div>
    </div>
    <!-- 高频高发事件Top3 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>高频高发事件Top3
      </div>
      <!-- <el-scrollbar height="106px" style="margin-top: 20px"> -->
      <ul
        style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
        :class="['barChart', highFrequencyEventsList.length == 1 ? 'oneRow' : highFrequencyEventsList.length < 5 ? 'static' : '']"
      >
        <Vue3SeamlessScroll hover-stop="true" :list="highFrequencyEventsList" hover limitScrollNum="5" :step="0.1">
          <li class="row" v-for="(item, index) in highFrequencyEventsList" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.total)">
            <span class="lable text_overflow_ellipsis" style="width: 80px; min-width: 80px; margin-right: 5px">{{ item.name }}</span>
            <div class="myProgress myProgress1 color1" style="width: 150px; min-width: 150px; margin-right: 8px">
              <span class="inner inner_color1" :style="{ width: getProportion(item.total, highFrequencyEvents_total) }"></span>
            </div>
            <span class="proportionValue text_overflow_ellipsis" style="color: #7f94ff"> {{ JSK.formatNumberWithCommas(item.total) }}</span>
          </li>
        </Vue3SeamlessScroll>
      </ul>
      <div v-if="!highFrequencyEventsList || highFrequencyEventsList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
      <!-- </el-scrollbar> -->
    </div>

    <!-- 各街镇矛盾纠纷发生数 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>各街镇矛盾纠纷发生数
      </div>
      <ul
        style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
        :class="['barChart', streetTownStatisticsList.length == 1 ? 'oneRow' : streetTownStatisticsList.length < 5 ? 'static' : '']"
      >
        <Vue3SeamlessScroll hover-stop="true" :list="streetTownStatisticsList" hover limitScrollNum="5" :step="0.1">
          <li class="row" v-for="(item, index) in streetTownStatisticsList" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
            <span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
            <div class="myProgress myProgress1 color2" style="width: 128px; min-width: 128px; margin-right: 8px">
              <span class="inner inner_color4" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
            </div>
            <span class="color2" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
            <span class="proportionValue color2 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
          </li>
        </Vue3SeamlessScroll>
      </ul>
      <div v-if="!streetTownStatisticsList || streetTownStatisticsList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
    </div>

    <!-- 矛盾纠纷涉重点人员 -->
    <div>
      <div class="title">
        <el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷涉重点人员
      </div>
      <div class="flex justify-between" style="margin-top: 14px">
        <el-radio-group v-model="dateClassKey_focusPeopleStatistics" size="small" class="sidebar_radio_group" @change="getFocusPeopleStatisticsList">
          <el-radio-button v-for="item in dateClass" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
        </el-radio-group>
        <el-select v-model="focusPeopleStatisticsTypeVal" style="width: 80px" class="sidebar_select" @change="getFocusPeopleStatisticsList">
          <el-option v-for="item in focusPeopleStatisticsType" :key="item.enums" :label="item.name" :value="item.enums" />
        </el-select>
      </div>
      <v-chart style="margin-top: 13px" ref="focusPeopleStatisticsChartRef" class="schart" :option="focusPeopleStatisticsChartOptions" :style="{ width: '290px', height: '160px' }"></v-chart>
    </div>

    <div style="height: 20px"></div>
  </div>
</template>
<script setup>
import { onMounted, reactive, ref, nextTick, watch, defineProps, defineExpose, computed } from "vue";
import { CaretRight } from "@element-plus/icons-vue";
import JSK from "@/utils/JSK";
import {
  policeSituationNum,
  yearOnYearStatistics,
  resolveSituation,
  contradictionRatio,
  subContradictionRatio,
  secondaryOrUpgradeStatistics,
  topN,
  streetTownStatistics,
  focusPeopleStatistics,
} from "@/api/map_sidebar.js";
import cloneDeep from "lodash/cloneDeep";
import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
const props = defineProps({
  sidebarType: "",
  // data: {
  //   type: Object,
  //   default: {},
  // },
  orgData: {
    type: Object,
    default: {},
  },
});
const dateClass = [
  { label: "近一周", value: "week" },
  { label: "近一月", value: "month" },
  { label: "近一季", value: "quarter" },
  { label: "近一年", value: "year" },
];
// 矛盾分类列表
const contradictionType = window.$dicts.getEnums("CaseEvent");

// 矛盾纠纷化解情况  日周月年关键字
const dateClassKey_conflictResolution = ref("week");
const contradictionTypeVal = ref("01");

const caseEventCompareChartRef = ref(null);
const conflictResolutionChartRef = ref(null);
const contradictionRatioChartRef = ref(null);
const subContradictionRatioChartRef = ref(null);

// 社会治安 总数
const caseEvent_total = computed(() => {
  return caseEventTypeList.value.reduce((prev, cur) => {
    return prev + cur.total;
  }, 0);
});

// 高频高发事件 总数
const highFrequencyEvents_total = computed(() => {
  return highFrequencyEventsList.value.reduce((prev, cur) => {
    return prev + cur.total;
  }, 0);
});
// 矛盾纠纷 总数
// const contradictionRatio_total = computed(() => {
//   return props.data.contradictionRatioList.reduce((prev, cur) => {
//     return prev + cur.value;
//   }, 0);
// });

let caseEventTypeList = ref([]); //社会治安列表
let contradictionRatioList = ref([]); //矛盾纠纷占比列表
let caseUp_proportion = ref(""); // 次生、升级、反复占比
let caseUp_WeekAdd = ref(0); //次生、升级、反复 总数本周新增
let caseUp_List = ref([]); //次生、升级、反复 列表
let highFrequencyEventsList = ref([]); //高频高发事件Top3 列表
// 次生、升级、反复  表头列表
const caseUp_columns = [
  { prop: "eventTime", label: "日期", align: "left", span: 8 },
  { prop: "eventStage", label: "事件阶段", align: "center", span: 4 },
  { prop: "directExpression", label: "直接表现", align: "center", span: 4 },
  { prop: "secondaryImpact", label: "次生影响", align: "center", span: 4 },
  { prop: "responseMeasure", label: "应对措施", align: "center", span: 4 },
];

const conflictResolutionType = [
  { label: "化解量", key: "resolved" },
  { label: "未化解量", key: "unresolved" },
  { label: "化解率", key: "resolutionRate" },
  { label: "平均化解率", key: "avgResolutionRate" },
];
// 矛盾纠纷化解情况(折线、柱状图)
const setOption_conflictResolutionChart = () => {
  resolveSituation(props.orgData.key, dateClassKey_conflictResolution.value, contradictionTypeVal.value).then((res) => {
    let dataZoom;
    if (res.data.length > 12) {
      dataZoom = [
        // { type: "slider", show: false, disabled: false, startValue: 0, endValue: 12 },
        {
          type: "slider",
          show: true,
          left: "9%",
          bottom: 8,
          startValue: 0,
          endValue: 12,
          handleSize: 0, //两侧手柄大小
          showDetail: false,
          height: 0,
          borderColor: "transparent",
          showDataShadow: false,
          minValueSpan: 12,
          maxValueSpan: 12,
        },
        // { type: "inside", show: false, disabled: false },
      ];
    } else {
      dataZoom = [
        { type: "slider", show: false, disabled: true, endValue: res.data.length, bottom: 0 },
        // { type: "inside", show: false, disabled: true },
      ];
    }

    conflictResolutionChartRef.value &&
      conflictResolutionChartRef.value.setOption({
        dataZoom,
        xAxis: {
          data: res.data.map((item) => item.hour || item.date || item.month),
        },
        series: conflictResolutionType.map((item) => {
          let obj = {
            name: item.label,
          };

          obj.data = res.data.map((item1) => {
            if (item.key == "resolutionRate" || item.key == "avgResolutionRate") {
              return (item1[item.key] * 100).toFixed(2);
            } else {
              return item1[item.key];
            }
          });
          return obj;
        }),
      });
  });

  // let data_ = props.data.conflictResolutionList[dateClassKey_conflictResolution.value];
  // conflictResolutionChartRef.value &&
  //   conflictResolutionChartRef.value.setOption({
  //     xAxis: {
  //       data: data_.time,
  //     },
  //     series: data_.dataList.map((item) => {
  //       return { data: item };
  //     }),
  //   });
};
// 各街镇矛盾纠纷发生数列表
const streetTownStatisticsList = ref([]);
// 矛盾纠纷涉重点人员  日周月年关键字
const dateClassKey_focusPeopleStatistics = ref("week");
const focusPeopleStatisticsTypeVal = ref("01");
// 矛盾纠纷涉重点人员  分类列表
const focusPeopleStatisticsType = window.$dicts.getEnums("KeyPersonnelClass");
// 获取矛盾纠纷涉重点人员列表
const getFocusPeopleStatisticsList = () => {
  focusPeopleStatistics(props.orgData.key, dateClassKey_focusPeopleStatistics.value, focusPeopleStatisticsTypeVal.value).then((res) => {
    focusPeopleStatisticsChartRef.value &&
      focusPeopleStatisticsChartRef.value.setOption({
        xAxis: {
          data: res.data.map((item) => item.name),
        },
        series: { data: res.data.map((item) => item.num) },
      });
  });
};
watch(
  () => props.orgData,
  (val) => {
    if (val && props.sidebarType == "caseEvent") {
      // 社会治安
      policeSituationNum(val.key).then((res) => {
        caseEventTypeList.value = res.data;
      });
      // 矛盾纠纷数据分析(折线图)
      yearOnYearStatistics(val.key).then((res) => {
        //平均发生数数据
        let average = {
          year: "平均发生数",
          detail: [],
        };
        let averageObj = {};
        res.data.forEach((item) => {
          item.detail.forEach((item1) => {
            if (averageObj[item1.name]) {
              averageObj[item1.name].total = averageObj[item1.name].total + item1.total;
            } else {
              averageObj[item1.name] = cloneDeep(item1);
            }
          });
        });
        res.data[0].detail.forEach((item) => {
          averageObj[item.name].total = (averageObj[item.name].total / res.data.length).toFixed(2);
          average.detail.push(averageObj[item.name]);
        });
        res.data.push(average);
        caseEventCompareChartRef.value &&
          caseEventCompareChartRef.value.setOption({
            legend: {
              data: res.data.map((item) => {
                let obj = {
                  name: item.year,
                  itemStyle: {
                    opacity: "0",
                  },
                };
                if (item.year == "平均发生数") {
                  obj.lineStyle = {
                    type: [1.7, 1.7],
                  };
                }
                return obj;
              }),
            },
            xAxis: { data: res.data[0].detail.map((item) => item.name) },
            series: res.data.map((item) => {
              let obj = { name: item.year };
              obj.data = item.detail.map((item) => item.total);

              return obj;
            }),
          });
      });
      // 矛盾纠纷化解情况(折线、柱状图)
      setOption_conflictResolutionChart();
      // 矛盾纠纷占比 饼图
      contradictionRatio(val.key).then((res) => {
        contradictionRatioList.value = res.data;
        contradictionRatioChartRef.value &&
          contradictionRatioChartRef.value.setOption({
            series: [
              {
                data: res.data.map((item) => {
                  return Object.assign(item, { value: item.total });
                }),
              },
            ],
          });
      });
      // 次生、升级、反复  表格数据
      secondaryOrUpgradeStatistics(val.key).then((res) => {
        caseUp_proportion.value = (res.data.proportion * 100).toFixed(2) + "%";
        caseUp_WeekAdd.value = res.data.increment;
        caseUp_List.value = res.data.list;
      });
      // 高频高发事件Top3
      topN(val.key).then((res) => {
        highFrequencyEventsList.value = res.data;
      });

      // 各街镇矛盾纠纷发生数
      streetTownStatistics(val.key).then((res) => {
        streetTownStatisticsList.value = res.data;
      });
      getFocusPeopleStatisticsList();
    }
  },
  { deep: true, immediate: true }
);

// watch(
//   () => props.data,
//   (val) => {
//     if (val && props.sidebarType == "caseEvent") {
//       nextTick(() => {
// 社会治安-矛盾纠纷数据分析(折线图)
// caseEventCompareChartRef.value &&
//   caseEventCompareChartRef.value.setOption({
//     legend: { data: props.data.caseEventCompareList.map((item) => item.time) },
//     xAxis: { data: props.data.caseEventCompareList[0].label },
//     series: props.data.caseEventCompareList.map((item) => {
//       return {
//         name: item.time,
//         data: item.nums,
//       };
//     }),
//   });
// 矛盾纠纷化解情况(折线、柱状图)
// setOption_conflictResolutionChart();
// 矛盾纠纷占比 饼图
// contradictionRatioChartRef.value &&
//   contradictionRatioChartRef.value.setOption({
//     series: [
//       {
//         data: val.contradictionRatioList.map((item) => {
//           return { value: item.value };
//         }),
//       },
//     ],
//   });
//       });
//     }
//   },
//   { deep: true, immediate: true }
// );

const caseEventCompareChartOptions = ref({
  tooltip: {
    trigger: "axis",
    extraCssText: "width:auto;height:auto",
    confine: true,
  },
  legend: {
    textStyle: {
      fontSize: 11,
      color: "#fff",
    },
    left: "7%",
    itemWidth: 12,
    itemHeight: 4,
    itemGap: 14,
    data: [],
  },
  grid: {
    left: "3%",
    right: "0%",
    bottom: "3%",
    top: "30%",
    containLabel: true,
  },

  xAxis: {
    type: "category",
    // boundaryGap: true,
    axisLabel: { color: "#fff", fontSize: 9, interval: -10, rotate: 35 },
    axisLine: {
      show: true,
      lineStyle: {
        // 使用深浅的间隔色
        color: "#6C8097",
      },
    },
    axisTick: {
      alignWithLabel: true,
    },
    data: [],
  },
  yAxis: {
    name: "单位(件)",
    nameTextStyle: {
      fontSize: 10,
      padding: [2, 0, -2, 0],
    },
    axisLine: {
      show: false,
    },
    axisTick: {
      show: false,
    },
    splitLine: {
      show: true, // 先隐藏默认的分割线
      lineStyle: {
        // 使用深浅的间隔色
        color: "rgba(108,128,151,0.3)",
      },
    },
    axisLabel: {
      color: "#999",
      fontSize: 10,
      margin: 3,
      padding: [0, 0, 10, 0],
    },
  },
  series: [
    {
      name: "",
      type: "line",
      // stack: "Total",//是否堆叠
      symbolSize: 0,
      itemStyle: {
        color: "#3FF5FF",
      },
      data: [],
    },
    {
      name: "",
      type: "line",
      // stack: "Total",//是否堆叠
      symbolSize: 0,
      itemStyle: {
        color: "#7F94FF",
      },
      data: [],
    },
    {
      name: "",
      type: "line",
      // stack: "Total",//是否堆叠
      symbolSize: 0,
      itemStyle: {
        color: "#D098CB",
      },
      data: [],
    },
    {
      name: "",
      type: "line",
      // stack: "Total",//是否堆叠
      symbolSize: 0,
      lineStyle: {
        type: [2, 2],
      },
      itemStyle: {
        color: "#FFAA39",
      },
      data: [],
    },
  ],
});
const conflictResolutionChartOptions = ref({
  tooltip: {
    trigger: "axis",
    extraCssText: "width:auto;height:auto",
    confine: true,
  },
  legend: {
    textStyle: {
      fontSize: 11,
      color: "#fff",
    },
    itemWidth: 12,
    itemHeight: 4,
    itemGap: 14,
    data: [
      {
        name: "化解量",
        icon: "rect",
      },
      {
        name: "未化解量",
        icon: "rect",
      },
      {
        name: "化解率",
        itemStyle: {
          opacity: "0",
        },
      },
      {
        name: "平均化解率",
        lineStyle: {
          type: [1.7, 1.7],
        },
        itemStyle: {
          opacity: "0",
        },
      },
    ],
  },

  grid: {
    left: "3%",
    right: "0%",
    bottom: "3%",
    top: "30%",
    containLabel: true,
  },
  xAxis: {
    type: "category",
    axisLabel: { color: "#fff", fontSize: 9, interval: 0, rotate: 40 },
    axisLine: {
      show: true,
      lineStyle: {
        // 使用深浅的间隔色
        color: "#6C8097",
      },
    },
    axisTick: {
      alignWithLabel: true,
    },
    data: [],
  },

  yAxis: [
    {
      type: "value",
      name: "单位(件)",
      nameTextStyle: {
        fontSize: 10,
        padding: [2, 0, -2, 0],
      },

      splitLine: {
        show: true, // 先隐藏默认的分割线
        lineStyle: {
          // 使用深浅的间隔色
          color: "rgba(108,128,151,0.3)",
        },
      },
    },
    {
      type: "value",
      name: "",
      splitLine: false,
      axisLabel: {
        formatter: "{value}%",
      },
    },
  ],
  series: [
    {
      name: "化解量",
      type: "bar",
      stack: "Ad",
      barWidth: 9,
      itemStyle: {
        color: "#00BAC5",
      },
      emphasis: {
        focus: "series",
      },
      data: [],
    },
    {
      name: "未化解量",
      type: "bar",
      stack: "Ad",
      barWidth: 9,
      itemStyle: {
        color: "#D098CB",
      },
      emphasis: {
        focus: "series",
      },
      data: [],
    },
    {
      name: "化解率",
      type: "line",
      symbolSize: 0,
      yAxisIndex: 1,
      data: [],
      itemStyle: {
        color: "#7F94FF",
      },
      tooltip: {
        valueFormatter: function (value) {
          return value + "%";
        },
      },
    },
    {
      name: "平均化解率",
      type: "line",
      symbolSize: 0,
      yAxisIndex: 1,
      lineStyle: {
        type: [2, 2],
      },
      itemStyle: {
        color: "#FFAA39",
      },
      data: [],
      tooltip: {
        valueFormatter: function (value) {
          return value + "%";
        },
      },
    },
  ],
});

const contradictionRatioChartOptions = ref({
  tooltip: {
    show: false,
    trigger: "item",
  },
  color: ["#5c7cea", "#00bac5", "#d098cb", "#fd742f", "#ffaa39", "#405ae9"],
  series: [
    {
      type: "pie",
      radius: ["50%", "80%"],
      center: ["47%", "50%"],
      itemStyle: {
        borderRadius: 0,
        borderColor: "#fff",
        borderWidth: 0,
      },
      label: {
        show: false,
      },
      emphasis: {
        scale: true, //禁止触摸缩放
      },
      labelLine: {
        show: false,
      },
      data: [],
    },
  ],
});
const subContradictionRatioChartOptions = ref({
  tooltip: {
    show: false,
    trigger: "item",
  },
  color: ["#5C7CEA", "#00BAC5", "#D098CB", "#CD6C27", "#FFAA39"],
  series: [
    {
      type: "pie",
      radius: ["28%", "45%"],
      center: ["50%", "50%"],
      itemStyle: {
        borderRadius: 0,
        borderColor: "#fff",
        borderWidth: 0,
      },

      label: {
        width: 120,
        height: 12,
        formatter: function (params) {
          return `{before|}{block${params.dataIndex + 1}|}{after|}\n{down|} {pro|${params.data.name}}\n{val${params.dataIndex + 1}|${(params.data.proportion * 100).toFixed(2)}%} {val${
            params.dataIndex + 1
          }|${params.data.value}}`;
        },
        rich: {
          before: {
            padding: [0, -4, 0, 0],
          },
          after: {
            padding: [0, -4, 0, 0],
          },
          down: {
            padding: [10, 0, -20, 0],
          },
          circle: {
            backgroundColor: "auto", //背景
            borderColor: "auto", //环颜色等于对应的部分颜色
            borderRadius: 4, //环的半径
            borderWidth: 1, //环的宽度
            width: 1, //强制撑开内容
            height: 1,
          },
          pro: {
            color: "#fff",
            fontSize: 12,
            lineHeight: 0,
            padding: [0, 6, 22, 4],
          },
          val1: {
            color: "#5C7CEA",
            padding: [-12, 0, 0, 0],
          },
          block1: {
            backgroundColor: "#5C7CEA",
            width: 8,
            height: 8,
            padding: [0, 0, 0, 0],
          },

          val2: {
            color: "#00BAC5",
            padding: [-12, 0, 0, 0],
          },
          block2: {
            backgroundColor: "#00BAC5",
            width: 8,
            height: 8,
          },

          val3: {
            color: "#D098CB",
            padding: [-12, 0, 0, 0],
          },
          block3: {
            backgroundColor: "#D098CB",
            width: 8,
            height: 8,
          },

          val4: {
            color: "#CD6C27",
            padding: [-12, 0, 0, 0],
          },
          block4: {
            backgroundColor: "#CD6C27",
            width: 8,
            height: 8,
          },

          val5: {
            color: "#FFAA39",
            padding: [-12, 0, 0, 0],
          },
          block5: {
            backgroundColor: "#FFAA39",
            width: 8,
            height: 8,
          },
        },
      },
      labelLine: {
        show: true,
        symbol: "circle",
        symbolSize: 12,
        symbolKeepAspect: true,

        lineStyle: {
          color: "#fff",
          cap: "circle",
          // dashOffset:
        },
      },
      emphasis: {
        scale: false, //禁止触摸缩放
      },

      data: [
        { value: 1048, name: "婚恋纠纷" },
        { value: 735, name: "家庭纠纷" },
        { value: 580, name: "邻里纠纷" },
        { value: 484, name: "其它一般性矛盾纠纷" },
      ],
    },
  ],
});
// const subContradictionRatio_Total = ref(800);
// 矛盾纠纷占比 选中的模块index
const contradictionRatioChartSelectIndex = ref(-1);
// 矛盾纠纷占比-饼图-鼠标进入事件
const contradictionRatioChart_mousemove = (param) => {
  if (contradictionRatioChartSelectIndex.value == param.dataIndex) return;
  contradictionRatioChartSelectIndex.value = param.dataIndex;
  popoverVisible.value = true;
  subContradictionRatio(props.orgData.key, param.data.firstDictCode).then((res) => {
    subContradictionRatioChartRef.value &&
      subContradictionRatioChartRef.value.setOption({
        series: [
          {
            data: res.data.map((item) => Object.assign(item, { value: item.total })),
            // data: props.data.contradictionRatioList[param.dataIndex].subContradictionRatioList,
          },
        ],
      });
  });
};
// 矛盾纠纷占比-饼图-鼠标离开事件
const contradictionRatioChart_globalout = () => {
  contradictionRatioChartSelectIndex.value = -1;
  popoverVisible.value = false;
};
const focusPeopleStatisticsChartRef = ref(null);
// 矛盾纠纷涉重点人员  - 折线图
const focusPeopleStatisticsChartOptions = ref({
  tooltip: {
    trigger: "axis",
    extraCssText: "width:auto;height:auto",
    confine: true,
  },

  grid: {
    left: "3%",
    right: "0%",
    bottom: "3%",
    top: "20%",
    containLabel: true,
  },

  xAxis: {
    type: "category",
    // boundaryGap: true,
    axisLabel: { color: "#fff", fontSize: 9, interval: -10, rotate: 35 },
    axisLine: {
      show: true,
      lineStyle: {
        // 使用深浅的间隔色
        color: "#6C8097",
      },
    },
    axisTick: {
      alignWithLabel: true,
    },
    data: [],
  },
  yAxis: {
    name: "单位(人)",
    nameTextStyle: {
      fontSize: 10,
      padding: [2, 0, -2, 0],
    },
    axisLine: {
      show: false,
    },
    axisTick: {
      show: false,
    },
    splitLine: {
      show: true, // 先隐藏默认的分割线
      lineStyle: {
        // 使用深浅的间隔色
        color: "rgba(108,128,151,0.3)",
      },
    },
    axisLabel: {
      color: "#999",
      fontSize: 10,
      margin: 3,
      padding: [0, 0, 10, 0],
    },
  },
  series: {
    name: "",
    type: "line",
    symbolSize: 0,
    itemStyle: {
      color: "#3FF5FF",
    },
    data: [],
  },
});

const popoverVisible = ref(false);
// 获取比例
const getProportion = (val, total) => {
  if (val && total) {
    return ((val / total) * 100).toFixed(2) + "%";
  } else {
    return 0 + "%";
  }
};
// 表单行样式
const setRowStyle = ({ row, rowIndex }) => {
  return {
    backgroundColor: rowIndex % 2 == 0 ? "#092F3A" : "#01222C",
  };
};
defineExpose({});
</script>
<style lang="scss" scoped>
.sidebar_right_caseEvent {
  padding: 0 12px;
}
.title {
  font-size: 12px;
  color: #00bac5;
  line-height: 17px;
  display: flex;
  align-items: center;
  margin-top: 20px;
  margin-left: -5px;
}

ul.barChart {
  li {
    display: flex;
    align-items: center;
    line-height: 24px;
    height: 24px;
    overflow: hidden;
  }
  &.static li {
    &:first-child {
      line-height: 12px;
      height: 17px;
      align-items: flex-start;
    }
    &:last-child {
      line-height: 12px;
      height: 17px;
      align-items: flex-end;
    }
  }
  &.oneRow li {
    line-height: 12px;
    height: 17px;
    align-items: center;
  }
}
.ageGroup {
  flex: 1;
  li {
    height: 18px;
    // width: 208px;
    background: rgba(255, 255, 255, 0.05);
    position: relative;
    display: flex;
    align-items: center;

    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 3px;
      height: 3px;
      border: 1px solid rgba(255, 255, 255, 0.5);
      border-right: 0;
      border-bottom: 0;
    }
  }
  li + li {
    margin-top: 4px;
  }
}
.colorBlock {
  width: 8px;
  height: 8px;
  margin-left: 15px;
  margin-right: 8px;
}
.bg1 {
  background: #5c7cea;
}
.bg2 {
  background: #00bac5;
}
.bg3 {
  background: #d098cb;
}
.bg4 {
  background: #fd742f;
}
.bg5 {
  background: #ffaa39;
}
.bg6 {
  background: #405ae9;
}
.color1 {
  color: #5c7cea;
}
.color2 {
  color: #00bac5;
}
.color3 {
  color: #d098cb;
}
.color4 {
  color: #fd742f;
}
.color5 {
  color: #ffaa39;
}
.color6 {
  color: #405ae9;
}
.myProgress {
  display: inline-block;
  height: 10px;
  background: rgba(255, 255, 255, 0.1);
  position: relative;
  .inner {
    height: 4px;
    position: absolute;
    top: 3px;
  }
  .inner_color1 {
    background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
  }
  .inner_color2 {
    background: linear-gradient(270deg, #d098cb 0%, rgba(205, 146, 200, 0.2) 100%);
  }
  .inner_color3 {
    background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
  }
  .inner_color4 {
    background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
  }
}
.myProgress1 {
  &::before {
    content: "";
    position: absolute;
    top: -14px;
    height: 38px;
    border-left: 1px solid #00bac5;
    opacity: 0.5;
  }
}
.myProgress1.color1::before {
  border-left-color: #5c7cea;
}
.myProgress1.color2::before {
  border-left-color: #00bac5;
}
.myProgress1.color3::before {
  border-left: 1px solid #d098cb;
}
.dataTotal {
  font-family: DINAlternate, DINAlternate;
  font-weight: bold;
  font-size: 18px;
}
.scroll-title {
  .el-col {
    height: 48px;

    & > span {
      padding: 0 10px;
      color: rgb(63, 245, 255);
      background: rgb(32, 87, 103);
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      box-sizing: border-box;
    }
  }
}
.scroll-item {
  height: 23px;
  margin-bottom: 2px;
}
.scroll-item:nth-child(2n) .el-col > span {
  width: 100%;
  display: inline-block;
  background: #01222c;
  line-height: 23px;
  padding: 0 10px;
  box-sizing: border-box;
}

.scroll-item:nth-child(2n + 1) .el-col > span {
  width: 100%;
  display: inline-block;
  background: #092f3a;
  line-height: 23px;
  padding: 0 10px;
  box-sizing: border-box;
}
.text-overflow {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>
<style lang="scss">
.sidebar_radio_group {
  .el-radio-button__original-radio:checked + .el-radio-button__inner {
    background: rgba(0, 186, 197, 0.2);
    border-color: #00bac5 !important;
    color: #3ff5ff;
  }
  .el-radio-button__inner {
    padding: 2px 6px;
    border-radius: 0px !important;
    background: transparent;
    border-color: #417174 !important;
    color: #417174;
    line-height: 12.5px;
  }
}
.sidebar_select {
  .el-input .el-select__caret {
    color: #417174;
  }
  .el-input__inner,
  .el-input,
  .el-input__wrapper {
    background-color: transparent !important;
    border-radius: 0px !important;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .el-input__wrapper {
    padding-left: 6px;
    padding-right: 6px;
    // background-color: transparent !important;
    box-shadow: 0 0 0 1px #417174 inset !important;
    &:hover {
      box-shadow: 0 0px 0px 0px !important;
    }
  }
  .el-input__inner {
    height: 16px;
    font-size: 11px;
    color: #3ff5ff !important;
  }
}
.custom-legend {
  background-color: #f0f0f0; /* 设置背景颜色 */
  border: 1px solid #ccc; /* 设置边框 */
  padding: 5px; /* 设置内边距 */
}
.subContradictionRatioPopper.el-popover.el-popper.is-dark {
  background: #1e2236;
  box-shadow: 0px 2px 4px 2px #000000;
}
</style>

6、滚动的水滴球
image.png

<!-- 安防设施详情 -->
<template>
  <div class="sidebar_right_securityEquipment" v-if="sidebarType == 'securityEquipment'">
    <div class="flex" style="flex-wrap: wrap; justify-content: center; margin-top: 4px">
      <div v-for="(item, index) in deviceFunctionList" :key="index" style="margin: 16px 0px; display: flex; flex-direction: column; align-items: center; width: 33.33%">
        <div class="Echart" :ref="(el) => setTableCompRefs(el, item.name)" :style="{ width: '70px', height: '70px' }"></div>
        <p style="text-align: center; margin-top: 10px">{{ item.name }}</p>
      </div>
      <div v-if="!deviceFunctionList || deviceFunctionList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
    </div>

    <!-- <div class="flex" style="flex-wrap: wrap; justify-content: center; margin-top: 4px">
      <div v-for="(item, index) in deviceFunctionList" :key="index" style="margin: 16px 0px; text-align: center; width: 33.33%">
        <el-progress type="circle" :percentage="(item.percent * 100).toFixed(2)" stroke-linecap="butt" color="#3CE0EB" :width="44" :stroke-width="4">
          <span>{{ (item.percent * 100).toFixed(2) + "%" }}</span>
        </el-progress>
        <p style="text-align: center; margin-top: 10px">{{ item.name }}</p>
      </div>
      <div v-if="!deviceFunctionList || deviceFunctionList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
    </div> -->
  </div>
</template>
<script setup>
import { onMounted, reactive, ref, nextTick, watch, defineProps, defineExpose, computed } from "vue";
import { CaretRight } from "@element-plus/icons-vue";
import JSK from "@/utils/JSK";
// import "echarts-liquidfill";
import { deviceRatio } from "@/api/map_sidebar.js";

const props = defineProps({
  sidebarType: "",
  // data: {
  //   type: Object,
  //   default: {},
  // },
  orgData: {
    type: Object,
    default: {},
  },
});
const tableCompRefs = ref({});
const deviceFunctionList = ref([]);
const setTableCompRefs = (el, label) => {
  if (el) {
    tableCompRefs.value[label] = el;
  }
};
var charts_ = {};
watch(
  () => props.orgData,
  (val) => {
    if (val && props.sidebarType == "securityEquipment") {
      deviceRatio(props.orgData.key).then((res) => {
        deviceFunctionList.value = res.data;
        nextTick(() => {
          for (let key in tableCompRefs.value) {
            var chart = echarts.init(tableCompRefs.value[key]);
            chart.setOption(eviceFunctionChartOptions.value);
            charts_[key] = chart;

            renderWaterPoloChart(key);
          }
        });
      });
    }
  },
  { deep: true, immediate: true }
);
// 渲染水球图
const renderWaterPoloChart = (key) => {
  let data_ = deviceFunctionList.value.filter((item) => item.name == key)[0];
  charts_[key] &&
    charts_[key].setOption({
      series: [
        { data: [data_.percent, data_.percent] },
        {
          data: [
            {
              name: "val1",
              value: (data_.percent * 100).toFixed(0), //外环数值
              labelLine: {
                show: false,
              },
              itemStyle: {
                //外环颜色
                color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [
                  {
                    offset: 0,
                    color: "rgba(227, 253, 255, 0)",
                  },
                  {
                    offset: 1,
                    color: "#00BAC5",
                  },
                ]),
              },
            },
            {
              //画中间的图标
              name: "",
              value: 0.8,
              labelLine: {
                show: false,
              },
              itemStyle: {
                color: "#00BAC5",
                normal: {
                  color: "#00BAC5",
                  borderColor: "#00BAC5",
                  borderWidth: 2, //外部圆小球大小
                  borderRadius: 1,
                },
              },
              label: {
                borderRadius: "100%",
              },
              emphasis: {
                labelLine: {
                  show: false,
                },
                itemStyle: {
                  color: "#00BAC5",
                },
              },
            },
            {
              //画剩余的刻度圆环
              name: "val2",
              value: ((1 - data_.percent) * 100).toFixed(0), //剩余的刻度圆环数值
              itemStyle: {
                color: "transparent",
              },
              label: {
                show: false,
              },
              labelLine: {
                show: false,
              },
              emphasis: {
                labelLine: {
                  show: false,
                },
                itemStyle: {
                  color: "#050038",
                },
              },
            },
          ],
        },
      ],
    });
};

const eviceFunctionChartOptions = ref({
  series: [
    {
      type: "liquidFill",
      radius: "80%",
      center: ["50%", "50%"],
      data: [0, 0], //水球占比 data个数代表波浪数[0.7, 0.7]
      color: ["rgb(94, 246, 255, 0.65)", "rgb(2, 151, 160, 0.8)"], //波浪的颜色
      backgroundStyle: {
        color: {
          type: "radial",
          x: 0.5,
          y: 0.5,
          r: 0.55,
          colorStops: [
            {
              offset: 0.5,
              color: "rgb(13, 15, 26) ", // 0% 处的颜色
            },
            {
              offset: 0.8,
              color: "rgb(40, 53, 69)", // 100% 处的颜色
            },
            {
              offset: 1,
              color: "rgb(68, 111, 124)", // 100% 处的颜色
            },
          ],
          globalCoord: false, // 缺省为 false
        },
      },
      label: {
        normal: {
          formatter: function (value) {
            if (!value.value) {
              return "加载中";
            } else {
              return (value.value * 100).toFixed(0) + "%";
            }
          },
          textStyle: {
            fontSize: 10, //中间文字大小
            color: "#fff",
          },
        },
      },
      outline: {
        show: false,
      },
    },
    {
      type: "pie",
      center: ["50%", "50%"],
      radius: ["90%", "94%"],
      hoverAnimation: false,
      data: [
        {
          name: "val1",
          value: 0, //外环数值60
          labelLine: {
            show: false,
          },
          itemStyle: {
            //外环颜色
            color: new echarts.graphic.LinearGradient(0, 0, 0.5, 1, [
              {
                offset: 0,
                color: "rgba(227, 253, 255, 0)",
              },
              {
                offset: 1,
                color: "#00BAC5",
              },
            ]),
          },
        },
        {
          //画中间的图标
          name: "",
          value: 0.8,
          labelLine: {
            show: false,
          },
          itemStyle: {
            color: "#00BAC5",
            normal: {
              color: "#00BAC5",
              borderColor: "#00BAC5",
              borderWidth: 2, //外部圆小球大小
              borderRadius: 1,
            },
          },
          label: {
            borderRadius: "100%",
          },
          emphasis: {
            labelLine: {
              show: false,
            },
            itemStyle: {
              color: "#00BAC5",
            },
          },
        },
        {
          //画剩余的刻度圆环
          name: "val2",
          value: 100, //剩余的刻度圆环数值40
          itemStyle: {
            color: "transparent",
          },
          label: {
            show: false,
          },
          labelLine: {
            show: false,
          },
          emphasis: {
            labelLine: {
              show: false,
            },
            itemStyle: {
              color: "#050038",
            },
          },
        },
      ],
    },
  ],
});

defineExpose({});
</script>
<style lang="scss" scoped>
.sidebar_right_securityEquipment {
  padding: 0 12px;

  .e-schart {
    position: relative;
  }
  .Echart {
    position: relative;
  }
}

.title {
  font-size: 12px;
  color: #00bac5;
  line-height: 17px;
  display: flex;
  align-items: center;
  margin-top: 20px;
  margin-left: -5px;
}

ul.barChart {
  li {
    display: flex;
    align-items: center;
    line-height: 24px;
    height: 24px;
    overflow: hidden;
  }
  &.static li {
    &:first-child {
      line-height: 12px;
      height: 17px;
      align-items: flex-start;
    }
    &:last-child {
      line-height: 12px;
      height: 17px;
      align-items: flex-end;
    }
  }
  &.oneRow li {
    line-height: 12px;
    height: 17px;
    align-items: center;
  }
}
.ageGroup {
  flex: 1;

  li {
    height: 18px;
    // width: 208px;
    background: rgba(255, 255, 255, 0.05);
    position: relative;
    display: flex;
    align-items: center;

    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 3px;
      height: 3px;
      border: 1px solid rgba(255, 255, 255, 0.5);
      border-right: 0;
      border-bottom: 0;
    }
  }

  li + li {
    margin-top: 4px;
  }
}

.colorBlock {
  width: 8px;
  height: 8px;
  margin-left: 15px;
  margin-right: 8px;
}

.bg1 {
  background: #5c7cea;
}

.bg2 {
  background: #00bac5;
}

.bg3 {
  background: #d098cb;
}

.bg4 {
  background: #fd742f;
}

.bg5 {
  background: #ffaa39;
}

.bg6 {
  background: #405ae9;
}

.color1 {
  color: #5c7cea;
}

.color2 {
  color: #00bac5;
}

.color3 {
  color: #d098cb;
}

.color4 {
  color: #fd742f;
}

.color5 {
  color: #ffaa39;
}

.color6 {
  color: #405ae9;
}

.myProgress {
  display: inline-block;
  height: 10px;
  background: rgba(255, 255, 255, 0.1);
  position: relative;

  .inner {
    height: 4px;
    position: absolute;
    top: 3px;
  }

  .inner_color1 {
    background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
  }

  .inner_color2 {
    background: linear-gradient(270deg, #d098cb 0%, rgba(205, 146, 200, 0.2) 100%);
  }

  .inner_color3 {
    background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
  }

  .inner_color4 {
    background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
  }
}

.myProgress1 {
  &::before {
    content: "";
    position: absolute;
    top: -14px;
    height: 38px;
    border-left: 1px solid #00bac5;
    opacity: 0.5;
  }
}

.myProgress1.color2::before {
  border-left-color: #00bac5;
}

.myProgress1.color3::before {
  border-left: 1px solid #d098cb;
}
</style>
<style lang="scss">
.sidebar_radio_group {
  .el-radio-button__original-radio:checked + .el-radio-button__inner {
    background: rgba(0, 186, 197, 0.2);
    border-color: #00bac5 !important;
    color: #3ff5ff;
  }

  .el-radio-button__inner {
    padding: 2px 6px;
    border-radius: 0px !important;
    background: transparent;
    border-color: #417174 !important;
    color: #417174;
    line-height: 12.5px;
  }
}

.sidebar_select {
  .el-input .el-select__caret {
    color: #417174;
  }

  .el-input__inner,
  .el-input,
  .el-input__wrapper {
    background-color: transparent !important;
    border-radius: 0px !important;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .el-input__wrapper {
    padding-left: 6px;
    padding-right: 6px;
    // background-color: transparent !important;
    box-shadow: 0 0 0 1px #417174 inset !important;

    &:hover {
      box-shadow: 0 0px 0px 0px !important;
    }
  }

  .el-input__inner {
    height: 16px;
    font-size: 11px;
    color: #3ff5ff !important;
  }
}
</style>

西安小哥
1.3k 声望88 粉丝

thinking、doing、do better、do much better than today。exchange 、sharing、improve as quickly as possible。