Browse Source

运单管理模块

jiana 2 weeks ago
parent
commit
5af8569f69

BIN
src/assets/images/qidian.png


BIN
src/assets/images/zhongdian.png


File diff suppressed because it is too large
+ 10643 - 0
src/assets/track.json


+ 3 - 2
src/layout/Tabs/index.vue

@@ -46,10 +46,11 @@ export default defineComponent({
     const scrollbarDom = ref(null)
     const allRoutes = router.options.routes
     const defaultMenu = {
-      path: '/classifyManageIndex',
-      meta: { title: '牛只分类', hideClose: true }
+      path: '',
+      meta: { title: '', hideClose: true }
     }
     const contentFullScreen = computed(() => store.state.app.contentFullScreen)
+    console.log(route.path,'route.path')
     const currentDisabled = computed(() => route.path === defaultMenu.path)
 
     let activeMenu = reactive({ path: '' })

+ 2 - 1
src/request/api.js

@@ -5,7 +5,7 @@ import * as apiCow  from './modules/apiCow' //牛只管理
 import * as apiOrder  from './modules/apiOrder' //牛只订单
 import * as apiEcontract  from './modules/apiEcontract'
 import * as apiItem  from './modules/apiItem'
-
+import * as apiTms from './modules/apiTms'
 
 export const user = apiUser
 export const content  = apiContent
@@ -13,3 +13,4 @@ export const cow  = apiCow
 export const order  = apiOrder
 export const econtract  = apiEcontract
 export const item  = apiItem
+export const tms = apiTms

+ 3 - 0
src/request/apiConfig.js

@@ -10,6 +10,7 @@ if (env === 'development') {  //开发环境
     apiOrder: 'http://orders2.aiyangniu.net',
     apiEcontract:'http://econtracts.aiyangniu.net',
     apiItem: 'http://items2.aiyangniu.net',
+    apiTms:'http://tms.aiyangniu.net',
   }
 } else if (env === 'testing') {
   api = {
@@ -20,6 +21,7 @@ if (env === 'development') {  //开发环境
     apiOrder: 'http://orders2.aiyangniu.net',
     apiEcontract:'http://econtracts.aiyangniu.net',
     apiItem: 'http://items2.aiyangniu.net',
+    apiTms:'http://tms.aiyangniu.net',
   }
 } else { //生产环境
   api = {
@@ -30,6 +32,7 @@ if (env === 'development') {  //开发环境
     apiOrder: 'http://orders.aiyangniu.cn',
     apiEcontract:'http://econtracts.aiyangniu.cn',
     apiItem: 'https://items.aiyangniu.cn',
+    apiTms:'https://tms.aiyangniu.cn',
   }
 }
 

+ 11 - 0
src/request/modules/apiTms.js

@@ -0,0 +1,11 @@
+/**
+ * 合同服务系统
+ */
+
+import res from '@/utils/system/request.js'
+
+// 分页查询运单列表 根据运单编号进行筛选
+export const queryWaybillByCondition = (params) => res('post', 'apiTms', '/admin/waybills/queryWaybillByCondition',params,{form:true})
+
+// 查询异常运单列表   type = DEVICE 设备异常,  type = LOADING 装货地异常  , type  = UNLOAD 卸货地异常
+export const queryWaybillExceptionByCondition = (params) => res('post', 'apiTms', '/admin/waybills/queryWaybillExceptionByCondition',params,{form:true})

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

@@ -1,30 +1,17 @@
 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: '/home',
-    meta: { title: '', icon: 'icon-bingtutongji' },
+    redirect: '/trackManager',
+    meta: { title: '', icon: 'icon-bingtutongji' ,hideMenuItem:true},
     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 }
+      // }
     ]
   }
 ]

+ 23 - 8
src/router/modules/pages.js

@@ -2,21 +2,36 @@ import Layout from '@/layout/index.vue'
 import { createNameComponent } from '../createNode'
 const route = [
   {
-    path: '/userPublishSellManage',
+    path: '/trackManager',
     component: Layout,
-    redirect: 'userPublishManage/pubSaleInfo',
+    redirect: 'trackManager/waybillManager',
     meta: { title: '运单管理', icon: 'icon-fabu' },
     children: [
       {
-        path: 'pubSaleInfo/1',
-        component: createNameComponent(() => import('@/views/userPublishManage/pubSaleInfo.vue')),
-        meta: { title: '待审核', cache: false, },
+        path: 'waybillManager',
+        component: createNameComponent(() => import('@/views/trackManager/waybillManager.vue')),
+        meta: { title: '运单管理', cache: false, },
       },
       {
-        path: 'publishDetail',
-        component: createNameComponent(() => import('@/views/userPublishManage/publishDetail.vue')),
-        meta: { title: '发布详情', hideMenuItem: true, cache: false, },
+        path: 'deviceAbnormality',
+        component: createNameComponent(() => import('@/views/trackManager/deviceAbnormality.vue')),
+        meta: { title: '设备异常管理', cache: false, },
       },
+      {
+        path: 'loadDeviation',
+        component: createNameComponent(() => import('@/views/trackManager/loadDeviation.vue')),
+        meta: { title: '装货地异常管理', cache: false, },
+      },
+      {
+        path: 'abnormalWaybill',
+        component: createNameComponent(() => import('@/views/trackManager/abnormalWaybill.vue')),
+        meta: { title: '卸货地异常管理', cache: false, },
+      },
+      // {
+      //   path: 'publishDetail',
+      //   component: createNameComponent(() => import('@/views/trackManager/publishDetail.vue')),
+      //   meta: { title: '发布详情', hideMenuItem: true, cache: false, },
+      // },
     ]
   }
 ]

