“hanlingqiang il y a 2 ans
Parent
commit
4788893da2

+ 49 - 0
src/api/home.ts

@@ -0,0 +1,49 @@
+import request from '@/utils/request'
+enum Api {
+  categoryList = '/home/category', //品类
+  productList = '/home/product', //产品
+  deviceList = '/home/device', //设备
+  deviceCategory = '/home/deviceCategory', //设备数量统计
+  deviceStates = '/home/deviceStates', //设备状态统计
+}
+
+// 获取品类
+export const categoryList = (data) => {
+  return request({
+    url: Api.categoryList,
+    method: 'post',
+    data,
+  })
+}
+// 获取产品
+export const productList = (data) => {
+  return request({
+    url: Api.productList,
+    method: 'post',
+    data,
+  })
+}
+// 获取设备
+export const deviceList = (data) => {
+  return request({
+    url: Api.deviceList,
+    method: 'post',
+    data,
+  })
+}
+// 设备数量统计
+export const deviceCategory = (data) => {
+  return request({
+    url: Api.deviceCategory,
+    method: 'post',
+    data,
+  })
+}
+// 设备状态统计
+export const deviceStates = (data) => {
+  return request({
+    url: Api.deviceStates,
+    method: 'post',
+    data,
+  })
+}

BIN
src/assets/images/home/home_1.png


BIN
src/assets/images/home/home_2.png


BIN
src/assets/images/home/home_3.png


BIN
src/assets/images/home/home_4.png


BIN
src/assets/images/home/home_5.png


+ 583 - 9
src/views/index.vue

@@ -1,28 +1,608 @@
 <template>
-  <div class="app-container home">首页</div>
+  <div class="app-container home">
+    <el-row :gutter="10" class="top_row">
+      <el-col :span="8" class="box-card_bg" v-for="(item, index) in state.deviceListData" :key="index">
+        <el-card :style="{ background: item.bgColor }" class="box-card-num">
+          <el-row class="box-card-row" style="margin:0">
+            <el-col :span="16" class="box_left">
+              <p>{{ item.name }}</p>
+              <p>{{ item.value }}</p>
+            </el-col>
+            <el-col :span="8">
+              <el-image style="width:125px;height:80px" :src="item.icon" fit="cover"></el-image>
+            </el-col>
+          </el-row>
+          <el-divider />
+          <el-row class="add_bg">
+            <div>
+              <span>今日新增</span>
+              <el-image style="width:9px;height:9px;" :src="item.addIcon"></el-image>
+              <span>{{ item.addValue }}</span>
+            </div>
+          </el-row>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="10">
+      <el-col :span="12" v-for="(item, index) in state.barList" :key="index">
+        <el-card class="pie_bg">
+          <p>{{ item.name }}</p>
+          <el-divider />
+          <div v-if="index == 0">
+            <div ref="deviceNum_ref" id="deviceNum" />
+          </div>
+          <div v-else class="device_bg">
+            <div class="device_item" v-for="(item, index) in state.deviceTypeList" :key="index">
+              <div class="icon_bg" :id="'devicechart' + index"></div>
+              <div class="title">{{ item.title }}</div>
+              <div class="num" :style="{'--color':state.deviceColor[index]}">{{ item.rate }}%</div>
+            </div>
+            <div class="legend_bg">
+              <div class="device_legend" v-for="(itemL, index) in state.deviceTypeList" :key="index">
+                <span :style="{'--color':state.deviceColor[index]}"></span>
+                <span>{{ itemL.title }}:</span>
+                <span>{{ itemL.num}}</span>
+              </div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="10">
+      <el-col :span="24">
+        <el-card class="box-bottom_bg">
+          <p>平台架构图</p>
+          <el-divider />
+          <el-image :src="home_5" fit="contain"></el-image>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
 </template>
 
 <script setup name="Index" lang="ts">
