|  | @@ -0,0 +1,263 @@
 | 
	
		
			
				|  |  | +/** // author:jiana // time:2025-04-23 // desc:运单管理 */
 | 
	
		
			
				|  |  | +<template>
 | 
	
		
			
				|  |  | +  <div class="mapMark">
 | 
	
		
			
				|  |  | +    <div class="btnBox">
 | 
	
		
			
				|  |  | +      <el-form :model="form" :rules="rules" label-width="auto">
 | 
	
		
			
				|  |  | +        <el-row>
 | 
	
		
			
				|  |  | +          <el-col :span="4">
 | 
	
		
			
				|  |  | +            <!-- 车牌号 -->
 | 
	
		
			
				|  |  | +            <el-form-item label="车牌号" prop="licensePlateNo">
 | 
	
		
			
				|  |  | +              <el-autocomplete
 | 
	
		
			
				|  |  | +                v-model="form.licensePlateNo"
 | 
	
		
			
				|  |  | +                :fetch-suggestions="querySearch"
 | 
	
		
			
				|  |  | +                clearable
 | 
	
		
			
				|  |  | +                placeholder="请输入车牌号"
 | 
	
		
			
				|  |  | +              />
 | 
	
		
			
				|  |  | +            </el-form-item>
 | 
	
		
			
				|  |  | +          </el-col>
 | 
	
		
			
				|  |  | +          <el-col :span="4">
 | 
	
		
			
				|  |  | +            <!-- 车牌颜色 -->
 | 
	
		
			
				|  |  | +            <el-form-item label="车牌颜色" prop="licensePlateColor">
 | 
	
		
			
				|  |  | +              <el-select clearable v-model="form.licensePlateColor" placeholder="请选择车牌颜色">
 | 
	
		
			
				|  |  | +                <el-option
 | 
	
		
			
				|  |  | +                  v-for="item in licensePlateColorList"
 | 
	
		
			
				|  |  | +                  :key="item.value"
 | 
	
		
			
				|  |  | +                  :label="item.label"
 | 
	
		
			
				|  |  | +                  :value="item.value"
 | 
	
		
			
				|  |  | +                ></el-option>
 | 
	
		
			
				|  |  | +              </el-select>
 | 
	
		
			
				|  |  | +            </el-form-item>
 | 
	
		
			
				|  |  | +          </el-col>
 | 
	
		
			
				|  |  | +          <el-col :span="8">
 | 
	
		
			
				|  |  | +            <!-- 时间范围 -->
 | 
	
		
			
				|  |  | +            <!-- 限制结束时间在开始时间三天内 -->
 | 
	
		
			
				|  |  | +            <el-form-item label="时间范围" prop="timeRange">
 | 
	
		
			
				|  |  | +              <el-date-picker
 | 
	
		
			
				|  |  | +                clearable
 | 
	
		
			
				|  |  | +                v-model="form.startTime"
 | 
	
		
			
				|  |  | +                type="datetime"
 | 
	
		
			
				|  |  | +                placeholder="开始日期"
 | 
	
		
			
				|  |  | +                value-format="YYYY-MM-DD HH:mm:ss"
 | 
	
		
			
				|  |  | +              ></el-date-picker>
 | 
	
		
			
				|  |  | +              <el-date-picker
 | 
	
		
			
				|  |  | +                clearable
 | 
	
		
			
				|  |  | +                v-model="form.endTime"
 | 
	
		
			
				|  |  | +                :disabled-date="disabledDateFn"
 | 
	
		
			
				|  |  | +                type="datetime"
 | 
	
		
			
				|  |  | +                placeholder="结束日期"
 | 
	
		
			
				|  |  | +                value-format="YYYY-MM-DD HH:mm:ss"
 | 
	
		
			
				|  |  | +              ></el-date-picker>
 | 
	
		
			
				|  |  | +            </el-form-item>
 | 
	
		
			
				|  |  | +          </el-col>
 | 
	
		
			
				|  |  | +          <el-col :span="6">
 | 
	
		
			
				|  |  | +            <!-- 查询按钮 -->
 | 
	
		
			
				|  |  | +            <el-form-item>
 | 
	
		
			
				|  |  | +              <el-button type="primary" @click="queryWaybill">查询轨迹</el-button>
 | 
	
		
			
				|  |  | +            </el-form-item>
 | 
	
		
			
				|  |  | +          </el-col>
 | 
	
		
			
				|  |  | +        </el-row>
 | 
	
		
			
				|  |  | +      </el-form>
 | 
	
		
			
				|  |  | +    </div>
 | 
	
		
			
				|  |  | +    <!-- 异常停留 -->
 | 
	
		
			
				|  |  | +    <div class="abnormal">
 | 
	
		
			
				|  |  | +      <div>
 | 
	
		
			
				|  |  | +        <span class="abnormal-title">共停留{{ parkArray.length }}次,共停留{{ totalMin }}分钟</span>
 | 
	
		
			
				|  |  | +        <el-button size="small" type="primary" @click="showTable = !showTable">查看停留明细</el-button>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  | +      <el-table :data="parkArray" style="width: 100%" border stripe size="small" v-if="showTable">
 | 
	
		
			
				|  |  | +        <el-table-column prop="parkBte" label="停留开始" width="180">
 | 
	
		
			
				|  |  | +          <template #default="scope">
 | 
	
		
			
				|  |  | +            {{ moment(Number(scope.row.parkBte)).format('yyyy-MM-DD hh:mm:ss') }}
 | 
	
		
			
				|  |  | +          </template>
 | 
	
		
			
				|  |  | +        </el-table-column>
 | 
	
		
			
				|  |  | +        <el-table-column prop="parkMins" label="停留时长(分钟)" width="180"></el-table-column>
 | 
	
		
			
				|  |  | +        <el-table-column prop="parkAdr" label="停留地址"></el-table-column>
 | 
	
		
			
				|  |  | +      </el-table>
 | 
	
		
			
				|  |  | +    </div>
 | 
	
		
			
				|  |  | +    <div id="container3"></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'
 | 
	
		
			
				|  |  | +import { Message, Modal, Spin } from 'view-ui-plus'
 | 
	
		
			
				|  |  | +import { transformFromWGSToGCJArr } from '../trackManager/utils.js'
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 限制选择的结束时间必须在选择的开始时间后的三天内
 | 
	
		
			
				|  |  | +const disabledDateFn = time => {
 | 
	
		
			
				|  |  | +  const startTime = new Date(form.value.startTime)
 | 
	
		
			
				|  |  | +  const endTime = new Date(time)
 | 
	
		
			
				|  |  | +  const diffTime = endTime - startTime
 | 
	
		
			
				|  |  | +  return diffTime > 3 * 24 * 60 * 60 * 1000
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +const route = useRoute()
 | 
	
		
			
				|  |  | +const trajectory = ref([])
 | 
	
		
			
				|  |  | +const tabPosition = ref('load')
 | 
	
		
			
				|  |  | +const map = ref(null)
 | 
	
		
			
				|  |  | +const form = ref({})
 | 
	
		
			
				|  |  | +const rules = {
 | 
	
		
			
				|  |  | +  licensePlateNo: [{ required: true, message: '请输入车牌号', trigger: 'blur' }],
 | 
	
		
			
				|  |  | +  licensePlateColor: [{ required: true, message: '请选择车牌颜色', trigger: 'change' }],
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +const licensePlateColorList = [
 | 
	
		
			
				|  |  | +  { label: '蓝色', value: '蓝色' },
 | 
	
		
			
				|  |  | +  { label: '黄色', value: '黄色' },
 | 
	
		
			
				|  |  | +  { label: '黄绿色', value: '黄绿色' },
 | 
	
		
			
				|  |  | +  { label: '白色', value: '白色' },
 | 
	
		
			
				|  |  | +  { label: '绿色', value: '绿色' },
 | 
	
		
			
				|  |  | +]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const restaurants = ref([])
 | 
	
		
			
				|  |  | +const querySearch = (queryString, cb) => {
 | 
	
		
			
				|  |  | +  const results = queryString ? restaurants.value.filter(createFilter(queryString)) : restaurants.value
 | 
	
		
			
				|  |  | +  // call callback function to return suggestions
 | 
	
		
			
				|  |  | +  cb(results)
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +const createFilter = queryString => {
 | 
	
		
			
				|  |  | +  return restaurant => {
 | 
	
		
			
				|  |  | +    return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +const loadAll = () => {
 | 
	
		
			
				|  |  | +  return [{ value: '蒙L22887' }, { value: '蒙L83719' }]
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const initMap = () => {
 | 
	
		
			
				|  |  | +  AMapLoader.load({
 | 
	
		
			
				|  |  | +    key: '81a1282308f1aae58082425a1ebb91b0',
 | 
	
		
			
				|  |  | +    version: '2.0',
 | 
	
		
			
				|  |  | +    plugins: ['AMap.MoveAnimation'],
 | 
	
		
			
				|  |  | +  })
 | 
	
		
			
				|  |  | +    .then(AMap => {
 | 
	
		
			
				|  |  | +      AMap.plugin('AMap.MoveAnimation', () => {
 | 
	
		
			
				|  |  | +        map.value = new AMap.Map('container3', {
 | 
	
		
			
				|  |  | +          center: [116.397428, 39.90923],
 | 
	
		
			
				|  |  | +        })
 | 
	
		
			
				|  |  | +      })
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +    .catch(e => {
 | 
	
		
			
				|  |  | +      console.error(e)
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +const data = ref(null)
 | 
	
		
			
				|  |  | +const parkArray = ref([])
 | 
	
		
			
				|  |  | +const totalMin = ref(0)
 | 
	
		
			
				|  |  | +const showTable = ref(false)
 | 
	
		
			
				|  |  | +const queryWaybill = async () => {
 | 
	
		
			
				|  |  | +  if (!form.value.licensePlateNo) {
 | 
	
		
			
				|  |  | +    return Message.error('请输入车牌号')
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (!form.value.startTime || !form.value.endTime) {
 | 
	
		
			
				|  |  | +    return Message.error('请选择时间范围')
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (!form.value.licensePlateColor) {
 | 
	
		
			
				|  |  | +    return Message.error('请选择车牌颜色')
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  console.log(form.value, '查询参数')
 | 
	
		
			
				|  |  | +  const params = {
 | 
	
		
			
				|  |  | +    licensePlateNo: form.value.licensePlateNo,
 | 
	
		
			
				|  |  | +    licensePlateColor: form.value.licensePlateColor,
 | 
	
		
			
				|  |  | +    startTime: form.value.startTime,
 | 
	
		
			
				|  |  | +    endTime: form.value.endTime,
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  const res = await tms.truckTrackQuery(params)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (res.code == 101) {
 | 
	
		
			
				|  |  | +    if (res.data.status !== 1001) {
 | 
	
		
			
				|  |  | +      Message.error(res.data.result)
 | 
	
		
			
				|  |  | +      return
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    data.value = res.data
 | 
	
		
			
				|  |  | +    if (data.value.result && data.value.result.trackArray) {
 | 
	
		
			
				|  |  | +      parkArray.value = data.value.result.parkArray
 | 
	
		
			
				|  |  | +      parkArray.value.forEach(item => {
 | 
	
		
			
				|  |  | +        totalMin.value += Number(item.parkMins)
 | 
	
		
			
				|  |  | +      })
 | 
	
		
			
				|  |  | +      data.value.result.trackArray.forEach(item => {
 | 
	
		
			
				|  |  | +        if (!isNaN(item.lon) && !isNaN(item.lat)) {
 | 
	
		
			
				|  |  | +          trajectory.value.push(transformFromWGSToGCJArr(Number(item.lon) / 600000, Number(item.lat) / 600000))
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // 清除之前的markers
 | 
	
		
			
				|  |  | +    map.value.clearMap()
 | 
	
		
			
				|  |  | +    var polyline = new AMap.Polyline({
 | 
	
		
			
				|  |  | +      map: map.value,
 | 
	
		
			
				|  |  | +      path: trajectory.value,
 | 
	
		
			
				|  |  | +      showDir: true,
 | 
	
		
			
				|  |  | +      strokeColor: '#28F', //线颜色
 | 
	
		
			
				|  |  | +      strokeWeight: 6, //线宽
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +    map.value.setFitView()
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 添加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()
 | 
	
		
			
				|  |  | +  })
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// 清除markers
 | 
	
		
			
				|  |  | +const clearMarkers = () => {
 | 
	
		
			
				|  |  | +  map.value.clearMap()
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +onMounted(async () => {
 | 
	
		
			
				|  |  | +  restaurants.value = loadAll()
 | 
	
		
			
				|  |  | +  await initMap()
 | 
	
		
			
				|  |  | +})
 | 
	
		
			
				|  |  | +</script>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<style lang="scss" scoped>
 | 
	
		
			
				|  |  | +.mapMark {
 | 
	
		
			
				|  |  | +  position: relative;
 | 
	
		
			
				|  |  | +  // padding: 20px;
 | 
	
		
			
				|  |  | +  #container3 {
 | 
	
		
			
				|  |  | +    width: 100%;
 | 
	
		
			
				|  |  | +    height: 78vh;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  .btnBox {
 | 
	
		
			
				|  |  | +    width: 100%;
 | 
	
		
			
				|  |  | +    position: absolute;
 | 
	
		
			
				|  |  | +    top: 10px;
 | 
	
		
			
				|  |  | +    left: 20px;
 | 
	
		
			
				|  |  | +    z-index: 100;
 | 
	
		
			
				|  |  | +    // 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;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +.abnormal {
 | 
	
		
			
				|  |  | +  text-align: right;
 | 
	
		
			
				|  |  | +  position: absolute;
 | 
	
		
			
				|  |  | +  width: 50%;
 | 
	
		
			
				|  |  | +  top: 45px;
 | 
	
		
			
				|  |  | +  right: 15px;
 | 
	
		
			
				|  |  | +  padding: 5px;
 | 
	
		
			
				|  |  | +  z-index: 10;
 | 
	
		
			
				|  |  | +  &-title {
 | 
	
		
			
				|  |  | +    padding: 5px;
 | 
	
		
			
				|  |  | +    background: #fff;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +</style>
 |