Newer
Older
baseResourceFront / src / views / overview / listoverview.vue
[wangxitong] on 2 Jul 2021 20 KB 0702车辆总览,轨迹,跟踪
<template>
  <app-container id="overview">
    <!--筛选条件-->
    <search-area size="small" @search="search">
      <search-item>
        <el-input v-model.trim="listQuery.description" placeholder="车辆描述" size="small" clearable/>
      </search-item>
      <search-item>
        <el-input v-model="listQuery.carCode" placeholder="车牌号" size="small" clearable/>
      </search-item>
      <search-item>
        <el-select v-model="listQuery.carType" filterable placeholder="车辆类型" size="small" clearable value="" @change="fetchData()">
          <el-option
            v-for="item in cartypelist"
            :key="item.value"
            :label="item.name"
            :value="item.value"/>
        </el-select>
      </search-item>
      <search-item>
        <dept-select v-model="listQuery.deptId" :dept-show="deptShow" placeholder="使用单位" size="small" clearable value=""/>
      </search-item>
    </search-area>
    <el-checkbox v-model="showBase" style="position: absolute;top: 20px;right: 100px">矢量</el-checkbox>
    <el-checkbox v-model="showImg" style="position: absolute;top: 20px;right: 50px">影像</el-checkbox>
    <div id="map">
      <!--查询结果Table显示-->
      <div slot="header" class="rightcard" >
        <el-row>
          <el-col :span="3">
            <div style="font-size: 14px;padding-top: 10px;">在线:{{ online }}</div>
          </el-col>
          <el-col :span="3">
            <div style="font-size: 14px;padding-top: 10px">离线:{{ offline }}</div>
          </el-col>
          <el-col :span="3">
            <div style="font-size: 14px;padding-top: 10px;padding-left: 5px">刷新时间</div>
          </el-col>
          <el-col :span="9" style="padding-top: 12px;padding-left: 10px">
            <el-radio-group v-model="refresh" @change="clicktime">
              <el-radio label="10">10秒</el-radio>
              <el-radio label="30">30秒</el-radio>
              <el-radio label="60">60秒</el-radio>
            </el-radio-group>
          </el-col>

          <el-col :span="4" style="padding-top: 10px">
            <label style="font-size: 14px;padding-left: 5px;color: red">{{ sec }}</label>
            <label style="font-size: 14px">秒后刷新</label>
          </el-col>
          <el-col :span="2" style="padding-top: 4px;padding-left: 5px">
            <el-button type="primary" size="small" @click="fetchData">立即刷新</el-button>
          </el-col>
        </el-row>
      </div>
      <div slot="header" class="table-title" @click="tableclick">
        <div class="table-name">
          车辆列表
        </div>
        <i v-if="showtable==true" class="el-icon-arrow-down" style="position:absolute; right:20px;top:20px;margin-top:-8px;"/>
        <i v-if="showtable==false" class="el-icon-arrow-right" style="position:absolute; right:20px;top:20px;margin-top:-8px;"/>
      </div>

      <el-table v-if="showtable==true" :row-style="{height: '35px'}" :cell-style="{padding: '0px'}" :header-cell-style="{background: '#ffffff'}" :data="list" max-height="300" class="table" border @row-dblclick="dbSelected" @selection-change="handleSelectionChange">
        <el-table-column :index="indexMethod" align="center" type="index" label="序号" width="55"/>
        <el-table-column v-for="column in columns" :key="column.value" :label="column.text" :width="column.width" :align="column.align" show-overflow-tooltip>
          <template slot-scope="scope">
            <span :class="column.class">{{ scope.row[column.value] }}</span>
          </template>
        </el-table-column>
        <el-table-column label="更多" width="110" align="center">
          <template slot-scope="scope">
            <el-button type="text" size="mini" @click="track(scope.row.carId)">跟踪</el-button>
            <el-button type="text" size="mini" @click="trail(scope.row.carId,scope.row)">轨迹</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div v-if="showtable==true" class="pagination-container">
        <el-pagination
          v-show="total>listQuery.limit"
          :current-page="listQuery.offset"
          :page-sizes="[10,15,20,30]"
          :page-size="listQuery.limit"
          :total="total"
          class="table-bottom"
          align="center"
          layout="total, sizes, prev, pager, next"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"/>
      </div>

    </div>
    <edit-car-info v-show="dialogFormVisible" ref="editCarInfo" @watchChild="fetchData"/>
  </app-container>
