pyt
2 天以前 e1b782bbac430d8e01d43d5f610a46e4a29f661e
feat
8个文件已修改
4个文件已添加
2725 ■■■■■ 已修改文件
H5/App.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/config/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/index/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/statistics/index.vue 2025 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/statistics/service.js 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/home/Group 2@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
management/config/env.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/config/routes.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/package.json 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/statistics/components/addAndEdit.jsx 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/statistics/index.jsx 558 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/statistics/service.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/App.vue
@@ -9,7 +9,6 @@
            trialNode.innerHTML = '试运行版本';
            document.body.appendChild(trialNode);
            // #endif
        },
        onShow: function() {
H5/config/index.js
@@ -1,5 +1,5 @@
export default {
    BASE_URL: 'https://huacheng.psciio.com',
    // BASE_URL: 'http://192.168.110.188:6194',
    // BASE_URL: 'https://huacheng.psciio.com',
    BASE_URL: 'http://192.168.110.111:6194',
    imageUrl: 'https://huacheng.psciio.com/api/huacheng-applets/common/uploadimages',
}
H5/pages/index/index.vue
@@ -71,7 +71,6 @@
                    <image class="w-79 h-77 ml-38" src="/static/home/img1.png" mode=""></image>
                    <text class="ml-37 font-bold">回访评价</text>
                </view> -->
                <view class="pt-38 pb-38 bg1 w-333 br-19 mt-19 flex a-center" @click="toStatistics">
                    <image class="w-81 h-77 ml-38" src="/static/home/img8.png" mode=""></image>
                    <text class="ml-37 font-bold">统计分析</text>
@@ -145,7 +144,7 @@
                <view class="lineBox mb-10"></view>
                <view style="height: 546rpx;overflow-y: auto;">
                    <view @tap.stop="changeRole(item)" v-for="(item,index) in userInfo.permissions" :key="index"
                        :class="item.identity == userInfo.identity && 'bgcolor2'"
                        :class="item.name == userInfo.roleName && 'bgcolor2'"
                        class="mt-38 bgcolor1 br-58 fs-31 ml-54 mr-62 py-37 txt-center">
                        <view>{{ item.name }}</view>
                    </view>
@@ -335,8 +334,10 @@
                    this.changeRolePopup = false
                    this.$refs.dongjiePop.showPopup()
                } else {
                    console.log(e);
                    changeIdentity({
                        identity: e.identity
                        identity: e.identity,
                        levelId: e.identity == 1 ? '' : e.levelId
                    }).then(res => {
                        this.$refs.uToast.show({
                            type: "success",
H5/pages/statistics/index.vue
@@ -1,179 +1,166 @@
 <template>
     <view class="content">
         <view v-if="userInfo.isAdmin == 1" class="flex a-center pl-31 pr-31 fs-31 color1 pt-38">
             <text class="mr-15">查看范围:</text>
             <view
                 class="h-77 flex a-center j-between flex1 pl-31 pr-23 border1 br-15 bgColor1" :class="!address && 'color2'">
                <view @click.top="selectPopup=true" class="flex1">
                    {{ address || '全部' }}
                </view>
                <u-icon class="shrink0" v-if="address" @click="clearAddress" name="close-circle"></u-icon>
                 <image v-else src="/static/down@2x.png" mode="aspectFill" class="w-31 h-31 shrink0"></image>
             </view>
         </view>
         <view class="fs-35 font-bold pt-38 ml-27">
             处理满意率
         </view>
         <view class="ml-29 mr-29 border2 br-15 mt-27 shadow1 flex j-between pl-19 pr-19 pb-35">
             <view class="mt-19 flex1">
                 <view class="flex a-center">
                     <view class="w-12 h-12 br-6 border3"></view>
                     <view class="fs-23 ml-15 color3">
                         总体满意率
                     </view>
                 </view>
                 <view class="fs-46 ml-27 mt-12 font-bold">
                     77%
                 </view>
             </view>
             <view class="flex1 flex j-between ml-50">
                 <view class="fs-23 mt-42 txt-center">
                     <view class="color6">
                         本月
                     </view>
                     <view class="fs-27 font-bold color5 mt-2">
                         88%
                     </view>
                 </view>
                 <view class="fs-23 mt-44">
                     <view class="color6">
                         同比上月
                     </view>
                     <view class="txt-aligin-r color4 font-bold mt-4">
                         +12%
                     </view>
                 </view>
             </view>
         </view>
         <view class="fs-35 font-bold mt-38 ml-27">
             诉求单统计
         </view>
         <view class="flex j-between a-center pl-29 pr-29 mt-27">
             <view class="pl-19 pr-21 shadow1 border2 pt-19 pb-42 flex1 br-15">
                 <view class="flex a-center">
                     <view class="w-12 h-12 br-6 border3"></view>
                     <view class="fs-23 ml-15 color3">
                         诉求单量总计
                     </view>
                 </view>
                 <view class="fs-46 mt-12 font-bold ml-27">
                     448451
                 </view>
                 <view class="flex j-between mt-21">
                     <view class="fs-23">
                         <view class="">
                             本月
                         </view>
                         <view class="fs-27 font-bold color3 mt-2">
                             4448
                         </view>
                     </view>
                     <view class="fs-23">
                         <view class="">
                             同比上月
                         </view>
                         <view class="font-bold color8 txt-aligin-r mt-4">
                             -12%
                         </view>
                     </view>
                 </view>
             </view>
             <view class="pl-19 pr-21 shadow1 border2 pt-19 pb-42 flex1 ml-31 br-15">
                 <view class="flex a-center">
                     <view class="w-12 h-12 br-6 border3"></view>
                     <view class="fs-23 ml-15 color3">
                         平均处理时间(天)
                     </view>
                 </view>
                 <view class="fs-46 mt-12 font-bold ml-27">
                     3.2
                 </view>
                 <view class="flex j-between mt-21">
                     <view class="fs-23">
                         <view class="">
                             本月
                         </view>
                         <view class="fs-27 font-bold color3 mt-2">
                             2.2
                         </view>
                     </view>
                     <view class="fs-23">
                         <view class="">
                             同比上月
                         </view>
                         <view class="font-bold color4 txt-aligin-r mt-4">
                             +12%
                         </view>
                     </view>
                 </view>
             </view>
         </view>
         <view class="flex mt-27 gap25 pl-29 pr-29">
             <view class="flex1 h-154 bgColor2 border4 shadow2 br-19 txt-center">
                 <view class="mt-37 fs-35 color4 font-bold">
                     4521
                 </view>
                 <view class="fs-23 mt-8 color3">
                     正在办理
                 </view>
             </view>
             <view class="flex1 h-154 bgColor3 border4 shadow2 br-19 txt-center">
                 <view class="mt-37 fs-35 color9 font-bold">
                     321
                 </view>
                 <view class="fs-23 mt-8 color3">
                     审核中
                 </view>
             </view>
             <view class="flex1 h-154 bgColor4 border4 shadow2 br-19 txt-center">
                 <view class="mt-37 fs-35 color10 font-bold">
                     6850
                 </view>
                 <view class="fs-23 mt-8 color3">
                     延期办理
                 </view>
             </view>
             <view class="flex1 h-154 bgColor5 border4 shadow2 br-19 txt-center">
                 <view class="mt-37 fs-35 color11 font-bold">
                     8451
                 </view>
                 <view class="fs-23 mt-8 color3">
                     已办结
                 </view>
             </view>
         </view>
         <view class="pb-35 ml-29 mr-29 border2 br-15 mt-27 shadow1 flex j-between pl-19 pr-19">
             <view class="mt-19 flex1">
                 <view class="flex a-center">
                     <view class="w-12 h-12 br-6 border3"></view>
                     <view class="fs-23 ml-15 color3">
                         超时办理
                     </view>
                 </view>
                 <view class="fs-46 ml-27 mt-12 font-bold">
                     6850
                 </view>
             </view>
             <view class="flex1 flex j-between ml-50">
                 <view class="fs-23 mt-42 txt-center">
                     <view class="color6">
                         本月
                     </view>
                     <view class="fs-27 font-bold color5 mt-2">
                         4448
                     </view>
                 </view>
                 <view class="fs-23 mt-44">
                     <view class="color6">
                         同比上月
                     </view>
                     <view class="txt-aligin-r color4 font-bold mt-4">
                         +12%
                     </view>
                 </view>
             </view>
         </view>
         <view class="pb-35 ml-29 mr-29 border2 br-15 mt-27 shadow1 flex j-between pl-19 pr-19">
<template>
  <view class="content">
    <view
      v-if="userInfo.isAdmin == 1"
      class="flex a-center pl-31 pr-31 fs-31 color1 pt-38"
    >
      <text class="mr-15">查看范围:</text>
      <view
        class="h-77 flex a-center j-between flex1 pl-31 pr-23 border1 br-15 bgColor1"
        :class="!address && 'color2'"
      >
        <view @click.top="selectPopup = true" class="flex1">
          {{ address || "全部" }}
        </view>
        <u-icon
          class="shrink0"
          v-if="address"
          @click="clearAddress"
          name="close-circle"
        ></u-icon>
        <image
          v-else
          src="/static/down@2x.png"
          mode="aspectFill"
          class="w-31 h-31 shrink0"
        ></image>
      </view>
    </view>
    <view class="fs-35 font-bold pt-38 ml-27"> 处理满意率 </view>
    <view
      class="ml-29 mr-29 border2 br-15 mt-27 shadow1 flex j-between pl-19 pr-19 pb-35"
    >
      <view class="mt-19 flex1">
        <view class="flex a-center">
          <view class="w-12 h-12 br-6 border3"></view>
          <view class="fs-23 ml-15 color3"> 总体满意率 </view>
        </view>
        <view class="fs-46 ml-27 mt-12 font-bold">
          {{ statisticsData.satisfaction.total }}%
        </view>
      </view>
      <view class="flex1 flex j-between ml-50">
        <view class="fs-23 mt-42 txt-center">
          <view class="color6"> 本月 </view>
          <view class="fs-27 font-bold color5 mt-2">
            {{ statisticsData.satisfaction.month }}%
          </view>
        </view>
        <view class="fs-23 mt-44">
          <view class="color6"> 同比上月 </view>
          <view class="txt-aligin-r color4 font-bold mt-4">
            {{ statisticsData.satisfaction.compare > 0 ? "+" : ""
            }}{{ statisticsData.satisfaction.compare }}%
          </view>
        </view>
      </view>
    </view>
    <view class="fs-35 font-bold mt-38 ml-27"> 诉求单统计 </view>
    <view class="flex j-between a-center pl-29 pr-29 mt-27">
      <view class="pl-19 pr-21 shadow1 border2 pt-19 pb-42 flex1 br-15">
        <view class="flex a-center">
          <view class="w-12 h-12 br-6 border3"></view>
          <view class="fs-23 ml-15 color3"> 诉求单量总计 </view>
        </view>
        <view class="fs-46 mt-12 font-bold ml-27">
          {{ statisticsData.demands.total }}
        </view>
        <view class="flex j-between mt-21">
          <view class="fs-23">
            <view class=""> 本月 </view>
            <view class="fs-27 font-bold color3 mt-2">
              {{ statisticsData.demands.month }}
            </view>
          </view>
          <view class="fs-23">
            <view class=""> 同比上月 </view>
            <view class="font-bold color8 txt-aligin-r mt-4">
              {{ statisticsData.demands.compare > 0 ? "+" : ""
              }}{{ statisticsData.demands.compare }}
            </view>
          </view>
        </view>
      </view>
      <view class="pl-19 pr-21 shadow1 border2 pt-19 pb-42 flex1 ml-31 br-15">
        <view class="flex a-center">
          <view class="w-12 h-12 br-6 border3"></view>
          <view class="fs-23 ml-15 color3"> 平均处理时间(天) </view>
        </view>
        <view class="fs-46 mt-12 font-bold ml-27">
          {{ statisticsData.processTime.total }}
        </view>
        <view class="flex j-between mt-21">
          <view class="fs-23">
            <view class=""> 本月 </view>
            <view class="fs-27 font-bold color3 mt-2">
              {{ statisticsData.processTime.month }}
            </view>
          </view>
          <view class="fs-23">
            <view class=""> 同比上月 </view>
            <view class="font-bold color4 txt-aligin-r mt-4">
              {{ statisticsData.processTime.compare > 0 ? "+" : ""
              }}{{ statisticsData.processTime.compare }}
            </view>
          </view>
        </view>
      </view>
    </view>
    <view class="flex mt-27 gap25 pl-29 pr-29">
      <view class="flex1 h-154 bgColor2 border4 shadow2 br-19 txt-center">
        <view class="mt-37 fs-35 color4 font-bold">
          {{ statisticsData.status.processing }}
        </view>
        <view class="fs-23 mt-8 color3"> 正在办理 </view>
      </view>
      <view class="flex1 h-154 bgColor3 border4 shadow2 br-19 txt-center">
        <view class="mt-37 fs-35 color9 font-bold">
          {{ statisticsData.status.reviewing }}
        </view>
        <view class="fs-23 mt-8 color3"> 审核中 </view>
      </view>
      <view class="flex1 h-154 bgColor4 border4 shadow2 br-19 txt-center">
        <view class="mt-37 fs-35 color10 font-bold">
          {{ statisticsData.status.delayed }}
        </view>
        <view class="fs-23 mt-8 color3"> 延期办理 </view>
      </view>
      <view class="flex1 h-154 bgColor5 border4 shadow2 br-19 txt-center">
        <view class="mt-37 fs-35 color11 font-bold">
          {{ statisticsData.status.completed }}
        </view>
        <view class="fs-23 mt-8 color3"> 已办结 </view>
      </view>
    </view>
    <view
      class="pb-35 ml-29 mr-29 border2 br-15 mt-27 shadow1 flex j-between pl-19 pr-19"
    >
      <view class="mt-19 flex1">
        <view class="flex a-center">
          <view class="w-12 h-12 br-6 border3"></view>
          <view class="fs-23 ml-15 color3"> 超时办理 </view>
        </view>
        <view class="fs-46 ml-27 mt-12 font-bold">
          {{ statisticsData.overtime.total }}
        </view>
      </view>
      <view class="flex1 flex j-between ml-50">
        <view class="fs-23 mt-42 txt-center">
          <view class="color6"> 本月 </view>
          <view class="fs-27 font-bold color5 mt-2">
            {{ statisticsData.overtime.month }}
          </view>
        </view>
        <view class="fs-23 mt-44">
          <view class="color6"> 同比上月 </view>
          <view class="txt-aligin-r color4 font-bold mt-4">
            {{ statisticsData.overtime.compare > 0 ? "+" : ""
            }}{{ statisticsData.overtime.compare }}
          </view>
        </view>
      </view>
    </view>
    <!-- <view class="pb-35 ml-29 mr-29 border2 br-15 mt-27 shadow1 flex j-between pl-19 pr-19">
             <view class="mt-19 flex1">
                 <view class="flex a-center">
                     <view class="w-12 h-12 br-6 border3"></view>
@@ -203,685 +190,1007 @@
                     </view>
                 </view>
             </view>
         </view>
         <view class="mt-27 shadow1 border2 ml-29 mr-29 pt-31 br-15" style="height: 511rpx;">
             <view class="flex mlr-o tabs mb-40">
                 <view v-for="(item, index) in tabs" :key="index"
                     :class="['tab-item', currentTab === index ? 'active' : '']" @click="handleTabClick(index)">
                     {{item}}
                 </view>
             </view>
             <view ref="chartRef" id="chart" style="width: 100%; height: 405rpx;"></view>
         </view>
         <view class="fs-35 font-bold mt-38 ml-27">
             问题类型排名
         </view>
         <view class="ml-29 mr-29 mt-27 shadow1 pt-31 border2 pl-38 pr-38 br-15">
             <uni-data-select v-model="value1" :localdata="range"></uni-data-select>
             <view class="fs-23">
                 <view class="flex a-center mb-38">
                     <view class="w-130 color3">
                         教育
                     </view>
                     <u-line-progress :percentage="95" inactiveColor="#EEEEEE"
                         :activeColor="'linear-gradient(270deg, #FF4934 0%, #FF8064 100%)'" height="38rpx">
                         <text class="u-percentage-slot pr-19 fs-23">248</text>
                     </u-line-progress>
                 </view>
                 <view class="flex a-center mb-38">
                     <view class="w-130 color3">
                         就业
                     </view>
                     <u-line-progress :percentage="75" inactiveColor="#EEEEEE"
                         :activeColor="'linear-gradient(270deg, #FEA834 0%, #FFD364 100%)'" height="38rpx">
                         <text class="u-percentage-slot pr-19 fs-23">200</text>
                     </u-line-progress>
                 </view>
                 <view class="flex a-center mb-38">
                     <view class="w-130 color3">
                         医疗
                     </view>
                     <u-line-progress :percentage="55" inactiveColor="#EEEEEE"
                         :activeColor="'linear-gradient(270deg, #02BAC0 0%, #05DEE1 100%)'" height="38rpx">
                         <text class="u-percentage-slot pr-19 fs-23">174</text>
                     </u-line-progress>
                 </view>
                 <view class="flex a-center mb-38">
                     <view class="w-130 color3">
                         住房
                     </view>
                     <u-line-progress :percentage="35" inactiveColor="#EEEEEE"
                         :activeColor="'linear-gradient(270deg, #4791FF 0%, #7DC4FF 100%)'" height="38rpx">
                         <text class="u-percentage-slot pr-19 fs-23">132</text>
                     </u-line-progress>
                 </view>
                 <view class="flex a-center mb-38">
                     <view class="w-130 color3">
                         养老
                     </view>
                     <u-line-progress :percentage="15" inactiveColor="#EEEEEE"
                         :activeColor="'linear-gradient(270deg, #4791FF 0%, #7DC4FF 100%)'" height="38rpx">
                         <text class="u-percentage-slot pr-19 fs-23">89</text>
                     </u-line-progress>
                 </view>
             </view>
         </view>
         <view class="fs-35 font-bold mt-38 ml-27">
             评价占比
         </view>
         <view class="ml-29 mr-29 mt-27 shadow1 border2 br-15" style="height: 417rpx;">
             <view class="flex pl-38 pr-38">
                 <view ref="rateChartRef" id="rateChart" style="width: 288rpx; height: 417rpx;"></view>
                 <view style="width: 46rpx;"></view>
                 <view class="flex1 pt-54">
                     <view class="mb-27">
                         <view class="flex a-center j-between mb-13">
                             <text class="fs-23">非常满意</text>
                             <text class="fs-23 color12">45%</text>
                         </view>
                         <view class="progress-bar">
                             <view class="progress-inner very-satisfied" style="width: 45%;"></view>
                         </view>
                     </view>
                     <view class="mb-27">
                         <view class="flex a-center j-between mb-13">
                             <text class="fs-23">满意</text>
                             <text class="fs-23 color12">20%</text>
                         </view>
                         <view class="progress-bar">
                             <view class="progress-inner satisfied" style="width: 20%;"></view>
                         </view>
                     </view>
                     <view class="mb-27">
                         <view class="flex a-center j-between mb-13">
                             <text class="fs-23">一般</text>
                             <text class="fs-23 color12">30%</text>
                         </view>
                         <view class="progress-bar">
                             <view class="progress-inner normal" style="width: 30%;"></view>
                         </view>
                     </view>
                     <view class="mb-27">
                         <view class="flex a-center j-between mb-13">
                             <text class="fs-23">不满意</text>
                             <text class="fs-23 color12">5%</text>
                         </view>
                         <view class="progress-bar">
                             <view class="progress-inner unsatisfied" style="width: 5%;"></view>
                         </view>
                     </view>
                 </view>
             </view>
         </view>
         <view class="h-40 safe-b"></view>
         <!-- 选择服务社区 -->
         <u-popup :show="selectPopup" round="16rpx" @close="selectPopup = false" :safe-area-inset-bottom="false"
             @open="openSelectPopup">
             <view class="relative pb-40">
                 <image @tap.stop="selectPopup = false" src="@/static/closeImg.png" class="w-35 h-35 absolute"
                     style="right: 31rpx;top: 46rpx;" />
                 <view class="txt-center pt-38 pb-40 fs-35 lh-48 font-bold">请选择服务社区</view>
                 <view class="flex a-center j-between txt-center py-10 fs-27 font-bold bgColor1">
                     <view class="flex1">区县</view>
                     <view class="flex1">街道</view>
                     <view class="flex1">社区</view>
                 </view>
                 <view class="mb-20">
                     <picker-view :value="value" @change="bindChange" class="picker-view" immediate-change>
                         <picker-view-column>
                             <view class="item" v-for="(item, index) in county" :key="index">
                                 {{ item.name }}
                             </view>
                         </picker-view-column>
                         <picker-view-column>
                             <view class="item" v-for="(item, index) in street" :key="index">
                                 {{ item.name }}
                             </view>
                         </picker-view-column>
                         <picker-view-column>
                             <view class="item" v-for="(item, index) in community" :key="index">
                                 {{ item.name }}
                             </view>
                         </picker-view-column>
                     </picker-view>
                 </view>
                 <view class="submitBtn" @click="chooseCommunity">确认</view>
             </view>
         </u-popup>
     </view>
 </template>
 <script>
     import * as echarts from 'echarts';
     import {
         getRegionTree
     } from './service.js'
     export default {
         data() {
             return {
                userInfo: {},
                 address: '',
                 value: [0, 0, 0],
                 confirmValue: [0, 0, 0],
                 county: [], //区县
                 street: [], //街道
                 community: [], //社区
                 value1: 0,
                 selectPopup: false,
                 range: [{
                         value: 0,
                         text: "排名前五"
                     },
                     {
                         value: 1,
                         text: "排名前十"
                     },
                     {
                         value: 2,
                         text: "所有排名"
                     },
                 ],
                 chart: null,
                 rateChart: null,
                 tabs: ['近7天', '近15天', '近30天'],
                 currentTab: 0,
                 chartData: {
                     dates: [
                         '2025\n04.17',
                         '2025\n04.18',
                         '2025\n04.19',
                         '2025\n04.20',
                         '2025\n04.21',
                         '2025\n04.22',
                         '2025\n04.23'
                     ],
                     demands: [80, 170, 240, 70, 130, 90, 160],
                     completed: [40, 130, 200, 30, 90, 50, 110]
                 },
                 rateData: [{
                         value: 45,
                         name: '非常满意',
                         itemStyle: {
                             color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [{
                                     offset: 0,
                                     color: '#FF8064'
                                 },
                                 {
                                     offset: 1,
                                     color: '#FF4934'
                                 }
                             ])
                         }
                     },
                     {
                         value: 20,
                         name: '满意',
                         itemStyle: {
                             color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [{
                                     offset: 0,
                                     color: '#05DEE1'
                                 },
                                 {
                                     offset: 1,
                                     color: '#02BAC0'
                                 }
                             ])
                         }
                     },
                     {
                         value: 30,
                         name: '一般',
                         itemStyle: {
                             color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [{
                                     offset: 0,
                                     color: '#7DC4FF'
                                 },
                                 {
                                     offset: 1,
                                     color: '#4791FF'
                                 }
                             ])
                         }
                     },
                     {
                         value: 5,
                         name: '不满意',
                         itemStyle: {
                             color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [{
                                     offset: 0,
                                     color: '#FFD364'
                                 },
                                 {
                                     offset: 1,
                                     color: '#FEA834'
                                 }
                             ])
                         }
                     }
                 ]
             }
         },
         onLoad() {
            this.userInfo = uni.getStorageSync('userInfo')
             getRegionTree().then(res => {
                 this.county = res.data
             })
         },
         mounted() {
             this.$nextTick(() => {
                 this.initChart()
                 this.initRateChart()
             })
         },
         methods: {
            clearAddress() {
                this.address = '';
                this.value = [0, 0, 0];
                this.confirmValue = [0, 0, 0];
                this.street = [];
                this.community = [];
            },
             //选择服务社区
             chooseCommunity() {
                 let districts = this.county[this.value[0]].name
                 let street = this.street[this.value[1]].name
                 let community = this.community[this.value[2]].name
                 this.address = `${districts}-${street}-${community}`;
                 this.confirmValue = this.value
                 this.selectPopup = false
             },
             // 切换社区
             bindChange(e, index) {
                 if (e.detail.value[0] != this.value[0]) {
                     e.detail.value[1] = 0
                     e.detail.value[2] = 0
                 }
                 if (e.detail.value[1] != this.value[1]) {
                     e.detail.value[2] = 0
                 }
                 this.value = e.detail.value
                 this.street = this.county[this.value[0]].children
                 this.community = this.street[this.value[1]].children
             },
             openSelectPopup() {
                 this.value = this.confirmValue
                 this.street = this.county[this.value[0]].children
                 this.community = this.street[this.value[1]].children
             },
             handleTabClick(index) {
                 this.currentTab = index
                 // 这里可以根据不同的 tab 加载不同时间段的数据
                 // this.loadChartData(index)
             },
             initChart() {
                 // 在 H5 端使用 document.getElementById
                 // 在小程序端使用 this.$refs.chartRef
                 const chartDom = document.getElementById('chart') || this.$refs.chartRef;
                 this.chart = echarts.init(chartDom);
                 this.updateChart();
             },
             updateChart() {
                 const option = {
                     color: ['#FF7B7B', '#FFB75B'],
                     tooltip: {
                         trigger: 'axis',
                         axisPointer: {
                             type: 'shadow'
                         }
                     },
                     legend: {
                         data: ['诉求单量', '诉求办结数'],
                         bottom: '0',
                         itemGap: uni.upx2px(60),
                         selectedMode: true
                     },
                     grid: {
                         left: '3%',
                         right: '4%',
                         bottom: '15%',
                         top: '3%',
                         containLabel: true
                     },
                     xAxis: {
                         type: 'category',
                         data: this.chartData.dates,
                         axisLine: {
                             lineStyle: {
                                 color: '#E5E5E5'
                             }
                         },
                         axisTick: {
                             show: false
                         },
                         axisLabel: {
                             color: '#888888',
                             fontSize: uni.upx2px(19),
                             lineHeight: uni.upx2px(23),
                             formatter: function(value) {
                                 return value.split('\\n').join('\n')
                             }
                         }
                     },
                     yAxis: {
                         type: 'value',
                         splitLine: {
                             lineStyle: {
                                 type: 'dashed',
                                 color: '#fff'
                             }
                         },
                         axisLine: {
                             show: false
                         },
                         axisTick: {
                             show: false
                         }
                     },
                     series: [{
                             name: '诉求单量',
                             type: 'bar',
                             barWidth: uni.upx2px(38),
                             itemStyle: {
                                 borderRadius: [20, 20, 20, 20],
                                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                                         offset: 0,
                                         color: '#FF807E'
                                     },
                                     {
                                         offset: 1,
                                         color: '#FF4948'
                                     }
                                 ])
                             },
                             data: this.chartData.demands
                         },
                         {
                             name: '诉求办结数',
                             type: 'line',
                             smooth: true,
                             symbol: 'circle',
                             symbolSize: 8,
                             itemStyle: {
                                 color: '#FFB75B',
                                 borderWidth: 2,
                                 borderColor: '#fff',
                                 shadowColor: '#FB9A0E',
                                 shadowBlur: 8,
                                 shadowOffsetY: 4
                             },
                             lineStyle: {
                                 width: 2,
                                 curveness: 0.3
                             },
                             data: this.chartData.completed
                         }
                     ]
                 };
                 this.chart && this.chart.setOption(option);
             },
             initRateChart() {
                 const chartDom = document.getElementById('rateChart') || this.$refs.rateChartRef;
                 this.rateChart = echarts.init(chartDom);
                 this.updateRateChart();
             },
             updateRateChart() {
                 const option = {
                     tooltip: {
                         trigger: 'item',
                         confine: true,
                         // formatter: '{b}: {c}%',
                         backgroundColor: 'rgba(255, 255, 255, 0.9)',
                         borderColor: '#FFE0E0',
                         borderWidth: 1,
                         textStyle: {
                             color: '#666666',
                             fontSize: 12
                         },
                         padding: [8, 12]
                     },
                     series: [{
                         name: '评价占比',
                         type: 'pie',
                         radius: ['55%', '100%'],
                         center: ['50%', '50%'],
                         avoidLabelOverlap: false,
                         label: {
                             show: false
                         },
                         labelLine: {
                             show: false
                         },
                         emphasis: {
                             scale: false,
                             scaleSize: 0
                         },
                         data: this.rateData
                     }]
                 };
                 this.rateChart && this.rateChart.setOption(option);
             }
         }
     }
 </script>
 <style scoped lang="scss">
     /deep/.uni-select {
         width: 231rpx;
         height: 65rpx;
         margin: 0 auto;
         margin-bottom: 38rpx;
         font-size: 27rpx;
         color: #797F81;
         border-color: #E5E5E5;
         border-radius: 33rpx;
         padding: 0 31rpx 0 40rpx;
         .uni-select__input-text {
             color: #797F81;
         }
     }
     .content {
         background: linear-gradient(180deg, #FFDCDB 0%, rgba(255, 255, 255, 0) 100rpx, #fff 100%);
     }
     .gap25 {
         gap: 25rpx;
     }
     .tabs {
         width: 412rpx;
         background-color: #FFF1F1;
         border-radius: 30rpx;
         height: 65rpx;
         line-height: 65rpx;
         font-size: 27rpx;
         position: relative;
         overflow: hidden;
     }
     .tab-item {
         color: #797F81;
         flex: 1;
         text-align: center;
         font-weight: 400;
         height: 54rpx;
         line-height: 54rpx;
         border-radius: 27rpx;
         margin: 6rpx;
         position: relative;
         z-index: 1;
         transform: translateZ(0);
         transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
         &.active {
             background-color: #fff;
             color: #FF4948;
             font-weight: 600;
             box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
             transform: scale(1.02);
         }
     }
     .chart-wrapper {
         background: #fff;
         border-radius: 20rpx;
         padding: 20rpx;
     }
     .color1 {
         color: #666565;
     }
     .color2 {
         color: #C1C1C1;
     }
     .color3 {
         color: #666666;
     }
     .color4 {
         color: #FF4948;
     }
     .color5 {
         color: #696969;
     }
     .color6 {
         color: #A4A4A4;
     }
     .color7 {
         color: #A7A7A7;
     }
     .color8 {
         color: #0FB269;
     }
     .color9 {
         color: #FF5600;
     }
     .color10 {
         color: #161998;
     }
     .color11 {
         color: #08AD60;
     }
     .color12 {
         color: #9C9C9E;
     }
     .bgColor1 {
         background-color: #fff;
     }
     .bgColor2 {
         background-color: #FFF1F4;
     }
     .bgColor3 {
         background-color: #FFF8F4;
     }
     .bgColor4 {
         background-color: #F4F5FF;
     }
     .bgColor5 {
         background-color: #F1FFF8;
     }
     .border1 {
         border: 2rpx solid #D9D9D9;
     }
     .border2 {
         border: 2rpx solid #FFE0E0;
     }
     .border3 {
         border: 4rpx solid #FF4948;
         box-sizing: border-box;
         box-shadow: 0rpx 4rpx 8rpx 0rpx rgba(255, 73, 72, 0.5);
     }
     .border4 {
         border: 2rpx solid #FFFFFF;
     }
     .shadow1 {
         box-shadow: 0rpx 0rpx 27rpx 0rpx rgba(0, 0, 0, 0.1);
     }
     .shadow2 {
         box-shadow: 0rpx 0rpx 15rpx 0rpx rgba(0, 0, 0, 0.1);
     }
     .progress-bar {
         width: 100%;
         height: 8rpx;
         background: #EEEEEE;
         border-radius: 4rpx;
         overflow: hidden;
     }
     .progress-inner {
         height: 100%;
         border-radius: 4rpx;
         transition: width 0.3s ease-in-out;
         &.very-satisfied {
             background: linear-gradient(270deg, #FF8064 0%, #FF4934 100%);
         }
         &.satisfied {
             background: linear-gradient(270deg, #05DEE1 0%, #02BAC0 100%);
         }
         &.normal {
             background: linear-gradient(270deg, #7DC4FF 0%, #4791FF 100%);
         }
         &.unsatisfied {
             background: linear-gradient(270deg, #FFD364 0%, #FEA834 100%);
         }
     }
     .picker-view {
         height: 460rpx;
         font-size: 35rpx;
     }
     /deep/.picker-view {
         margin: 0 auto;
         .item {
             text-align: center;
             font-family: PingFangSC, PingFang SC;
             font-weight: 600;
             font-size: 36rpx;
             color: #333333;
             line-height: 50rpx;
         }
     }
     .submitBtn {
         width: calc(100% - 62rpx);
         margin: 0 31rpx;
         line-height: 96rpx;
         text-align: center;
         background: linear-gradient(270deg, #FC8D55 0%, #FF4948 100%);
         border-radius: 48rpx;
         font-weight: 600;
         font-size: 35rpx;
         color: #fff;
     }
 </style>
         </view> -->
    <view
      class="mt-27 shadow1 border2 ml-29 mr-29 pt-31 br-15"
      style="height: 511rpx"
    >
      <view class="flex mlr-o tabs mb-40">
        <view
          v-for="(item, index) in tabs"
          :key="index"
          :class="['tab-item', currentTab === index ? 'active' : '']"
          @click="handleTabClick(index)"
        >
          {{ item }}
        </view>
      </view>
      <view
        ref="chartRef"
        id="chart"
        style="width: 100%; height: 405rpx"
      ></view>
    </view>
    <view class="fs-35 font-bold mt-38 ml-27"> 问题类型排名 </view>
    <view class="ml-29 mr-29 mt-27 shadow1 pt-31 border2 pl-38 pr-38 br-15">
      <uni-data-select v-model="value1" :localdata="range"></uni-data-select>
      <view class="fs-23">
        <view class="flex a-center mb-38" v-for="(item, idx) in typeRankList" :key="idx">
          <view class="w-130 color3">{{ item.typeName }}</view>
          <u-line-progress
            :percentage="item.percent"
            inactiveColor="#EEEEEE"
            :activeColor="item.gradientColor"
            height="38rpx"
          >
            <text class="u-percentage-slot pr-19 fs-23">{{ item.count }}</text>
          </u-line-progress>
        </view>
      </view>
    </view>
    <view class="fs-35 font-bold mt-38 ml-27"> 评价占比 </view>
    <view
      class="ml-29 mr-29 mt-27 shadow1 border2 br-15"
      style="height: 417rpx"
    >
      <view class="flex pl-38 pr-38">
        <view
          ref="rateChartRef"
          id="rateChart"
          style="width: 288rpx; height: 417rpx"
        ></view>
        <view style="width: 46rpx"></view>
        <view class="flex1 pt-54">
          <view class="mb-27">
            <view class="flex a-center j-between mb-13">
              <text class="fs-23">非常满意</text>
              <text class="fs-23 color12">45%</text>
            </view>
            <view class="progress-bar">
              <view
                class="progress-inner very-satisfied"
                style="width: 45%"
              ></view>
            </view>
          </view>
          <view class="mb-27">
            <view class="flex a-center j-between mb-13">
              <text class="fs-23">满意</text>
              <text class="fs-23 color12">20%</text>
            </view>
            <view class="progress-bar">
              <view class="progress-inner satisfied" style="width: 20%"></view>
            </view>
          </view>
          <view class="mb-27">
            <view class="flex a-center j-between mb-13">
              <text class="fs-23">一般</text>
              <text class="fs-23 color12">30%</text>
            </view>
            <view class="progress-bar">
              <view class="progress-inner normal" style="width: 30%"></view>
            </view>
          </view>
          <view class="mb-27">
            <view class="flex a-center j-between mb-13">
              <text class="fs-23">不满意</text>
              <text class="fs-23 color12">5%</text>
            </view>
            <view class="progress-bar">
              <view class="progress-inner unsatisfied" style="width: 5%"></view>
            </view>
          </view>
        </view>
      </view>
    </view>
    <view class="h-40 safe-b"></view>
    <!-- 选择服务社区 -->
    <u-popup
      :show="selectPopup"
      round="16rpx"
      @close="selectPopup = false"
      :safe-area-inset-bottom="false"
      @open="openSelectPopup"
    >
      <view class="relative pb-40">
        <image
          @tap.stop="selectPopup = false"
          src="@/static/closeImg.png"
          class="w-35 h-35 absolute"
          style="right: 31rpx; top: 46rpx"
        />
        <view class="txt-center pt-38 pb-40 fs-35 lh-48 font-bold"
          >请选择服务社区</view
        >
        <view
          class="flex a-center j-between txt-center py-10 fs-27 font-bold bgColor1"
        >
          <view v-if="hasTier(2)" class="flex1">区县</view>
          <view v-if="hasTier(3)" class="flex1">街道</view>
          <view v-if="hasTier(4)" class="flex1">社区</view>
        </view>
        <view class="mb-20">
          <picker-view
            :value="value"
            @change="bindChange"
            class="picker-view"
            immediate-change
          >
            <picker-view-column v-if="hasTier(2)">
              <view
                class="item"
                v-for="(item, index) in regionTree"
                :key="index"
              >
                {{ item.name }}
              </view>
            </picker-view-column>
            <picker-view-column v-if="hasTier(3)">
              <view
                class="item"
                v-for="(item, index) in getStreets()"
                :key="index"
              >
                {{ item.name }}
              </view>
            </picker-view-column>
            <picker-view-column v-if="hasTier(4)">
              <view
                class="item"
                v-for="(item, index) in getCommunities()"
                :key="index"
              >
                {{ item.name }}
              </view>
            </picker-view-column>
          </picker-view>
        </view>
        <view class="submitBtn" @click="chooseCommunity">确认</view>
      </view>
    </u-popup>
  </view>
</template>
<script>
import * as echarts from "echarts";
import {
  getRegionTree,
  getStaticsPartOne,
  getStaticsPartTwo,
  getStaticsPartThree,
} from "./service.js";
export default {
  data() {
    return {
      userInfo: {},
      address: "",
      value: [0, 0, 0],
      confirmValue: [0, 0, 0],
      regionTree: [], // 区域树数据
      value1: 0,
      selectPopup: false,
      range: [
        {
          value: 0,
          text: "排名前五",
        },
        {
          value: 1,
          text: "排名前十",
        },
        {
          value: 2,
          text: "所有排名",
        },
      ],
      chart: null,
      rateChart: null,
      tabs: ["近7天", "近15天", "近30天"],
      currentTab: 0,
      chartData: {
        dates: [
          "2025\n04.17",
          "2025\n04.18",
          "2025\n04.19",
          "2025\n04.20",
          "2025\n04.21",
          "2025\n04.22",
          "2025\n04.23",
        ],
        demands: [80, 170, 240, 70, 130, 90, 160],
        completed: [40, 130, 200, 30, 90, 50, 110],
      },
      rateData: [
        {
          value: 45,
          name: "非常满意",
          itemStyle: {
            color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
              {
                offset: 0,
                color: "#FF8064",
              },
              {
                offset: 1,
                color: "#FF4934",
              },
            ]),
          },
        },
        {
          value: 20,
          name: "满意",
          itemStyle: {
            color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
              {
                offset: 0,
                color: "#05DEE1",
              },
              {
                offset: 1,
                color: "#02BAC0",
              },
            ]),
          },
        },
        {
          value: 30,
          name: "一般",
          itemStyle: {
            color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
              {
                offset: 0,
                color: "#7DC4FF",
              },
              {
                offset: 1,
                color: "#4791FF",
              },
            ]),
          },
        },
        {
          value: 5,
          name: "不满意",
          itemStyle: {
            color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
              {
                offset: 0,
                color: "#FFD364",
              },
              {
                offset: 1,
                color: "#FEA834",
              },
            ]),
          },
        },
      ],
      statisticsData: {
        satisfaction: {
          total: 0,
          month: 0,
          compare: 0,
        },
        demands: {
          total: 0,
          month: 0,
          compare: 0,
        },
        processTime: {
          total: 0,
          month: 0,
          compare: 0,
        },
        status: {
          processing: 0,
          reviewing: 0,
          delayed: 0,
          completed: 0,
        },
        overtime: {
          total: 0,
          month: 0,
          compare: 0,
        },
        completionRate: {
          total: 0,
          month: 0,
          compare: 0,
        },
      },
      currentAreaId: '',
      currentTier: -1,
      typeRankList: [],
    };
  },
  onLoad() {
    this.userInfo = uni.getStorageSync("userInfo");
    this.initRegionData();
  },
  mounted() {
    this.$nextTick(() => {
      this.initChart();
      this.initRateChart();
    });
  },
  methods: {
    clearAddress() {
      this.address = "";
      this.value = [0, 0, 0];
      this.confirmValue = [0, 0, 0];
      this.currentAreaId = '';
      this.currentTier = -1;
      this.getStatisticsData("", -1);
      this.getChartData(1);
      this.getTypeRankData();
    },
    //初始化区域数据
    initRegionData() {
      getRegionTree().then((res) => {
        if (res.code === 200) {
          this.regionTree = [{name: '全部', id: 'all', tier: 2, children: []}, ...(res.data || [])];
          this.value = [0, 0, 0];
          this.confirmValue = [0, 0, 0];
          this.address = "全部";
          this.currentAreaId = '';
          this.currentTier = -1;
          this.getStatisticsData("", -1);
          this.getChartData(1);
          this.getTypeRankData();
        }
      });
    },
    // 获取当前选中的区域对象
    getSelectedRegion() {
      // 区县
      const county = this.regionTree[this.value[0]];
      if (!county || county.id === "all") return { id: "", tier: -1 };
      // 有街道
      const streets =
        county.children && county.children.filter((c) => c && c.tier === 3);
      if (streets && streets.length) {
        const street = streets[this.value[1] - 1]; // -1 因为有"全部"选项
        if (!street || this.value[1] === 0 || street.id === "all")
          return { id: county.id, tier: county.tier };
        const communities =
          street.children && street.children.filter((c) => c && c.tier === 4);
        if (communities && communities.length) {
          const community = communities[this.value[2] - 1];
          if (!community || this.value[2] === 0 || community.id === "all")
            return { id: street.id, tier: street.tier };
          return { id: community.id, tier: community.tier };
        }
        return { id: street.id, tier: street.tier };
      }
      // 区县下直接有社区
      const communities =
        county.children && county.children.filter((c) => c && c.tier === 4);
      if (communities && communities.length) {
        const community = communities[this.value[1] - 1];
        if (!community || this.value[1] === 0 || community.id === "all")
          return { id: county.id, tier: county.tier };
        return { id: community.id, tier: community.tier };
      }
      // 只选了区县
      return { id: county.id, tier: county.tier };
    },
    // 根据value设置地址显示
    setAddressByValue() {
      const names = [];
      const county = this.regionTree[this.value[0]];
      if (!county) {
        this.address = "全部";
        return;
      }
      // 如果选中区县的全部
      if (county.id === "all") {
        this.address = "全部";
        return;
      }
      names.push(county.name);
      // 检查是否有街道
      const streets =
        county.children && county.children.filter((c) => c && c.tier === 3);
      if (streets && streets.length) {
        const street = streets[this.value[1] - 1]; // -1 因为有"全部"选项
        if (street && street.id !== "all") {
          names.push(street.name);
          // 检查街道下是否有社区
          const communities =
            street.children && street.children.filter((c) => c && c.tier === 4);
          if (communities && communities.length) {
            const community = communities[this.value[2] - 1];
            if (community && community.id !== "all") {
              names.push(community.name);
            }
          }
        }
      } else {
        // 区县下直接有社区
        const communities =
          county.children && county.children.filter((c) => c && c.tier === 4);
        if (communities && communities.length) {
          const community = communities[this.value[1] - 1];
          if (community && community.id !== "all") {
            names.push(community.name);
          }
        }
      }
      this.address = names.join("-");
    },
    //选择服务社区
    chooseCommunity() {
      this.confirmValue = [...this.value];
      this.setAddressByValue();
      const { id, tier } = this.getSelectedRegion();
      this.currentAreaId = id;
      this.currentTier = tier;
      this.selectPopup = false;
      this.getStatisticsData(id, tier);
      this.getChartData(this.currentTab + 1);
      this.getTypeRankData();
    },
    // 切换社区
    bindChange(e) {
      const newValue = e.detail.value;
      // 级联重置逻辑
      if (newValue[0] !== this.value[0]) {
        newValue[1] = 0;
        newValue[2] = 0;
      } else if (newValue[1] !== this.value[1]) {
        newValue[2] = 0;
      }
      this.value = newValue;
    },
    openSelectPopup() {
      this.value = this.confirmValue;
    },
    handleTabClick(index) {
      this.currentTab = index;
      this.getChartData(index + 1);
    },
    initChart() {
      // 在 H5 端使用 document.getElementById
      // 在小程序端使用 this.$refs.chartRef
      const chartDom = document.getElementById("chart") || this.$refs.chartRef;
      this.chart = echarts.init(chartDom);
      this.updateChart();
    },
    updateChart() {
      const option = {
        color: ["#FF7B7B", "#FFB75B"],
        tooltip: {
          trigger: "axis",
          axisPointer: {
            type: "shadow",
          },
        },
        legend: {
          data: ["诉求单量", "诉求办结数"],
          bottom: "0",
          itemGap: uni.upx2px(60),
          selectedMode: true,
        },
        grid: {
          left: "3%",
          right: "4%",
          bottom: "15%",
          top: "3%",
          containLabel: true,
        },
        xAxis: {
          type: "category",
          data: this.chartData.dates,
          axisLine: {
            lineStyle: {
              color: "#E5E5E5",
            },
          },
          axisTick: {
            show: false,
          },
          axisLabel: {
            color: "#888888",
            fontSize: uni.upx2px(19),
            lineHeight: uni.upx2px(23),
            formatter: function (value) {
              return value.split("\\n").join("\n");
            },
          },
        },
        yAxis: {
          type: "value",
          splitLine: {
            lineStyle: {
              type: "dashed",
              color: "#fff",
            },
          },
          axisLine: {
            show: false,
          },
          axisTick: {
            show: false,
          },
        },
        series: [
          {
            name: "诉求单量",
            type: "bar",
            barWidth: uni.upx2px(38),
            itemStyle: {
              borderRadius: [20, 20, 20, 20],
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                {
                  offset: 0,
                  color: "#FF807E",
                },
                {
                  offset: 1,
                  color: "#FF4948",
                },
              ]),
            },
            data: this.chartData.demands,
          },
          {
            name: "诉求办结数",
            type: "line",
            smooth: true,
            symbol: "circle",
            symbolSize: 8,
            itemStyle: {
              color: "#FFB75B",
              borderWidth: 2,
              borderColor: "#fff",
              shadowColor: "#FB9A0E",
              shadowBlur: 8,
              shadowOffsetY: 4,
            },
            lineStyle: {
              width: 2,
              curveness: 0.3,
            },
            data: this.chartData.completed,
          },
        ],
      };
      this.chart && this.chart.setOption(option);
    },
    initRateChart() {
      const chartDom =
        document.getElementById("rateChart") || this.$refs.rateChartRef;
      this.rateChart = echarts.init(chartDom);
      this.updateRateChart();
    },
    updateRateChart() {
      const option = {
        tooltip: {
          trigger: "item",
          confine: true,
          // formatter: '{b}: {c}%',
          backgroundColor: "rgba(255, 255, 255, 0.9)",
          borderColor: "#FFE0E0",
          borderWidth: 1,
          textStyle: {
            color: "#666666",
            fontSize: 12,
          },
          padding: [8, 12],
        },
        series: [
          {
            name: "评价占比",
            type: "pie",
            radius: ["55%", "100%"],
            center: ["50%", "50%"],
            avoidLabelOverlap: false,
            label: {
              show: false,
            },
            labelLine: {
              show: false,
            },
            emphasis: {
              scale: false,
              scaleSize: 0,
            },
            data: this.rateData,
          },
        ],
      };
      this.rateChart && this.rateChart.setOption(option);
    },
    // 添加获取统计数据的方法
    async getStatisticsData(areaId = "", tier = -1) {
      try {
        const res = await getStaticsPartOne({
          areaId,
          tier,
        });
        if (res.code === 200) {
          this.statisticsData = {
            satisfaction: {
              total: res.data.satisfactionRate ?? 0,
              month: res.data.thisMonthSatisfactionRate ?? 0,
              compare: res.data.lastMonthCompareSatisfactionRate ?? 0,
            },
            demands: {
              total: res.data.allTotal ?? 0,
              month: res.data.thisMonthTotal ?? 0,
              compare: res.data.lastMonthCompareTotal ?? 0,
            },
            processTime: {
              total: res.data.averageTime ?? 0,
              month: res.data.thisMonthAverageTime ?? 0,
              compare: res.data.lastMonthCompareAverageTime ?? 0,
            },
            status: {
              processing: res.data.nowTransactTotal ?? 0,
              reviewing: res.data.auditTransactTotal ?? 0,
              delayed: res.data.postponeTransactTotal ?? 0,
              completed: res.data.completeTransactTotal ?? 0,
            },
            overtime: {
              total: res.data.overtimeTransactTotal ?? 0,
              month: res.data.thisMonthOvertimeTransactTotal ?? 0,
              compare: res.data.lastMonthOvertimeTransactCompareTotal ?? 0,
            },
            completionRate: {
              total: 0,
              month: 0,
              compare: 0,
            },
          };
          // 更新图表数据
          this.updateChart();
          this.updateRateChart();
        }
      } catch (error) {
        console.error("获取统计数据失败:", error);
      }
    },
    hasTier(tier) {
      // tier=2: 区县始终有
      if (tier === 2) return true;
      // tier=3: 当前区县children里有tier=3
      if (tier === 3) {
        const county = this.regionTree[this.value[0]];
        return (
          county &&
          Array.isArray(county.children) &&
          county.children.some((c) => c && c.tier === 3)
        );
      }
      // tier=4: 当前区县children里有tier=4,或街道children里有tier=4
      if (tier === 4) {
        const county = this.regionTree[this.value[0]];
        if (!county || !Array.isArray(county.children)) return false;
        // 区县下直接有社区
        if (county.children.some((c) => c && c.tier === 4)) return true;
        // 区县下有街道,街道下有社区
        const street = county.children[this.value[1]];
        return (
          street &&
          Array.isArray(street.children) &&
          street.children.some((c) => c && c.tier === 4)
        );
      }
      return false;
    },
    getStreets() {
      const county = this.regionTree[this.value[0]];
      if (!county || !Array.isArray(county.children)) return [];
      // 只返回tier=3的
      const streets = county.children.filter((c) => c && c.tier === 3);
      return streets.length
        ? [{ name: "全部", id: "all", tier: 3, children: [] }, ...streets]
        : [];
    },
    getCommunities() {
      const county = this.regionTree[this.value[0]];
      if (!county || !Array.isArray(county.children)) return [];
      // 区县下直接有社区
      const communities = county.children.filter((c) => c && c.tier === 4);
      if (communities.length)
        return [{ name: "全部", id: "all", tier: 4 }, ...communities];
      // 区县下有街道,街道下有社区
      const street = county.children[this.value[1]];
      if (street && Array.isArray(street.children)) {
        const comms = street.children.filter((c) => c && c.tier === 4);
        if (comms.length)
          return [{ name: "全部", id: "all", tier: 4 }, ...comms];
      }
      return [];
    },
    async getChartData(timeType = 1) {
      try {
        const res = await getStaticsPartTwo({
          areaId: this.currentAreaId,
          tier: this.currentTier,
          timeType
        });
        if (res.code === 200 && res.data) {
          // 转换数据格式
          const dates = res.data.map(item => item.time.replace(/-/g, '\n').replace(/\n(\d{2})$/, '.$1'));
          const demands = res.data.map(item => item.allTotal);
          const completed = res.data.map(item => item.completeTotal);
          this.chartData = {
            dates,
            demands,
            completed
          };
          this.updateChart();
        }
      } catch (e) {
        console.error('获取图表数据失败', e);
      }
    },
    async getTypeRankData() {
      let rank;
      if (this.value1 === 0) rank = 5;
      else if (this.value1 === 1) rank = 10;
      // value1 === 2 时不传rank
      const params = {
        areaId: this.currentAreaId,
        tier: this.currentTier
      };
      if (rank) params.rank = rank;
      try {
        const res = await getStaticsPartThree(params);
        console.log(JSON.stringify(res.data));
        if (res.code === 200 && Array.isArray(res.data)) {
          // 计算最大值
          const max = Math.max(...res.data.map(item => item.allTotal), 1);
          // 处理数据,增加percent字段
          this.typeRankList = res.data.map((item, idx) => ({
            typeName: item.name,
            count: item.allTotal,
            percent: Math.round(item.allTotal / max * 100),
            gradientColor: 'linear-gradient(270deg, #FF4934 0%, #FF8064 100%)'
          }));
        }
      } catch (e) {
        console.error('获取问题类型排名失败', e);
      }
    },
  },
  watch: {
    value1() {
      this.getTypeRankData();
    }
  },
};
</script>
<style scoped lang="scss">
/deep/.uni-select {
  width: 231rpx;
  height: 65rpx;
  margin: 0 auto;
  margin-bottom: 38rpx;
  font-size: 27rpx;
  color: #797f81;
  border-color: #e5e5e5;
  border-radius: 33rpx;
  padding: 0 31rpx 0 40rpx;
  .uni-select__input-text {
    color: #797f81;
  }
}
.content {
  background: linear-gradient(
    180deg,
    #ffdcdb 0%,
    rgba(255, 255, 255, 0) 100rpx,
    #fff 100%
  );
}
.gap25 {
  gap: 25rpx;
}
.tabs {
  width: 412rpx;
  background-color: #fff1f1;
  border-radius: 30rpx;
  height: 65rpx;
  line-height: 65rpx;
  font-size: 27rpx;
  position: relative;
  overflow: hidden;
}
.tab-item {
  color: #797f81;
  flex: 1;
  text-align: center;
  font-weight: 400;
  height: 54rpx;
  line-height: 54rpx;
  border-radius: 27rpx;
  margin: 6rpx;
  position: relative;
  z-index: 1;
  transform: translateZ(0);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  &.active {
    background-color: #fff;
    color: #ff4948;
    font-weight: 600;
    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
    transform: scale(1.02);
  }
}
.chart-wrapper {
  background: #fff;
  border-radius: 20rpx;
  padding: 20rpx;
}
.color1 {
  color: #666565;
}
.color2 {
  color: #c1c1c1;
}
.color3 {
  color: #666666;
}
.color4 {
  color: #ff4948;
}
.color5 {
  color: #696969;
}
.color6 {
  color: #a4a4a4;
}
.color7 {
  color: #a7a7a7;
}
.color8 {
  color: #0fb269;
}
.color9 {
  color: #ff5600;
}
.color10 {
  color: #161998;
}
.color11 {
  color: #08ad60;
}
.color12 {
  color: #9c9c9e;
}
.bgColor1 {
  background-color: #fff;
}
.bgColor2 {
  background-color: #fff1f4;
}
.bgColor3 {
  background-color: #fff8f4;
}
.bgColor4 {
  background-color: #f4f5ff;
}
.bgColor5 {
  background-color: #f1fff8;
}
.border1 {
  border: 2rpx solid #d9d9d9;
}
.border2 {
  border: 2rpx solid #ffe0e0;
}
.border3 {
  border: 4rpx solid #ff4948;
  box-sizing: border-box;
  box-shadow: 0rpx 4rpx 8rpx 0rpx rgba(255, 73, 72, 0.5);
}
.border4 {
  border: 2rpx solid #ffffff;
}
.shadow1 {
  box-shadow: 0rpx 0rpx 27rpx 0rpx rgba(0, 0, 0, 0.1);
}
.shadow2 {
  box-shadow: 0rpx 0rpx 15rpx 0rpx rgba(0, 0, 0, 0.1);
}
.progress-bar {
  width: 100%;
  height: 8rpx;
  background: #eeeeee;
  border-radius: 4rpx;
  overflow: hidden;
}
.progress-inner {
  height: 100%;
  border-radius: 4rpx;
  transition: width 0.3s ease-in-out;
  &.very-satisfied {
    background: linear-gradient(270deg, #ff8064 0%, #ff4934 100%);
  }
  &.satisfied {
    background: linear-gradient(270deg, #05dee1 0%, #02bac0 100%);
  }
  &.normal {
    background: linear-gradient(270deg, #7dc4ff 0%, #4791ff 100%);
  }
  &.unsatisfied {
    background: linear-gradient(270deg, #ffd364 0%, #fea834 100%);
  }
}
.picker-view {
  height: 460rpx;
  font-size: 35rpx;
}
/deep/.picker-view {
  margin: 0 auto;
  .item {
    text-align: center;
    font-family: PingFangSC, PingFang SC;
    font-weight: 600;
    font-size: 36rpx;
    color: #333333;
    line-height: 50rpx;
  }
}
.submitBtn {
  width: calc(100% - 62rpx);
  margin: 0 31rpx;
  line-height: 96rpx;
  text-align: center;
  background: linear-gradient(270deg, #fc8d55 0%, #ff4948 100%);
  border-radius: 48rpx;
  font-weight: 600;
  font-size: 35rpx;
  color: #fff;
}
</style>
H5/pages/statistics/service.js
@@ -1,9 +1,26 @@
import request from '@/utils/request.js'
// 获取区县-街道-社区树
export const getRegionTree =()=>{
    return request.get(`/api/huacheng-sangeshenbian/bc-region/regionTree-applet`)
// 获取统计分析-第一部分(处理满意率+诉求单量统计上面部分)
export const getStaticsPartOne = (params) => {
    return request.post(`/api/huacheng-sangeshenbian/applet/statics/part-one`,params)
// 获取统计分析-第二部分(处理满意率+诉求单量统计下面部分)
export const getStaticsPartTwo = (params) => {
    return request.post(`/api/huacheng-sangeshenbian/applet/statics/part-two`,params)
}
// 获取统计分析-第三部分(党员申请+编辑党员信息+党员信息详情)
export const getStaticsPartThree = (params) => {
    return request.post(`/api/huacheng-sangeshenbian/applet/statics/part-three`,params)
}
// 获取区域树
export const getRegionTree = (params) => {
    return request.get(`/api/huacheng-sangeshenbian/applet/statics/region-tree`,params)
}
// 党员申请
export const apply =(params)=>{
    return request.post(`/api/huacheng-sangeshenbian/applet/party-member/apply`,params)
H5/static/home/Group 2@2x.png
management/config/env.ts
@@ -1,6 +1,6 @@
export default {
  dev: {
    SERVER_URL: 'http://192.168.110.188:6194',
    SERVER_URL: 'http://192.168.110.106:6194',
    // SERVER_URL: 'https://huacheng.psciio.com',
  },
  test: {
management/config/routes.ts
@@ -34,6 +34,12 @@
    ],
  },
  {
    name: '统计分析',
    path: '/statistics',
    component: './statistics/index',
    // access: '/system_setting/position_management',
  },
  {
    path: '/setting',
    // layout: false,
    name: '系统设置',
management/package.json
@@ -31,7 +31,7 @@
    "not ie <= 10"
  ],
  "dependencies": {
    "@ant-design/charts": "^2.1.2",
    "@ant-design/charts": "^2.3.0",
    "@ant-design/icons": "^4.8.0",
    "@ant-design/plots": "^2.2.6",
    "@ant-design/pro-components": "^2.6.35",
@@ -42,6 +42,8 @@
    "braft-editor": "^2.3.9",
    "classnames": "^2.3.2",
    "crypto-js": "^4.2.0",
    "echarts": "^5.6.0",
    "echarts-for-react": "^3.0.2",
    "moment": "^2.29.4",
    "omit.js": "^2.0.2",
    "rc-menu": "^9.8.2",
management/src/pages/statistics/components/addAndEdit.jsx
New file
@@ -0,0 +1,72 @@
import { Form, Input, Modal,Button } from 'antd';
import { forwardRef, useImperativeHandle, useState } from 'react';
const formItemLayout = {
  labelCol: { span: 7 },
  wrapperCol: { span: 12 },
};
const AddEditView = ({ visible, onSave, onUpdate, onCancel }, ref) => {
  const [form] = Form.useForm();
  const [editData, setEditData] = useState({});
  const [rolesList, setRolesList] = useState();
  /**
   * 确定按钮事件
   */
  const okHandle = () => {
    form.validateFields().then((values) => {
      if (editData.id) {
        values.id = editData.id;
        onUpdate(values);
      } else {
        onSave(values);
      }
    });
  };
  useImperativeHandle(ref, () => {
    return {
      refreshData: (data) => {
        form.resetFields();
        form.setFieldsValue(data);
        setEditData(data);
      },
      clean: () => {
        form.resetFields();
      },
    };
  });
  return (
    <Modal
      getContainer={false}
      width="25%"
      destroyOnClose
      title={editData.id ? '编辑职位' : '添加职位'}
      open={visible}
      onCancel={() => onCancel(false)}
      footer={[
        <Button key="back" onClick={() => onCancel(false)}>
          关闭
        </Button>,
        <Button key="submit" type="primary" onClick={okHandle}>
          确认
        </Button>
      ]}
    >
      <Form layout="horizontal" {...formItemLayout} form={form} initialValues={{ isAuctioneer: 1 }}>
        <Form.Item
          name="name"
          required
          label="职位名称"
          rules={[{ required: true, message: '请输入职位名称' }]}
        >
          <Input placeholder="请输入职位名称" />
        </Form.Item>
      </Form>
    </Modal>
  );
};
export default forwardRef(AddEditView);
management/src/pages/statistics/index.jsx
New file
@@ -0,0 +1,558 @@
import { buildProTableDataSource, sendRequest, showDelConfirm } from '@/utils/antdUtils';
import { PageContainer } from '@ant-design/pro-components';
import { Button, InputNumber, Select, Space, Form, Card, Row, Col, Statistic, DatePicker } from 'antd';
import { useEffect, useState } from 'react';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
import { Access, useAccess } from 'umi';
import AddAndEdit from './components/addAndEdit';
import { getCommunityList, getNextRegion, getStatisticsData } from './service';
import ReactECharts from 'echarts-for-react';
import moment from 'moment';
// 同比趋势组件
const Trend = ({ value }) => {
  if (value > 0) {
    return (
      <span style={{ color: 'red', marginLeft: 4 }}>
        <ArrowUpOutlined /> {Math.abs(value)}%
      </span>
    );
  }
  if (value < 0) {
    return (
      <span style={{ color: 'green', marginLeft: 4 }}>
        <ArrowDownOutlined /> {Math.abs(value)}%
      </span>
    );
  }
  return <span style={{ marginLeft: 4 }}>0%</span>;
};
const statistics = () => {
  const [adminLevel, setAdminLevel] = useState(JSON.parse(localStorage.getItem('userInfo')).accountLevel);
  const [form] = Form.useForm();
  const [districtOptions, setDistrictOptions] = useState([]);
  const [streetOptions, setStreetOptions] = useState([]);
  const [communityOptions, setCommunityOptions] = useState([]);
  const [dateRange, setDateRange] = useState([]);
  const [loading, setLoading] = useState(false);
  const [rankType, setRankType] = useState('top5');
  // 统计数据
  const [stats, setStats] = useState({
    total: 0,
    month: 0,
    totalMonthRate: 0,
    processing: 0,
    reviewing: 0,
    delayed: 0,
    finished: 0,
    overtime: 0,
    overtimeMonth: 0,
    overtimeMonthRate: 0,
    avgTime: 0,
    avgTimeMonth: 0,
    avgTimeRate: 0,
    satisfaction: 0,
    satisfactionMonth: 0,
    satisfactionRate: 0,
  });
  // 图表数据
  const [chartData, setChartData] = useState([]);
  const [allTypeData, setAllTypeData] = useState([]);
  const [pieData, setPieData] = useState([]);
  // 诉求单量统计图表配置
  const echartsOption = {
    tooltip: {
      trigger: 'axis',
    },
    legend: {
      data: ['诉求单量', '诉求办结数'],
    },
    grid: {
      left: '3%',
      right: '3%',
      bottom: '10%',
      containLabel: true,
    },
    dataZoom: [
      {
        type: 'slider',
        show: true,
        startValue: 0,
        endValue: 10,
        height: 12,
        bottom: 0,
        showDetail: false,
        showDataShadow: false,
        fillerColor: '#dbdee5',
        borderColor: 'transparent',
        zoomLock: true,
        brushSelect: false,
        handleStyle: {
          opacity: 0
        }
      },
      {
        type: "inside",
        zoomOnMouseWheel: false,
        moveOnMouseMove: true,
        moveOnMouseWheel: true,
      },
    ],
    xAxis: {
      type: 'category',
      data: chartData.map(item => item.date),
      axisLabel: {
        interval: 'auto',
      }
    },
    yAxis: [
      {
        type: 'value',
        name: '诉求单量',
        min: 0,
      },
      {
        type: 'value',
        name: '诉求办结数',
        min: 0,
      },
    ],
    series: [
      {
        name: '诉求单量',
        type: 'bar',
        data: chartData.map(item => item.count),
        yAxisIndex: 0,
        itemStyle: { color: '#3b7cff' },
        barWidth: 30,
      },
      {
        name: '诉求办结数',
        type: 'line',
        data: chartData.map(item => item.finish),
        yAxisIndex: 1,
        itemStyle: { color: '#3bcfcf' },
        lineStyle: { width: 2 },
        smooth: true,
      },
    ],
  };
  // 问题类型排名图表配置
  const typeRankOption = {
    tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
    grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
    dataZoom: [
      {
        type: 'slider',
        show: true,
        startValue: 0,
        endValue: 4,
        bottom: 0,
        showDetail: false,
        showDataShadow: false,
        height: '100%',
        width: 10,
        fillerColor: '#dbdee5',
        borderColor: 'transparent',
        zoomLock: true,
        orient: 'vertical',
        brushSelect: false,
        handleStyle: {
          opacity: 0
        }
      },
      {
        type: "inside",
        zoomOnMouseWheel: false,
        moveOnMouseMove: true,
        moveOnMouseWheel: true,
        orient: 'vertical'
      },
    ],
    xAxis: { type: 'value', boundaryGap: [0, 0.01] },
    yAxis: { type: 'category', data: allTypeData.map(item => item.name) },
    series: [
      {
        name: '数量',
        type: 'bar',
        data: allTypeData.map(item => item.value),
        itemStyle: { color: '#4a90e2' },
        barWidth: 20,
        label: { show: true, position: 'right' },
      },
    ],
  };
  // 问题评价占比图表配置
  const pieOption = {
    tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
    legend: { orient: 'vertical', left: 'right', data: pieData.map(i => i.name) },
    color: ['#4a90e2', '#6dd400', '#f5a623', '#f44336'],
    series: [
      {
        name: '评价占比',
        type: 'pie',
        radius: ['50%', '70%'],
        avoidLabelOverlap: false,
        label: { show: false, position: 'center' },
        emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold' } },
        labelLine: { show: false },
        data: pieData,
      },
    ],
  };
  // 处理表单和rank筛选,组装参数并请求接口
  const handleSearch = async (values) => {
    setLoading(true);
    let time = '';
    if (dateRange && dateRange.length === 2) {
      time = `${dateRange[0].format('YYYY-MM-DD')} - ${dateRange[1].format('YYYY-MM-DD')}`;
    }
    const params = {
      cityCode: values.cityCode,
      districtCode: values.districtId,
      streetId: values.streetId,
      communityId: values.communityId,
      time,
      rank: rankType === 'all' ? undefined : rankType.replace('top', ''),
    };
    try {
      const res = await getStatisticsData(params);
      const data = res.data.analyticStatisticsOneVo;
      setStats({
        total: data.allTotal,
        month: data.thisMonthTotal,
        totalMonthRate: data.lastMonthCompareTotal,
        processing: data.nowTransactTotal,
        reviewing: data.auditTransactTotal,
        delayed: data.postponeTransactTotal,
        finished: data.completeTransactTotal,
        overtime: data.overtimeTransactTotal,
        overtimeMonth: data.thisMonthOvertimeTransactTotal,
        overtimeMonthRate: data.lastMonthOvertimeTransactCompareTotal,
        avgTime: data.averageTime,
        avgTimeMonth: data.thisMonthAverageTime,
        avgTimeRate: data.lastMonthCompareAverageTime,
        satisfaction: data.satisfactionRate,
        satisfactionMonth: data.thisMonthSatisfactionRate,
        satisfactionRate: data.lastMonthCompareSatisfactionRate,
      });
      setChartData((res.data.analyticStatisticsTwoVos || []).map(item => ({
        date: item.time,
        count: item.allTotal,
        finish: item.completeTotal,
      })));
      setAllTypeData((res.data.analyticStatisticsThreeVos || []).map(item => ({
        name: item.name,
        value: item.allTotal,
      })));
      const fourVo = res.data.analyticStatisticsFourVo || {};
      setPieData([
        { value: fourVo.greatSatisfactionRate, name: '非常满意' },
        { value: fourVo.satisfactionRate, name: '满意' },
        { value: fourVo.generalSatisfactionRate, name: '一般' },
        { value: fourVo.dissatisfactionRate, name: '不满意' },
      ]);
    } catch (e) {
      // 错误处理
    }
    setLoading(false);
  };
  // rankType变化时自动触发表单查询
  useEffect(() => {
    form.submit();
  }, [rankType]);
  useEffect(() => {
    getCommunityList().then(res => {
      const { bcRegions, comActs, comStreets } = res.data;
      if (adminLevel <= 2) {
        setDistrictOptions(bcRegions);
        return;
      }
      if (adminLevel <= 3) {
        setStreetOptions(comStreets);
        return;
      }
      setCommunityOptions(comActs);
    });
  }, [adminLevel]);
  // 处理区县选择
  const handleDistrictChange = async (value) => {
    if (!value) {
      setStreetOptions([]);
      setCommunityOptions([]);
      form.setFieldsValue({ streetId: undefined, communityId: undefined });
      return;
    }
    const res = await getNextRegion({ id: value, type: 1 });
    setStreetOptions(res.data || []);
    setCommunityOptions([]);
    form.setFieldsValue({ streetId: undefined, communityId: undefined });
  };
  // 处理街道选择
  const handleStreetChange = async (value) => {
    if (!value) {
      setCommunityOptions([]);
      form.setFieldsValue({ communityId: undefined });
      return;
    }
    const res = await getNextRegion({ id: value, type: 2 });
    setCommunityOptions(res.data || []);
    form.setFieldsValue({ communityId: undefined });
  };
  // 根据管理员级别获取可用的筛选项
  const getFilterItems = () => {
    const baseItems = [
      {
        name: 'communityId',
        label: '社区',
        component: <Select
          style={{ width: 200 }}
          options={communityOptions}
          placeholder="请选择社区"
          fieldNames={{ label: 'name', value: 'communityId' }}
          disabled={!form.getFieldValue('streetId')}
        />,
      },
    ];
    if (adminLevel <= 3) {
      baseItems.unshift({
        name: 'streetId',
        label: '街道',
        component: <Select
          style={{ width: 200 }}
          options={streetOptions}
          placeholder="请选择街道"
          fieldNames={{ label: 'name', value: 'streetId' }}
          onChange={handleStreetChange}
          disabled={!form.getFieldValue('districtId')}
        />,
      });
    }
    if (adminLevel <= 2) {
      baseItems.unshift({
        name: 'districtId',
        label: '区县',
        component: <Select
          style={{ width: 200 }}
          options={districtOptions}
          placeholder="请选择区县"
          fieldNames={{ label: 'regionName', value: 'regionCode' }}
          onChange={handleDistrictChange}
        />,
      });
    }
    return baseItems;
  };
  return (
    <div>
      <PageContainer header={{
        breadcrumb: {},
      }}
      title={'统计分析'}
      >
        {/* 筛选表单 */}
        <Card style={{ marginBottom: 24 }}>
          <Form
            form={form}
            layout="inline"
            style={{ marginBottom: 24 }}
            onFinish={handleSearch}
          >
            {getFilterItems().map((item) => (
              <Form.Item
                key={item.name}
                name={item.name}
                label={item.label}
              >
                {item.component}
              </Form.Item>
            ))}
            <Form.Item>
              <Space>
                <Button type="primary" onClick={() => form.submit()}>
                  查询
                </Button>
                <Button onClick={() => {
                  form.resetFields();
                  setStreetOptions([]);
                  setCommunityOptions([]);
                }}>
                  重置
                </Button>
              </Space>
            </Form.Item>
          </Form>
        </Card>
        {/* 标题 */}
        <div style={{ fontWeight: 600, fontSize: 18, marginBottom: 16, borderLeft: '4px solid #3b7cff', paddingLeft: 8 }}>
          诉求单量统计
        </div>
        {/* 统计卡片区 */}
        <Row gutter={[16, 16]} wrap={false}>
          <Col flex="1">
            <Card style={{ height: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
              <Statistic title="诉求单量总计" value={stats.total} />
              <div>
                本月 {stats.month}
                <span style={{ marginLeft: 8 }}>
                  同比上月
                  <Trend value={stats.totalMonthRate} />
                </span>
              </div>
            </Card>
          </Col>
          <Col flex="0 0 180px">
            <Card style={{ height: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
              <Statistic title="正在办理" value={stats.processing} />
            </Card>
          </Col>
          <Col flex="0 0 180px">
            <Card style={{ height: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
              <Statistic title="审核中" value={stats.reviewing} />
            </Card>
          </Col>
          <Col flex="0 0 180px">
            <Card style={{ height: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
              <Statistic title="延期办理" value={stats.delayed} />
            </Card>
          </Col>
          <Col flex="0 0 180px">
            <Card style={{ height: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
              <Statistic title="已办结" value={stats.finished} />
            </Card>
          </Col>
          <Col flex="1">
            <Card style={{ height: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
              <Statistic title="超时办理" value={stats.overtime} />
              <div>
                本月 {stats.overtimeMonth}
                <span style={{ marginLeft: 8 }}>
                  同比上月
                  <Trend value={stats.overtimeMonthRate} />
                </span>
              </div>
            </Card>
          </Col>
        </Row>
        <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
          <Col>
            <Card>
              <Statistic title="平均处理时间" value={stats.avgTime + '天'} />
              <div>
                本月 {stats.avgTimeMonth}天
                <span style={{ marginLeft: 8 }}>
                  同比上月
                  <Trend value={stats.avgTimeRate} />
                </span>
              </div>
            </Card>
          </Col>
          <Col>
            <Card>
              <Statistic title="总体满意率" value={stats.satisfaction + '%'} />
              <div>
                本月 {stats.satisfactionMonth}%
                <span style={{ marginLeft: 8 }}>
                  同比上月
                  <Trend value={stats.satisfactionRate} />
                </span>
              </div>
            </Card>
          </Col>
        </Row>
        {/* 时间筛选区 */}
        <div style={{ margin: '24px 0' }}>
          <Space>
            <DatePicker.RangePicker
              value={dateRange}
              onChange={(dates) => {
                setDateRange(dates);
                form.setFieldsValue({ time: dates });
                form.submit();
              }}
            />
            <Button onClick={() => {
              const end = moment();
              const start = moment().subtract(7, 'days');
              const dates = [start, end];
              setDateRange(dates);
              form.setFieldsValue({ time: dates });
              form.submit();
            }}>近7天</Button>
            <Button onClick={() => {
              const end = moment();
              const start = moment().subtract(30, 'days');
              const dates = [start, end];
              setDateRange(dates);
              form.setFieldsValue({ time: dates });
              form.submit();
            }}>近30天</Button>
          </Space>
        </div>
        {/* 图表区 */}
        <div style={{ background: '#fff', padding: 24 }}>
          <div style={{ width: '100%', overflowX: 'auto' }}>
            <div style={{ minWidth: '100%', height: 300 }}>
              <ReactECharts
                option={echartsOption}
                style={{ height: '100%', width: '100%' }}
                opts={{ renderer: 'svg' }}
              />
            </div>
          </div>
        </div>
        {/* 问题类型排名 */}
        <div style={{ background: '#fff', padding: 24, marginTop: 32 }}>
          <div style={{ fontWeight: 600, fontSize: 16, marginBottom: 16, borderLeft: '4px solid #3b7cff', paddingLeft: 8 }}>
            问题类型排名
            <Select
              style={{ width: 120, marginLeft: 16 }}
              value={rankType}
              onChange={setRankType}
              options={[
                { label: '排名前5', value: 'top5' },
                { label: '排名前10', value: 'top10' },
                { label: '全部排名', value: 'all' },
              ]}
            />
          </div>
          <div style={{ maxHeight: 320, overflowY: 'auto' }}>
            <ReactECharts option={typeRankOption} style={{ height: Math.max(60 * allTypeData.length, 300) }} />
          </div>
        </div>
        {/* 问题评价占比 */}
        <div style={{ background: '#fff', padding: 24, marginTop: 32 }}>
          <div style={{ fontWeight: 600, fontSize: 16, marginBottom: 16, borderLeft: '4px solid #3b7cff', paddingLeft: 8 }}>
            问题评价占比
          </div>
          <ReactECharts option={pieOption} style={{ height: 300 }} />
        </div>
      </PageContainer>
    </div>
  );
};
export default statistics;
management/src/pages/statistics/service.js
New file
@@ -0,0 +1,23 @@
import { request } from '@umijs/max';
// 获取社区列表
export const getCommunityList = async (params) => {
    return request('/api/huacheng-sangeshenbian/analytic-statistics/getRegion', {
        method: 'GET',
        params
    });
}
// 获取下一级区域列表
export const getNextRegion = async (params) => {
    return request('/api/huacheng-sangeshenbian/analytic-statistics/getNextRegion', {
        method: 'GET',
        params
    });
}
// 获取统计数据
export const getStatisticsData = async (data) => {
    return request('/api/huacheng-sangeshenbian/analytic-statistics/data', {
        method: 'post',
        data
    });
}