+ 17 - 109
src/utils/system/filters.js

@@ -2,65 +2,6 @@
  * 自定义过滤器
  */
 
-   
-
-  /* 订单状态 */
-  function orderStatus(val){
-    switch (val) {
-      // case 0:
-      //   return '待分批发货'
-      case 1:
-        return '待签署合同'
-      case 2:
-        return '待分批发货'
-    }
-  }
-
-    
-  /* 看牛模式 */
-  function previewTypes (val){
-    switch (val) {
-      case 1:
-        return '预约看牛'
-      case 2:
-        return '现场看牛'
-    }
-  }
-
-  // 支付状态
-  function payOrderState (val){
-    switch (val) {
-      case 1:
-        return '已付订金'
-      case 2:
-        return '待付订金'
-    }
-  }
-
-  // 价格单位
-  function priceUnit(val){
-    switch (val) {
-      case 1:
-        return '元/头'
-      case 2:
-        return '元/公斤'
-      case 3:
-        return '元/斤'
-    }
-  }
-
-  // 数量单位
-  function countUnit (val){
-    switch (val) {
-      case '1':
-        return '头'
-      case 2:
-        return '公斤'
-      case 3:
-        return '斤'
-    }
-  }
-
   // 发布状态
   function publishStatus(val){
     switch (val) {
@@ -78,57 +19,24 @@
         return '已过期'        
     }
   }