</template>
<script>
  import editCarInfo from '@/views/carinfo/editCarInfo'
  import { getOverviewList, offlineCount, getOverviewAllList } from '@/api/overview'
  import { getDictCode } from '@/api/dict'
  import DeptSelect from '@/components/DeptSelect'
  import { getServerList } from '@/api/server'
  import L from 'leaflet'
  import 'leaflet/dist/leaflet.css'
  import 'leaflet-rotatedmarker/leaflet.rotatedMarker.js'
  var esri = require('esri-leaflet')
  export default {
    name: 'ListOverview',
    components: { DeptSelect, editCarInfo },
    data() {
      return {
        dialogFormVisible: false, // 是否显示编辑框
        refresh: '30',
        showBase:false,
        showImg:true,
        online: 0,
        offline: 0,
        showtable: true,
        deptShow: true,
        listQuery: {
          description: '',
          carCode: '',
          carType: '',
          deptId: '',
          offset: 1,
          limit: 15
        }, // 查询条件
        columns: [
          {
            text: '车辆描述',
            value: 'description',
            align: 'center'
          },
          {
            text: '车牌号',
            value: 'carCode',
            align: 'center',
            width: 90
          },
          {
            text: '状态',
            value: 'status',
            align: 'center',
            width: 70
          }
        ], // 动态加载的表头
        TianDiTu: {
          Normal: {
            Map: 'https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8',
            Annotion: 'https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8'
          },
          Satellite: {
            Map: 'https://t{s}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8',
            Annotion: 'https://t{s}.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8'
          },
          Terrain: {
            Map: 'https://t{s}.tianditu.gov.cn/ter_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ter&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8',
            Annotion: 'https://t{s}.tianditu.gov.cn/cta_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cta&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8'
          },
          Subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']
        },
        pointlayer: null,
        imglayer: null,
        featurelayer: null,
        baselayer: [],
        baseUrl: '',
        mapUrl: '',
        list: [], // 列表数据
        alllist: [], // 列表数据
        map: null,
        initlocation: false,
        total: 0, // 数据总数
        sec: 10, // 数据总数
        // listLoading: true, // 加载动画
        fullscreenLoading: false, // 全屏加载动画
        cartypelist: [], // 类型列表
        deptlist: [] // 类型列表
      }
    },
    watch: {
      showBase(val) {
        if(val)  this.map.addLayer(this.featurelayer)
        else this.map.removeLayer(this.featurelayer)
      },
      showImg(val) {
        if(val)  this.map.addLayer(this.imglayer)
        else this.map.removeLayer(this.imglayer)
      },
      sec(val) {
        if (val === 0) {
          this.sec = Number(this.refresh)
          this.fetchData()
        }
      }
    },
    mounted() {
      getDictCode('carType').then(response => {
        this.cartypelist = response.data
      })
      this.initMap()
      setInterval(() => {
        this.sec--
      }, 1000)
    },
    methods: {
      dbSelected(row) {
        this.map.setView({ lat: Number(row.lat), lng: Number(row.lng) }, 18)
      },
      tableclick() {
        this.showtable = !this.showtable
      },
      detail(id) {
        this.dialogFormVisible = true
        var item = { id: id }
        this.$refs.editCarInfo.initDialog('detail', this.dialogFormVisible, item)
      },
      track(id) {
        this.$router.push({
          name: 'Track',
          query: {
            id: id
          }
        })
      },
      trail(id, row) {
        this.$router.push({
          name: 'Trail',
          query: {
            id: id,
            name: row.carCode + '-' + row.carTypeName
          }
        })
      },
      async initMap() {
        const map = L.map('map', {
          minZoom: 3,
          maxZoom: 18,
          center: [27.75962, 116.06021],
          zoom: 16,
          zoomControl: false,
          attributionControl: false,
          crs: L.CRS.EPSG3857
        })
        map.doubleClickZoom.disable()
        this.map = map // data上需要挂载
        window.map = map
        this.baselayer.push(L.tileLayer(
          'https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8',
          { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] }
        ).addTo(map))
        this.baselayer.push(L.tileLayer(
          'https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8',
          { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] }
        ).addTo(map))
        this.imglayer = L.tileLayer(this.baseConfig.satelliteUrl, {
          maxZoom: 22
        }).addTo(map)
        var  listQuery={
          offset: 1,
          limit: 15
        }
        const res = await getServerList(listQuery)
        var list = res.data.rows
        for (var i = 0; i < list.length; i++) {
          if (list[i].name === 'GIS地图') {
            this.baseUrl = list[i].url
          } else if (list[i].name === '二维地图URL') {
            this.mapUrl = list[i].url
          }
        }
        this.featurelayer = esri.dynamicMapLayer({
          url: this.baseUrl + this.mapUrl,
          minZoom: 4, // 最小缩放等级
          maxZoom: 21// 最大缩放等级
        })
        this.fetchData()
      },
      search() {
        this.fetchData(false)
      },
      clicktime() {
        this.sec = Number(this.refresh)
      },
      setZoom(points) {
        // console.log(points)
        if (points.length > 0) {
          var maxLng = points[0][1]
          var minLng = points[0][1]
          var maxLat = points[0][0]
          var minLat = points[0][0]
          var res
          for (var i = points.length - 1; i >= 0; i--) {
            res = points[i]
            if (res[1] > maxLng) maxLng = res[1]
            if (res[1] < minLng) minLng = res[1]
            if (res[0] > maxLat) maxLat = res[0]
            if (res[0] < minLat) minLat = res[0]
          }
          var cenLng = (parseFloat(maxLng) + parseFloat(minLng)) / 2
          var cenLat = (parseFloat(maxLat) + parseFloat(minLat)) / 2
          var zoom = this.getZoom(maxLng, minLng, maxLat, minLat)
          this.map.setView({ lat: cenLat, lng: cenLng }, zoom)
        } else {
          // 没有坐标,显示全中国
          this.map.setView({ lat: 103.388611, lng: 35.563611 }, 5)
        }
      },
      getZoom(maxLng, minLng, maxLat, minLat) {
        var zoom = ['50', '100', '200', '500', '1000', '2000', '5000', '10000', '20000', '25000', '50000', '100000', '200000', '500000', '1000000', '2000000']// 级别18到3。
        var latlng = L.latLng(maxLat, maxLng)
        var distance = latlng.distanceTo(L.latLng(minLat, minLng))
        // var distance = pointA.distanceTo(pointB)// 获取两点距离,保留小数点后两位
        for (var i = 0, zoomLen = zoom.length; i < zoomLen; i++) {
          if (zoom[i] - distance > 0) {
            return 18 - i + 2 // 之所以会多2,是因为地图范围常常是比例尺距离的10倍以上。所以级别会增加3。
          }
        }
      },
      fetchData(isNowPage = true) {
        offlineCount(this.listQuery).then(response => {
          this.online = response.data.online
          this.offline = response.data.offline
        })
        this.sec = Number(this.refresh)
        if (!isNowPage) {
          this.listQuery.offset = 1
        }
        getOverviewList(this.listQuery).then(response => {
          this.list = response.data.rows
          this.total = parseInt(response.data.total)
        })
        getOverviewAllList(this.listQuery).then(response => {
          this.alllist = response.data
          if(this.pointlayer!==null) this.map.removeLayer(this.pointlayer)
          this.pointlayer = new L.layerGroup().addTo(this.map)
          var list = []
          for (i = 0; i < this.alllist.length; i++) {
            if (Number(this.alllist[i].lat) === 0 && Number(this.alllist[i].lng) === 0) {
              continue
            }
            var item = [Number(this.alllist[i].lat), Number(this.alllist[i].lng)]
            list.push(item)
          }
          // if (list.length !== 0) {
          //   this.setZoom(list)
          // }
          var Icon
          for (var i = 0; i < this.alllist.length; i++) {
            if(i===0 && !this.initlocation){
              this.map.setView({ lat: this.alllist[0].lat, lng:  this.alllist[0].lng },16)
              this.initlocation = true
            }
            if (this.alllist[i].status === '离线') {
              Icon = L.icon({
                iconUrl: require('../../assets/global_images/car-offline.png'), // 灰
                iconSize: [40, 40]
              })
            } else if (this.alllist[i].status === '超时') {
              Icon = L.icon({
                iconUrl: require('../../assets/global_images/car-timeout.png'), // 橙
                iconSize: [40, 40]
              })
            } else if (this.alllist[i].status === '停车') {
              Icon = L.icon({
                iconUrl: require('../../assets/global_images/car-stop.png'), // 黄
                iconSize: [40, 40]
              })
            } else if (this.alllist[i].status === '行驶') {
              Icon = L.icon({
                iconUrl: require('../../assets/global_images/car-online.png'), // 绿
                iconSize: [40, 40]
              })
            } else if (this.alllist[i].status === '报警') {
              Icon = L.icon({
                iconUrl: require('../../assets/global_images/car-alarm.png'), // 红
                iconSize: [40, 40]
              })
            }
            var myIcon = L.divIcon({
              html: '<div style="font-size: 13px;font-weight: bold;padding-top: 17px;text-align:center;"> ' + this.alllist[i].deptName + '_' + this.alllist[i].description + '</div>',
              className: '',
              iconSize: [100, 15]
            })
            L.marker([this.alllist[i].lat, this.alllist[i].lng], { icon: myIcon }).addTo(this.pointlayer)
            const { carId,description, carTypeName, deptName, status,lng,lat, upTime,speed } = this.alllist[i]
            var btndetail = 'btndetail'+ carId
            var btntrack = 'btntrack'+ carId
            var btntrail = 'btntrail'+ carId
            var str = `<div style="font-size: 15px;padding: 6px"> <div style="font-weight: bold;padding-bottom: 10px">车辆位置概要信息</div>
            <div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">描述</label> ${description}</div>
            <div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">类型</label> ${carTypeName}</div>
            <div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">单位</label> ${deptName}</div>
            <div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">状态</label> ${status}</div>
            <div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">定位</label> ${lng} , ${lat}</div>
            <div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">时间</label> ${upTime}</div>
            <div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">速度</label> ${speed} (km/h)</div>
            <div style="padding-top: 8px"><button style="margin-left: 80px;background-color: #42b983;border-color: #42b983;color: white;padding: 5px 8px;font-size: 12px;border-radius: 4px"  id=${btndetail}>详情</button>
            <button style="margin-right: 5px;margin-left: 5px;background-color: #409eff;border-color: #409eff;
            color: white;padding: 5px 8px;font-size: 12px;border-radius: 4px" id=${btntrack}>跟踪</button>
            <button style="background-color: #e6a23c;border-color: #e6a23c;color: white;padding: 5px 8px;font-size: 12px;border-radius: 4px" id=${btntrail}>轨迹</button></div></div>`
            var popup = L.popup().setContent(str)
            var marker = L.marker([this.alllist[i].lat, this.alllist[i].lng], {
              icon: Icon,
              slug: this.alllist[i],
              rotationAngle: this.alllist[i].direction,
              zIndexOffset: 100000
            }).addTo(this.pointlayer).bindPopup(popup)

            var that = this
            marker.on('click', function(e) {
              console.log('按钮:',document.getElementById(btndetail))
              document.getElementById(btndetail).onclick = function() {
                console.log('carId:',e.sourceTarget.options.slug.carId)
                that.detail(e.sourceTarget.options.slug.carId)
              }
              document.getElementById(btntrack).onclick = function() {
                that.track(e.sourceTarget.options.slug.carId)
              }
              document.getElementById(btntrail).onclick = function() {
                that.trail(e.sourceTarget.options.slug.carId, e.sourceTarget.options.slug)
              }
            })
          }
        })
      },
      indexMethod(index) {
        return this.listQuery.limit * (this.listQuery.offset - 1) + index + 1
      },
      // 改变页容量
      handleSizeChange(val) {
        this.listQuery.limit = val
        this.fetchData()
      },
      // 改变当前页
      handleCurrentChange(val) {
        this.listQuery.offset = val
        this.fetchData()
      },
      // 多选触发方法
      handleSelectionChange(val) {
        this.multipleSelection = val
      }
    }
  }
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
  /*.el-table__header tr,*/
  /*.el-table__header th {*/
  /*padding: 0;*/
  /*height: 40px;*/
  /*background-color: #f8f8f8;*/
  /*}*/
  /*.table{*/
  /*margin-bottom: 20px;*/
  /*}*/
  /*.pagination-container{*/
  /*margin-bottom: 100px;*/
  /*}*/
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
  .table{
    width: 460px;
    /*height: 60vh;*/
    z-index: 10000;
    margin-top: 40px;
    position: relative;
    left:20px;
    top:30px;
    /*padding-bottom: 20px;*/
  }
  .rightcard{
    /*width: 750px;*/
    height: 40px;
    z-index: 10000;
    position: absolute;
    /*left:500px;*/
    top:20px;
    right: 0;
    background-color: #ffffff;
    padding-left: 20px;
    padding-right: 50px;
  }
  .table-bottom{
    position: relative;
    left: 21px;
    /*top: 20px;*/
    z-index: 10001;
    float: left;
    width: 458px;
    background-color: #ffffff;
    /* margin-top: 20px; */
    margin-top: 25px;
    padding-bottom: 10px;
  }
  .table-name{
    position:relative;
    width: 100px;
    top:20px;
    margin:-10px auto; //外面的div高度的一半
    z-index: 100001;
  }
  .table-title{
    font-size: 15px;
    text-align:center;
    width: 460px;
    height: 40px;
    background-color: #ffffff;
    z-index: 10000;
    position: absolute;
    left:20px;
    top:20px;
    font-weight: bold;
  }
  #map {
    width:100%;
    height: 72vh;
    border-top: 12px solid #ebebeb;
  }
  /deep/ .el-table--scrollable-y .el-table__body-wrapper{
    padding-bottom: 11px !important;
  }
  /*/deep/ .el-radio__input.is-checked+.el-radio__label {*/
  /*color: red;*/
  /*}*/
  /*/deep/ .el-radio__input.is-checked .el-radio__inner {*/
  /*border-color: #e91e63;*/
  /*background: #e91e63;*/
  /*}*/
  /deep/ .el-radio{
    margin-right: 5px;
  }
  /deep/ .el-radio__label{
    padding-left: 5px;
  }

</style>