<template>
  <div id="map-1">
    <el-row>
      <el-col :span="24" class="main">
        <el-col :span="24" class="one">
          <div id="container"></div>
        </el-col>
      </el-col>
    </el-row>
  </div>
</template>

<script>
const moment = require('moment');
import axios from 'axios';
const _ = require('lodash');
import { EventBus } from '../eventBus';
import { mapState, createNamespacedHelpers } from 'vuex';
const { mapActions } = createNamespacedHelpers('position');
const { mapActions: companyBase } = createNamespacedHelpers('company_base');
const { mapActions: baoanStatis } = createNamespacedHelpers('baoan_statis');
const { mapActions: companyServiceObject } = createNamespacedHelpers('company_service_object');
const { mapActions: sgb } = createNamespacedHelpers('security_guard_base');
export default {
  name: 'map-1',
  props: {},
  components: {},
  data: function () {
    return {
      zoom: '11',
      center: [125.3281, 43.911952], //初始定位到中心
      mapStyle: 'amap://styles/32e687510f26553e59dfd896c133cafc',
      height: 0, //棱柱及标记高度
      circleMarkRadius: 100, // 圆形标记的半径
      map: undefined,
      markList: [],
      points: [],
      infoWindow: undefined,
      timeRange: 60,
      timer: undefined, // 定时任务
      timer_limit: 20, // 一轮显示多少点
      timer_time: 10000, // ms- 10s 换一轮点
      moveTime: undefined, //鼠标移动的时间
      overTimeRange: 0.5, // 超时时长, moveTime + overTimeRange(m) 为超时时长. 单位为:分
      replayTimer: undefined, //监听鼠标,不操作还原的定时任务
      replay_time: 5000, // ms - 5s 查一次是否超时
      // 保安查询的条件缓存
      query: {},

      // 第一版,先将服务对象都查出来,分组显示定位点
      groupNumber: 0,
      tempList: [],

      baoanIcon: require('@/assets/babs.png'),
      bamsgIcon: require('@/assets/bamsg.png'),
      colorList: ['#70F2FF', '#7F1CAD', '#F1D4E8', '#9D2932', '#54004E', '#EFDEB0', '#ED5736', '#494166', '#D9B612', '#A98175', '#177CB0'],
    };
  },
  created() {
    this.connectBus();
  },
  async mounted() {
    this.init();
    this.withNoOpera();
  },
  methods: {
    ...mapActions(['queryMap']),
    ...companyBase({ getCompanyList: 'query', getCompanyInfo: 'fetch' }),
    ...baoanStatis(['fiveQuery']),
    ...companyServiceObject({ getServiceObject: 'fetch' }),
    ...sgb({ getSGB: 'fetch' }),
    // 数据更新,标记物更换
    changeData() {
      if (!this.map) this.init();
      // 销毁标记
      for (const mark of this.markList) {
        this.map.remove(mark);
      }
      this.markList = [];
      this.addMark(this.points);
    },
    // 初始化地图
    init() {
      let that = this;
      let map = new AMap.Map('container', {
        zoom: this.zoom,
        // showLabel: false,
        viewMode: '2D',
        center: this.center,
        mapStyle: this.mapStyle,
      });
      // 隐藏长春以外的地方
      AMap.plugin('AMap.DistrictSearch', function () {
        new AMap.DistrictSearch({
          extensions: 'all',
          subdistrict: 1,
        }).search('长春', function (status, result) {
          // 外多边形坐标数组和内多边形坐标数组
          // 获取城市列表
          let cityList = result.districtList[0].districtList;
          let outer = [new AMap.LngLat(-360, 90, true), new AMap.LngLat(-360, -90, true), new AMap.LngLat(360, -90, true), new AMap.LngLat(360, 90, true)];
          let holes = result.districtList[0].boundaries;
          let pathArray = [outer];
          pathArray.push.apply(pathArray, holes);
          let polygon = new AMap.Polygon({
            pathL: pathArray,
            strokeColor: 'red', //城市边界颜色
            strokeWeight: 3,
            fillColor: '#112473', // 遮罩背景色黑色
            fillOpacity: 1,
          });
          polygon.setPath(pathArray);
          map.add(polygon);
          for (let i = 0; i < cityList.length; i++) {
            const { name } = cityList[i];
            that.setBorder(map, name, i);
          }
        });
      });
      map.on('complete', () => {});
      this.map = map;
      // 添加地图事件
      this.addMapEvents();
      // 添加标记
      this.addMark(this.points);
      // 创建定时任务
      this.setActions();
      // 创建监听 超时还原定时任务 任务
      // this.setTimeOverActions();
    },
    // 设置边界及区域多边形
    setBorder(map, cityName, i) {
      let color = this.colorList[i];
      AMap.plugin('AMap.DistrictSearch', function async() {
        new AMap.DistrictSearch({
          level: 'district',
          extensions: 'all',
          subdistrict: 0,
        }).search(cityName, function (status, result) {
          // 因为会查出个北京朝阳区,所以用城市代码确定在长春
          let holes = result.districtList.find((f) => f.citycode === '0431');
          // 会返回好几个数组,这些数组的点不知道是什么,但边界线一定是点最多的
          let pathArray = [_.maxBy(holes.boundaries, (e) => e.length)];
          //生成行政区划的多边形
          new AMap.Polygon({
            map: map,
            path: pathArray,
            strokeWeight: 1, //轮廓线宽度
            strokeColor: 'red', //轮廓线颜色
            fillOpacity: 0.3, // 多边形透明度
            fillColor: color, // 多边形颜色
          });
        });
      });
    },
    // 添加标记
    addMark(data) {
      if (!this.map) return;
      const marks = [];
      for (const i of data) {
        if (!_.get(i, 'longitude') || !_.get(i, 'latitude')) continue;
        const { longitude, latitude, noAction = false, markLabel, baoanMark = false } = i;
        let obj = {
          anchor: 'bottom-center',
          position: [longitude, latitude, this.height],
          clickable: true, // 鼠标变为手指
          extData: i, // 添加自定义属性
        };
        if (markLabel) obj.label = markLabel;
        if (baoanMark) obj.icon = this.baoanIcon;
        let mark = new AMap.Marker(obj);
        marks.push(mark);
        // 点击事件 改为鼠标经过事件
        if (!noAction) {
          mark.on('mouseover', this.markClick);
          mark.on('mouseout', () => {
            if (!this.infoWindow) return;
            const isOpen = this.infoWindow.getIsOpen();
            if (isOpen) this.closeInfoWindow();
          });
        }
        this.markList.push(mark);
      }
      this.map.add(marks);
    },
    // 更换标记
    changeMark(users) {
      // 是否变为原图标的变量
      let returnAllIcon = false;
      if (users) returnAllIcon = true;
      for (const m of this.markList) {
        const d = m.getExtData();
        const user_id = _.get(d, 'user_id');
        if (!user_id) continue;
        if (returnAllIcon) {
          // 可以尝试更换图标
          if (users.includes(user_id)) m.setIcon(this.bamsgIcon);
          else m.setIcon(this.baoanIcon);
        } else m.setIcon(this.baoanIcon);
      }
    },
    // 添加圆标记
    addCircleMark(data) {
      if (!this.map) return;
      const marks = [];
      for (const i of data) {
        if (!_.get(i, 'longitude') || !_.get(i, 'latitude')) continue;
        const { longitude, latitude } = i;
        let obj = {
          center: [longitude, latitude],
          radius: this.circleMarkRadius,
          fillOpacity: 0,
        };
        let mark = new AMap.Circle(obj);
        marks.push(mark);
        this.markList.push(mark);
      }
      this.map.add(marks);
    },

    // 点击事件
    async markClick(mark) {
      this.initInfoWindow();
      const data = this.getExtData(mark);
      this.openInfoWindow(data);
      let info;
      if (data.type === 'server_point') {
        // 打开服务对象框体,判断:
        // 轮 服务对象 的任务是否建立: 因为如果是点企业类型,则该任务已经被销毁.已经处于中断的操作,不应该操作
        // 销毁服务对象定位定时任务,建立无操作还原定时任务
        if (this.timer) {
          this.destroyActions();
          // 创建监听 超时还原定时任务 任务
          this.setTimeOverActions();
        }
        info = await this.getServerPointContent(data);
      } else {
        // 获取保安员的信息
        const res = await this.getSGB({ id: data.user_id });
        if (this.$checkRes(res)) {
          const sgb = _.get(res, 'data');
          if (sgb) data.phone = _.get(sgb, 'phone');
        }
        info = this.getWindowInfo(data);
      }
      this.infoWindow.setContent(info);
    },
    initInfoWindow() {
      if (this.infoWindow) return;
      const iw = new AMap.InfoWindow({
        offset: [0, -30],
      });
      this.infoWindow = iw;
    },
    // 打开窗体
    openInfoWindow(data) {
      if (!this.infoWindow) {
        console.error('窗体信息未创建,不能使用');
        return;
      }
      const { longitude, latitude } = data;
      this.infoWindow.setContent(`<div style="height:20px;width:20px"><i class="el-icon-loading"></i></div>`);
      this.infoWindow.open(this.map, [longitude, latitude]);
    },
    // 关闭窗体
    closeInfoWindow() {
      if (this.infoWindow) this.infoWindow.close();
      // 如果有这个定时任务存在,说明是轮起来的打开窗体,需要将监控的打掉,然后恢复轮起来
      if (this.replayTimer) {
        this.destroyTimeOverActions();
        if (!this.timer) {
          this.setActions();
        }
      }
    },
    // 获取窗体信息
    getWindowInfo(data) {
      let arr = [];
      arr.push(`<div style="text-align:center;">`);
      if (data.name) arr.push(`<div>姓名:${data.name}</div>`);
      if (data.phone) arr.push(`<div>联系电话:${data.phone}</div>`);
      if (data.company_name) arr.push(`<div>所属企业:${data.company_name}</div>`);
      if (data.company_service_object_name) arr.push(`<div>服务对象:${data.company_service_object_name}</div>`);
      arr.push(`</div>`);
      return arr.join('');
    },
    // 获取自定义属性
    getExtData(mark) {
      return _.get(mark, 'target._opts.extData');
    },
    // 添加地图事件
    addMapEvents() {
      if (!this.map) {
        console.error('地图未初始化,无法添加事件');
        return;
      }
      // this.map.on('click', () => {
      //   if (!this.infoWindow) return;
      //   const isOpen = this.infoWindow.getIsOpen();
      //   if (isOpen) this.closeInfoWindow();
      // });
    },

    // 总线部分,数据交互,显示
    // 总线事件
    connectBus() {
      // 选择企业类型
      EventBus.$on('dealCompany', ({ purpose_id }) => {
        // 清除timer
        this.destroyActions();
        this.toGetTypeCompany(purpose_id);
      });
      // 选择企业
      EventBus.$on('dealCompanyInfo', ({ id }) => {
        // 清除timer
        this.destroyActions();
        this.getServerObject(id);
      });
      // 点击服务对象
      EventBus.$on('dealService', ({ id }) => {
        // 清除timer
        this.destroyActions();
        this.query = { is_class: '在岗', company_service_object_id: id };
        this.search();
      });
      // 重置超时任务的判断时间
      EventBus.$on('resetMapTime', (time) => {
        this.setMove(time);
      });
      // 复位
      EventBus.$on('backToInit', () => {
        this.backToInit();
      });
      // 更换用户id在参数列表中的标记的图标
      EventBus.$on('sendMessagePerson', (users) => {
        this.changeMark(users);
      });
    },
    // 通过企业类型查询企业列表
    async toGetTypeCompany(purpose_id) {
      const res = await this.getCompanyList({ purpose_id });
      if (this.$checkRes(res)) {
        const ids = await res.data.map((i) => i.id);
        //查询出企业的id后,合并请求查询
        const arr = [];
        for (const id of ids) arr.push(this.fiveQuery({ company_id: id }));
        const results = await axios.all(arr);
        const positions = this.dealServerPoint(results);
        this.$set(this, 'points', positions);
        this.changeData();
      }
    },
    // 企业id查询服务对象
    async getServerObject(id) {
      const res = await this.fiveQuery({ company_id: id });
      if (this.$checkRes(res)) {
        const mark = this.dealServerPoint([res]);
        this.$set(this, 'points', mark);
        this.changeData();
      }
    },
    // 保安实时位置查询
    async search() {
      const { company_service_object_id } = this.query;
      const res = await this.queryMap(this.query);
      if (this.$checkRes(res)) {
        let data = _.cloneDeep(res.data);
        data = data.map((i) => ({ ...i, baoanMark: true }));
        this.$set(this, `points`, data);
      }
      this.changeData();
      // 点击的服务对象的处理,移动到那个位置,放大,显示点,显示个圈,半径自己定,以后可以改成他们定
      const serviceObject = await this.getServiceObject({ id: company_service_object_id });
      if (this.$checkRes(serviceObject)) {
        const data = this.dealServerPointPosition(serviceObject.data);
        if (data.latitude && data.longitude) {
          this.map.setZoomAndCenter('18', [data.longitude, data.latitude], true);
          const toMarkData = _.cloneDeep(data);
          if (_.get(toMarkData, 'name')) toMarkData.markLabel = { content: _.get(toMarkData, 'name'), direction: 'top' };
          this.addMark([toMarkData]);
          this.addCircleMark([toMarkData]);
        }
      }
    },
    //处理服务对象的点数据
    dealServerPoint(results) {
      const positions = [];
      for (const i of results) {
        let list = _.get(i, 'data.list', []);
        list = list.map((i) => this.dealServerPointPosition(i));
        positions.push(...list);
      }
      return positions;
    },
    dealServerPointPosition(i) {
      let pos = _.get(i, 'position');
      if (!pos) return i;
      if (!_.isString(pos)) return i;
      pos = JSON.parse(pos);
      const obj = { ...i, type: 'server_point' };
      obj.longitude = _.head(pos);
      obj.latitude = _.last(pos);
      delete obj.position;
      return obj;
    },
    // 服务对象的显示内容
    async getServerPointContent(data) {
      const arr = [];
      arr.push(`<div style="text-align:center;">`);
      if (data.name) arr.push(`<div>服务对象:${data.name}</div>`);

      if (_.get(data, 'id')) {
        const { id } = data;
        const sres = await this.getServiceObject({ id });
        if (sres) {
          const { company_id, type, key_address, recreation_address } = sres.data;
          if (company_id) {
            const company = await this.getCompanyInfo({ id: company_id });
            if (_.get(company, 'data.name')) arr.push(`<div>服务公司: ${_.get(company, 'data.name')}</div>`);
          }
          if (type) arr.push(`<div>服务对象类型:${type}</div>`);
          if (key_address) arr.push(`<div>是否重点单位:${key_address}</div>`);
          if (recreation_address) arr.push(`<div>是否娱乐场所:${recreation_address}</div>`);
        }
      }

      arr.push(`</div>`);
      return arr.join('');
    },

    // 定时任务部分
    // 创建定时任务
    setActions() {
      if (this.timer) return;
      // 先执行一次,再创建定时任务
      if (this.tempList.length <= 0) this.viewLoop();
      // 10s一次
      this.timer = setInterval(this.viewLoop, this.timer_time);
    },
    // 循环事件
    async viewLoop() {
      // // 关闭窗体
      // this.closeInfoWindow();
      // 第一版,先查出服务对象所有的数据
      if (this.tempList.length <= 0) {
        const res = await this.fiveQuery({});
        if (this.$checkRes(res)) {
          // 然后将数据分组,存放起来;10个一组
          const list = this.dealServerPoint([res]);
          this.tempList = _.chunk(list, this.timer_limit);
        }
      }
      if (this.tempList.length <= 0) return;
      const arr = _.get(this.tempList, this.groupNumber);
      // 加点
      this.$set(this, 'points', arr);
      this.changeData();
      // groupNumber计算
      if (this.groupNumber + 1 <= this.tempList.length - 1) this.groupNumber++;
      else this.groupNumber = 0;
    },
    //销毁定时任务
    destroyActions() {
      if (!this.timer) return;
      clearInterval(this.timer);
      this.timer = undefined;
    },
    // 监听 超时还原定时任务 任务
    setTimeOverActions() {
      if (this.replayTimer) return;
      this.replayTimer = setInterval(this.timeOverLoop, this.replay_time);
    },
    // 销毁 超时还原定时任务 任务
    destroyTimeOverActions() {
      if (!this.replayTimer) return;
      clearInterval(this.replayTimer);
      this.replayTimer = undefined;
    },
    // 超时还原定时任务
    timeOverLoop() {
      // 先判断定时任务存不存在
      // 存在的话就不用继续.这个函数目的就是为了复活它的,既然活着就别费劲了.
      if (this.timer) return;
      // 再判断有没有鼠标移动时间
      // 如果进来都没有移动过,就把定时器干了,那基本就bug了,别整了
      if (!this.moveTime) return;
      // 计算获得超时时间
      const overTime = moment(this.moveTime).add(this.overTimeRange, 'm');
      // 判断当前时间是否为 超时时间 或 其后
      const boo = moment(new Date()).isSameOrAfter(overTime);
      if (boo) {
        // 超时了,该复活timer了
        // this.setActions();
        this.closeInfoWindow();
      }
    },
    // 鼠标移动后保存时间
    setMove(time) {
      this.moveTime = time;
    },
    // 长时间不操作,还原循环随机服务对象
    withNoOpera() {
      if (!this.map) return;
      this.map.on('mousemove', (e) => {
        EventBus.$emit('resetMapTime', moment(new Date()).format('YYYY-MM-DD HH:mm:ss'));
      });
    },
    // 回到初始状态
    backToInit() {
      //中心点回归
      if (!this.map) return;
      this.map.setZoomAndCenter(this.zoom, this.center, true);
      //定时任务启动
      if (!this.timer) {
        this.viewLoop();
        this.setActions();
      }
    },
  },
  beforeDestroy() {
    if (this.timer) clearInterval(this.timer);
    if (this.replayTimer) clearInterval(this.replayTimer);
    if (this.map) this.map.destroy();
    if (this.infoWindow) this.infoWindow.destroy();
  },
  computed: {
    ...mapState(['user']),
  },
};
</script>

<style lang="less" scoped>
.main {
  .one {
    #container {
      width: 100%;
      height: 89vh;
      overflow: hidden;
    }
  }
}
</style>