-    // 报价状态
-   function quoteStatus(val) {
-      switch (val) {
-        case 0:
-          return '一轮已报价'
-        case 1:
-          return '二轮报价邀请'
-        case 2:
-          return '已失效'
-        case 3:
-          return '已形成意向单'
-        case 4:
-          return '二轮报价已报价'
-      }
-    }
-    //  意向单状态
-    function intentionStatus(val) {
-      switch (val) {
-        case 0:
-          return '待签单'
-        case 1:
-          return '签单待确认'
-        // case 2:
-        //   return '报价中'
-        case 3:
-          return '意向单作废'
-        case 4:
-          return '已生成订单'
-      }
-    }
-
-    function subOrderStatus(val) {
-      switch (val) {
-        case 1:
-          return '子订单待审核'
-        case 2:
-          return '待付款'
-        case 3:
-          return '审核拒绝'
-        case 4:
-          return '子订单修改待审核'
-        case 5:
-          return '待付款(子订单修改同意)'
-        case 6:
-          return '子订单修改拒绝'
-        case 7:
-          return '已支付'
-  
-      }
+  // 运单状态
+  function wayBillStatus(val){
+    switch (val) {
+      case 'CREATED':
+        return '已创建'
+      case 'AT_ORIGIN':
+        return '司机到达装货物地'
+      case 'IN_TRANSIT':
+        return '运输中'
+      case 'AT_DESTINATION':
+        return '司机到达卸货地'
+      case 'FINISHED':
+        return '运单结束'
+      case 'CANCELLED':
+        return '已取消'
     }
+  }
 export{
-  orderStatus,previewTypes,payOrderState,priceUnit,countUnit,publishStatus,quoteStatus,intentionStatus,subOrderStatus
+  wayBillStatus,publishStatus,
 }
 

+ 200 - 0
src/views/trackManager/abnormalWaybill.vue

@@ -0,0 +1,200 @@
+/**
+  // author:jiana
+  // time:2025-04-23
+  // desc:运单管理
+*/
+<template>
+  <div class="abnormalWaybill">
+    <Card>
+      <!-- 筛选 -->
+      <Row :gutter="8">
+        <Col span="10">
+          运单号 <Input v-model="filtInfoData.waybillNum" placeholder="请输入运单号" style="width:80%"/>
+        </Col>
+        <Col span="2">
+          <Button type="primary" @click="getData">查询</Button>
+        </Col>
+        <Col span="2">
+          <Button @click="resetData">重置</Button>
+        </Col>
+      </Row>
+    </Card>
+    <Card style="margin-top:20px;">
+      <Row>
+        <Col span="12">
+          运单列表
+        </Col>
+        <Col span="12" style="text-align: right;">
+          <!-- <Button @click="batchOperation">批量操作</Button> -->
+        </Col>
+      </Row>
+      <!-- 表格部分 -->
+      <el-table v-loading="loading" :data="TabData.data" border style="width: 100%;margin-top:20px;" @selection-change="handleSelectionChange">
+        <!-- <el-table-column type="selection" align="center" width="50"/> -->
+        <el-table-column label="运单号" width="180" prop="waybillNum" align="center"/>
+        <el-table-column label="运输起始地" width="100" prop="waybill.transportOrigin" align="center"></el-table-column>
+        <el-table-column label="运输目的地" width="120" prop="waybill.transportDestination" align="center"></el-table-column>
+        <el-table-column label="司机姓名" prop="waybill.driverName" width="120"  align="center"></el-table-column>
+        <el-table-column label="司机手机号" prop="waybill.driverPhone" width="120" align="center"></el-table-column>
+        <el-table-column label="车牌" prop="waybill.plateNumber" width="120" align="center"></el-table-column>
+        <el-table-column label="状态" align="center" width="100">
+          <template #default="scope">
+            <Tag color="green" v-if="scope.row.status == 'NORMAL'">正常</Tag>
+            <Tag color="red" v-if="scope.row.status == 'ABNORMAL'">异常</Tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="关联单号" prop="waybill.orderNum" width="220" align="center"></el-table-column>
+        <el-table-column label="添加时间" width="160" align="center">
+          <template #default="scope">
+            {{ moment(scope.row.addTime).format('yyyy-MM-DD hh:mm:ss') || '暂无'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="180"  fixed="right" align="center">
+          <template  #default="scope">
+            <Button class="opt_btn" size="small" type="primary" ghost @click="lookTrack(scope.row.id)">查看轨迹</Button>
+            <Button class="opt_btn" size="small" type="warning" @click="lookInfo(scope.row.exceptionInfo)">查看异常</Button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <div class="page_style">
+        <Page :total="TabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+      </div>
+    </Card>
+    <!-- 查看轨迹 -->
+    <Modal v-model="showModal" width="75%" title="查看轨迹">
+      <mapTrack></mapTrack>
+    </Modal>
+    <!-- 查看异常 -->
+    <Modal v-model="showInfo" width="60%" title="查看异常">
+      <el-descriptions title="">
+          <el-descriptions-item label="设备经度:">{{exceptionInfo.deviceLon}}</el-descriptions-item>
+          <el-descriptions-item label="设备纬度:">{{exceptionInfo.devideLat}}</el-descriptions-item>
+          <el-descriptions-item label="车辆经度:">{{exceptionInfo.carLon}}</el-descriptions-item>
+          <el-descriptions-item label="车辆纬度:">{{exceptionInfo.carLat}}</el-descriptions-item>
+          <el-descriptions-item label="车辆是否在电子围栏:">{{exceptionInfo.carinCircle == true ? '是':'否'}}</el-descriptions-item>
+          <el-descriptions-item label="设备是否在电子围栏:">{{exceptionInfo.deviceInCircle == true ? '是':'否'}}</el-descriptions-item>
+          <el-descriptions-item label="设备到电子围栏中心距离:">{{exceptionInfo.distance}}</el-descriptions-item>
+          <el-descriptions-item label="车辆到电子围栏中心距离:">{{exceptionInfo.cardistance}}</el-descriptions-item>
+          <el-descriptions-item label="设备及车辆之间的距离:">{{exceptionInfo.carDevieDistance}}</el-descriptions-item>
+      </el-descriptions>
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref ,reactive, onMounted} from 'vue'
+import { tms } from '@/request/api'
+import { Message,Modal,Spin,Input } from 'view-ui-plus'
+import moment from 'moment'
+import {useRoute} from 'vue-router'
+import mapTrack from "./mapTrack.vue"
+export default defineComponent({
+  components:{
+    mapTrack
+  },
+  setup() {
+    const route = useRoute()
+    let filtInfoData = reactive({ // 搜索
+      limit:10,offset:0,waybillNum:'',type:'UNLOAD'
+    })
+
+    let TabData = ref([])  // 列表数据
+
+    let loading = ref(false)
+    //获取列表内容
+    async function getData(){
+      loading.value = true
+      await tms.queryWaybillExceptionByCondition(filtInfoData).then(res =>{
+        if (res.code == 0) {
+          TabData.value = res
+        }
+      })
+      loading.value = false
+    }
+    //重置
+    function resetData(){
+      filtInfoData.offset = 0
+      filtInfoData.limit = 10
+      filtInfoData.waybillNum = ''
+      filtInfoData.type = 'DEVICE'
+      getData()
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(TabData.value.limit){
+        filtInfoData.offset = (page -1) *  TabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData() 
+      }
+    }
+
+    //删除
+    function deleteInfo(id){
+      this.$Modal.confirm({
+        title: '提示',
+        content: '<p>确定删除该信息吗?</p>',
+        onOk: async() => {
+          const params={
+            id:id,
+            reason:offSafeValue.value
+          }
+          await cow.cattleDealoffSale(params).then(res=>{
+            if(res.code === 101){
+              Message.success('删除成功!');
+              getData()
+            }
+          })
+        },
+        onCancel: () => {
+          this.$Message.info('Clicked cancel');
+        }
+      })
+    }
+
+    //批量操作
+    function batchOperation(){
+      
+    }
+
+    // 选择监听器
+    const handleSelectionChange = (val) =>{
+      context.emit("selection-change", val)
+    }
+
+    const showModal = ref(false)
+    // 查看详情
+    function lookTrack(){
+      showModal.value = true
+    }
+    const showInfo = ref(false)
+    const exceptionInfo = ref({})
+    //查看异常
+    function lookInfo(data){
+      showInfo.value = true
+      exceptionInfo.value = JSON.parse(data)
+    }
+    onMounted(()=>{
+      getData() 
+    })
+
+    return {
+      handleSelectionChange,getData,changePage,moment,deleteInfo,TabData,filtInfoData,
+      loading,batchOperation,resetData,exceptionInfo,showInfo,lookInfo,lookTrack,showModal
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.abnormalWaybill{
+  padding: 1em;
+  .opt_btn{margin-bottom:3px; margin-right: 3px;}
+  .page_style{
+    text-align: right; margin-top: 1em;
+    // background-color: var(--system-container-background);
+  }
+}
+</style>

+ 200 - 0
src/views/trackManager/deviceAbnormality.vue

@@ -0,0 +1,200 @@
+/**
+  // author:jiana
+  // time:2025-04-23
+  // desc:运单管理
+*/
+<template>
+  <div class="deviceAbnormality">
+    <Card>
+      <!-- 筛选 -->
+      <Row :gutter="8">
+        <Col span="10">
+          运单号 <Input v-model="filtInfoData.waybillNum" placeholder="请输入运单号" style="width:80%"/>
+        </Col>
+        <Col span="2">
+          <Button type="primary" @click="getData">查询</Button>
+        </Col>
+        <Col span="2">
+          <Button @click="resetData">重置</Button>
+        </Col>
+      </Row>
+    </Card>
+    <Card style="margin-top:20px;">
+      <Row>
+        <Col span="12">
+          运单列表
+        </Col>
+        <Col span="12" style="text-align: right;">
+          <!-- <Button @click="batchOperation">批量操作</Button> -->
+        </Col>
+      </Row>
+      <!-- 表格部分 -->
+      <el-table v-loading="loading" :data="TabData.data" border style="width: 100%;margin-top:20px;" @selection-change="handleSelectionChange">
+        <!-- <el-table-column type="selection" align="center" width="50"/> -->
+        <el-table-column label="运单号" width="180" prop="waybillNum" align="center"/>
+        <el-table-column label="运输起始地" width="100" prop="waybill.transportOrigin" align="center"></el-table-column>
+        <el-table-column label="运输目的地" width="120" prop="waybill.transportDestination" align="center"></el-table-column>
+        <el-table-column label="司机姓名" prop="waybill.driverName" width="120"  align="center"></el-table-column>
+        <el-table-column label="司机手机号" prop="waybill.driverPhone" width="120" align="center"></el-table-column>
+        <el-table-column label="车牌" prop="waybill.plateNumber" width="120" align="center"></el-table-column>
+        <el-table-column label="状态" align="center" width="100">
+          <template #default="scope">
+            <Tag color="green" v-if="scope.row.status == 'NORMAL'">正常</Tag>
+            <Tag color="red" v-if="scope.row.status == 'ABNORMAL'">异常</Tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="关联单号" prop="waybill.orderNum" width="220" align="center"></el-table-column>
+        <el-table-column label="添加时间" width="160" align="center">
+          <template #default="scope">
+            {{ moment(scope.row.addTime).format('yyyy-MM-DD hh:mm:ss') || '暂无'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="180"  fixed="right" align="center">
+          <template  #default="scope">
+            <Button class="opt_btn" size="small" type="primary" ghost @click="lookTrack(scope.row.id)">查看轨迹</Button>
+            <Button class="opt_btn" size="small" type="warning" @click="lookInfo(scope.row.exceptionInfo)">查看异常</Button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <div class="page_style">
+        <Page :total="TabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+      </div>
+    </Card>
+    <!-- 查看轨迹 -->
+    <Modal v-model="showModal" width="75%" title="查看轨迹">
+      <mapTrack></mapTrack>
+    </Modal>
+    <!-- 查看异常 -->
+    <Modal v-model="showInfo" width="60%" title="查看异常">
+      <el-descriptions title="">
+          <el-descriptions-item label="设备经度:">{{exceptionInfo.deviceLon}}</el-descriptions-item>
+          <el-descriptions-item label="设备纬度:">{{exceptionInfo.devideLat}}</el-descriptions-item>
+          <el-descriptions-item label="车辆经度:">{{exceptionInfo.carLon}}</el-descriptions-item>
+          <el-descriptions-item label="车辆纬度:">{{exceptionInfo.carLat}}</el-descriptions-item>
+          <el-descriptions-item label="车辆是否在电子围栏:">{{exceptionInfo.carinCircle == true ? '是':'否'}}</el-descriptions-item>
+          <el-descriptions-item label="设备是否在电子围栏:">{{exceptionInfo.deviceInCircle == true ? '是':'否'}}</el-descriptions-item>
+          <el-descriptions-item label="设备到电子围栏中心距离:">{{exceptionInfo.distance}}</el-descriptions-item>
+          <el-descriptions-item label="车辆到电子围栏中心距离:">{{exceptionInfo.cardistance}}</el-descriptions-item>
+          <el-descriptions-item label="设备及车辆之间的距离:">{{exceptionInfo.carDevieDistance}}</el-descriptions-item>
+      </el-descriptions>
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref ,reactive, onMounted} from 'vue'
+import { tms } from '@/request/api'
+import { Message,Modal,Spin,Input } from 'view-ui-plus'
+import moment from 'moment'
+import {useRoute} from 'vue-router'
+import mapTrack from "./mapTrack.vue"
+export default defineComponent({
+  components:{
+    mapTrack
+  },
+  setup() {
+    const route = useRoute()
+    let filtInfoData = reactive({ // 搜索
+      limit:10,offset:0,waybillNum:'',type:'DEVICE'
+    })
+
+    let TabData = ref([])  // 列表数据
+
+    let loading = ref(false)
+    //获取列表内容
+    async function getData(){
+      loading.value = true
+      await tms.queryWaybillExceptionByCondition(filtInfoData).then(res =>{
+        if (res.code == 0) {
+          TabData.value = res
+        }
+      })
+      loading.value = false
+    }
+    //重置
+    function resetData(){
+      filtInfoData.offset = 0
+      filtInfoData.limit = 10
+      filtInfoData.waybillNum = ''
+      filtInfoData.type = 'DEVICE'
+      getData()
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(TabData.value.limit){
+        filtInfoData.offset = (page -1) *  TabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData() 
+      }
+    }
+
+    //删除
+    function deleteInfo(id){
+      this.$Modal.confirm({
+        title: '提示',
+        content: '<p>确定删除该信息吗?</p>',
+        onOk: async() => {
+          const params={
+            id:id,
+            reason:offSafeValue.value
+          }
+          await cow.cattleDealoffSale(params).then(res=>{
+            if(res.code === 101){
+              Message.success('删除成功!');
+              getData()
+            }
+          })
+        },
+        onCancel: () => {
+          this.$Message.info('Clicked cancel');
+        }
+      })
+    }
+
+    //批量操作
+    function batchOperation(){
+      
+    }
+
+    // 选择监听器
+    const handleSelectionChange = (val) =>{
+      context.emit("selection-change", val)
+    }
+
+    const showModal = ref(false)
+    // 查看详情
+    function lookTrack(){
+      showModal.value = true
+    }
+    const showInfo = ref(false)
+    const exceptionInfo = ref({})
+    //查看异常
+    function lookInfo(data){
+      showInfo.value = true
+      exceptionInfo.value = JSON.parse(data)
+    }
+    onMounted(()=>{
+      getData() 
+    })
+
+    return {
+      handleSelectionChange,getData,changePage,moment,deleteInfo,TabData,filtInfoData,
+      loading,batchOperation,resetData,exceptionInfo,showInfo,lookInfo,lookTrack,showModal
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.deviceAbnormality{
+  padding: 1em;
+  .opt_btn{margin-bottom:3px; margin-right: 3px;}
+  .page_style{
+    text-align: right; margin-top: 1em;
+    // background-color: var(--system-container-background);
+  }
+}
+</style>

+ 200 - 0
src/views/trackManager/loadDeviation.vue

@@ -0,0 +1,200 @@
+/**
+  // author:jiana
+  // time:2025-04-23
+  // desc:运单管理
+*/
+<template>
+  <div class="deviceAbnormality">
+    <Card>
+      <!-- 筛选 -->
+      <Row :gutter="8">
+        <Col span="10">
+          运单号 <Input v-model="filtInfoData.waybillNum" placeholder="请输入运单号" style="width:80%"/>
+        </Col>
+        <Col span="2">
+          <Button type="primary" @click="getData">查询</Button>
+        </Col>
+        <Col span="2">
+          <Button @click="resetData">重置</Button>
+        </Col>
+      </Row>
+    </Card>
+    <Card style="margin-top:20px;">
+      <Row>
+        <Col span="12">
+          运单列表
+        </Col>
+        <Col span="12" style="text-align: right;">
+          <!-- <Button @click="batchOperation">批量操作</Button> -->
+        </Col>
+      </Row>
+      <!-- 表格部分 -->
+      <el-table v-loading="loading" :data="TabData.data" border style="width: 100%;margin-top:20px;" @selection-change="handleSelectionChange">
+        <!-- <el-table-column type="selection" align="center" width="50"/> -->
+        <el-table-column label="运单号" width="180" prop="waybillNum" align="center"/>
+        <el-table-column label="运输起始地" width="100" prop="waybill.transportOrigin" align="center"></el-table-column>
+        <el-table-column label="运输目的地" width="120" prop="waybill.transportDestination" align="center"></el-table-column>
+        <el-table-column label="司机姓名" prop="waybill.driverName" width="120"  align="center"></el-table-column>
+        <el-table-column label="司机手机号" prop="waybill.driverPhone" width="120" align="center"></el-table-column>
+        <el-table-column label="车牌" prop="waybill.plateNumber" width="120" align="center"></el-table-column>
+        <el-table-column label="状态" align="center" width="100">
+          <template #default="scope">
+            <Tag color="green" v-if="scope.row.status == 'NORMAL'">正常</Tag>
+            <Tag color="red" v-if="scope.row.status == 'ABNORMAL'">异常</Tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="关联单号" prop="waybill.orderNum" width="220" align="center"></el-table-column>
+        <el-table-column label="添加时间" width="160" align="center">
+          <template #default="scope">
+            {{ moment(scope.row.addTime).format('yyyy-MM-DD hh:mm:ss') || '暂无'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="180"  fixed="right" align="center">
+          <template  #default="scope">
+            <Button class="opt_btn" size="small" type="primary" ghost @click="lookTrack(scope.row.id)">查看轨迹</Button>
+            <Button class="opt_btn" size="small" type="warning" @click="lookInfo(scope.row.exceptionInfo)">查看异常</Button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <div class="page_style">
+        <Page :total="TabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+      </div>
+    </Card>
+    <!-- 查看轨迹 -->
+    <Modal v-model="showModal" width="75%" title="查看轨迹">
+      <mapTrack></mapTrack>
+    </Modal>
+    <!-- 查看异常 -->
+    <Modal v-model="showInfo" width="60%" title="查看异常">
+      <el-descriptions title="">
+          <el-descriptions-item label="设备经度:">{{exceptionInfo.deviceLon}}</el-descriptions-item>
+          <el-descriptions-item label="设备纬度:">{{exceptionInfo.devideLat}}</el-descriptions-item>
+          <el-descriptions-item label="车辆经度:">{{exceptionInfo.carLon}}</el-descriptions-item>
+          <el-descriptions-item label="车辆纬度:">{{exceptionInfo.carLat}}</el-descriptions-item>
+          <el-descriptions-item label="车辆是否在电子围栏:">{{exceptionInfo.carinCircle == true ? '是':'否'}}</el-descriptions-item>
+          <el-descriptions-item label="设备是否在电子围栏:">{{exceptionInfo.deviceInCircle == true ? '是':'否'}}</el-descriptions-item>
+          <el-descriptions-item label="设备到电子围栏中心距离:">{{exceptionInfo.distance}}</el-descriptions-item>
+          <el-descriptions-item label="车辆到电子围栏中心距离:">{{exceptionInfo.cardistance}}</el-descriptions-item>
+          <el-descriptions-item label="设备及车辆之间的距离:">{{exceptionInfo.carDevieDistance}}</el-descriptions-item>
+      </el-descriptions>
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref ,reactive, onMounted} from 'vue'
+import { tms } from '@/request/api'
+import { Message,Modal,Spin,Input } from 'view-ui-plus'
+import moment from 'moment'
+import {useRoute} from 'vue-router'
+import mapTrack from "./mapTrack.vue"
+export default defineComponent({
+  components:{
+    mapTrack
+  },
+  setup() {
+    const route = useRoute()
+    let filtInfoData = reactive({ // 搜索
+      limit:10,offset:0,waybillNum:'',type:'LOADING'
+    })
+
+    let TabData = ref([])  // 列表数据
+
+    let loading = ref(false)
+    //获取列表内容
+    async function getData(){
+      loading.value = true
+      await tms.queryWaybillExceptionByCondition(filtInfoData).then(res =>{
+        if (res.code == 0) {
+          TabData.value = res
+        }
+      })
+      loading.value = false
+    }
+    //重置
+    function resetData(){
+      filtInfoData.offset = 0
+      filtInfoData.limit = 10
+      filtInfoData.waybillNum = ''
+      filtInfoData.type = 'DEVICE'
+      getData()
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(TabData.value.limit){
+        filtInfoData.offset = (page -1) *  TabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData() 
+      }
+    }
+
+    //删除
+    function deleteInfo(id){
+      this.$Modal.confirm({
+        title: '提示',
+        content: '<p>确定删除该信息吗?</p>',
+        onOk: async() => {
+          const params={
+            id:id,
+            reason:offSafeValue.value
+          }
+          await cow.cattleDealoffSale(params).then(res=>{
+            if(res.code === 101){
+              Message.success('删除成功!');
+              getData()
+            }
+          })
+        },
+        onCancel: () => {
+          this.$Message.info('Clicked cancel');
+        }
+      })
+    }
+
+    //批量操作
+    function batchOperation(){
+      
+    }
+
+    // 选择监听器
+    const handleSelectionChange = (val) =>{
+      context.emit("selection-change", val)
+    }
+
+    const showModal = ref(false)
+    // 查看详情
+    function lookTrack(){
+      showModal.value = true
+    }
+    const showInfo = ref(false)
+    const exceptionInfo = ref({})
+    //查看异常
+    function lookInfo(data){
+      showInfo.value = true
+      exceptionInfo.value = JSON.parse(data)
+    }
+    onMounted(()=>{
+      getData() 
+    })
+
+    return {
+      handleSelectionChange,getData,changePage,moment,deleteInfo,TabData,filtInfoData,
+      loading,batchOperation,resetData,exceptionInfo,showInfo,lookInfo,lookTrack,showModal
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.deviceAbnormality{
+  padding: 1em;
+  .opt_btn{margin-bottom:3px; margin-right: 3px;}
+  .page_style{
+    text-align: right; margin-top: 1em;
+    // background-color: var(--system-container-background);
+  }
+}
+</style>

+ 125 - 0
src/views/trackManager/mapTrack.vue

@@ -0,0 +1,125 @@
+/**
+  // author:jiana
+  // time:2025-04-23
+  // desc:运单管理
+*/
+<template>
+  <div class="mapTrack">
+    <!-- <div class="btnBox">
+        <div>轨迹回放控制</div>
+        <div style="margin:10px 0;">
+          <Button size="small" type="primary" @click="startAnimation" ghost style="margin-right:20px;">开始动画</Button>
+          <Button size="small" type="primary" @click="pauseAnimation" ghost>暂停动画</Button>
+        </div>
+        <div>
+          <Button size="small" type="primary" @click="resumeAnimation" ghost style="margin-right:20px;">继续动画</Button>
+          <Button size="small" type="primary" @click="stopAnimation" ghost>停止动画</Button>
+        </div>
+      </div> -->
+    <div id="container"></div>
+  </div>
+</template>
+<script>
+import { defineComponent, ref ,reactive, onMounted} from 'vue'
+import {useRoute} from 'vue-router'
+import AMapLoader from "@amap/amap-jsapi-loader";
+import axios from 'axios';
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+    const route = useRoute()
+    const data = ref(null);
+    const trajectory = ref([]);
+    const loadData = async () =>{
+      trajectory.value = []
+      const module = await import('@/assets/track.json');
+      data.value = module.default; // 对于JSON文件,默认导出是整个JSON对象
+      data.value.obj.runRoute.forEach(item => {
+        if (!isNaN(item.lon) && !isNaN(item.lat)) {
+          trajectory.value.push([Number(item.lon),Number(item.lat)])
+        }
+      });
+    }
+
+    const getMap = () =>{
+      AMapLoader.load({
+        key: '81a1282308f1aae58082425a1ebb91b0',
+        version: '2.0',
+        plugins: ['AMap.MoveAnimation']
+      }).then(AMap => {
+        AMap.plugin('AMap.MoveAnimation', () => {
+          var map = new AMap.Map('container', {
+            center: trajectory.value[0],
+          });
+          // 起点
+          var marker = new AMap.Marker({
+            map: map,
+            position: trajectory.value[0],
+            icon: require('@/assets/images/qidian.png'), // 自定义图标
+            offset: new AMap.Pixel(-13, -26),
+          });
+          // 终点
+          var markerEnd = new AMap.Marker({
+            map: map,
+            position: trajectory.value.slice(-1).pop(),
+            icon: require('@/assets/images/zhongdian.png'), // 自定义图标
+            offset: new AMap.Pixel(-13, -26),
+          });
+          // 绘制轨迹
+          var polyline = new AMap.Polyline({
+            map: map,
+            path: trajectory.value,
+            showDir:true,
+            strokeColor: "#28F",  //线颜色
+            strokeWeight: 6,      //线宽
+          });
+          map.setFitView();
+          map.setZoom(7);
+        });
+      }).catch(e => {
+        console.error(e);
+      });
+    }
+    onMounted(async ()=>{
+      await loadData()
+      await getMap()
+    })
+    return {
+      
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.mapTrack{
+  // padding: 20px;
+  #container{
+    width: 100%; 
+    height: 460px;
+  }
+  .btnBox{
+    position: absolute;
+    top: 70px;
+    left: 20px;
+    z-index: 1;
+    background: #fff;
+    width: 200px;
+    height: 110px;
+    padding: 10px;
+    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>

+ 178 - 0
src/views/trackManager/waybillManager.vue

@@ -0,0 +1,178 @@
+/**
+  // author:jiana
+  // time:2025-04-23
+  // desc:运单管理
+*/
+<template>
+  <div class="waybillManager">
+    <Card>
+      <!-- 筛选 -->
+      <Row :gutter="8">
+        <Col span="10">
+          运单号 <Input v-model="filtInfoData.waybillNum" placeholder="请输入运单号" style="width:80%"/>
+        </Col>
+        <Col span="2">
+          <Button type="primary" @click="getData">查询</Button>
+        </Col>
+        <Col span="2">
+          <Button @click="resetData">重置</Button>
+        </Col>
+      </Row>
+    </Card>
+    <Card style="margin-top:20px;">
+      <Row>
+        <Col span="12">
+          运单列表
+        </Col>
+        <Col span="12" style="text-align: right;">
+          <!-- <Button @click="batchOperation">批量操作</Button> -->
+        </Col>
+      </Row>
+      <!-- 表格部分 -->
+      <el-table v-loading="loading" :data="TabData.data" border style="width: 100%;margin-top:20px;" @selection-change="handleSelectionChange">
+        <!-- <el-table-column type="selection" align="center" width="50"/> -->
+        <el-table-column label="运单号" width="180" prop="waybillNum" align="center"/>
+        <el-table-column label="运输起始地" width="100" prop="transportOrigin" align="center"></el-table-column>
+        <el-table-column label="运输目的地" width="120" prop="transportDestination" align="center"></el-table-column>
+        <el-table-column label="司机姓名" prop="driverName" width="120"  align="center"></el-table-column>
+        <el-table-column label="司机手机号" prop="driverPhone" width="120" align="center"></el-table-column>
+        <el-table-column label="车牌" prop="plateNumber" width="120" align="center"></el-table-column>
+        <el-table-column label="状态" align="center" width="100">
+          <template #default="scope">
+            <span>{{ wayBillStatus(scope.row.status)}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="订单号" prop="orderNum" width="220" align="center"></el-table-column>
+        <el-table-column label="添加时间" width="160" align="center">
+          <template #default="scope">
+            {{ moment(scope.row.addTime).format('yyyy-MM-DD hh:mm:ss') || '暂无'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="130"  fixed="right" align="center">
+          <template  #default="scope">
+            <Button class="opt_btn" size="small" type="primary" ghost @click="lookTrack(scope.row.id)">查看轨迹</Button>
+            <!-- <Button class="opt_btn" size="small" type="warning" @click="deleteInfo(scope.row.id)">删除</Button> -->
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <div class="page_style">
+        <Page :total="TabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+      </div>
+    </Card>
+    <Modal v-model="showModal" width="75%" title="查看轨迹">
+      <mapTrack></mapTrack>
+    </Modal>
+  </div>
+</template>
+<script>
+import { defineComponent, ref ,reactive, onMounted} from 'vue'
+import { tms } from '@/request/api'
+import { Message,Modal,Spin,Input } from 'view-ui-plus'
+import moment from 'moment'
+import {useRouter,useRoute} from 'vue-router'
+import AMapLoader from "@amap/amap-jsapi-loader";
+import mapTrack from "./mapTrack.vue"
+import {wayBillStatus} from '@/utils/system/filters'
+export default defineComponent({
+  components:{
+    mapTrack
+  },
+  setup() {
+    const router = useRouter();
+    const route = useRoute();
+    let filtInfoData = reactive({ // 搜索
+      limit:10,offset:0,waybillNum:''
+    })
+
+    let TabData = ref([])  // 列表数据
+
+    let loading = ref(false)
+    //获取列表内容
+    async function getData(){
+      loading.value = true
+      await tms.queryWaybillByCondition(filtInfoData).then(res =>{
+        if (res.code == 0) {
+          TabData.value = res
+        }
+      })
+      loading.value = false
+    }
+    //重置
+    function resetData(){
+      filtInfoData.offset = 0
+      filtInfoData.limit = 10
+      filtInfoData.waybillNum = ''
+      getData()
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(TabData.value.limit){
+        filtInfoData.offset = (page -1) *  TabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData() 
+      }
+    }
+
+    //删除
+    function deleteInfo(id){
+      this.$Modal.confirm({
+        title: '提示',
+        content: '<p>确定删除该信息吗?</p>',
+        onOk: async() => {
+          const params={
+            id:id,
+            reason:offSafeValue.value
+          }
+          await cow.cattleDealoffSale(params).then(res=>{
+            if(res.code === 101){
+              Message.success('删除成功!');
+              getData()
+            }
+          })
+        },
+        onCancel: () => {
+          this.$Message.info('Clicked cancel');
+        }
+      })
+    }
+
+    //批量操作
+    function batchOperation(){
+      
+    }
+    const showModal = ref(false)
+    // 查看详情
+    function lookTrack(){
+      showModal.value = true
+    }
+
+    // 选择监听器
+    const handleSelectionChange = (val) =>{
+      context.emit("selection-change", val)
+    }
+
+    onMounted(()=>{
+      getData() 
+    })
+    return {
+      handleSelectionChange,getData,changePage,moment,deleteInfo,TabData,filtInfoData,
+      loading,batchOperation,resetData,lookTrack,showModal,wayBillStatus
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.waybillManager{
+  padding: 1em;
+  .opt_btn{margin-bottom:3px; margin-right: 3px;}
+  .page_style{
+    text-align: right; margin-top: 1em;
+    // background-color: var(--system-container-background);
+  }
+}
+
+</style>