Browse Source

增加数据统计

zhuzhongjie 2 weeks ago
parent
commit
52c341be34

+ 16 - 1
src/request/modules/apiTms.js

@@ -20,4 +20,19 @@ export const warehouseList = (params) => res('post', 'apiTms', '/warehouse/revie
 export const warehouseApprove = (params) => res('post', 'apiTms', '/warehouse/'+params.id+'/approve',params,{form:true})
 
 //供应商仓库-审核拒绝
-export const warehouseReject = (params) => res('post', 'apiTms', '/warehouse/'+params.id+'/reject',params,{form:true})
+export const warehouseReject = (params) => res('post', 'apiTms', '/warehouse/'+params.id+'/reject',params,{form:true})
+
+// 异常运单统计
+export const queryExceptionWaybillStatistics = (params) => res('get', 'apiTms', '/dashboard/queryExceptionWaybillStatistics',params)
+
+// 获取总的运单数量&获取运单发货地运力地图
+export const queryAllWaybill = (params) => res('get', 'apiTms', '/dashboard/queryAllWaybill',params)
+
+// ### 获取审核通过的仓库列表 & 仓库分布地图
+export const queryApprovedWarehouses = (params) => res('get', 'apiTms', '/dashboard/queryApprovedWarehouses',params)
+
+// ### 查看供应商 运单排行榜    
+export const querySupplierRanking = (params) => res('get', 'apiTms', '/dashboard/querySupplierRanking',params)
+
+// ### 获取订单运输异常率 
+export const queryExceptionRate = (params) => res('get', 'apiTms', '/dashboard/queryExceptionRate',params)

+ 20 - 7
src/router/modules/dashboard.js

@@ -1,17 +1,30 @@
 import Layout from '@/layout/index.vue'
 import { createNameComponent } from '../createNode'
 const route = [
+  // {
+  //   path: '/',
+  //   component: Layout,
+  //   redirect: '/dashboard',
+  //   meta: { title: '', icon: 'icon-bingtutongji' },
+  //   children: [
+  //     {
+  //       path: 'dashboard',
+  //       component: createNameComponent(() => import('@/views/dashboard/index.vue')),
+  //       meta: { title: '数据看板', icon: 'icon-bingtutongji', hideClose: true }
+  //     }
+  //   ]
+  // }
   {
     path: '/',
     component: Layout,
-    redirect: '/trackManager',
-    meta: { title: '', icon: 'icon-bingtutongji' ,hideMenuItem:true},
+    redirect: '/home',
+    meta: { title: '', icon: 'icon-bingtutongji' },
     children: [
-      // {
-      //   path: 'home',
-      //   component: createNameComponent(() => import('@/views/dashboard/index.vue')),
-      //   meta: { title: '首页', icon: 'icon-yingyong', hideClose: true }
-      // }
+      {
+        path: 'home',
+        component: createNameComponent(() => import('@/views/dashboard/index.vue')),
+        meta: { title: '数据看板', icon: 'icon-yingyong', hideClose: true }
+      }
     ]
   }
 ]

+ 55 - 51
src/views/dashboard/components/cardBox.vue

@@ -1,17 +1,17 @@
 <template>
-  <div class="cardBox" :style="'width:'+boxWidth">
-    <div class="card_border" :style="'background-image: linear-gradient(to right,'+startCor+','+endCor+');'">
+  <div class="cardBox" :style="'width:' + boxWidth">
+    <div class="card_border" :style="'background-image: linear-gradient(to right,' + startCor + ',' + endCor + ');'">
       <Row>
         <Col span="16">
           <p class="main_num">
-            <count-to :startVal='0' class='main_num' :endVal='numValue' :duration='2500'></count-to>
-            <span class="num_unit">{{numUnit}}</span>
+            <count-to :startVal="0" class="main_num" :endVal="numValue" :duration="2500"></count-to>
+            <span class="num_unit">{{ numUnit }}</span>
           </p>
-          <p class="sunTitle">{{mainTitle}}</p>
+          <p class="sunTitle">{{ mainTitle }}</p>
         </Col>
         <Col span="8">
           <div class="other_conyent">
-            <p class="other_title">{{subTitle}}</p>
+            <p class="other_title">{{ subTitle }}</p>
           </div>
         </Col>
       </Row>
@@ -21,75 +21,79 @@
 
 <script>
 import { defineComponent } from 'vue'
-import { CountTo } from 'vue3-count-to';
+import { CountTo } from 'vue3-count-to'
 export default defineComponent({
-  components:{
-    CountTo
+  components: {
+    CountTo,
   },
-  props:{
-    startCor:{
-      type:String,
-      default:'#23cde4'
+  props: {
+    startCor: {
+      type: String,
+      default: '#23cde4',
     },
-    endCor:{
-      type:String,
-      default:'#4b87f9'
+    endCor: {
+      type: String,
+      default: '#4b87f9',
     },
-    boxWidth:{
-      type:String,
-      default:'20%'
+    boxWidth: {
+      type: String,
+      default: '20%',
     },
-    numValue:{
-      type:Number,
-      default:0
+    numValue: {
+      type: Number,
+      default: 0,
     },
-    mainTitle:{
-      type:String,
-      default:'暂无内容',
+    mainTitle: {
+      type: String,
+      default: '暂无内容',
     },
-    numUnit:{
-      type:String,
-      default:''
+    numUnit: {
+      type: String,
+      default: '',
+    },
+    subTitle: {
+      type: String,
+      default: '',
     },
-    subTitle:{
-      type:String,
-      default:''
-    }
   },
   setup() {
-    return {
-      
-    }
+    return {}
   },
-  methods: {
-    
-  }
+  methods: {},
 })
 </script>
 
 <style lang="scss" scoped>
-.cardBox{
-  .card_border{
+.cardBox {
+  .card_border {
     height: 12vh;
-    border-radius: 8px; padding: 5%;
+    border-radius: 8px;
+    padding: 5%;
     box-shadow: 0px 35px 30px -20px #ccc;
-    .main_num{
-      font-size:3.5vh; color: #fff; font-weight: bold;
-      .num_unit{
-        font-size:1.5vh; color: #fff; font-weight: 200; margin-left: 6px;
+    .main_num {
+      font-size: 3.5vh;
+      color: #fb1818;
+      font-weight: bold;
+      .num_unit {
+        font-size: 1.5vh;
+        font-weight: 200;
+        margin-left: 6px;
       }
     }
-    .sunTitle{
-      color: #fff; font-size: 1.2vh; margin-left: 2px;
+    .sunTitle {
+      color: #fff;
+      font-size: 14px;
+      margin-left: 2px;
       margin-bottom: 10px;
     }
-    .other_conyent{
-      text-align: right; margin-right: 5px;
-      .other_title{
+    .other_conyent {
+      text-align: right;
+      margin-right: 5px;
+      .other_title {
         color: #fff;
         font-size: 1.5vh;
       }
     }
   }
 }
-</style>
+</style>

+ 163 - 0
src/views/dashboard/components/mapChart.vue

@@ -0,0 +1,163 @@
+/** // author:jiana // time:2025-04-23 // desc:运单管理 */
+<template>
+  <div class="mapMark">
+    <div class="btnBox">
+      <el-radio-group v-model="tabPosition" @change="changeTabPosition">
+        <el-radio-button value="load">发货地统计</el-radio-button>
+        <el-radio-button value="unload">收货地统计</el-radio-button>
+        <el-radio-button value="warehouse">仓库地址统计</el-radio-button>
+        <!-- <el-radio-button value="left">left</el-radio-button> -->
+      </el-radio-group>
+    </div>
+    <div id="container1"></div>
+  </div>
+</template>
+<script setup>
+import { defineComponent, ref, reactive, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import AMapLoader from '@amap/amap-jsapi-loader'
+import { tms } from '@/request/api'
+const route = useRoute()
+const trajectory = ref([])
+const tabPosition = ref('load')
+const map = ref(null)
+const initMap = () => {
+  AMapLoader.load({
+    key: '81a1282308f1aae58082425a1ebb91b0',
+    version: '2.0',
+    plugins: ['AMap.MoveAnimation'],
+  })
+    .then(AMap => {
+      AMap.plugin('AMap.MoveAnimation', () => {
+        map.value = new AMap.Map('container1', {
+          center: [116.397428, 39.90923],
+        })
+      })
+    })
+    .catch(e => {
+      console.error(e)
+    })
+}
+
+// 添加marker
+const addMarker = data => {
+  data.forEach(item => {
+    const marker = new AMap.Marker({
+      position: item,
+      icon: require('@/assets/images/dian.png'), // 自定义图标
+      offset: new AMap.Pixel(-13, -26),
+    })
+    marker.setMap(map.value)
+    map.value.setFitView()
+  })
+}
+
+const waybillData = ref([]) // 运单数据
+// 获取运单发货地运力地图数据
+const getWaybillData = async () => {
+  const res = await tms.queryAllWaybill()
+  if (res.code === 101) {
+    waybillData.value = res.data
+    setTimeout(() => {
+      addloadData()
+    }, 1500)
+  }
+}
+
+// 发货地运力地图
+const addloadData = () => {
+  trajectory.value = []
+  waybillData.value.forEach(item => {
+    if (item.originLng && item.originLat) {
+      trajectory.value.push([item.originLng, item.originLat])
+    }
+  })
+  addMarker(trajectory.value)
+}
+
+// 收货地运力地图
+const addunloadData = () => {
+  trajectory.value = []
+  waybillData.value.forEach(item => {
+    if (item.destinationLng && item.destinationLat) {
+      trajectory.value.push([item.destinationLng, item.destinationLat])
+    }
+  })
+  addMarker(trajectory.value)
+}
+// 清除markers
+const clearMarkers = () => {
+  map.value.clearMap()
+}
+
+// 切换标签
+const changeTabPosition = () => {
+  clearMarkers()
+  if (tabPosition.value === 'load') {
+    addloadData()
+  } else if (tabPosition.value === 'unload') {
+    addunloadData()
+  } else if (tabPosition.value === 'warehouse') {
+    addWarehouseData()
+  } else {
+    trajectory.value = []
+  }
+}
+
+const warehouselist = ref([]) // 仓库列表
+// 获取审核通过的仓库列表 & 仓库分布地图
+const getWarehouseData = async () => {
+  const res = await tms.queryApprovedWarehouses()
+  if (res.code === 101) {
+    // console.log(res.data, '仓库列表')
+    warehouselist.value = res.data
+  }
+}
+
+const addWarehouseData = () => {
+  trajectory.value = []
+  warehouselist.value.forEach(item => {
+    if (item.longitude && item.latitude) {
+      trajectory.value.push([Number(item.longitude), Number(item.latitude)])
+    }
+  })
+  addMarker(trajectory.value)
+}
+
+
+onMounted(async () => {
+  await initMap()
+  getWaybillData()
+  getWarehouseData()
+})
+</script>
+
+<style lang="scss" scoped>
+.mapMark {
+  position: relative;
+  // padding: 20px;
+  #container1 {
+    width: 100%;
+    height: 550px;
+  }
+  .btnBox {
+    position: absolute;
+    top: 10px;
+    left: 20px;
+    z-index: 1;
+    background: #fff;
+    // width: 200px;
+    border-radius: 5px;
+  }
+}
+</style>
+<style lang="scss">
+.amap-icon {
+  width: 40px !important;
+  height: 40px !important;
+}
+.amap-icon img {
+  width: 40px !important;
+  height: 40px !important;
+}
+</style>

+ 138 - 61
src/views/dashboard/index.vue

@@ -1,89 +1,166 @@
-/**
-  // author:zhangb
-  // time:2022-10-19
-  // desc:可视化组件图标部分拆解
-*/
+/** // author:zhangb // time:2022-10-19 // desc:可视化组件图标部分拆解 */
 <template>
   <div class="dashboard">
     <!-- 顶部卡片汇总 -->
     <div class="top_card_border">
-      <card-box :boxWidth="'19%'" :numValue="8866" :mainTitle="'牧场总数量'" :numUnit="'个'"></card-box>
-      <card-box :startCor="'#f382ee'" :endCor="'#7d7cfe'" :boxWidth="'19%'" :numValue="65244" :mainTitle="'奶牛总数量'" :numUnit="'头'"></card-box>
-      <card-box :startCor="'#40ecb0'" :endCor="'#0eb4e8'" :boxWidth="'19%'" :numValue="10521" :mainTitle="'原奶总产量'" :numUnit="'公斤'"></card-box>
-      <card-box :startCor="'#fe8e82'" :endCor="'#ff6fb7'" :boxWidth="'19%'" :numValue="8452" :mainTitle="'草料需求量'" :numUnit="'万吨'" :subTitle="'(按每月)'"></card-box>
-      <card-box :startCor="'#fe8e82'" :endCor="'#ff6fb7'" :boxWidth="'19%'" :numValue="65121" :mainTitle="'饲料需求量'" :numUnit="'万吨'" :subTitle="'(按每月)'"></card-box>
-    </div>
+      <card-box
+        :startCor="'#fe8e82'"
+        :endCor="'#ff6fb7'"
+        @click="router.push('/trackManager/waybillManager')"
+        :boxWidth="'19%'"
+        :numValue="waybillData.length"
+        :mainTitle="'运单数量'"
+        :numUnit="'条'"
+      ></card-box>
 
-    <!-- TODO 暂不使用 -->
-    <!-- 中部区域 -->
-    <div class="mid_content" v-show="false">
-      <!-- 左边 -->
-      <div class="let_step">
-        <des-card></des-card>
-        <des-card></des-card>
-        <des-card></des-card>
-        <des-card></des-card>
-        <des-card></des-card>
-        <des-card></des-card>
+      <card-box
+        :boxWidth="'19%'"
+        @click="router.push('/trackManager/deviceAbnormality')"
+        :numValue="exceptionWaybillStatistics.device"
+        :mainTitle="'设备异常'"
+        :numUnit="'条'"
+      ></card-box>
+      <card-box
+        :startCor="'#f382ee'"
+        :endCor="'#7d7cfe'"
+        :boxWidth="'19%'"
+        @click="router.push('/trackManager/loadDeviation')"
+        :numValue="exceptionWaybillStatistics.loading"
+        :mainTitle="'装货地异常'"
+        :numUnit="'条'"
+      ></card-box>
+      <card-box
+        :startCor="'#40ecb0'"
+        :endCor="'#0eb4e8'"
+        @click="router.push('/trackManager/abnormalWaybill')"
+        :boxWidth="'19%'"
+        :numValue="exceptionWaybillStatistics.unload"
+        :mainTitle="'卸货地异常'"
+        :numUnit="'条'"
+      ></card-box>
+      <card-box
+        :startCor="'#fe8e82'"
+        :endCor="'#ff6fb7'"
+        :boxWidth="'19%'"
+        :numValue="exceptionRate"
+        :mainTitle="'运单运输异常率'"
+        :numUnit="'%'"
+      ></card-box>
+    </div>
+    <div class="mid_content">
+      <!-- 核心可视化地图内容 -->
+      <div class="map_style">
+        <map-chart :heightStyle="'height:800px;'"></map-chart>
       </div>
-      <!-- echarts 图表 -->
       <div class="right_step">
-        <pie-chart></pie-chart>
+        <div class="title">运单数据排行榜</div>
+        <el-table :data="tableData" border stripe style="width: 100%">
+          <el-table-column label="供应商" prop="supplierName" width=""></el-table-column>
+          <el-table-column label="运单数" prop="waybillCount" width=""></el-table-column>
+        </el-table>
       </div>
     </div>
-    
-    <!-- 核心可视化地图内容 -->
-    <div class="map_style">
-      <map-chart :heightStyle="'height:800px;'"></map-chart>
-    </div>
-    
   </div>
 </template>
 
-<script>
-import { defineComponent } from 'vue'
+<script setup>
+import { tms } from '@/request/api'
 import cardBox from './components/cardBox.vue'
-import desCard from './components/desCard.vue'
-export default defineComponent({
-  components:{
-    cardBox,desCard
-  },
-  setup() {
-    return {
-      
-    }
-  },
-  methods: {
-    
+import { ref, reactive, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import mapChart from './components/mapChart.vue'
+const router = useRouter()
+// 异常运单统计
+const exceptionWaybillStatistics = ref({
+  device: 0,
+  loading: 0,
+  unload: 0,
+}) // 异常运单统计
+const getData = async () => {
+  const response = await tms.queryExceptionWaybillStatistics()
+  if (response.code === 101) {
+    exceptionWaybillStatistics.value.device = response.data.find(item => item.type === 'DEVICE').count
+    exceptionWaybillStatistics.value.loading = response.data.find(item => item.type === 'LOADING').count
+    exceptionWaybillStatistics.value.unload = response.data.find(item => item.type === 'UNLOAD').count
   }
+}
+
+const waybillData = ref([]) // 运单数据
+// 获取运单发货地运力地图数据
+const getWaybillData = async () => {
+  const res = await tms.queryAllWaybill()
+  if (res.code === 101) {
+    waybillData.value = res.data
+  }
+}
+
+const tableData = ref([]) // 运单数据排行
+// 查看供应商数据排行
+const getSupplierData = async () => {
+  const res = await tms.querySupplierRanking()
+  if (res.code === 101) {
+    console.log(res.data, '数据排行')
+    tableData.value = res.data
+  }
+}
+
+const exceptionRate = ref(0) // 运单号
+// 订单运输异常率
+const getExceptionRate = async () => {
+  const res = await tms.queryExceptionRate()
+  if (res.code === 101) {
+    console.log(res.data, '订单运输异常率')
+    exceptionRate.value = res.data.exceptionWaybillCount / res.data.totalWaybillCount * 100
+  }
+}
+
+onMounted(() => {
+  getData()
+  getWaybillData()
+  getSupplierData()
+  getExceptionRate()
 })
 </script>
 
 <style lang="scss" scoped>
-.dashboard{
+.dashboard {
   padding: 1em;
-  .top_card_border{
+  width: 100%;
+  .top_card_border {
     width: 100%;
     display: flex;
-    justify-content:space-between;
+    justify-content: space-between;
   }
-  .mid_content{
-    margin-top:30px;
-    display: flex; justify-content:space-between;
-    .let_step{
+  .mid_content {
+    display: flex;
+    width: 100%;
+    justify-content: space-evenly;
+    .let_step {
       // border: 1px solid #000;
-      display: flex; justify-content:space-between;
-      width:55%; height: calc(35vh);
-      flex-wrap:wrap;
+      display: flex;
+      justify-content: space-between;
+      width: 55%;
+      height: calc(35vh);
+      flex-wrap: wrap;
     }
-    .right_step{
-      width: 44%; height: 34vh;
-      background-color: #fff;
+    .right_step {
+      text-align: center;
+      width: 25%;
+      padding-top: 30px;
+      padding-left: 10px;
+      .title {
+        font-size: 20px;
+        font-weight: 600;
+        color: #000;
+        margin-bottom: 20px;
+      }
     }
   }
-  .map_style{
-    border: 1px solid #000; margin-top:30px;
-    height: calc(100vh*0.7);
+  .map_style {
+    width: 75%;
+    border: 1px solid #309ef3;
+    margin-top: 30px;
+    // height: calc(100vh * 0.7);
   }
 }
-</style>
+</style>