Browse Source

fix: a1,a2屏接口联调

mzr 3 days ago
parent
commit
99b107647c

+ 7 - 1
.eslintrc.js

@@ -1,10 +1,16 @@
+/*
+ * @Author: wjc
+ * @Date: 2021-05-31 09:36:11
+ * @LastEditors: wjc
+ * @LastEditTime: 2025-11-25 10:07:44
+ * @Description: 
+ */
 module.exports = {
   root: true,
   env: {
     node: true
   },
   'extends': [
-    'plugin:vue/essential',
     '@vue/standard'
   ],
   rules: {

+ 10 - 0
babel.config.js

@@ -1,5 +1,15 @@
+/*
+ * @Author: wjc
+ * @Date: 2021-05-31 09:36:11
+ * @LastEditors: wjc
+ * @LastEditTime: 2025-11-26 15:49:51
+ * @Description:
+ */
 module.exports = {
   presets: [
     '@vue/app'
+  ],
+  plugins: [
+    '@babel/plugin-proposal-optional-chaining'
   ]
 }

+ 1 - 0
package.json

@@ -27,6 +27,7 @@
     "vuex": "^3.0.1"
   },
   "devDependencies": {
+    "@babel/plugin-proposal-optional-chaining": "^7.21.0",
     "@vue/cli-plugin-babel": "^3.11.0",
     "@vue/cli-plugin-eslint": "^3.11.0",
     "@vue/cli-service": "^3.11.0",

+ 135 - 0
src/api/hui-jia.js

@@ -0,0 +1,135 @@
+/*
+ * @Author: wjc
+ * @Date: 2025-11-21 10:08:08
+ * @LastEditors: wjc
+ * @LastEditTime: 2025-11-26 15:40:49
+ * @Description:
+ */
+import axios from '../assets/js/api.request'
+export default {
+  /**
+   * 部分业务 http://yapi.wisdomcity.com.cn/project/25/interface/api/26531
+   */
+  getBusinessData (data) {
+    return axios.request({
+      url: '/platform/bigData/business/statistics',
+      params: data,
+      method: 'get'
+    })
+  },
+  /**
+   * 智能门禁 http://yapi.wisdomcity.com.cn/project/25/interface/api/26522
+   */
+  getFaceDeviceData (data) {
+    return axios.request({
+      url: '/platform/bigData/faceDeviceManage/statistics',
+      params: data,
+      method: 'get'
+    })
+  },
+  /**
+   * 智能电表 http://yapi.wisdomcity.com.cn/project/25/interface/api/26504
+   */
+  getElectricData (data) {
+    return axios.request({
+      url: '/platform/bigData/meterHouse/electricMeter/monthStats',
+      params: data,
+      method: 'get'
+    })
+  },
+  /**
+   * 智能水表 http://yapi.wisdomcity.com.cn/project/25/interface/api/26513
+   */
+  getWaterData (data) {
+    return axios.request({
+      url: '/platform/bigData/meterHouse/waterMeter/monthStats',
+      params: data,
+      method: 'get'
+    })
+  },
+  /**
+   * 实施相关数据 http://yapi.wisdomcity.com.cn/project/25/interface/api/26576
+   */
+  getScreenData (data) {
+    return axios.request({
+      url: '/platform/bigData/screen/statistics',
+      params: data,
+      method: 'get'
+    })
+  },
+  /**
+   * 数据大屏线上流水数据统计 http://yapi.wisdomcity.com.cn/project/25/interface/api/26468
+   */
+  getOnlineWaterStatistics () {
+    return axios.request({
+      url: '/platform/bigData/payRecord/statistics',
+      method: 'get'
+    })
+  },
+  /** 线上缴费金额月统计表(近12月) http://yapi.wisdomcity.com.cn/project/25/interface/api/26477
+   */
+  getOnlinePayMonth () {
+    return axios.request({
+      url: '/platform/bigData/payRecord/monthStats',
+      method: 'get'
+    })
+  },
+  /**
+   * 在线支付-周 线上缴费金额周统计表(近5周)http://yapi.wisdomcity.com.cn/project/25/interface/api/26486
+   */
+  getOnlinePayWeek () {
+    return axios.request({
+      url: '/platform/bigData/payRecord/weekStats',
+      method: 'get'
+    })
+  },
+  /**
+   * 线上缴费金额日统计表(近1月) http://yapi.wisdomcity.com.cn/project/25/interface/api/26495
+   */
+  getOnlinePayDay () {
+    return axios.request({
+      url: '/platform/bigData/payRecord/dayStats',
+      method: 'get'
+    })
+  },
+  /**
+   * 客户数据 http://yapi.wisdomcity.com.cn/project/25/interface/api/26540
+   */
+  getCustomerStatistics () {
+    return axios.request({
+      url: '/platform/bigData/customers/statistics',
+      method: 'get'
+    })
+  },
+
+  /**
+   * 项目体量 http://yapi.wisdomcity.com.cn/project/25/interface/api/26549
+   */
+  getCommunityTypeSizeStatistics () {
+    return axios.request({
+      url: '/platform/bigData/community/typeSize/statistics',
+      method: 'get'
+    })
+  },
+
+  /**
+   * 项目地区分布 http://yapi.wisdomcity.com.cn/project/25/interface/api/26558
+   */
+  getCommunityRegionStatistics () {
+    return axios.request({
+      url: '/platform/bigData/community/region/statistics',
+      method: 'get'
+    })
+  },
+
+  /**
+   * 用户信息统计 http://yapi.wisdomcity.com.cn/project/25/interface/api/26567
+   */
+  getUsersStatistics () {
+    return axios.request({
+      url: '/platform/bigData/users/statistics',
+      method: 'get'
+    })
+  }
+
+}

+ 4 - 4
src/api/index.js

@@ -1,16 +1,16 @@
 /*
  * @Author: mozhuangru
- * @LastEditors: wangjiacheng
+ * @LastEditors: wjc
  * @Description: api
  * @Date: 2019-10-09 09:21:46
- * @LastEditTime: 2021-10-14 12:04:06
+ * @LastEditTime: 2025-11-21 10:10:57
  */
 import api from './api'
 import totalSum from './total-sum'
-import screen from './screen'
+import huiJiaApi from './hui-jia'
 
 export {
   api,
   totalSum,
-  screen
+  huiJiaApi
 }

+ 0 - 28
src/api/screen.js

@@ -1,28 +0,0 @@
-import axios from '../assets/js/api.request'
-//  http://yapi.wisdomcity.com.cn/project/25/interface/api/26468
-export default {
-  getOnlineWaterStatistics () {
-    return axios.request({
-      url: '/platform/bigData/payRecord/statistics',
-      method: 'get'
-    })
-  },
-  getOnlinePayMonth () {
-    return axios.request({
-      url: '/platform/bigData/payRecord/monthStats',
-      method: 'get'
-    })
-  },
-  getOnlinePayWeek () {
-    return axios.request({
-      url: '/platform/bigData/payRecord/weekStats',
-      method: 'get'
-    })
-  },
-  getOnlinePayDay () {
-    return axios.request({
-      url: '/platform/bigData/payRecord/dayStats',
-      method: 'get'
-    })
-  }
-}

+ 13 - 1
src/main.js

@@ -2,7 +2,7 @@
  * @Author: WangQiBiao
  * @Date: 2019-09-24 17:50:53
  * @LastEditors: wjc
- * @LastEditTime: 2024-05-11 10:58:33
+ * @LastEditTime: 2025-11-26 15:54:39
  * @Description:
  */
 import Vue from 'vue'
@@ -22,6 +22,18 @@ Vue.use(ElementUI)
 Object.keys(filters).forEach(key => {
   Vue.filter(key, filters[key])
 })
+Vue.filter('safeGet', function (obj, path, defaultValue = '无') {
+  // 分割路径(如 'service.work_template.display_name' 拆分为数组)
+  const keys = path.split('.')
+  let result = obj
+  for (const key of keys) {
+    // 若中间层级为 null/undefined,直接返回默认值
+    if (result === null || result === undefined) return defaultValue
+    result = result[key]
+  }
+  // 最终值为空时返回默认值
+  return result || defaultValue
+})
 
 Vue.use(VueTianditu, {
   v: '4.0',

+ 151 - 44
src/views/hui-jia/b1-screen/index.vue

@@ -5,12 +5,10 @@
     </div>
     <div class="b1-screen-content">
       <div class="item-1">
-        <CommunityMap />
+        <CommunityMap :center="[105,34]" :zoom="4" />
       </div>
       <div class="middle-wrap">
-        <div class="item-2">
-          <User />>
-        </div>
+        <div class="item-2"><User />></div>
         <div class="item-3">
           <Resident :showOperate="false" class="b1-resident" />
         </div>
@@ -19,40 +17,149 @@
         </div>
       </div>
       <div class="right-wrap">
-        <Market />
-        <implementationTotal />
+        <Market :data="state.market_vs_month" />
+        <implementationTotal :data="state.summary" />
       </div>
       <div class="bottom-wrap">
-        <implementationIng />
-        <implementationNowYear />
+        <implementationIng :data="state.data" />
+        <implementationNowYear :data="serviceData" />
       </div>
     </div>
   </div>
 </template>
 
 <script>
-import implementationTotal from '../components/implementary/total.vue'
-import implementationIng from '../components/implementary/ing.vue'
-import implementationNowYear from '../components/implementary/now-year.vue'
-import Market from '../components/market.vue'
-import CommunityMap from '../components/community-map'
-import CommunityType from '../components/community-type'
-import Resident from '../components/resident'
-import User from '../components/user'
+  import implementationTotal from "../components/implementary/total.vue"
+  import implementationIng from "../components/implementary/ing.vue"
+  import implementationNowYear from "../components/implementary/now-year.vue"
+  import Market from "../components/market.vue"
+  import CommunityMap from "../components/community-map"
+  import CommunityType from "../components/community-type"
+  import Resident from "../components/resident"
+  import User from "../components/user"
+
+  import { huiJiaApi } from "@/api"
 
-export default {
-  name: 'B1Screen',
-  components: {
-    implementationTotal,
-    implementationIng,
-    implementationNowYear,
-    Market,
-    CommunityMap,
-    CommunityType,
-    Resident,
-    User
+  export default {
+    name: "B1Screen",
+    components: {
+      implementationTotal,
+      implementationIng,
+      implementationNowYear,
+      Market,
+      CommunityType,
+      Resident,
+      User,
+      CommunityMap,
+    },
+    data() {
+      return {
+        state: {
+          data: [], // 进行中的实施服务列表
+          market_vs_month: {
+            customer: {
+              // 新增企业
+              last_month: 0,
+              this_month: 0,
+            },
+            followup: {
+              // 新增企业
+              last_month: 0,
+              this_month: 0,
+            },
+            contact: {
+              // 新增联系人
+              last_month: 0,
+              this_month: 0,
+            },
+            residence: {
+              // 新增小区
+              last_month: 0,
+              this_month: 0,
+            },
+            contract: {
+              // 新增合同
+              last_month: 0,
+              this_month: 0,
+            },
+            household: {
+              // 新增户数
+              last_month: 0,
+              this_month: 1,
+            },
+          }, // 市场拓展数据
+          summary: {
+            cities: 0, // 城市总数
+            customers: 0, // 企业总数
+            contracts: 0, // 合同总数
+            perform_times: 0, // 履约次数
+            service_time: 0, // 服务时长
+            residences: 0, // 小区总数
+            contracted_households: 0, // 签约户数
+            actual_households: 0, // 实施户数
+          }, // 实施服务,汇总
+          year_summary: {
+            cities: 0, // 履约城市
+            customers: 0, // 服务企业
+            residences: 0, // 服务小区
+            contracts: 0, // 合同份数
+            contracted_households: "0", // 签约户数
+            actual_households: "0", // 实施户数
+          },
+          service_ing: {
+            // 实施服务,进行中
+            cities: 0,
+            customers: 0,
+            contracts: 0,
+            residences: 0,
+            contracted_households: "0",
+            plan: 0,
+          },
+          service_last_month: {
+            // 实施服务,上月
+            cities: 0,
+            customers: 0,
+            contracts: 0,
+            residences: 0,
+            contracted_households: 0,
+            actual_households: 0,
+          },
+        },
+        serviceData: {},
+      }
+    },
+    mounted() {
+      this.getData()
+    },
+    methods: {
+      getServiceData() {
+        // 图表字段顺序
+        const properties = [
+          "contracted_households",
+          "contracts",
+          "residences",
+          "customers",
+          "cities",
+        ]
+        const result = {
+          year_summary: properties.map((key) => this.state.year_summary[key]),
+          service_ing: properties.map((key) => this.state.service_ing[key]),
+          service_last_month: properties.map(
+            (key) => this.state.service_last_month[key]
+          ),
+        }
+        this.serviceData = result
+      },
+      getData() {
+        huiJiaApi.getScreenData().then((res) => {
+          if (res && res.data) {
+            this.state = res.data.data
+            this.getServiceData()
+          }
+        })
+      },
+    },
   }
-}
 </script>
 
 <style lang="scss" scoped>
@@ -76,43 +183,41 @@ export default {
     width: 100%;
     display: flex;
     flex-wrap: wrap;
-    justify-content: flex-start;
+    justify-content: center;
+    align-items: center;
     gap: halfW(16);
     .item-1 {
-      width: calc(25% - $gap-padding);
-      height: calc(64% - $gap-padding);
-      // background: #f5f5f5;
+      width: calc(25% - $gap-padding - $gap-padding);
+      height: calc(64% - $gap-padding - $gap-padding);
     }
     .middle-wrap {
-      width: calc(50% - $gap-padding - $gap-padding);
-      height: calc(64% - $gap-padding);
+      width: calc(
+        50% - $gap-padding - $gap-padding - $gap-padding - $gap-padding
+      );
+      height: calc(64% - $gap-padding - $gap-padding);
       display: flex;
       flex-wrap: wrap;
       gap: halfW(16);
       justify-content: flex-start;
+      align-items: center;
       .item-2 {
         flex: 0 0 100%;
         width: 100%;
-        height: calc(38% - $gap-padding);
-        // background: #f5f5f5;
+        height: calc(37% - $gap-padding);
       }
       .item-3 {
-        // background: green;
-        flex: 0 0 calc(50% - $gap-padding);
         width: calc(50% - $gap-padding);
-        height: calc(62% - $gap-padding);
+        height: calc(63% - $gap-padding);
       }
       .item-4 {
-        flex: 0 0 calc(50% - $gap-padding);
         width: calc(50% - $gap-padding);
-        height: calc(62% - $gap-padding);
-        // background: green;
+        height: calc(63% - $gap-padding);
       }
     }
     .right-wrap {
       display: flex;
       flex-direction: column;
-      width: calc(25% - $gap-padding);
+      width: calc(25% - $gap-padding - $gap-padding);
       height: calc(64% - $gap-padding);
       gap: calc($gap-padding * 2);
       .total-container {
@@ -126,7 +231,9 @@ export default {
       display: flex;
       justify-content: space-around;
       gap: halfW(16);
-      width: 100%;
+      width: calc(
+        100% - $gap-padding - $gap-padding - $gap-padding - $gap-padding
+      );
       height: calc(36% - $gap-padding);
       .now-year-container {
         width: 50%;

+ 2 - 2
src/views/hui-jia/b2-screen/index.vue

@@ -2,8 +2,8 @@
  * @Author: wjc
  * @Date: 2025-11-19 16:09:12
  * @LastEditors: wjc
- * @LastEditTime: 2025-11-20 10:58:02
- * @Description:
+ * @LastEditTime: 2025-11-25 10:25:53
+ * @Description: 
 -->
 <template>
   <div class="b2-screen">

+ 91 - 10
src/views/hui-jia/components/community-map/components/map-list.vue

@@ -15,7 +15,16 @@
           <p class="list-name inline-block" v-text="title"></p>
           <p class="list-count inline-block right" v-text="extra"></p>
       </div>
-
+      <vueSeamless
+        :data="list"
+        :class-option="optionScroll"
+        class="map-scroll"
+      >
+        <div v-for="(info, index) in list" :key="index" class="list">
+          <p class="list-name inline-block" v-text="info.label"></p>
+          <p class="list-count inline-block right" v-text="info.content"></p>
+        </div>
+      </vueSeamless>
       <!-- <WcScrollList :list="list" class="list">
           <template v-slot:default="{ info }">
           <p class="list-name inline-block" v-text="info.label"></p>
@@ -27,9 +36,11 @@
 
 <script>
 // import WcScrollList from '_c/wc-scroll-list/wc-scroll-list.vue'
+import vueSeamless from "vue-seamless-scroll"
 
 export default {
   components: {
+    vueSeamless
     // WcScrollList
   },
   name: 'MapList',
@@ -65,6 +76,71 @@ export default {
       default: true
     }
   },
+  data() {
+    return {
+      mapData: [
+        {
+          label: '海南省',
+          content: '100'
+        },
+        {
+          label: '湖北省',
+          content: '100'
+        },
+        {
+          label: '湖南省',
+          content: '100'
+        },
+        {
+          label: '广东省',
+          content: '100'
+        },
+        {
+          label: '广西省',
+          content: '100'
+        },
+        {
+          label: '江西省',
+          content: '100'
+        },
+        {
+          label: '江苏省',
+          content: '100'
+        },
+        {
+          label: '浙江省',
+          content: '100'
+        },
+        {
+          label: '福建省',
+          content: '100'
+        },
+        {
+          label: '河南省',
+          content: '100'
+        },
+        {
+          label: '河北省',
+          content: '100'
+        },
+        {
+          label: '河南省',
+          content: '100'
+        },
+        {
+          label: '河北省',
+          content: '100'
+        }
+      ]
+    }
+  },
+  computed: {
+    optionScroll() {
+        return {
+          step: 0.5,
+        }
+      },
+  },
   watch: {
     position: {
       handler () {
@@ -73,6 +149,9 @@ export default {
       deep: true
     }
   },
+  mounted () {
+    this.draw()
+  },
   methods: {
     draw () {
       if (this.$refs.customOverlay) {
@@ -114,26 +193,28 @@ export default {
   }
 
   .header {
-    height:halfH(58);
-    padding: 0px 20px;
-    line-height:halfH(58);
+    // height:halfH(50%);
+    padding: halfH(12) halfW(20);
+    // line-height:halfH(58);
     background:rgba(34,127,233,0.6);
-    font-size:20px;
+    font-size: halfH(18);
     font-family:Hiragino Sans GB;
     font-weight:bold;
     color:rgba(255,255,255,1);
   }
-
+  .map-scroll {
+    overflow: auto;
+  }
   .list {
-    padding: 20px;
-    top: halfH(58);
-    font-size:16px;
+    padding: halfH(12) halfW(20);
+    // top: halfH(58);
+    font-size: halfH(16);
     font-family:Hiragino Sans GB;
     font-weight:normal;
     color:rgba(255,255,255,1);
 
     .list-name {
-      height: halfH(30);
+      // height: halfH(30);
       width: 70%;
       @include text-ellipsis;
     }

+ 90 - 86
src/views/hui-jia/components/community-map/index.vue

@@ -1,13 +1,8 @@
 <template>
     <Card :title="title" :icon="icon" class="community-map">
       <div class="map-con">
-        <tdt-map style="position: relative;z-index: 0;" mapStyle="indigo" :center="[
-            105,
-            34,
-          ]"
-          :zoom="5"
-          :minZoom="5"
-          :maxZoom="5"
+        <tdt-map style="position: relative;z-index: 0;" mapStyle="indigo" :center="center"
+          :zoom="zoom"
           :controls="[
             {
               name: 'Scale',
@@ -37,13 +32,13 @@
             </tdt-label>
           <!-- </div> -->
         </tdt-map>
-        <MapList title="省份" :isShow="provinceList.length" :list="provinceList" :position="{left: 30, bottom: 30}"></MapList>
+        <MapList title="省份" :list="provinceList" :position="{left: 30, bottom: 30}"></MapList>
       </div>
     </Card>
   </template>
 <script>
 import provincePosition from './province-positon'
-// import { screen } from '@/api'
+import { huiJiaApi } from '@/api'
 import Card from '@/views/hui-jia/components/card'
 import icon from './images/icon-title.png'
 import MapList from './components/map-list.vue'
@@ -53,99 +48,108 @@ export default {
     Card,
     MapList
   },
+  props: {
+    zoom: {
+      type: Number,
+      default: 5
+    },
+    center: {
+      type: Array,
+      default: () => [
+            107,
+            32,
+          ]
+    }
+  },
   data () {
     return {
       title: '项目地区分布',
       icon: icon,
       provinceList: [],
-      state: {
-        center: [113.280637, 23.125178],
-        zoom: 12
-      }
     }
   },
   mounted () {
     // setTimeout(() => {
     //   this.mapInit()
     // }, 500)
-    // this.getProjectStatistics()
+    // this.getCommunityRegionStatistics()
   },
   methods: {
     mapInit () {
-      const _data = [
-        {
-          provinceName: '海南省',
-          count: 100
-        },
-        {
-          provinceName: '广东省',
-          count: 200
-        },
-        {
-          provinceName: '四川省',
-          count: 300
-        },
-        {
-          provinceName: '贵州省',
-          count: 400
-        },
-        {
-          provinceName: '云南省',
-          count: 500
-        },
-        {
-          provinceName: '西藏自治区',
-          count: 600
-        },
-        {
-          provinceName: '陕西省',
-          count: 700
-        },
-        {
-          provinceName: '甘肃省',
-          count: 800
-        }
-      ]
-      let arr = []
-      _data.forEach((item, i) => {
-        let cName = item.provinceName.substring(0, 2)
-        let province = provincePosition.find(p => p.name.indexOf(cName) !== -1)
-        let ele = {
-          label: province.name,
-          content: item.count,
-          point: {
-            lng: province.geoCoord[0],
-            lat: province.geoCoord[1]
-          }
-        }
-        arr.push(ele)
-      })
-      this.provinceList = arr
-    }
-    // getProjectStatistics () {
-    //   this.provinceList = []
-    //   screen.getProjectStatistics()
-    //     .then(({ data }) => {
-    //       if (data.code === '200') {
-    //         let _data = data.data
-    //         let arr = []
-    //         _data.provinceStatistics.forEach((item, i) => {
-    //           let cName = item.provinceName.substring(0, 2)
-    //           let province = provincePosition.find(p => p.name.indexOf(cName) !== -1)
-    //           let ele = {
-    //             label: province.name,
-    //             content: item.count,
-    //             point: {
-    //               lng: province.geoCoord[0],
-    //               lat: province.geoCoord[1]
-    //             }
-    //           }
-    //           arr.push(ele)
-    //         })
-    //         this.provinceList = arr
+      // const _data = [
+      //   {
+      //     provinceName: '海南省',
+      //     count: 100
+      //   },
+      //   {
+      //     provinceName: '广东省',
+      //     count: 200
+      //   },
+      //   {
+      //     provinceName: '四川省',
+      //     count: 300
+      //   },
+      //   {
+      //     provinceName: '贵州省',
+      //     count: 400
+      //   },
+      //   {
+      //     provinceName: '云南省',
+      //     count: 500
+      //   },
+      //   {
+      //     provinceName: '西藏自治区',
+      //     count: 600
+      //   },
+      //   {
+      //     provinceName: '陕西省',
+      //     count: 700
+      //   },
+      //   {
+      //     provinceName: '甘肃省',
+      //     count: 800
+      //   }
+      // ]
+    //   let arr = []
+    //   _data.forEach((item, i) => {
+    //     let cName = item.provinceName.substring(0, 2)
+    //     let province = provincePosition.find(p => p.name.indexOf(cName) !== -1)
+    //     let ele = {
+    //       label: province.name,
+    //       content: item.count,
+    //       point: {
+    //         lng: province.geoCoord[0],
+    //         lat: province.geoCoord[1]
     //       }
-    //     })
+    //     }
+    //     arr.push(ele)
+    //   })
+    //   this.provinceList = arr
     // }
+    // getCommunityRegionStatistics () {
+      this.provinceList = []
+      huiJiaApi.getCommunityRegionStatistics()
+        .then(({ data }) => {
+          if (data.code === '200') {
+            let _data = data.data
+            let arr = []
+            _data.provinceStatistics.forEach((item, i) => {
+              let cName = item.provinceName.substring(0, 2)
+              let province = provincePosition.find(p => p.name.indexOf(cName) !== -1)
+              let ele = {
+                label: province.name,
+                content: item.count,
+                point: {
+                  lng: province.geoCoord[0],
+                  lat: province.geoCoord[1]
+                }
+              }
+              arr.push(ele)
+            })
+            this.provinceList = arr
+          }
+        })
+    }
   }
 }
 </script>

+ 30 - 2
src/views/hui-jia/components/community-type/components/bar.vue

@@ -6,6 +6,34 @@ import * as echarts from 'echarts'
 
 export default {
   name: 'bar',
+  props:{
+    xAxisData: {
+      type: Array,
+      default: () => []
+    },
+    seriesData: {
+      type: Array,
+      default: () => []
+    }
+  },
+  watch: {
+    xAxisData: {
+      handler (newVal, oldVal) {
+        if (newVal.length) {
+          this.initChart()
+        }
+      },
+      deep: true
+    },
+    seriesData: {
+      handler (newVal, oldVal) {
+        if (newVal.length) {
+          this.initChart()
+        }
+      },
+      deep: true
+    }
+  },
   mounted () {
     this.initChart()
   },
@@ -23,8 +51,8 @@ export default {
     },
     initChart () {
       const myChart = echarts.init(this.$refs.onlinePayBar)
-      const xAxisData = ['1k以下', '1-5k', '5-10k', '10-20k', '20k以上']
-      const seriesData = [5, 20, 36, 10, 10]
+      const xAxisData = this.xAxisData
+      const seriesData = this.seriesData
       // let max = 0
       // max = seriesData.reduce((max, num) => {
       //   return Math.max(Number(max), Number(num))

+ 25 - 7
src/views/hui-jia/components/community-type/index.vue

@@ -1,7 +1,7 @@
 <template>
     <Card :title="title" :icon="icon" class="community-type">
       <ThreePie :value="value" :legendIcons="legendIcons" class="w-full"></ThreePie>
-      <Bar :value="value" v-if="showBar" class="w-full"></Bar>
+      <Bar :xAxisData="xAxisData" :seriesData="seriesData" v-if="showBar" class="w-full"></Bar>
     </Card>
   </template>
 <script>
@@ -13,6 +13,7 @@ import iconZz from './images/icon-zz.png'
 import iconSy from './images/icon-sy.png'
 import iconZh from './images/icon-zh.png'
 import Bar from './components/bar.vue'
+import { huiJiaApi } from '@/api'
 
 export default {
   name: 'CommunityType',
@@ -29,20 +30,25 @@ export default {
   },
   data () {
     return {
+      xAxisData: [],
+      seriesData: [],
       title: '项目类型',
       icon: icon,
       value: [
         {
           value: 100,
-          name: '住宅'
+          name: '住宅',
+          key: 'flatCount'
         },
         {
           value: 200,
-          name: '商业'
+          name: '商业',
+          key: 'businessCount'
         },
         {
           value: 300,
-          name: '综合'
+          name: '综合',
+          key: 'otherCount'
         }
       ],
       legendIcons: [
@@ -54,10 +60,22 @@ export default {
       subtext: '项目类型占比'
     }
   },
+  mounted() {
+    this.getCommunityTypeSizeStatistics()
+  },
   methods: {
-    // getLegendIcons (url) {
-    //   return require(url)
-    // }
+    getCommunityTypeSizeStatistics () {
+      huiJiaApi.getCommunityTypeSizeStatistics().then(res => {
+        if(res && res.data){
+          const _data = res.data.data
+          this.xAxisData = [_data.intervalOneName, _data.intervalTwoName, _data.intervalThreeName, _data.intervalFourName, _data.intervalFiveName]
+          this.seriesData = [_data.intervalOneCount, _data.intervalTwoCount, _data.intervalThreeCount, _data.intervalFourCount, _data.intervalFiveCount]
+          this.value.forEach(item => {
+            item.value = res.data.data[item.key]
+          })
+        }
+      })
+    }
   }
 }
 </script>

+ 105 - 268
src/views/hui-jia/components/hardware.vue

@@ -11,7 +11,7 @@
       <div class="device-info">
         <img src="@/assets/images/dblxzhb.png" class="title-icon" />
         <div>
-          <div class="device-count">8921</div>
+          <div class="device-count">{{ electricState.deviceCount }}</div>
           <div class="device-name">智能电表</div>
         </div>
       </div>
@@ -19,28 +19,28 @@
         <div class="data-card">
           <div class="data-label">年度用电</div>
           <div>
-            <div class="data-value">428921</div>
+            <div class="data-value">{{ electricState.annualUsage }}</div>
             <div class="data-unit">kW·h</div>
           </div>
         </div>
         <div class="data-card">
           <div class="data-label">月均用电</div>
           <div>
-            <div class="data-value">18921</div>
+            <div class="data-value">{{ electricState.monthlyAvgUsage }}</div>
             <div class="data-unit">kW·h</div>
           </div>
         </div>
         <div class="data-card">
           <div class="data-label">日均用电</div>
           <div>
-            <div class="data-value">8921</div>
+            <div class="data-value">{{ electricState.dailyAvgUsage }}</div>
             <div class="data-unit">kW·h</div>
           </div>
         </div>
         <div class="data-card">
           <div class="data-label">开关次数</div>
           <div>
-            <div class="data-value">887</div>
+            <div class="data-value">{{ electricState.switchCount }}</div>
             <div class="data-unit">近12个月</div>
           </div>
         </div>
@@ -57,7 +57,7 @@
       <div class="device-info">
         <img src="@/assets/images/zhnmj.png" class="title-icon" />
         <div>
-          <div class="device-count">8921</div>
+          <div class="device-count">{{ waterState.deviceCount }}</div>
           <div class="water-device-name">智能水表</div>
         </div>
       </div>
@@ -65,28 +65,28 @@
         <div class="data-card">
           <div class="data-label">年度用水</div>
           <div>
-            <div class="data-value">89321</div>
+            <div class="data-value">{{ waterState.annualUsage }}</div>
             <div class="water-data-unit">m³</div>
           </div>
         </div>
         <div class="data-card">
           <div class="data-label">月均用水</div>
           <div>
-            <div class="data-value">9212</div>
+            <div class="data-value">{{ waterState.monthlyAvgUsage }}</div>
             <div class="water-data-unit">m³</div>
           </div>
         </div>
         <div class="data-card">
           <div class="data-label">日均用水</div>
           <div>
-            <div class="data-value">921</div>
+            <div class="data-value">{{ waterState.dailyAvgUsage }}</div>
             <div class="water-data-unit">m³</div>
           </div>
         </div>
         <div class="data-card">
           <div class="data-label">开关次数</div>
           <div>
-            <div class="data-value">887</div>
+            <div class="data-value">{{ waterState.switchCount }}</div>
             <div class="water-data-unit">近12个月</div>
           </div>
         </div>
@@ -96,268 +96,105 @@
 </template>
 
 <script>
-import * as echarts from 'echarts'
-export default {
-  name: 'HardwareData',
-  data () {
-    return {
-      powerChartInstance: null,
-      waterChartInstance: null,
-      // 模拟月度数据
-      monthlyData: [
-        { month: '十二月', power: 220000, water: 180000 },
-        { month: '一月', power: 210000, water: 170000 },
-        { month: '二月', power: 230000, water: 190000 },
-        { month: '三月', power: 225000, water: 185000 },
-        { month: '四月', power: 215000, water: 175000 },
-        { month: '五月', power: 200000, water: 160000 },
-        { month: '六月', power: 205000, water: 165000 },
-        { month: '七月', power: 210000, water: 170000 },
-        { month: '八月', power: 215000, water: 175000 },
-        { month: '九月', power: 220000, water: 180000 },
-        { month: '十月', power: 225000, water: 185000 },
-        { month: '十一月', power: 230000, water: 190000 }
-      ]
-    }
-  },
-  mounted () {
-    this.initPowerChart()
-    this.initWaterChart()
-    window.addEventListener('resize', this.handleResize)
-  },
-  beforeDestroy () {
-    window.removeEventListener('resize', this.handleResize)
-    if (this.powerChartInstance) {
-      this.powerChartInstance.dispose()
-    }
-    if (this.waterChartInstance) {
-      this.waterChartInstance.dispose()
-    }
-  },
-  methods: {
-    // 初始化用电量柱状图
-    initPowerChart () {
-      this.powerChartInstance = echarts.init(this.$refs.powerChart)
+  import { huiJiaApi } from "@/api"
+  import * as echarts from "echarts"
 
-      const option = {
-        tooltip: {
-          trigger: 'axis',
-          backgroundColor: 'rgba(0, 0, 0, 0.8)',
-          borderColor: 'rgba(64, 158, 255, 0.3)',
-          textStyle: {
-            color: '#fff'
-          },
-          formatter: (params) => {
-            let result = ``
-            params.forEach((param) => {
-              result += `${param.name}<br/>${
-                param.seriesName
-              }: ${param.value.toLocaleString()}<br/>`
-            })
-            return result
-          }
-        },
-        grid: {
-          left: '1%',
-          right: '1%',
-          top: '5%',
-          bottom: '2%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: this.monthlyData.map((item) => item.month),
-          axisLine: {
-            lineStyle: {
-              color: 'rgba(160, 179, 214, 0.3)',
-              type: 'dashed'
-            }
-          },
-          axisLabel: {
-            color: 'rgba(160, 179, 214, 0.7)',
-            fontSize: 11,
-            interval: 0
-          },
-          axisTick: {
-            show: false
-          },
-          splitLine: {
-            show: true,
-            // 将坐标轴内的线设置为虚线
-            lineStyle: {
-              color: 'rgba(160, 179, 214, 0.3)',
-              type: 'dashed'
-            }
-          }
+  export default {
+    name: "HardwareData",
+    data() {
+      return {
+        electricState: {
+          deviceCount: 8921, // 设备数量
+          annualUsage: 428921, // 年度用量 近12月的用量之和
+          monthlyAvgUsage: 18921, // 月均用量 近12月用量月平均数
+          dailyAvgUsage: 8921, // 日均用量 近12月用量日平均数
+          switchCount: 887, // 开关次数 近12月成功开启、关闭次数
+          list: [
+            {
+              statData: "2024-01", // 时间段
+              count: 8921, // 总数
+            },
+            {
+              statData: "2024-02", // 时间段
+              count: 7891, // 总数
+            },
+            {
+              statData: "2024-03", // 时间段
+              count: 8931, // 总数
+            },
+            {
+              statData: "2024-04", // 时间段
+              count: 6131, // 总数
+            },
+            {
+              statData: "2024-05", // 时间段
+              count: 1891, // 总数
+            },
+          ],
         },
-        yAxis: {
-          type: 'value',
-          axisLine: {
-            show: false
-          },
-          axisLabel: {
-            color: 'rgba(160, 179, 214, 0.7)',
-            fontSize: 11
-          },
-          axisTick: {
-            show: false
-          },
-          splitLine: {
-            lineStyle: {
-              color: 'rgba(160, 179, 214, 0.1)',
-              type: 'dashed',
-              width: 1
-            }
-          }
+        waterState: {
+          deviceCount: 8921, // 设备数量
+          annualUsage: 428921, // 年度用量 近12月的用量之和
+          monthlyAvgUsage: 18921, // 月均用量 近12月用量月平均数
+          dailyAvgUsage: 8921, // 日均用量 近12月用量日平均数
+          switchCount: 887, // 开关次数 近12月成功开启、关闭次数
+          list: [
+            {
+              statData: "2024-01", // 时间段
+              count: 8921, // 总数
+            },
+            {
+              statData: "2024-02", // 时间段
+              count: 7891, // 总数
+            },
+            {
+              statData: "2024-03", // 时间段
+              count: 8931, // 总数
+            },
+            {
+              statData: "2024-04", // 时间段
+              count: 6131, // 总数
+            },
+            {
+              statData: "2024-05", // 时间段
+              count: 1891, // 总数
+            },
+          ],
         },
-        series: [
-          {
-            name: '用电量',
-            type: 'bar',
-            data: this.monthlyData.map((item) => item.power),
-            barWidth: '30%',
-            itemStyle: {
-              color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
-                { offset: 0, color: '#FCE2B4' },
-                { offset: 1, color: '#F66757' }
-              ]),
-              borderRadius: [20, 20, 0, 0]
-            },
-            label: {
-              show: true,
-              position: 'top',
-              color: '#fff',
-              fontSize: 10,
-              formatter: (params) =>
-                params.value >= 10000
-                  ? (params.value / 10000).toFixed(1) + '万'
-                  : params.value
-            }
-          }
-        ]
+        powerChartInstance: null,
+        waterChartInstance: null,
       }
-
-      this.powerChartInstance.setOption(option)
     },
-
-    // 初始化用水量柱状图
-    initWaterChart () {
-      this.waterChartInstance = echarts.init(this.$refs.waterChart)
-
-      const option = {
-        tooltip: {
-          trigger: 'axis',
-          backgroundColor: 'rgba(0, 0, 0, 0.8)',
-          borderColor: 'rgba(64, 158, 255, 0.3)',
-          textStyle: {
-            color: '#fff'
-          },
-          formatter: (params) => {
-            let result = ``
-            params.forEach((param) => {
-              result += `${param.name}<br/>${
-                param.seriesName
-              }: ${param.value.toLocaleString()}<br/>`
-            })
-            return result
-          }
-        },
-        grid: {
-          left: '1%',
-          right: '1%',
-          top: '2%',
-          bottom: '5%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: this.monthlyData.map((item) => item.month),
-          axisLine: {
-            lineStyle: {
-              color: 'rgba(160, 179, 214, 0.3)',
-              type: 'dashed'
-            }
-          },
-          axisLabel: {
-            color: 'rgba(160, 179, 214, 0.7)',
-            fontSize: 11,
-            interval: 0,
-            show: false
-          },
-          axisTick: {
-            show: false
-          },
-          splitLine: {
-            show: true,
-            // 将坐标轴内的线设置为虚线
-            lineStyle: {
-              color: 'rgba(160, 179, 214, 0.3)',
-              type: 'dashed'
-            }
-          }
-        },
-        yAxis: {
-          type: 'value',
-          // 反转 y 轴的 min 和 max,实现“倒立”
-          min: 400000,
-          max: 0,
-          axisLine: {
-            show: false
-          },
-          axisLabel: {
-            color: 'rgba(160, 179, 214, 0.7)',
-            fontSize: 11
-          },
-          axisTick: {
-            show: false
-          },
-          splitLine: {
-            lineStyle: {
-              color: 'rgba(160, 179, 214, 0.1)',
-              type: 'dashed',
-              width: 1
-            }
-          }
-        },
-        series: [
-          {
-            name: '用水量',
-            type: 'bar',
-            data: this.monthlyData.map((item) => item.water),
-            barWidth: '30%',
-            itemStyle: {
-              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-                { offset: 0, color: '#3AB3E3' },
-                { offset: 1, color: '#1620D4' }
-              ]),
-              borderRadius: [0, 0, 20, 20]
-            },
-            label: {
-              show: true,
-              position: 'bottom',
-              color: '#fff',
-              fontSize: 10,
-              formatter: (params) =>
-                params.value >= 10000
-                  ? (params.value / 10000).toFixed(1) + '万'
-                  : params.value
-            }
-          }
-        ]
-      }
-
-      this.waterChartInstance.setOption(option)
+    mounted() {
+      this.initPowerChart()
+      this.initWaterChart()
+      window.addEventListener("resize", this.handleResize)
+      this.getData()
     },
-
-    handleResize () {
+    beforeDestroy() {
+      window.removeEventListener("resize", this.handleResize)
       if (this.powerChartInstance) {
-        this.powerChartInstance.resize()
+        this.powerChartInstance.dispose()
       }
       if (this.waterChartInstance) {
-        this.waterChartInstance.resize()
+        this.waterChartInstance.dispose()
       }
     },
     methods: {
+      getData() {
+        huiJiaApi.getElectricData().then((res) => {
+          if (res && res.data) {
+            this.state = res.data.data
+          }
+          console.log("业务数据----", res.data.data)
+        })
+        huiJiaApi.getWaterData().then((res) => {
+          if (res && res.data) {
+            this.state = res.data.data
+          }
+          console.log("业务数据----", res.data.data)
+        })
+      },
       // 初始化用电量柱状图
       initPowerChart() {
         this.powerChartInstance = echarts.init(this.$refs.powerChart)
@@ -389,7 +226,7 @@ export default {
           },
           xAxis: {
             type: "category",
-            data: this.monthlyData.map((item) => item.month),
+            data: this.electricState.list.map((item) => item.statData),
             axisLine: {
               lineStyle: {
                 color: "rgba(160, 179, 214, 0.3)",
@@ -415,6 +252,8 @@ export default {
           },
           yAxis: {
             type: "value",
+            min: 0,
+            max: 40000,
             axisLine: {
               show: false,
             },
@@ -437,7 +276,7 @@ export default {
             {
               name: "用电量",
               type: "bar",
-              data: this.monthlyData.map((item) => item.power),
+              data: this.electricState.list.map((item) => item.count),
               barWidth: "30%",
               itemStyle: {
                 color: new echarts.graphic.LinearGradient(1, 1, 0, 0, [
@@ -495,7 +334,7 @@ export default {
           },
           xAxis: {
             type: "category",
-            data: this.monthlyData.map((item) => item.month),
+            data: this.waterState.list.map((item) => item.statData),
             axisLine: {
               lineStyle: {
                 color: "rgba(160, 179, 214, 0.3)",
@@ -523,7 +362,7 @@ export default {
           yAxis: {
             type: "value",
             // 反转 y 轴的 min 和 max,实现“倒立”
-            min: 400000,
+            min: 40000,
             max: 0,
             axisLine: {
               show: false,
@@ -547,7 +386,7 @@ export default {
             {
               name: "用水量",
               type: "bar",
-              data: this.monthlyData.map((item) => item.water),
+              data: this.waterState.list.map((item) => item.count),
               barWidth: "30%",
               itemStyle: {
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@@ -584,7 +423,6 @@ export default {
       },
     },
   }
-}
 </script>
 
 <style lang="scss" scoped>
@@ -603,8 +441,7 @@ export default {
     display: flex;
     align-items: center;
     color: var(--title-primary);
-    font-size: halfW(18);
-    font-weight: 500;
+    font-size: halfW(16);
     border-radius: 4px;
     padding: halfH(4) halfW(10);
     background: var(--title-bg);

+ 84 - 150
src/views/hui-jia/components/implementary/ing.vue

@@ -5,66 +5,85 @@
       进行中的实施服务
     </div>
     <vueSeamless
-      :data="implementingList"
+      :data="data"
       :class-option="optionScroll"
       class="implementing-list"
     >
-      <!-- <div class="implementing-list"> -->
       <div
-        v-for="(item, index) in implementingList"
+        v-for="(item, index) in data"
         :key="index"
         class="implementing-item"
         :class="{ 'even-item': index % 2 === 1 }"
       >
-        <div class="item-content">
-          <div class="item-date">{{ item.date }}</div>
-          <div class="item-city">{{ item.city }}</div>
-          <div class="project-info">
-            <div class="project-name">{{ item.projectName }}</div>
-            <div class="company-name">{{ item.companyName }}</div>
-          </div>
-          <div class="project-stats">
-            <div class="stat-item">
-              <span class="stat-value">{{ item.householdCount }}</span>
-              <span class="stat-label">户</span>
+        <el-row class="item-content">
+          <el-col :span="3" class="item-date">{{
+            dayjsObj(item.created_at)
+          }}</el-col>
+          <el-col :span="3" class="item-city">
+            {{ item | safeGet("residence.location.region_name", "-") }}
+          </el-col>
+          <el-col :span="7" class="project-info">
+            <div class="project-name">
+              {{ item | safeGet("residence.residence_name", "-") }}
             </div>
-            <div class="stat-item">
-              <span class="stat-value">{{ item.area }}</span>
+            <div class="company-name">
+              {{ item | safeGet("service.customer.customer_name", "-") }}
             </div>
-            <div class="stat-item">
-              <div class="progress-wrapper">
-                <span class="progress-text">{{ item.progress }}%</span>
-              </div>
-            </div>
-            <div class="stat-item service-item">
-              <span class="service-content">{{ item.serviceContent }}</span>
-            </div>
-            <div class="stat-item">
-              <span class="period">{{ item.period }}</span>
-            </div>
-          </div>
-        </div>
+          </el-col>
+          <el-col :span="11" class="project-stats">
+            <el-row class="project-row">
+              <el-col :span="3" class="stat-item" style="text-align: right">
+                <span class="stat-value">{{
+                  item | safeGet("residence_contracted_households", "-")
+                }}</span>
+                <span class="stat-label">户</span>
+              </el-col>
+              <el-col :span="4" class="stat-item">
+                <span class="stat-value">{{
+                  item | safeGet("service.liable_person.real_name", "-")
+                }}</span>
+              </el-col>
+              <el-col :span="3" class="stat-item">
+                <div class="progress-wrapper">
+                  <span class="progress-text"
+                    >{{ item | safeGet("service.perform_ratio", "-") }}%</span
+                  >
+                </div>
+              </el-col>
+              <el-col :span="10" class="stat-item service-item">
+                <span class="service-content">{{
+                  item | safeGet("service.work_template.display_name", "-")
+                }}</span>
+              </el-col>
+              <el-col :span="4" class="stat-item">
+                <span class="period">{{ item.updated_at }}</span>
+              </el-col>
+            </el-row>
+          </el-col>
+        </el-row>
       </div>
-      <!-- </div> -->
     </vueSeamless>
   </div>
 </template>
 
 <script>
+  import dayjs from "dayjs"
   import { mapState } from "vuex"
   import vueSeamless from "vue-seamless-scroll"
-  import { api } from "@/api"
 
   export default {
     name: "ImplementingServices",
     components: {
       vueSeamless,
     },
+    props: {
+      data: {
+        type: Array,
+        default: () => [],
+      },
+    },
     data() {
-      return {
-        implementingList: [],
-        timer: null,
-      }
+      return {}
     },
     computed: {
       ...mapState(["entCode", "communityId"]),
@@ -74,106 +93,9 @@
         }
       },
     },
-    mounted() {
-      this.init()
-      // 设置定时刷新
-      this.timer = setInterval(() => {
-        this.init()
-      }, 30000) // 每30秒刷新一次
-    },
-    beforeDestroy() {
-      if (this.timer) {
-        clearInterval(this.timer)
-      }
-    },
     methods: {
-      // 初始化数据
-      async init() {
-        try {
-          const params = {
-            entCode: this.entCode,
-            communityId: this.communityId,
-          }
-          // 这里应该调用实际的API获取数据
-          // const res = await api.getImplementingServices(params)
-          // this.implementingList = res.data || []
-
-          // 暂时使用模拟数据
-          this.loadMockData()
-        } catch (error) {
-          console.error("获取进行中实施服务数据失败:", error)
-        }
-      },
-      // 加载模拟数据
-      loadMockData() {
-        this.implementingList = [
-          {
-            date: "2025-11-19",
-            city: "五指山市",
-            projectName: "项目名称",
-            companyName: "物业公司名称",
-            householdCount: "999",
-            area: "北滨端",
-            progress: 90,
-            serviceContent: "数据清单模板填写培训",
-            period: "3周前",
-          },
-          {
-            date: "2025-11-19",
-            city: "五指山市",
-            projectName: "项目名称",
-            companyName: "物业公司名称",
-            householdCount: "999",
-            area: "北滨端",
-            progress: 90,
-            serviceContent: "数据清单模板填写培训",
-            period: "3周前",
-          },
-          {
-            date: "2025-11-19",
-            city: "五指山市",
-            projectName: "项目名称",
-            companyName: "物业公司名称",
-            householdCount: "999",
-            area: "北滨端",
-            progress: 90,
-            serviceContent: "数据清单模板填写培训",
-            period: "3周前",
-          },
-          {
-            date: "2025-11-19",
-            city: "五指山市",
-            projectName: "项目名称",
-            companyName: "物业公司名称",
-            householdCount: "999",
-            area: "北滨端",
-            progress: 90,
-            serviceContent: "数据清单模板填写培训",
-            period: "3周前",
-          },
-          {
-            date: "2025-11-19",
-            city: "五指山市",
-            projectName: "项目名称",
-            companyName: "物业公司名称",
-            householdCount: "999",
-            area: "北滨端",
-            progress: 90,
-            serviceContent: "数据清单模板填写培训",
-            period: "3周前",
-          },
-          {
-            date: "2025-11-19",
-            city: "五指山市",
-            projectName: "项目名称",
-            companyName: "物业公司名称",
-            householdCount: "999",
-            area: "北滨端",
-            progress: 90,
-            serviceContent: "数据清单模板填写培训",
-            period: "3周前",
-          },
-        ]
+      dayjsObj(date) {
+        return dayjs(date).format("YYYY-MM-DD")
       },
     },
   }
@@ -191,8 +113,7 @@
 
   .implementing-title {
     background: var(--title-bg);
-    font-size: halfW(18);
-    font-weight: 500;
+    font-size: halfW(14);
     color: var(--title-primary);
     padding: halfH(4) halfW(10);
     border-radius: halfW(4);
@@ -223,50 +144,57 @@
     display: flex;
     justify-content: space-between;
     align-items: center;
-    gap: halfW(16);
+    gap: halfW(10);
     .item-date {
       color: var(--title-secondary);
       font-size: halfW(14);
-      margin-right: halfW(16);
     }
 
     .item-city {
       color: var(--title-primary);
       font-size: halfW(14);
       font-weight: 500;
-      padding: halfH(2) halfW(8);
+      width: halfW(80);
+      padding: halfH(2) 0px;
       border-radius: halfW(4);
     }
 
     .project-info {
-      flex: 1;
-      min-width: halfW(200);
-      margin-right: halfW(20);
+      flex: 1 1 0%;
+      text-align: left;
 
       .project-name {
         flex: 0 0 1;
         color: var(--title-primary);
         font-size: halfW(14);
-        margin-bottom: halfH(4);
+        margin-bottom: halfH(8);
         font-weight: 400;
       }
 
       .company-name {
         color: var(--title-secondary);
         font-size: halfW(14);
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
       }
     }
 
     .project-stats {
-      display: flex;
-      align-items: center;
-      flex-wrap: wrap;
-      gap: halfW(20);
+      .project-row {
+        display: flex;
+        align-items: center;
+        flex-wrap: nowrap;
+        gap: halfW(10);
+      }
     }
 
     .stat-item {
+      // flex: 1 1 0%;
       display: flex;
       align-items: center;
+      text-align: left;
+      // width: halfW(50);
       &:first-child {
         .stat-value {
           color: var(--primary);
@@ -274,7 +202,7 @@
       }
       .stat-value {
         color: #fff;
-        font-size: halfW(16);
+        font-size: halfW(14);
         font-weight: 500;
         margin-right: halfW(4);
       }
@@ -292,7 +220,7 @@
           color: #40d9ac;
           font-size: halfW(14);
           font-weight: 500;
-          min-width: halfW(35);
+          width: halfW(35);
         }
       }
 
@@ -304,10 +232,16 @@
       .period {
         color: #a0b3d6;
         font-size: halfW(14);
+        text-align: right;
       }
     }
     .service-item {
-      flex: 1;
+      min-width: halfW(100);
+      .service-content {
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
     }
   }
 </style>

+ 28 - 55
src/views/hui-jia/components/implementary/now-year.vue

@@ -10,32 +10,43 @@
 
 <script>
   import { mapState } from "vuex"
-  import { api } from "@/api"
   import * as echarts from "echarts"
 
   export default {
     name: "NowYearServices",
+    props: {
+      data: {
+        type: Object,
+        default: () => ({}),
+      },
+    },
     data() {
       return {
         chartInstance: null,
         chartData: {
           categories: [
-            "履约城市",
-            "服务企业",
-            "服务项目",
-            "合同数",
             "签约户数",
+            "合同份数",
+            "服务小区",
+            "服务企业",
+            "履约城市",
           ],
-          ongoing: [88888, 88888, 88888, 88888, 88888], // 进行中
-          lastMonthCompleted: [88888, 88888, 88888, 88888, 88888], // 上月完成
-          yearTotal: [88888, 88888, 888888, 88888, 88888], // 今年累计
-          totalNumbers: [8888, 8888, 8888, 8888, 8888], // 左侧显示的总数
         },
       }
     },
     computed: {
       ...mapState(["entCode", "communityId"]),
     },
+    watch: {
+      data: {
+        handler(newVal, oldVal) {
+          if (newVal) {
+            this.initChart()
+          }
+        },
+        deep: true,
+      },
+    },
     mounted() {
       this.init()
       window.addEventListener("resize", this.handleResize)
@@ -50,7 +61,6 @@
       // 初始化组件
       init() {
         this.initChart()
-        this.loadData()
       },
 
       // 初始化图表
@@ -88,7 +98,7 @@
             containLabel: true,
           },
           xAxis: {
-            type: "value",
+            type: "log",
             show: false, // 隐藏x轴
             axisLine: {
               show: false,
@@ -124,9 +134,7 @@
               name: "进行中",
               type: "bar",
               stack: "total",
-              emphasis: {
-                focus: "series",
-              },
+              // barMinHeight: 10,
               itemStyle: {
                 color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
                   { offset: 0, color: "#1620D4" },
@@ -138,18 +146,15 @@
                 show: true,
                 color: "#fff",
               },
-              barWidth: 30,
               barGap: "0%",
               barCategoryGap: "20%",
-              data: this.chartData.ongoing,
+              data: this.data.service_ing,
             },
             {
               name: "上月完成",
               type: "bar",
               stack: "total",
-              emphasis: {
-                focus: "series",
-              },
+              // barMinHeight: 10,
               itemStyle: {
                 color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
                   { offset: 0, color: "#192FC9" },
@@ -161,17 +166,14 @@
                 show: true,
                 color: "#fff",
               },
-              barWidth: 30,
               barGap: "0%",
-              data: this.chartData.lastMonthCompleted,
+              data: this.data.service_last_month,
             },
             {
               name: "今年累计",
               type: "bar",
               stack: "total",
-              emphasis: {
-                focus: "series",
-              },
+              // barMinHeight: 10,
               label: {
                 show: true,
                 color: "#fff",
@@ -183,9 +185,8 @@
                 ]),
                 borderRadius: [0, 20, 20, 0],
               },
-              barWidth: 30,
               barGap: "0%",
-              data: this.chartData.yearTotal,
+              data: this.data.year_summary,
             },
           ],
         }
@@ -194,33 +195,6 @@
         this.chartInstance.resize()
       },
 
-      // 加载数据
-      async loadData() {
-        this.updateChart()
-      },
-
-      // 更新图表
-      updateChart() {
-        if (this.chartInstance) {
-          this.chartInstance.setOption({
-            yAxis: {
-              data: this.chartData.categories,
-            },
-            series: [
-              {
-                data: this.chartData.ongoing,
-              },
-              {
-                data: this.chartData.lastMonthCompleted,
-              },
-              {
-                data: this.chartData.yearTotal,
-              },
-            ],
-          })
-        }
-      },
-
       // 处理窗口大小变化
       handleResize() {
         if (this.chartInstance) {
@@ -242,8 +216,7 @@
   }
 
   .now-year-title {
-    font-size: halfW(18);
-    font-weight: 500;
+    font-size: halfW(16);
     border-radius: halfW(4);
     color: var(--title-primary);
     background: var(--title-bg);

+ 25 - 68
src/views/hui-jia/components/implementary/total.vue

@@ -7,35 +7,39 @@
     <div class="total-grid">
       <div class="total-item">
         <div class="total-label">城市总数</div>
-        <div class="total-value">{{ cityCount }}</div>
+        <div class="total-value">{{ formatNumber(data.cities) }}</div>
       </div>
       <div class="total-item">
         <div class="total-label">企业总数</div>
-        <div class="total-value">{{ enterpriseCount }}</div>
+        <div class="total-value">{{ data.customers }}</div>
       </div>
       <div class="total-item">
         <div class="total-label">合同总数</div>
-        <div class="total-value">{{ contractCount }}</div>
+        <div class="total-value">{{ formatNumber(data.contracts) }}</div>
       </div>
       <div class="total-item">
         <div class="total-label">履约次数</div>
-        <div class="total-value">{{ performanceCount }}</div>
+        <div class="total-value">{{ formatNumber(data.perform_times) }}</div>
       </div>
       <div class="total-item">
         <div class="total-label">服务时长</div>
-        <div class="total-value">{{ serviceHours }}</div>
+        <div class="total-value">{{ formatNumber(data.service_time) }}</div>
       </div>
       <div class="total-item">
         <div class="total-label">项目总数</div>
-        <div class="total-value">{{ projectCount }}</div>
+        <div class="total-value">{{ formatNumber(data.residences) }}</div>
       </div>
       <div class="total-item">
         <div class="total-label">签约户数</div>
-        <div class="total-value">{{ signedUserCount }}</div>
+        <div class="total-value">
+          {{ formatNumber(data.contracted_households) }}
+        </div>
       </div>
       <div class="total-item">
         <div class="total-label">实施户数</div>
-        <div class="total-value">{{ implementationUserCount }}</div>
+        <div class="total-value">
+          {{ formatNumber(data.actual_households) }}
+        </div>
       </div>
     </div>
   </div>
@@ -43,73 +47,27 @@
 
 <script>
   import { mapState } from "vuex"
-  import { api } from "@/api"
 
   export default {
     name: "ImplementationTotal",
+    props: {
+      data: {
+        type: Object,
+        default: () => ({}),
+      },
+    },
     data() {
-      return {
-        cityCount: 99,
-        enterpriseCount: 99,
-        contractCount: 99,
-        performanceCount: 99,
-        serviceHours: 99,
-        projectCount: 99,
-        signedUserCount: 99,
-        implementationUserCount: 99,
-        timer: null,
-      }
+      return {}
     },
     computed: {
       ...mapState(["entCode", "communityId"]),
     },
-    mounted() {
-      this.init()
-      // 设置定时刷新
-      this.timer = setInterval(() => {
-        this.init()
-      }, 60000) // 每分钟刷新一次
-    },
-    beforeDestroy() {
-      if (this.timer) {
-        clearInterval(this.timer)
-      }
-    },
     methods: {
-      // 初始化数据
-      async init() {
-        try {
-          const params = {
-            entCode: this.entCode,
-            communityId: this.communityId,
-          }
-          // 这里应该调用实际的API获取数据
-          // const res = await api.getImplementationTotal(params)
-          // 暂时使用模拟数据
-          this.updateData()
-        } catch (error) {
-          console.error("获取实施服务汇总数据失败:", error)
-        }
-      },
-      // 更新数据
-      updateData() {
-        // 这里可以根据实际API返回的数据进行更新
-        // 目前使用固定值展示
-        this.cityCount = 99
-        this.enterpriseCount = 99
-        this.contractCount = 99
-        this.performanceCount = 99
-        this.serviceHours = 99
-        this.projectCount = 99
-        this.signedUserCount = 99
-        this.implementationUserCount = 99
-      },
       // 格式化数字显示
       formatNumber(num) {
-        if (num >= 10000) {
-          return (num / 10000).toFixed(1) + "万"
-        }
-        return num.toString()
+        return num.toLocaleString("zh", {
+          maximumFractionDigits: 2,
+        })
       },
     },
   }
@@ -125,11 +83,10 @@
     overflow: hidden;
 
     .total-title {
-      padding:  halfH(4) halfW(10);
+      padding: halfH(4) halfW(10);
       display: flex;
       align-items: center;
-      font-size: halfW(18);
-      font-weight: 500;
+      font-size: halfW(16);
       color: var(--title-primary);
       background: var(--title-bg);
       margin-bottom: halfH(20);
@@ -157,7 +114,7 @@
       }
 
       .total-value {
-        font-size: halfW(28);
+        font-size: halfW(20);
         font-weight: bold;
         color: #ffffff;
       }

+ 141 - 99
src/views/hui-jia/components/market.vue

@@ -8,7 +8,10 @@
       <div class="legend-item">上月</div>
       <div class="legend-item">本月</div>
     </div>
-    <div id="marketChart" class="chart"></div>
+    <div class="chart-wrap">
+      <div id="marketChart" class="chart"></div>
+      <div id="marketChart2" class="chart2"></div>
+    </div>
   </div>
 </template>
 
@@ -19,33 +22,48 @@
 
   export default {
     name: "MarketExpansion",
+    props: {
+      data: {
+        type: Object,
+        default: () => ({}),
+      },
+    },
     data() {
       return {
-        chartInstance: null,
+        chartInstanceA: null,
+        chartInstanceB: null,
         chartData: {
           categories: [
-            "新增企业",
-            "跟进项目",
-            "新增联系人",
-            "新增合同",
-            "新增项目",
             "新增户数",
+            "新增项目",
+            "新增合同",
+            "新增联系人",
+            "跟进企业",
+            "新增企业",
           ],
-          lastMonthData: [-9, -9, -9, -9, -3, -12], // 上月数据
-          thisMonthData: [9, 9, 1, 1, 9, 10], // 本月数据
         },
       }
     },
     computed: {
       ...mapState(["entCode", "communityId"]),
     },
+    watch: {
+      data: {
+        handler(newVal, oldVal) {
+          if (newVal) {
+            this.initChart()
+          }
+        },
+        deep: true,
+      },
+    },
     mounted() {
       this.init()
       window.addEventListener("resize", this.handleResize)
     },
     beforeDestroy() {
-      if (this.chartInstance) {
-        this.chartInstance.dispose()
+      if (this.chartInstanceA) {
+        this.chartInstanceA.dispose()
       }
       window.removeEventListener("resize", this.handleResize)
     },
@@ -53,16 +71,30 @@
       // 初始化组件
       init() {
         this.initChart()
-        this.loadData()
       },
 
       // 初始化图表
       initChart() {
-        this.chartInstance = echarts.init(
+        this.chartInstanceA = echarts.init(
           document.getElementById("marketChart")
         )
+        this.chartInstanceB = echarts.init(
+          document.getElementById("marketChart2")
+        )
+
+        const lastMonthData = Object.keys(this.data)
+          .map((key, index) => {
+            return -this.data[key].last_month
+          })
+          .reverse()
 
-        const option = {
+        const thisMonthData = Object.keys(this.data)
+          .map((key, index) => {
+            return this.data[key].this_month
+          })
+          .reverse()
+
+        const optionA = {
           tooltip: {
             trigger: "axis",
             axisPointer: {
@@ -88,10 +120,10 @@
             },
           },
           grid: {
-            left: "25%",
-            right: "25%",
-            bottom: "3%",
-            top: 50,
+            left: "10%",
+            right: "1%",
+            bottom: "5%",
+            top: "5%",
             containLabel: true,
           },
           xAxis: {
@@ -118,34 +150,13 @@
                 show: false,
               },
             },
-            {
-              type: "category",
-              data: this.chartData.categories,
-              axisLine: {
-                show: false,
-              },
-              axisTick: {
-                show: false,
-              },
-              axisLabel: {
-                show: false,
-                color: "#a0b3d6",
-                fontSize: 14,
-              },
-            },
           ],
           series: [
             {
               name: "上月",
               type: "bar",
-              //   stack: "sameStack",
-              stack: "Total",
-              emphasis: {
-                focus: "series",
-              },
-              barMinHeight: 4,
-              data: this.chartData.lastMonthData,
-              barWidth: 16,
+              // barMinHeight: 20,
+              data: lastMonthData,
               itemStyle: {
                 // 左侧渐变色:从#F66757到#FCE2B4
                 color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
@@ -165,17 +176,75 @@
                 },
               },
             },
+          ],
+        }
+
+        const optionB = {
+          tooltip: {
+            trigger: "axis",
+            axisPointer: {
+              type: "shadow",
+            },
+            backgroundColor: "rgba(17, 28, 49, 0.9)",
+            borderColor: "rgba(64, 158, 255, 0.2)",
+            textStyle: {
+              color: "#ffffff",
+            },
+            formatter: (params) => {
+              // 自定义tooltip格式
+              let result = params[0].name + "<br/>"
+              params.forEach((item) => {
+                result +=
+                  item.marker +
+                  item.seriesName +
+                  ": " +
+                  Math.abs(item.value) +
+                  "<br/>"
+              })
+              return result
+            },
+          },
+          grid: {
+            left: "1%",
+            right: "10%",
+            bottom: "5%",
+            top: "5%",
+            containLabel: true,
+          },
+          xAxis: {
+            type: "value",
+            axisLabel: {
+              formatter: (value) => {
+                if (value < 0) return -value //这里是针对取负值
+                else return value
+              },
+            },
+            show: false, // 隐藏x轴
+          },
+          yAxis: [
+            {
+              type: "category",
+              data: this.chartData.categories,
+              axisLine: {
+                show: false,
+              },
+              axisTick: {
+                show: false,
+              },
+              axisLabel: {
+                show: true,
+                color: "#a0b3d6",
+                fontSize: 14,
+                align: "right",
+              },
+            },
+          ],
+          series: [
             {
               name: "本月",
               type: "bar",
-              //   stack: "sameStack",
-              stack: "Total",
-              emphasis: {
-                focus: "series",
-              },
-              barMinHeight: 4,
-              data: this.chartData.thisMonthData,
-              barWidth: 16,
+              // barMinHeight: 20,
+              data: thisMonthData,
               itemStyle: {
                 // 右侧渐变色:从#1620D4到#3AB3E3
                 color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
@@ -195,56 +264,19 @@
           ],
         }
 
-        this.chartInstance.setOption(option)
-        this.chartInstance.resize()
-      },
-
-      // 加载数据
-      async loadData() {
-        try {
-          const params = {
-            entCode: this.entCode,
-            communityId: this.communityId,
-          }
-          // 这里应该调用实际的API获取数据
-          // const res = await api.getMarketExpansionData(params)
-          // this.chartData = res.data || this.chartData
-
-          // 暂时使用模拟数据
-          this.updateChart()
-        } catch (error) {
-          console.error("获取市场拓展数据失败:", error)
-        }
-      },
-
-      // 更新图表
-      updateChart() {
-        if (this.chartInstance) {
-          this.chartInstance.setOption({
-            yAxis: [
-              {
-                data: this.chartData.categories,
-              },
-              {
-                data: this.chartData.categories,
-              },
-            ],
-            series: [
-              {
-                data: this.chartData.lastMonthData,
-              },
-              {
-                data: this.chartData.thisMonthData,
-              },
-            ],
-          })
-        }
+        this.chartInstanceA.setOption(optionA)
+        this.chartInstanceA.resize()
+        this.chartInstanceB.setOption(optionB)
+        this.chartInstanceB.resize()
       },
 
       // 处理窗口大小变化
       handleResize() {
-        if (this.chartInstance) {
-          this.chartInstance.resize()
+        if (this.chartInstanceA) {
+          this.chartInstanceA.resize()
+        }
+        if (this.chartInstanceB) {
+          this.chartInstanceB.resize()
         }
       },
     },
@@ -267,8 +299,7 @@
     border-radius: halfW(4);
     background: var(--title-bg);
     padding: halfH(4) halfW(10);
-    font-size: halfW(18);
-    font-weight: 500;
+    font-size: halfW(16);
     color: var(--title-primary);
     display: flex;
     align-items: center;
@@ -284,9 +315,20 @@
     border-bottom: 1px solid rgba(64, 158, 255, 0.2);
   }
 
-  .chart {
+  .chart-wrap {
+    display: flex;
+    align-items: center;
     width: 100%;
     height: 100%;
-    background: var(--content-bg);
+    .chart {
+      width: 40%;
+      height: 100%;
+      background: var(--content-bg);
+    }
+    .chart2 {
+      width: 60%;
+      height: 100%;
+      background: var(--content-bg);
+    }
   }
 </style>

+ 2 - 2
src/views/hui-jia/components/online-pay-day/index.vue

@@ -26,7 +26,7 @@
   </template>
 <script>
 import * as echarts from 'echarts'
-import { screen } from '@/api'
+import { huiJiaApi } from '@/api'
 // import { getBigNumberWithUint } from '@/libs/tools.js'
 import Card from '@/views/hui-jia/components/card'
 import icon from './images/icon-title.png'
@@ -61,7 +61,7 @@ export default {
   },
   methods: {
     getOnlinePayDay () {
-      screen.getOnlinePayDay().then(res => {
+      huiJiaApi.getOnlinePayDay().then(res => {
         this.xAxisData = res.data.data.list.map(item => item.statData)
         this.seriesData = res.data.data.list.map(item => item.payAmount)
         this.initChart()

+ 2 - 2
src/views/hui-jia/components/online-pay-week/index.vue

@@ -26,7 +26,7 @@
   </template>
 <script>
 import * as echarts from 'echarts'
-import { screen } from '@/api'
+import { huiJiaApi } from '@/api'
 import Card from '@/views/hui-jia/components/card'
 import icon from './images/icon-title.png'
 
@@ -60,7 +60,7 @@ export default {
   },
   methods: {
     getOnlinePayWeek () {
-      screen.getOnlinePayWeek().then(res => {
+      huiJiaApi.getOnlinePayWeek().then(res => {
         this.xAxisData = res.data.data.list.map(item => item.statData)
         this.seriesData = res.data.data.list.map(item => item.payAmount)
         this.initChart()

+ 2 - 2
src/views/hui-jia/components/online-pay/index.vue

@@ -16,7 +16,7 @@
 <script>
 import * as echarts from 'echarts'
 // import { getBigNumberWithUint } from '@/libs/tools.js'
-import { screen } from '@/api'
+import { huiJiaApi } from '@/api'
 import Card from '@/views/hui-jia/components/card'
 import icon from './images/icon-title.png'
 
@@ -51,7 +51,7 @@ export default {
   },
   methods: {
     getOnlinePayMonth () {
-      screen.getOnlinePayMonth().then(res => {
+      huiJiaApi.getOnlinePayMonth().then(res => {
         this.total = res.data.data.allSum
         this.xAxisData = res.data.data.list.map(item => item.statData)
         this.seriesData = res.data.data.list.map(item => item.payAmount)

+ 2 - 2
src/views/hui-jia/components/online-water/index.vue

@@ -61,7 +61,7 @@
   </Card>
 </template>
 <script>
-import { screen } from '@/api'
+import { huiJiaApi } from '@/api'
 import Card from '@/views/hui-jia/components/card'
 import icon from './images/icon-title.png'
 
@@ -88,7 +88,7 @@ export default {
   },
   methods: {
     getOnlineWaterStatistics () {
-      screen.getOnlineWaterStatistics().then(res => {
+      huiJiaApi.getOnlineWaterStatistics().then(res => {
         this.onlineWaterInfo = res.data.data
       })
     }

+ 38 - 12
src/views/hui-jia/components/resident/index.vue

@@ -6,8 +6,8 @@
         </div>
         <div class="num display-flex flex-1">
           <div v-for="item in residentData" :key="item.title" class="resident-item flex-1">
-          <div class="title">{{ item.title }}</div>
-          <div class="value">{{ item.value }}</div>
+            <div class="title">{{ item.title }}</div>
+            <div class="value">{{ item.value }}</div>
           </div>
         </div>
       </div>
@@ -29,6 +29,7 @@ import icon from './images/icon-title.png'
 import iconReport from './images/icon-report.png'
 import iconQrcode from './images/icon-qrcode.png'
 import iconPay from './images/icon-pay.png'
+import { huiJiaApi } from '@/api'
 
 export default {
   name: 'Resident',
@@ -43,46 +44,69 @@ export default {
   },
   data () {
     return {
+      rate: 0,
       title: '用户信息',
       icon: icon,
       residentData: [
         {
           title: '公众号关注数',
-          value: 1000
+          value: 1000,
+          key: 'mpFollowCount'
         },
         {
           title: '绘服务绑定数',
-          value: 1000
+          value: 1000,
+          key: 'hfwBindCount'
         },
         {
           title: '日活跃数(近1月)',
-          value: 1000
+          value: 1000,
+          key: 'userLiveCount'
         }
       ],
       operateData: [
         {
           title: '绘服务缴费次数',
           value: 1000,
-          icon: iconPay
+          icon: iconPay,
+          key: 'payCount'
         },
         {
           title: '绘服务扫码次数',
           value: 1000,
-          icon: iconQrcode
+          icon: iconQrcode,
+          key: 'scanCodeCount'
         },
         {
           title: '绘服务报事次数',
           value: 1000,
-          icon: iconReport
+          icon: iconReport,
+          key: 'workCount'
         }
       ],
       colorArr: ['#182744', '#82D1F6', '#02338C']
     }
   },
   mounted () {
-    this.initChart()
+    
+    this.getUsersStatistics()
   },
   methods: {
+    getUsersStatistics () {
+      huiJiaApi.getUsersStatistics().then(res => {
+        if(res && res.data){
+          this.rate = res.data.data.attentionRate || 0
+          this.residentData.forEach(item => {
+            item.value = res.data.data[item.key] || 0
+          })
+          this.operateData.forEach(item => {
+            item.value = res.data.data[item.key] || 0
+          })
+          this.initChart()
+        }
+       
+      })
+    },
     initChart () {
       const myChart = echarts.init(this.$refs.dom)
       const option = {
@@ -121,7 +145,7 @@ export default {
         // 圆环位置和大小
         polar: {
           center: ['50%', '50%'],
-          radius: '170'
+          radius: '170%'
         },
         series: [{
           type: 'bar',
@@ -200,9 +224,8 @@ export default {
             color: '#00D2ED'
           },
           data: [{
-            value: 75,
+            value: this.rate,
             name: '绘服务'
-            // name: 75 + '\n' + '{a|' + '首要污染物:PM10' + '}'
           }]
         }
         ]
@@ -229,6 +252,9 @@ export default {
     flex-direction: column;
     text-align: center;
     padding-left: halfW(10);
+    .resident-item{
+      align-content: center;
+    }
     .title{
       font-size: halfH(16);
       color: #82D1F6;

+ 34 - 20
src/views/hui-jia/components/smart.vue

@@ -9,17 +9,17 @@
     <!-- 数据卡片 -->
     <div class="data-cards">
       <div class="data-card">
-        <div class="data-value">{{ doorDevices }}</div>
+        <div class="data-value">{{ state.count }}</div>
         <div class="data-label">门禁设备</div>
       </div>
 
       <div class="data-card">
-        <div class="data-value">{{ activeProjects }}</div>
+        <div class="data-value">{{ state.accessControlProjectCount }}</div>
         <div class="data-label">开通项目</div>
       </div>
 
       <div class="data-card">
-        <div class="data-value">{{ accessTimes }}</div>
+        <div class="data-value">{{ state.openCount }}</div>
         <div class="data-label">出入次数</div>
       </div>
     </div>
@@ -33,30 +33,35 @@
 </template>
 
 <script>
+import { huiJiaApi } from "@/api"
+
 export default {
   name: 'SmartDoor',
   data () {
     return {
-      doorDevices: '921',
-      activeProjects: '92',
-      accessTimes: '900000',
-      chartInstance: null,
-      // 模拟的通行流量数据
-      trafficData: [
-        { time: '01:00', count: 150 },
-        { time: '04:00', count: 250 },
-        { time: '07:00', count: 420 },
-        { time: '10:00', count: 380 },
-        { time: '13:00', count: 320 },
-        { time: '16:00', count: 230 },
-        { time: '19:00', count: 220 },
-        { time: '22:00', count: 300 }
-      ]
+      state: {
+        count: '921',
+        accessControlProjectCount: '92',
+        openCount: '900000',
+        chartInstance: null,
+        // 模拟的通行流量数据
+        list: [
+          { statData: '01:00', count: 150 },
+          { statData: '04:00', count: 250 },
+          { statData: '07:00', count: 420 },
+          { statData: '10:00', count: 380 },
+          { statData: '13:00', count: 320 },
+          { statData: '16:00', count: 230 },
+          { statData: '19:00', count: 220 },
+          { statData: '22:00', count: 300 }
+        ]
+      }
     }
   },
   mounted () {
     this.initChart()
     window.addEventListener('resize', this.handleResize)
+    this.getData()
   },
   beforeDestroy () {
     window.removeEventListener('resize', this.handleResize)
@@ -65,6 +70,14 @@ export default {
     }
   },
   methods: {
+    getData () {
+      huiJiaApi.getFaceDeviceData().then((res) => {
+        if (res && res.data) {
+          this.state = res.data.data
+        }
+        console.log("业务数据----", res.data.data)
+      })
+    },
     initChart () {
       const echarts = require('echarts')
       this.chartInstance = echarts.init(this.$refs.trafficChart)
@@ -98,7 +111,7 @@ export default {
         },
         xAxis: {
           type: 'category',
-          data: this.trafficData.map((item) => item.time),
+          data: this.state.list.map((item) => item.statData),
           axisLine: {
             lineStyle: {
               color: 'rgba(160, 179, 214, 0.6)'
@@ -144,7 +157,7 @@ export default {
           {
             name: '开门次数',
             type: 'bar',
-            data: this.trafficData.map((item) => item.count),
+            data: this.state.list.map((item) => item.count),
             barWidth: '30%',
             // 添加标签配置,设置颜色为白色
             label: {
@@ -190,6 +203,7 @@ export default {
   .module-title {
     display: flex;
     border-radius: halfW(4);
+    font-size: halfW(16);
     align-items: center;
     color: var(--title-primary);
     background: var(--title-bg);

+ 28 - 3
src/views/hui-jia/components/user/index.vue

@@ -1,6 +1,6 @@
 <template>
     <Card :title="title" :icon="icon" class="user">
-      <div class="content display-flex">
+      <div class="content">
         <div v-for="item in userData" :key="item.title" class="user-item flex-1">
           <div class="icon">
             <img :src="item.icon" alt="" class="img">
@@ -12,6 +12,8 @@
     </Card>
   </template>
 <script>
+import { huiJiaApi } from '@/api'
+
 import Card from '@/views/hui-jia/components/card'
 import icon from './images/icon-title.png'
 import iconPark from './images/icon-park.png'
@@ -33,38 +35,59 @@ export default {
       userData: [
         {
           title: '客户数',
+          key: 'customersCount',
           value: 1000,
           icon: iconUser
         },
         {
           title: '项目数',
           value: 1000,
+          key: 'communityCount',
           icon: iconCommunity
         },
         {
           title: '房屋数',
           value: 1000,
+          key: 'houseCount',
           icon: iconHouse
         },
         {
           title: '住户数',
           value: 1000,
+          key: 'residentCount',
           icon: iconResident
         },
         {
           title: '车位数',
           value: 1000,
+          key: 'parkingSpaceCount',
           icon: iconPark
         },
         {
           title: '车辆数',
           value: 1000,
+          key: 'carCount',
           icon: iconCar
         }
 
       ]
     }
-  }
+  },
+  mounted () {
+    this.getCustomerStatistics()
+  },
+  methods: {
+    getCustomerStatistics () {
+      huiJiaApi.getCustomerStatistics().then(res => {
+        if(res && res.data){
+          this.userData.forEach(item => {
+            item.value = res.data.data[item.key]
+          })
+        }
+       
+      })
+    }
+  },
 }
 </script>
 <style lang="scss">
@@ -75,6 +98,8 @@ export default {
   .content{
     width: 100%;
     height: 100%;
+    display: flex;
+    align-items: center;
   }
   .user-item{
     text-align: center;
@@ -91,7 +116,7 @@ export default {
     margin: halfH(16) 0;
   }
   .value{
-    font-size: halfH(28);
+    font-size: halfH(22);
     color: #fff;
   }
 }

+ 162 - 139
src/views/hui-jia/components/work-card.vue

@@ -5,14 +5,72 @@
       部分业务统计
     </div>
     <div class="work-card">
-      <div class="work-card-item" v-for="item in dataList" :key="item.title">
+      <div class="work-card-item">
         <div class="card-header">
-          <img :src="item.icon" class="title-icon" />
-          <span class="card-title">{{ item.title }}</span>
+          <img src="@/assets/images/jgxx.png" class="title-icon" />
+          <span class="card-title">数电票业务</span>
         </div>
         <div class="card-content">
-          <div class="card-subtitle">{{ item.subtitle }}</div>
-          <div class="card-value">{{ formatNumber(item.value) }}</div>
+          <div class="card-subtitle">已开发票数</div>
+          <div class="card-value">
+            {{ formatNumber(state.issuedInvoiceCount) }}
+          </div>
+        </div>
+      </div>
+      <div class="work-card-item">
+        <div class="card-header">
+          <img src="@/assets/images/glgd.png" class="title-icon" />
+          <span class="card-title">道闸系统</span>
+        </div>
+        <div class="card-content">
+          <div class="card-subtitle">设备数量</div>
+          <div class="card-value">
+            {{ formatNumber(state.gateDeviceCount) }}
+          </div>
+        </div>
+      </div>
+      <div class="work-card-item">
+        <div class="card-header">
+          <img src="@/assets/images/ewm.png" class="title-icon" />
+          <span class="card-title">智慧二维码</span>
+        </div>
+        <div class="card-content">
+          <div class="card-subtitle">配置数量</div>
+          <div class="card-value">{{ formatNumber(state.qrCount) }}</div>
+        </div>
+      </div>
+      <div class="work-card-item">
+        <div class="card-header">
+          <img src="@/assets/images/zhxggd.png" class="title-icon" />
+          <span class="card-title">短信业务</span>
+        </div>
+        <div class="card-content">
+          <div class="card-subtitle">销售数量</div>
+          <div class="card-value">{{ formatNumber(state.smsSalesQty) }}</div>
+        </div>
+      </div>
+      <div class="work-card-item">
+        <div class="card-header">
+          <img src="@/assets/images/gwxx.png" class="title-icon" />
+          <span class="card-title">房屋租售</span>
+        </div>
+        <div class="card-content">
+          <div class="card-subtitle">发布资产数</div>
+          <div class="card-value">
+            {{ formatNumber(state.rentalPublishCount) }}
+          </div>
+        </div>
+      </div>
+      <div class="work-card-item">
+        <div class="card-header">
+          <img src="@/assets/images/ryzb.png" class="title-icon" />
+          <span class="card-title">业委会</span>
+        </div>
+        <div class="card-content">
+          <div class="card-subtitle">开通数量</div>
+          <div class="card-value">
+            {{ formatNumber(state.ownerCommitteeOpenCount) }}
+          </div>
         </div>
       </div>
     </div>
@@ -20,156 +78,121 @@
 </template>
 
 <script>
-  import ewm from "@/assets/images/ewm.png"
-  import ryzb from "@/assets/images/ryzb.png"
-  import gwxx from "@/assets/images/gwxx.png"
-  import jgxx from "@/assets/images/jgxx.png"
-  import glgd from "@/assets/images/glgd.png"
-  import zhxggd from "@/assets/images/zhxggd.png"
+import { huiJiaApi } from "@/api"
 
-  export default {
-    name: "WorkCard",
-    props: {
-      title: {
-        type: String,
-        required: true,
-      },
-      subtitle: {
-        type: String,
-        required: true,
-      },
-      value: {
-        type: [Number, String],
-        required: true,
-        default: 0,
+export default {
+  name: "WorkCard",
+  props: {},
+  data() {
+    return {
+      state: {
+        issuedInvoiceCount: "", // 数电票业务
+        gateDeviceCount: "", // 闸道设备数量
+        qrCount: "", // 二维码数量
+        smsSalesQty: "", // 短信销售数量
+        rentalPublishCount: "", // 房屋租赁发布数量
+        ownerCommitteeOpenCount: "", // 业委会开通数量
       },
-    },
-    data() {
-      return {
-        dataList: [
-          {
-            title: "数电票业务",
-            subtitle: "已开发票数",
-            value: 345345,
-            icon: jgxx,
-          },
-          {
-            title: "道闸系统",
-            subtitle: "设备数量",
-            value: 453,
-            icon: glgd,
-          },
-          {
-            title: "智慧二维码",
-            subtitle: "配置数量",
-            value: 45,
-            icon: ewm,
-          },
-          {
-            title: "短信业务",
-            subtitle: "销售数量",
-            value: 3456,
-            icon: zhxggd,
-          },
-          {
-            title: "房屋租售",
-            subtitle: "发布资产数",
-            value: 887,
-            icon: gwxx,
-          },
-          {
-            title: "业委会",
-            subtitle: "开通数量",
-            value: 34,
-            icon: ryzb,
-          },
-        ],
-      }
-    },
-    methods: {
-      // 格式化数字显示
-      formatNumber(num) {
-        if (typeof num === "string") {
-          return num
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    getData() {
+      huiJiaApi.getBusinessData().then((res) => {
+        if (res && res.data) {
+          this.state = res.data.data
         }
-        // 如果数字是整数,直接返回字符串形式
-        return num.toString()
-      },
+        console.log("业务数据----", res.data.data)
+      })
     },
-  }
+    // 格式化数字显示
+    formatNumber(num) {
+      return num.toLocaleString("zh", {
+        maximumFractionDigits: 2,
+      })
+    },
+  },
+}
 </script>
 
 <style lang="scss" scoped>
-  @import "@/assets/css/theme.scss";
-  .work-card-container {
-    background: var(--content-bg);
-    border-radius: halfW(4);
-    height: 100%;
-    width: 100%;
-    display: flex;
-    flex-direction: column;
-    .title {
-      display: flex;
-      border-radius: halfW(4);
-      align-items: center;
-      color: var(--title-primary);
-      background: var(--title-bg);
-      padding: halfH(4) halfW(10);
-    }
-  }
-  .work-card {
-    flex: 1 1 0%;
-    padding: 0px halfW(20);
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    gap: halfW(10);
-  }
+@import "@/assets/css/theme.scss";
 
-  .work-card-item {
-    background: var(--title-bg);
-    border-radius: halfW(4);
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-  }
+.work-card-container {
+  background: var(--content-bg);
+  border-radius: halfW(4);
+  height: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
 
-  .card-header {
+  .title {
+    font-size: halfW(16);
     display: flex;
+    border-radius: halfW(4);
     align-items: center;
-    margin-bottom: halfH(10);
-    padding: halfH(4) halfW(10);
+    color: var(--title-primary);
     background: var(--title-bg);
+    padding: halfH(4) halfW(10);
+  }
+}
 
-    .iconfont {
-      font-size: halfH(24);
-      margin-right: halfW(10);
-      // 根据不同图标使用不同颜色
-      color: var(--primary-color, #409eff);
-    }
+.work-card {
+  flex: 1 1 0%;
+  padding: 0px halfW(20);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: halfW(10);
+}
 
-    .card-title {
-      font-size: halfW(16);
-      font-weight: 500;
-      color: var(--title-primary);
-    }
+.work-card-item {
+  background: var(--title-bg);
+  border-radius: halfW(4);
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.card-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: halfH(10);
+  padding: halfH(4) halfW(10);
+  background: var(--title-bg);
+
+  .iconfont {
+    font-size: halfH(24);
+    margin-right: halfW(10);
+    // 根据不同图标使用不同颜色
+    color: var(--primary-color, #409eff);
   }
 
-  .card-content {
-    display: flex;
-    align-items: center;
-    padding: halfH(20) halfW(20);
-    .card-subtitle {
-      font-size: halfW(14);
-      color: #82d1f6;
-    }
+  .card-title {
+    font-size: halfW(16);
+    font-weight: 500;
+    color: var(--title-primary);
+  }
+}
 
-    .card-value {
-      margin-left: halfW(12);
-      font-size: halfW(18);
-      font-weight: 600;
-      color: #fff;
-      letter-spacing: 1px;
-    }
+.card-content {
+  display: flex;
+  align-items: center;
+  padding: halfH(20) halfW(20);
+
+  .card-subtitle {
+    font-size: halfW(14);
+    color: #82d1f6;
+  }
+
+  .card-value {
+    margin-left: halfW(12);
+    font-size: halfW(16);
+    font-weight: 500;
+    color: #fff;
+    letter-spacing: 1px;
   }
+}
 </style>

+ 63 - 51
src/views/login/index.vue

@@ -1,8 +1,8 @@
 <!--
  * @Author: WangQiBiao
  * @Date: 2019-10-08 15:33:28
- * @LastEditors: WangJiaCheng
- * @LastEditTime: 2021-05-31 10:02:47
+ * @LastEditors: wjc
+ * @LastEditTime: 2025-11-26 09:24:04
  * @Description: 登录
  -->
 <template>
@@ -10,69 +10,81 @@
     <el-form class="login-form" :model="formRule" :rules="rules" ref="formRule">
       <h1 class="login-form-label">绘管家落地数据大屏</h1>
       <el-form-item prop="user" class="input-item">
-        <el-input v-model="formRule.user" placeholder="请输入用户名"></el-input>
+        <el-input
+          v-model="formRule.user"
+          placeholder="请输入用户名"
+          @keyup.enter.native="onSubmit"
+        ></el-input>
       </el-form-item>
       <el-form-item prop="pwd" class="input-item">
-        <el-input v-model="formRule.pwd" show-password placeholder="请输入登录密码"></el-input>
+        <el-input
+          v-model="formRule.pwd"
+          show-password
+          placeholder="请输入登录密码"
+          @keyup.enter.native="onSubmit"
+        ></el-input>
       </el-form-item>
       <el-form-item>
-        <el-button type="primary" v-loading="isLoading" style="width: 100%; " @click="onSubmit" class="login-btn">登录</el-button>
+        <el-button
+          type="primary"
+          v-loading="isLoading"
+          style="width: 100%"
+          @click="onSubmit"
+          class="login-btn"
+          >登录</el-button
+        >
       </el-form-item>
     </el-form>
   </div>
 </template>
 <script>
-export default {
-  data () {
-    return {
-      formRule: {
-        user: '',
-        pwd: ''
-      },
-      rules: {
-        user: [
-          { required: true, message: '请输入账号' }
-        ],
-        pwd: [
-          { required: true, message: '请输入密码' }
-        ]
-      },
-      whiteUser: ['wisdomcity', 'wisdomcity2019'], // 白名单账号
-      isLoading: false
-    }
-  },
-  methods: {
-    onSubmit () {
-      this.$refs['formRule'].validate((valid) => {
-        if (valid) {
-          this.isLoading = true
-          if (this.whiteUser.includes(this.formRule.user)) {
-            localStorage.setItem('token', 'fdsfas41234123fdsf4132423vsvd134')
-            setTimeout(() => {
+  export default {
+    data() {
+      return {
+        formRule: {
+          user: "",
+          pwd: "",
+        },
+        rules: {
+          user: [{ required: true, message: "请输入账号" }],
+          pwd: [{ required: true, message: "请输入密码" }],
+        },
+        whiteUser: ["wisdomcity", "wisdomcity2019"], // 白名单账号
+        isLoading: false,
+      }
+    },
+    methods: {
+      onSubmit() {
+        this.$refs["formRule"].validate((valid) => {
+          if (valid) {
+            this.isLoading = true
+            if (this.whiteUser.includes(this.formRule.user)) {
+              localStorage.setItem("token", "fdsfas41234123fdsf4132423vsvd134")
+              setTimeout(() => {
+                this.isLoading = false
+                let isDemo = localStorage.getItem("demo")
+                if (isDemo === "true") {
+                  this.$router.push({ path: "/index?demo=true" })
+                } else {
+                  this.$router.push({ path: "/index" })
+                }
+              }, 1)
+            } else {
               this.isLoading = false
-              let isDemo = localStorage.getItem('demo')
-              if (isDemo === 'true') {
-                this.$router.push({ path: '/index?demo=true' })
-              } else {
-                this.$router.push({ path: '/index' })
-              }
-            }, 1)
+              this.$message({
+                message: `您输入的账号或者密码有误,请重新输入正确的账号和密码!`,
+                type: "warning",
+              })
+            }
           } else {
-            this.isLoading = false
-            this.$message({
-              message: `您输入的账号或者密码有误,请重新输入正确的账号和密码!`,
-              type: 'warning'
-            })
+            return false
           }
-        } else {
-          return false
-        }
-      })
-    }
+        })
+      },
+    },
   }
-}
 </script>
 
 <style lang="scss" scoped>
-@import "./index.scss";
+  @import "./index.scss";
 </style>

+ 127 - 0
yarn.lock

@@ -18,6 +18,15 @@
     "@babel/highlight" "^7.24.2"
     picocolors "^1.0.0"
 
+"@babel/code-frame@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
+  integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.27.1"
+    js-tokens "^4.0.0"
+    picocolors "^1.1.1"
+
 "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5":
   version "7.24.4"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a"
@@ -54,6 +63,17 @@
     "@jridgewell/trace-mapping" "^0.3.25"
     jsesc "^2.5.1"
 
+"@babel/generator@^7.28.5":
+  version "7.28.5"
+  resolved "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298"
+  integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==
+  dependencies:
+    "@babel/parser" "^7.28.5"
+    "@babel/types" "^7.28.5"
+    "@jridgewell/gen-mapping" "^0.3.12"
+    "@jridgewell/trace-mapping" "^0.3.28"
+    jsesc "^3.0.2"
+
 "@babel/helper-annotate-as-pure@^7.22.5":
   version "7.22.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882"
@@ -127,6 +147,11 @@
     "@babel/template" "^7.22.15"
     "@babel/types" "^7.23.0"
 
+"@babel/helper-globals@^7.28.0":
+  version "7.28.0"
+  resolved "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
+  integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
+
 "@babel/helper-hoist-variables@^7.22.5":
   version "7.22.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
@@ -196,6 +221,14 @@
   dependencies:
     "@babel/types" "^7.24.5"
 
+"@babel/helper-skip-transparent-expression-wrappers@^7.20.0":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56"
+  integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==
+  dependencies:
+    "@babel/traverse" "^7.27.1"
+    "@babel/types" "^7.27.1"
+
 "@babel/helper-skip-transparent-expression-wrappers@^7.22.5":
   version "7.22.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847"
@@ -215,11 +248,21 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e"
   integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==
 
+"@babel/helper-string-parser@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
+  integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+
 "@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.24.5":
   version "7.24.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62"
   integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==
 
+"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5":
+  version "7.28.5"
+  resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
+  integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
+
 "@babel/helper-validator-option@^7.23.5":
   version "7.23.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
@@ -258,6 +301,13 @@
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790"
   integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==
 
+"@babel/parser@^7.27.2", "@babel/parser@^7.28.5":
+  version "7.28.5"
+  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08"
+  integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==
+  dependencies:
+    "@babel/types" "^7.28.5"
+
 "@babel/plugin-proposal-async-generator-functions@^7.2.0":
   version "7.20.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326"
@@ -312,6 +362,15 @@
     "@babel/helper-plugin-utils" "^7.18.6"
     "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
 
+"@babel/plugin-proposal-optional-chaining@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.npmmirror.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea"
+  integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
 "@babel/plugin-proposal-unicode-property-regex@^7.2.0":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e"
@@ -369,6 +428,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
+"@babel/plugin-syntax-optional-chaining@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.npmmirror.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
 "@babel/plugin-transform-arrow-functions@^7.2.0":
   version "7.24.1"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz#2bf263617060c9cc45bcdbf492b8cc805082bf27"
@@ -682,6 +748,15 @@
     "@babel/parser" "^7.24.0"
     "@babel/types" "^7.24.0"
 
+"@babel/template@^7.27.2":
+  version "7.27.2"
+  resolved "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
+  integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
+  dependencies:
+    "@babel/code-frame" "^7.27.1"
+    "@babel/parser" "^7.27.2"
+    "@babel/types" "^7.27.1"
+
 "@babel/traverse@^7.24.5", "@babel/traverse@^7.7.0":
   version "7.24.5"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8"
@@ -698,6 +773,19 @@
     debug "^4.3.1"
     globals "^11.1.0"
 
+"@babel/traverse@^7.27.1":
+  version "7.28.5"
+  resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b"
+  integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==
+  dependencies:
+    "@babel/code-frame" "^7.27.1"
+    "@babel/generator" "^7.28.5"
+    "@babel/helper-globals" "^7.28.0"
+    "@babel/parser" "^7.28.5"
+    "@babel/template" "^7.27.2"
+    "@babel/types" "^7.28.5"
+    debug "^4.3.1"
+
 "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.24.5", "@babel/types@^7.7.0":
   version "7.24.5"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7"
@@ -707,6 +795,14 @@
     "@babel/helper-validator-identifier" "^7.24.5"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.27.1", "@babel/types@^7.28.5":
+  version "7.28.5"
+  resolved "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b"
+  integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==
+  dependencies:
+    "@babel/helper-string-parser" "^7.27.1"
+    "@babel/helper-validator-identifier" "^7.28.5"
+
 "@hapi/address@2.x.x":
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -760,6 +856,14 @@
     wrap-ansi "^8.1.0"
     wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
 
+"@jridgewell/gen-mapping@^0.3.12":
+  version "0.3.13"
+  resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
+  integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
 "@jridgewell/gen-mapping@^0.3.5":
   version "0.3.5"
   resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
@@ -784,6 +888,11 @@
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
   integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
 
+"@jridgewell/sourcemap-codec@^1.5.0":
+  version "1.5.5"
+  resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
+  integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
+
 "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
   version "0.3.25"
   resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
@@ -792,6 +901,14 @@
     "@jridgewell/resolve-uri" "^3.1.0"
     "@jridgewell/sourcemap-codec" "^1.4.14"
 
+"@jridgewell/trace-mapping@^0.3.28":
+  version "0.3.31"
+  resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
+  integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.1.0"
+    "@jridgewell/sourcemap-codec" "^1.4.14"
+
 "@mrmlnc/readdir-enhanced@^2.2.1":
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -5713,6 +5830,11 @@ jsesc@^2.5.1:
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
   integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
 
+jsesc@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
+  integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
+
 jsesc@~0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
@@ -6959,6 +7081,11 @@ picocolors@^1.0.0:
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
   integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
 
+picocolors@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
 picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"