+import { categoryList, productList, deviceList, deviceCategory,deviceStates } from '@/api/home'
+import * as echarts from 'echarts'
+import home_1 from '@/assets/images/home/home_1.png'
+import home_2 from '@/assets/images/home/home_2.png'
+import home_3 from '@/assets/images/home/home_3.png'
+import home_4 from '@/assets/images/home/home_4.png'
+import home_5 from '@/assets/images/home/home_5.png'
+const deviceNum_ref = ref()
+const state = reactive({
+  deviceListData: [
+    {
+      name: '品类数量',
+      value: 0,
+      addValue: 0,
+      bgColor: 'linear-gradient(225deg, #F1F7FF 0%, #FFFFFF 36%, #FFFFFF 100%)',
+      icon: home_1,
+      addIcon: home_4
+    },
+    {
+      name: '产品数量',
+      value: 11,
+      addValue: 4,
+      bgColor: 'linear-gradient(225deg, #FFF6EE 0%, #FFFFFF 36%, #FFFFFF 100%)',
+      icon: home_2,
+      addIcon: home_4
+    },
+    {
+      name: '设备数量',
+      value: 12,
+      addValue: 5,
+      bgColor: 'linear-gradient(225deg, #EEFFF5 0%, #FFFFFF 36%, #FFFFFF 100%)',
+      icon: home_3,
+      addIcon: home_4
+    }
+  ],
+  barList: [
+    {
+      name: '设备数量统计'
+    },
+    {
+      name: '设备状态统计'
+    }
+  ],
+  pieOptionData: [],
+  pieColor: [
+    'rgba(255, 162, 23, 1)',
+    'rgba(255, 119, 106, 1)',
+    'rgba(108, 201, 255, 1)',
+    'rgba(108, 158, 254, 1)',
+    'rgba(85, 116, 251, 1)',
+    'rgba(139, 126, 255, 1)',
+    'rgba(95, 208, 164, 1)',
+    'rgba(250, 204, 62, 1)'
+  ],
+
+  // 设备状态统计
+  deviceColor:[
+    'rgba(96, 208, 164, 1)',
+    'rgba(254, 103, 86, 1)',
+    'rgba(103, 154, 255, 1)'
+  ],
+  deviceTypeList: [
+    { title: '在线设备', num: 0, rate: 0 },
+    { title: '离线设备', num: 0, rate: 0 },
+    { title: '待激活设备', num: 0, rate: 0 },
+  ]
+})
+// 设备数量统计图表
+const setBar1 = (dataObj:object) => {
+  const keys = Object.keys(dataObj)
+  const values = Object.values(dataObj)
+  let totlaNum = 0
+  const pieDataT = values.map((item,index)=>{
+    totlaNum += item
+    return {
+      name: keys[index],
+      value: item
+    }
+  })
+  const chartDom: any = document.getElementById('deviceNum')
+  const myChart: any = echarts.init(chartDom)
+  // const deviceNum_refIntance = echarts.init(deviceNum_ref.value)
+  myChart.setOption({
+    legend: {
+      backgroundColor: 'transparent',
+      type: 'plain',
+      show: 'true',
+      height: '100%',
+      right: '8%',
+      top: 0,
+      bottom: 0,
+      orient: 'vertical',
+      itemGap: 10,
+      itemWidth: 12,
+      itemHeight: 12,
+      icon: 'circle',
+      borderRadius: 0,
+      formatter: function (name) {
+        // let titleList = name.split('|')
+        // return `{a|${titleList[0]}}{b|${titleList[1]}}`
+        const pieItem = pieDataT.find(item => item.name == name)
+        return `{a|${name}:}{b|${pieItem?.value}}`
+      },
+      textStyle: {
+        color: 'rgba(175, 187, 206, 1)',
+        fontSize: 10,
+        rich: {
+          a: {
+            minWidth: 30,
+            align: 'left',
+            color: 'rgba(0, 11, 25, 1)',
+            fontSize: 14,
+            fontWeight: 500,
+            padding: [0, 0, 0, 5]
+          },
+          b: {
+            width: 80,
+            color: 'rgba(0, 11, 25, 1)',
+            align: 'left',
+            fontSize: 18,
+            fontWeight: 'bold',
+            backgroundColor: 'transparent',
+            padding: [0, 0, 0, 8]
+
+          }
+        }
+      }
+    },
+    tooltip: {
+      trigger: 'item',
+      formatter: '{a} <br/>{b} : {c} ({d}%)'
+    },
+    graphic: [
+      {
+        z: 9,
+        type: 'text',
+        left: '18%',
+        top: '40%',
+        background: 'red',
+        style: {
+          text: totlaNum,
+          fontSize: 18,
+          textAlign: 'right',
+          fill: 'rgba(0, 11, 25, 1)',
+          font: 'OPPOSans-B,OPPOSans',
+          width: 100,
+          height: 20,
+        }
+      },
+      {
+        z: 9,
+        type: 'text',
+        left: '17%',
+        top: '52%',
+        style: {
+          text: '设备总数',
+          fontSize: 12,
+          textAlign: 'left',
+          fill: 'rgba(0, 11, 25, 1)',
+          width: 100,
+          height: 30,
+        }
+      },
+    ],
+    series: [
+      {
+        name: '设备总数',
+        type: 'pie',
+        // roseType: 'radius',
+        radius: ['55%', '95%'],
+        center: ['20%', '50%'],
+        label: {
+          show: false,
+          position: 'center',
+        },
+        // 数据引线
+        labelLine: {
+          show: false
+        },
+        data: pieDataT,
+        animationEasing: 'cubicInOut',
+        animationDuration: 100
+      }
+    ]
+  }, true)
+}
+
+
+// 设备状态统计
+const setCommonOption = (serveRate: number,deviceNum:number, barColor: string) => {
+  let option = {
+    graphic: [
+      {
+        z: 9,
+        type: 'text',
+        left: '41%',
+        top: '42%',
+        style: {
+          text: deviceNum+'个',
+          fontSize: 18,
+          textAlign: 'center',
+          fill: 'rgba(0, 11, 25, 1)',
+          font: 'OPPOSans-B,OPPOSans',
+          width: 100,
+          height: 20,
+        }
+      }
+    ],
+    series: [
+      {
+        name: '服务覆盖率',
+        type: 'pie',
+        radius: ['72%', '72%'],
+        center: ['50%', '50%'],
+        startAngle: 270,
+        avoidLabelOverlap: false,
+        label: {
+          show: false
+        },
+        z: 3,
+        data: [
+          {
+            value: serveRate,
+            name: '服务覆盖率',
+            itemStyle: {
+              borderWidth: 15,
+              borderCap: 'round',
+              borderColor: barColor
+            },
+
+
+          },
+          {
+            value: 100 - serveRate,
+            name: '占位背景',
+          },
+
+        ]
+      },
+      {
+        name: '占位背景',
+        type: 'pie',
+        radius: ['72%', '72%'],
+        center: ['50%', '50%'],
+        startAngle: 270,
+        avoidLabelOverlap: false,
+        label: {
+          show: false
+        },
+        data: [
+          {
+            value: 1,
+            name: '占位背景',
+            itemStyle: {
+              borderWidth: 15,
+              borderColor: 'rgba(230, 230, 230, 1)'
+            },
+
+          }
+        ]
+      },
+    ]
+  }
+  return option
+}
+interface deviceObj {
+  offline: number
+  online: number
+  unactivated: number
+}
+const setBar2 = (dataObj:deviceObj) => {
+  setTimeout(() => {
+    let deviceTotalNum = dataObj.offline + dataObj.online +dataObj.unactivated
+    const unactivatedRate = ((dataObj.unactivated / deviceTotalNum)*100).toFixed(2)
+    state.deviceTypeList[0].num =  dataObj.online
+    state.deviceTypeList[0].rate = parseFloat(((dataObj.online / deviceTotalNum)*100).toFixed(2)) || 0
+    state.deviceTypeList[1].num =  dataObj.offline
+    state.deviceTypeList[1].rate = parseFloat(((dataObj.offline / deviceTotalNum)*100).toFixed(1)) || 0
+    state.deviceTypeList[2].num =  dataObj.unactivated
+    state.deviceTypeList[2].rate = parseFloat(unactivatedRate) || 0
+    console.log('state.deviceTypeList:')
+    console.log(state.deviceTypeList)
+    const chartDom1: any = document.getElementById('devicechart0')
+    const chartDom2: any = document.getElementById('devicechart1')
+    const chartDom3: any = document.getElementById('devicechart2')
+    const myChart1: any = echarts.init(chartDom1)
+    const myChart2: any = echarts.init(chartDom2)
+    const myChart3: any = echarts.init(chartDom3)
+    myChart1.setOption(setCommonOption(state.deviceTypeList[0].rate,state.deviceTypeList[0].num,state.deviceColor[0]))
+    myChart2.setOption(setCommonOption(state.deviceTypeList[1].rate,state.deviceTypeList[1].num,state.deviceColor[1]))
+    myChart3.setOption(setCommonOption(state.deviceTypeList[2].rate,state.deviceTypeList[2].num,state.deviceColor[2]))
+  }, 100)
+}
+
+const getDataList = async () => {
+  const categoryData = await categoryList({
+    data:{},
+    requestId:''
+  })
+  const productData = await productList({
+    data:{},
+    requestId:''
+  })
+  const deviceData = await deviceList({
+    data:{},
+    requestId:''
+  })
+  state.deviceListData[0].value = categoryData.data && categoryData.data.categoryAllNum
+  state.deviceListData[0].addValue = categoryData.data && categoryData.data.categoryTodayNum
+
+  state.deviceListData[1].value = productData.data && productData.data.productAllNum
+  state.deviceListData[1].addValue = productData.data && productData.data.productTodayNum
+
+  state.deviceListData[2].value = deviceData.data && deviceData.data.viceAllNum
+  state.deviceListData[2].addValue = deviceData.data && deviceData.data.deviceTodayNum
 
-const goTarget = (url:string) => {
+
+  const deviceCategoryData = await deviceCategory({
+    data:{},
+    requestId:''
+  })
+  const deviceStatesData = await deviceStates({
+    data:{},
+    requestId:''
+  })
+
+  setTimeout(() => {
+    setBar1(deviceCategoryData.data)
+    setBar2(deviceStatesData.data)
+  }, 200)
+}
+const goTarget = (url: string) => {
   window.open(url, '__blank')
 }
+onMounted(() => {
+  getDataList()
+})
 </script>
 
 <style scoped lang="scss">
 .home {
+  font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 13px;
+  color: #676a6c;
+  overflow-x: hidden;
+
+  .top_row {
+    margin-bottom: 15px;
+  }
+
+  .box-card-num {
+    width: 100%;
+    height: 154px;
+
+    .box-card-row {
+      width: 100%;
+      height: 80px;
+
+      .box_left {
+        width: 100%;
+        height: 100%;
+
+        p:nth-of-type(1) {
+          height: 10px;
+          font-size: 16px;
+          font-family: SourceHanSansCN, SourceHanSansCN;
+          font-weight: 500;
+          color: #A4ACB4;
+        }
+
+        p:nth-of-type(2) {
+          font-size: 34px;
+          font-family: PingFangSC, PingFang SC;
+          font-weight: 600;
+          color: #000B19;
+        }
+      }
+    }
+
+    .el-divider--horizontal {
+      margin: 12px 0;
+    }
+
+    .add_bg {
+      width: 100%;
+      height: 65px;
+
+      span:nth-of-type(1) {
+        font-size: 14px;
+        font-family: SourceHanSansCN, SourceHanSansCN;
+        font-weight: 500;
+        color: #A4ACB4;
+        margin-right: 3px;
+      }
+
+      span:nth-of-type(2) {
+        font-size: 14px;
+        font-family: SourceHanSansCN, SourceHanSansCN;
+        font-weight: 500;
+        color: #03B32A;
+        margin-left: 3px;
+      }
+    }
+  }
+
+  .pie_bg {
+    width: 100%;
+    height: 255px;
+
+    p {
+      font-size: 16px;
+      font-family: SourceHanSansCN, SourceHanSansCN;
+      font-weight: 500;
+      color: #000B19;
+    }
+
+    .el-divider--horizontal {
+      margin: 12px 0;
+    }
+
+    #deviceNum {
+      width: 100%;
+      height: 170px;
+    }
+
+    .device_bg {
+      width: 100%;
+      height: 170px;
+      display: flex;
+
+      .device_item {
+        width: 25%;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+
+        .icon_bg {
+          width: 100%;
+          height: 75%;
+        }
+
+        .title {
+          font-size: 12px;
+          font-family: SourceHanSansCN, SourceHanSansCN;
+          font-weight: 500;
+          color: #000B19;
+        }
+
+        .num {
+          font-size: 13px;
+          font-family: SourceHanSansCN, SourceHanSansCN;
+          font-weight: bolder;
+          color: var(--color);
+        }
+      }
+
+      .legend_bg {
+        width: 25%;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        .device_legend {
+          width: 100%;
+          height: 23%;
+          span:nth-of-type(1) {
+            display: inline-block;
+            width: 12px;
+            height: 12px;
+            border-radius: 6px;
+            background-color: var(--color);
+            margin-right: 8px;
+          }
+          span:nth-of-type(2) {
+            font-size: 14px;
+            font-family: SourceHanSansCN, SourceHanSansCN;
+            font-weight: 500;
+            color: #000B19;
+            margin-right: 8px;
+          }
+          span:nth-of-type(3) {
+            font-size: 18px;
+            font-family: SourceHanSansCN, SourceHanSansCN;
+            font-weight: bold;
+            color: #000B19;
+          }
+        }
+      }
+    }
+  }
+
+  .el-row:nth-of-type(3) {
+    margin-top: 15px;
+
+    .box-bottom_bg {
+      width: 100%;
+      min-height: 337px;
+
+      p {
+        font-size: 16px;
+        font-family: SourceHanSansCN, SourceHanSansCN;
+        font-weight: 500;
+        color: #000B19;
+      }
+
+      .el-divider--horizontal {
+        margin: 12px 0;
+      }
+
+      .el-image {
+        width: 100%;
+        height: 400px;
+      }
+    }
+  }
+
+
+
+
+
+
+
+
   blockquote {
     padding: 10px 20px;
     margin: 0 0 20px;
     font-size: 17.5px;
     border-left: 5px solid #eee;
   }
+
   hr {
     margin-top: 20px;
     margin-bottom: 20px;
     border: 0;
     border-top: 1px solid #eee;
   }
+
   .col-item {
     margin-bottom: 20px;
   }
@@ -32,11 +612,6 @@ const goTarget = (url:string) => {
     margin: 0;
   }
 
-  font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
-  font-size: 13px;
-  color: #676a6c;
-  overflow-x: hidden;
-
   ul {
     list-style-type: none;
   }
@@ -70,5 +645,4 @@ const goTarget = (url:string) => {
       padding-inline-start: 40px;
     }
   }
-}
-</style>
+}</style>