记一次多张图片标注(编辑,回显,回显编辑)vue+AILablel

1,943 阅读1分钟

image.png

产品需求

需要对路口的图片进行标注,红绿灯、车道线、车道、虚拟线圈等进行标注,用于算法来对视频进行识别

采用的插件是AILablel.js

直接上代码吧

没有拆分

    <template>
  <div style="display: flex;width: 100%;">
    <div :id="id" style="overflow: hidden;
                position: relative;
                height: 500px;
                width: 800px;
                border: 1px dashed #ccc;">
      <div class="button-wrap">
        <el-radio-group size="mini" v-model="mode" style="overflow: hidden;">
          <el-radio-button label="PAN">拖动</el-radio-button>
          <el-radio-button label="POINT"></el-radio-button>
          <el-radio-button label="LINE">线段</el-radio-button>
          <el-radio-button label="POLYLINE">多线段</el-radio-button>
          <el-radio-button label="RECT">矩形</el-radio-button>
          <el-radio-button label="POLYGON">多边形</el-radio-button>
          <el-button type="primary" size="mini" style="float: right; margin-left: 20px;" @click="info">标注规则</el-button>
        </el-radio-group>
        <span style="margin-left: 10px;">当前绘制 <span style="color: #4ED83A">{{ curDraw }}</span> </span>
        <!--        <button class="btn btn-default" @click="getFeatures();">保存标注数据</button>-->
      </div>
    </div>
    <div class="rightOperate">
      <el-radio-group v-model="radioType">
        <el-radio-button label="标定"></el-radio-button>
        <el-radio-button label="算法"></el-radio-button>
      </el-radio-group>
      <div class="leftCon" v-show="radioType==='标定'">
        <div class="title">车道标定</div>
        <div style="color: #E9F3FF;font-size: 12px;margin-bottom: 10px;">车道线配置</div>
        <el-button @click="draw('lane')" class="fullButton" style="border: 1px solid #4ED83A;background: #15212F;">
          绘制车道线
          <i class="el-icon-edit"></i>
        </el-button>
        <el-tag
            v-for="tag in lane"
            :key="tag.id"
            closable
            @close="delLaneLine(tag.id,'lane')"
            type="info">
          {{ tag.id }}
        </el-tag>
        <div style="color: #E9F3FF;font-size: 12px;margin: 10px 0;">车道配置</div>
        <el-button @click="addLane" class="fullButton" style="border: 1px solid #37475A;background: #15212F;">
          添加车道
        </el-button>
        <div v-for="(i,n) in lanes" style="margin-bottom: 10px;">
          <!--          <span>车道</span>-->
          <el-input style="width: 80px;margin: 0 6px;" v-model="i.laneName"></el-input>
          <el-select style="width: 120px;" v-model="i.laneLine[0]">
            <el-option
                v-for="item in lane" :key="item.id" :label="item.id" :value="item.id">
            </el-option>
          </el-select>
          <span style="margin: 0 6px;">-</span>
          <el-select style="width: 120px;" v-model="i.laneLine[1]">
            <el-option
                v-for="item in lane" :key="item.id" :label="item.id" :value="item.id">
            </el-option>
          </el-select>
          <i @click="delLane(n)" class="el-icon-close" style="font-size: 20px;margin-left: 16px;cursor: pointer;"></i>
        </div>
        <div class="title">坐标标定</div>
        <div class="coordinateCalibration"
             v-for="(i,n) in coordinateCalibration"
        >
          <div class="pointTop" style="overflow: hidden;margin-bottom: 10px;">
            <el-input style="width: 200px;margin: 0 6px;" v-model="i.name"></el-input>
            <i class="el-icon-location" @click="location(n)"
               style="font-size: 20px;margin-left: 16px;cursor: pointer;float: right;"
            ></i>
          </div>
          <div class="pointBottom" style="overflow: hidden;">
            <span>经度</span>
            <el-input style="width: 100px;margin: 0 6px;" v-model="i.longitude"></el-input>
            <span>纬度</span>
            <el-input style="width: 100px;margin: 0 6px;" v-model="i.latitude"></el-input>
            <div style="float: right;">
              <span>X:{{ i.point.x }}</span>
              <span>Y:{{ i.point.y }}</span>
            </div>
          </div>
        </div>
      </div>
      <div class="rightCon" v-show="radioType==='算法'">
        <div class="topCheckbox">
          <div class="title">目标检测</div>
          <el-checkbox-group v-model="objectDetection">
            <el-checkbox label="1" key="1">机动车</el-checkbox>
            <el-checkbox label="2" key="2">非机动车</el-checkbox>
            <el-checkbox label="3" key="3">行人</el-checkbox>
            <el-checkbox label="4" key="4">全部</el-checkbox>
          </el-checkbox-group>
          <div class="title">车辆属性</div>
          <el-checkbox-group v-model="carAttribute">
            <el-checkbox label="5" key="5">车辆识别</el-checkbox>
            <el-checkbox label="6" key="6">车型识别</el-checkbox>
            <el-checkbox label="7" key="7">车身颜色</el-checkbox>
            <el-checkbox label="8" key="8">车辆品牌</el-checkbox>
          </el-checkbox-group>
          <div class="title">其他</div>
          <el-checkbox-group v-model="other">
            <el-checkbox label="9" key="9">车流统计</el-checkbox>
            <el-checkbox label="10" key="10">信号灯识别</el-checkbox>
          </el-checkbox-group>
        </div>
        <div class="drawArea">
          <div class="title">
            <el-checkbox true-label="1" false-label="0" v-model="interestGoalRadio"></el-checkbox>
            感兴趣区域-目标检测
          </div>
          <el-button @click="draw('interestGoal')" class="fullButton"
                     style="border: 1px solid #0091FF;background: rgba(0,145,255,0.30);">
            绘制区域
            <i class="el-icon-edit"></i>
          </el-button>
          <el-tag
              v-for="tag in interestGoal"
              :key="tag.id"
              closable
              @close="delLaneLine(tag.id,'interestGoal')"
              type="info">
            {{ tag.id }}
          </el-tag>
          <div class="title">
            <el-checkbox true-label="1" false-label="0" v-model="interestLightRadio"></el-checkbox>
            感兴趣区域-信号灯检测
          </div>
          <el-button @click="draw('interestLight')" class="fullButton"
                     style="border: 1px solid #F94040;background: rgba(249,64,64,0.30);">
            绘制区域
            <i class="el-icon-edit"></i>
          </el-button>
          <el-tag
              v-for="tag in interestLight"
              :key="tag.id"
              closable
              @close="delLaneLine(tag.id,'interestLight')"
              type="info">
            {{ tag.id }}
          </el-tag>
          <div class="title">
            <el-checkbox true-label="1" false-label="0" v-model="attributeRadio"></el-checkbox>
            属性采集线
          </div>
          <el-button @click="draw('attribute')" class="fullButton"
                     style="border: 1px solid #C73AEF;background: #15212F;">
            绘制采集线
            <i class="el-icon-edit"></i>
          </el-button>
          <el-tag
              v-for="tag in attribute"
              :key="tag.id"
              closable
              @close="delLaneLine(tag.id,'attribute')"
              type="info">
            {{ tag.id }}
          </el-tag>
          <div class="title">
            <el-checkbox true-label="1" false-label="0" v-model="virtualCoilRadio"></el-checkbox>
            虚拟线圈
          </div>
          <el-button @click="draw('virtualCoilArea')" class="fullButton"
                     style="border: 1px solid #C4AA17;background: rgba(196,170,23,0.30);">
            绘制区域
            <i class="el-icon-edit"></i>
          </el-button>
          <el-tag
              v-for="tag in virtualCoilArea"
              :key="tag.id"
              closable
              @close="delLaneLine(tag.id,'virtualCoilArea')"
              type="info">
            {{ tag.id }}
          </el-tag>
          <el-button @click="draw('virtualCoilLine')" class="fullButton"
                     style="border: 1px solid #F7D200;background: #15212F;margin-left: 0;">
            绘制采集线
            <i class="el-icon-edit"></i>
          </el-button>
          <el-tag
              v-for="tag in virtualCoilLine"
              :key="tag.id"
              closable
              @close="delLaneLine(tag.id,'virtualCoilLine')"
              type="info">
            {{ tag.id }}
          </el-tag>
        </div>
        <div class="switchOpen">
          <div class="title">编码开关</div>
          <div v-for="i in codingSwitchArr" style="display: inline-block;margin: 0 10px 10px 0;">
            <span style="margin-right: 6px;color: #E9F3FF;">{{ i.label }}</span>
            <el-switch
                v-model="codingSwitch[i.key]"
                active-color="#45B035"
                inactive-color="#223142"
                active-value="1"
                inactive-value="0"
            >
            </el-switch>
          </div>
        </div>
      </div>
    </div>

    <el-dialog :modal=false :close-on-click-modal=false title="配置管理" :visible.sync="configManager">
      <el-table :data="configManagerData">
        <el-table-column
            v-for="i in configManagerColumns"
            :label="i.label"
            :prop="i.prop"
        ></el-table-column>
        <el-table-column label="操作" align="center" width="230">
          <template slot-scope="{ row }">
            <el-button type="text" size="mini" @click="delConfig(row.id)">
              删除配置
            </el-button>
            <el-button type="text" size="mini"
                       @click="readConfig(row.id)">
              加载配置
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <Pagination
          v-show="configParams.total > 0"
          :total="configParams.total"
          :page.sync="configParams.page"
          :limit.sync="configParams.size"
          layout="prev, pager, next"
          @pagination="getConfigList"
      />
      <div class="configInfo">
        <div class="configInfoTitle">说明:</div>
        <div class="configInfoCon">
          <p class="text">1.删除配置后 不可恢复!</p>
          <p class="text">
            2.加载配置后配置文件中,
            <span class="strong">视频流、结果地址</span>
            自动变更为
            <span class="strong">当前更新数据</span>
            其余参数请加载参数完成后再修改确认下发。
          </p>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import AILabel from 'ailabel';
import Pagination from '@/components/Pagination';
import {secondSave, getConfigList, delConfig, getConfig} from '@/api/project';

let pointEnum = {
  '0': '标定点①',
  '1': '标定点②',
  '2': '标定点③',
  '3': '标定点④',
};
export default {
  name: 'Ailable',
  components: {Pagination},
  props: {
    id: {// domID
      type: String,
      default: 'map'
    },
    operateId: {
      type: String | Number,
      default: ''
    },
    imgUrl: {
      type: String,
      default: ''
    },
    videoName: {
      type: String,
      default: ''
    },
    // step: {
    //   type: Number,
    //   default: 0
    // },
    videoId: {
      type: String | Number,
      default: ''
    }
  },
  data() {
    return {
      codingSwitchArr: [
        {key: 'objectDetection', label: '目标检测'},
        {key: 'carAttribute', label: '车辆属性'},
        {key: 'regionOfInterest', label: '感兴趣区域'},
        {key: 'attributeAcquisitionLine', label: '属性采集线'},
        {key: 'virtualCoil', label: '虚拟线圈'},
        {key: 'lane', label: '车道'},
        {key: 'gps', label: 'GPS坐标'},
        {key: 'all', label: '全部'},
      ],
      drawingStyle: {},
      textStyle: {
        fillStyle: 'rgba(247, 210, 0, 0.3)',
        strokeStyle: '#F7D200',
        background: true,
        globalAlpha: 1,
        fontColor: '#E9F3FF'
      },
      mode: 'PAN',
      itemName: '',
      editId: '',
      deleteIconId: 'delete01',
      imgInfo: {
        width: 1280,
        height: 720
      },
      radioType: '标定',
      curDraw: '',//当前绘制的类型
      curDrawType: '',//当前绘制的类型
      tags: [ //车道线tags
        {name: '标签一', type: 'info'},
        {name: '标签二', type: 'info'},
        {name: '标签三', type: 'info'},
        {name: '标签四', type: 'info'},
        {name: '标签五', type: 'info'}
      ],
      lane: [],//车道线数据
      lanes: [// 车道信息
        {laneName: '车道1', laneLine: ['', '']},
        {laneName: '车道2', laneLine: ['', '']},
      ],
      coordinateCalibration: [// 坐标标定
        {
          name: '标定点①',
          point: {x: '', y: ''}, // 坐标
          longitude: '', // 经度
          latitude: '', // 纬度
        },
        {name: '标定点②', point: {x: '', y: ''}, longitude: '', latitude: ''},
        {name: '标定点③', point: {x: '', y: ''}, longitude: '', latitude: ''},
        {name: '标定点④', point: {x: '', y: ''}, longitude: '', latitude: ''},
      ],
      curEditPointIdx: '',//当前编辑第几个坐标点
      objectDetection: [],//目标检测
      carAttribute: [],// 车辆属性
      other: [],// 其他
      codingSwitch: { //编码开关
        objectDetection: 0, // 目标检测
        carAttribute: 0, // 车辆属性
        regionOfInterest: 0, // 感兴趣区域
        attributeAcquisitionLine: 0, // 属性采集线
        virtualCoil: 0,//虚拟线圈
        lane: 0, // 车道
        gps: 0, // GPS坐标
        all: 0, // 全部
      },
      interestGoal: [],//感兴趣区域-目标检测数据
      interestGoalRadio: '0',//感兴趣区域-目标检测数据-
      interestLight: [],//感兴趣区域-信号灯检测数据
      interestLightRadio: '0',//感兴趣区域-信号灯检测数据-
      attribute: [],//属性采集线数据
      attributeRadio: '0',//属性采集线数据-
      virtualCoilArea: [],//虚拟线圈-区域数据
      virtualCoilLine: [],//虚拟线圈-采集线数据
      virtualCoilRadio: '0',//虚拟线圈-采集线数据-
      editData: {},//编辑时回显的第二步的数据
      configManager: false,//配置列表是否显示
      configManagerData: [],//配置列表数据
      configManagerColumns: [
        {label: '任务名称', prop: 'projectName'},
        {label: '视频流id', prop: 'videoId'},
        {label: '视频流名称', prop: 'videoName'},
        {label: '保存时间', prop: 'saveTime'},
      ],
      configParams: {
        total: 0,
        page: 1,
        size: 10
      }
    };
  },
  watch: {
    mode(mode) {
      this.gMap.setMode(mode);
      // this.setDrawingStyle(mode);
    },
    curDrawType(type) {
      this.setDrawingStyleByType(type);
    },
    configManager(val) {
      if (val) {
        this.configParams = {
          total: 0,
          page: 1,
          size: 10
        };
        this.getConfigList();
      }
    }
  },
  methods: {
    // 绘制
    draw(type) {// 车道线
      this.curDrawType = type;
      switch (type) {
        case 'lane'://车道线
          this.mode = 'LINE';
          this.curDraw = '车道线';
          break;
        case 'interestGoal': //感兴趣区域-目标检测
          this.mode = 'POLYGON';
          this.curDraw = '感兴趣区域-目标检测';
          break;
        case 'interestLight': //感兴趣区域-信号灯检测
          this.mode = 'RECT';
          this.curDraw = '感兴趣区域-信号灯检测';
          break;
        case 'attribute': //属性采集线
          this.mode = 'LINE';
          this.curDraw = '属性采集线';
          break;
        case 'virtualCoilArea': //虚拟线圈-区域
          this.mode = 'POLYGON';
          this.curDraw = '虚拟线圈-区域';
          break;
        case 'virtualCoilLine': //虚拟线圈-采集线
          this.mode = 'LINE';
          this.curDraw = '虚拟线圈-采集线';
          break;
        default:
          break;
      }
    },
    location(n) {// 标定坐标
      this.curEditPointIdx = n;
      this.mode = 'POINT';
      this.curDrawType = 'coordinateCalibration';
      this.curDraw = pointEnum[n];
    },
    addLane() {// 添加车道
      this.lanes.push({laneName: '车道' + (this.lanes.length + 1), laneLine: ['', ''],});
    },
    delLane(idx) {
      this.lanes.splice(idx, 1);
    },
    info() {
      this.$alert(`
        <div>
            <p>1.选择模式之后进行标注</p>
            <p>2.滚轮可以控制缩放</p>
            <p>3.默认状态下可以随意拖动</p>
            <p>4.双击标注进行编辑</p>
            <p>5.ctrl+z撤销操作</p>
            <p>6.编辑时可以随意调整</p>
            <p>7.暂不支持修改标注的名称,如果想改名称,则删除重新添加</p>
            <p style="color:#F94040;">8.绘制越早的标注,层级越高(建议把较大的标注放在最后标注)</p>
        </div>
       `, '标注规则', {
        dangerouslyUseHTMLString: true
      });
    },
    zoomIn() {
      this.gMap.zoomIn();
    },
    zoomOut() {
      this.gMap.zoomOut();
    },
    setMode(mode) {
      this.mode = mode;
    },
    // 获取所有features
    getFeatures() {
      const allFeatures = this.gFirstFeatureLayer.getAllFeatures();
      let arr = [];
      allFeatures.forEach(item => {
        let o = {
          options: this.getPoints(item),
          id: item.id,
          props: item.props,
          shape: item.shape,
          type: item.type,
        };
        console.log(o);
        arr.push(o);
      });
      // window.localStorage.setItem('featureItems', JSON.stringify(arr));
      // this.getMarkers();
      this.getText();
    },
    // 获取所有markers
    getMarkers() {
      const allMarkers = this.gMap.markerLayer.getAllMarkers();
      console.log(allMarkers);
    },
    // 获取所有text标注
    getText() {
      const allText = this.gFirstTextLayer.getAllTexts();
      let arr = [];
      allText.forEach(item => {
        let o = {
          id: item.id,
          props: item.props,
          shape: item.textInfo,
          type: item.type,
        };
        arr.push(o);
      });
      window.localStorage.setItem('textItems', JSON.stringify(arr));
    },
    // 初始样式
    setDrawingStyle(mode) {
      switch (mode) {
        case 'PAN': {
          break;
        }
        case 'MARKER': {
          // 忽略
          break;
        }
        case 'POINT': {
          this.drawingStyle = {fillStyle: '#9370DB', zIndex: 99};
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'CIRCLE': {
          this.drawingStyle = {fillStyle: '#9370DB', strokeStyle: '#0000FF', lineWidth: 2, zIndex: 98};
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'LINE': {
          this.drawingStyle = {
            strokeStyle: '#C73AEF',
            lineJoin: 'round',
            lineCap: 'round',
            lineWidth: 6,
            arrow: false,
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'POLYLINE': {
          this.drawingStyle = {strokeStyle: '#FF1493', lineJoin: 'round', lineCap: 'round', lineWidth: 10, zIndex: 9};
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'RECT': {
          this.drawingStyle = {strokeStyle: '#F94040', lineWidth: 1, zIndex: 19};
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'POLYGON': {
          this.drawingStyle = {
            strokeStyle: '#F94040',
            fillStyle: 'rgba(249,64,64,0.60)',
            globalAlpha: .3,
            lineWidth: 1,
            fill: true,
            stroke: true, zIndex: 555
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'DRAWMASK': {
          this.drawingStyle = {strokeStyle: 'rgba(255, 0, 0, .5)', fillStyle: '#00f', lineWidth: 50};
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'CLEARMASK': {
          this.drawingStyle = {fillStyle: '#00f', lineWidth: 30};
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        default:
          break;
      }
    },// 初始样式
    setDrawingStyleByType(type) {
      switch (type) {
        case 'coordinateCalibration': {
          this.textStyle.fillStyle = 'rgba(78, 216, 58, 0.3)';//文字的背景
          this.textStyle.strokeStyle = '#4ED83A';//文字的边框
          this.drawingStyle = {fillStyle: '#4ED83A'};
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'lane': {
          this.textStyle.fillStyle = 'rgba(78, 216, 58, 0.3)';//文字的背景
          this.textStyle.strokeStyle = '#4ED83A';//文字的边框
          this.drawingStyle = {
            strokeStyle: '#4ED83A',
            lineJoin: 'round',
            lineCap: 'round',
            lineWidth: 6,
            arrow: false,
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'attribute': {
          this.textStyle.fillStyle = 'rgba(199, 58, 239, 0.3)';//文字的背景
          this.textStyle.strokeStyle = '#C73AEF';//文字的边框
          this.drawingStyle = {
            strokeStyle: '#C73AEF',
            lineJoin: 'round',
            lineCap: 'round',
            lineWidth: 6,
            arrow: false,
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'virtualCoilLine': {
          this.textStyle.fillStyle = 'rgba(196, 170, 23, 0.3)';//文字的背景
          this.textStyle.strokeStyle = '#F7D200';//文字的边框
          this.drawingStyle = {
            strokeStyle: '#F7D200',
            lineJoin: 'round',
            lineCap: 'round',
            lineWidth: 6,
            arrow: false,
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'interestGoal': {
          this.textStyle.fillStyle = 'rgba(0, 145, 255, 0.3)';//文字的背景
          this.textStyle.strokeStyle = '#0091FF';//文字的边框
          this.drawingStyle = {
            strokeStyle: '#0091FF',
            fillStyle: 'rgba(0,145,255,0.60)',
            globalAlpha: .3,
            lineWidth: 1,
            fill: true,
            stroke: true, zIndex: 555
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'interestLight': {
          this.textStyle.fillStyle = 'rgba(249,64,64, 0.3)';//文字的背景
          this.textStyle.strokeStyle = '#F94040';//文字的边框
          this.drawingStyle = {
            strokeStyle: '#F94040',
            fillStyle: 'rgba(249,64,64,0.60)',
            globalAlpha: .3,
            lineWidth: 1,
            fill: true,
            stroke: true, zIndex: 555
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        case 'virtualCoilArea': {
          this.textStyle.fillStyle = 'rgba(247,210,0, 0.3)';//文字的背景
          this.textStyle.strokeStyle = '#F7D200';//文字的边框
          this.drawingStyle = {
            strokeStyle: '#F7D200',
            fillStyle: 'rgba(247,210,0,0.60)',
            globalAlpha: .3,
            lineWidth: 1,
            fill: true,
            stroke: true, zIndex: 555
          };
          this.gMap.setDrawingStyle(this.drawingStyle);
          break;
        }
        default:
          break;
      }
    },
    // 添加图形
    addFeature(typeData, type, name, id = +new Date()) {
      let that = this;
      // this.setDrawingStyle(type);
      this.setDrawingStyleByType(that.curDrawType);
      let drawingStyle = this.drawingStyle;
      let {type: dataType, data} = typeData;
      let shape = {};
      if (dataType === 'data') {
        const scale = that.gMap.getScale();
        const width = drawingStyle.lineWidth / scale;
        switch (type) {
          case 'LINE':
            shape = {...data, width};
            break;
          case 'POLYLINE':
            shape = {points: data, width};
            break;
          case 'RECT':
            shape = data;
            break;
          case 'POLYGON':
            shape = {points: data};
            break;
          case 'POINT':
            shape = {...data, sr: 6};
            break;
          default:
            shape = {};
            break;
        }
      } else {
        shape = data;
      }
      let addItem = null;
      if (type === 'LINE') {
        addItem = new AILabel.Feature.Line(
            id, // id
            // {...data, width}, // shape
            shape,
            {name}, // props
            drawingStyle // style
        );
      } else if (type === 'POLYLINE') {
        addItem = new AILabel.Feature.Polyline(
            id, // id
            // {points: data, width}, // shape
            shape,
            {name}, // props
            drawingStyle // style
        );
      } else if (type === 'RECT') {
        addItem = new AILabel.Feature.Rect(
            id, // id
            data, // shape
            {name}, // props
            drawingStyle // style
        );
      } else if (type === 'POLYGON') {
        addItem = new AILabel.Feature.Polygon(
            id, // id
            // {points: data}, // shape
            shape,
            {name}, // props
            drawingStyle // style
        );
      } else if (type === 'POINT') {
        addItem = new AILabel.Feature.Point(
            id, // id
            // {...data, sr: 6}, // shape
            shape,
            {name}, // props
            drawingStyle // style
        );
      }
      addItem.delType = that.curDrawType;
      that.gFirstFeatureLayer.addFeature(addItem);
    },
    // 画完取名
    getName(mode) {
      return this.$prompt(`请填写【${this.curDraw}】名字`, {
        confirmButtonText: '确定',
        showCancelButton: false,
        inputValue: this.curDraw,
      }).then(({value}) => {
        return value;
      }).catch(() => {
        return null;
      });
    },
    // 删除实例~~~
    delLaneLine(id, delType) {
      this.delItem(id, delType);
      let feature = this.gFirstFeatureLayer.getFeatureById(id);
      let marker = this.gMap.markerLayer.getMarkerById(this.deleteIconId);
      this.del(marker, feature);
    },
    // 删除
    delItem(id, type) {
      let arr = [];
      if (type === 'coordinateCalibration') {
        arr = this[type].map(i => {
          if (i.name === id) {
            i.point = {x: '', y: ''};
          }
          return i;
        });
      } else {
        arr = this[type].filter(i => i.id !== id);
      }
      this[type] = arr;
    },
    // 删除实例
    del(marker, feature) {
      // 首先删除当前marker
      marker && this.gMap.markerLayer.removeMarkerById(marker.id);
      // 删除对应text
      // gFirstTextLayer.removeTextById(textId);
      // 删除对应feature
      feature && this.gFirstFeatureLayer.removeFeatureById(feature.id);
      feature && this.gFirstTextLayer.removeTextById(feature.id);
    },
    // 增加删除图标
    addDeleteIcon(feature) {
      let gMap = this.gMap;
      let that = this;
      // 添加delete-icon
      let points = that.getPoints(feature);
      const gFirstMarker = new AILabel.Marker(
          that.deleteIconId, // id
          {
            src: 'delete.png',
            position: points[1] || points[0], // 矩形右上角
            offset: {
              x: -20,
              y: -4
            }
          }, // markerInfo
          {name: 'delete'} // props
      );
      gFirstMarker.events.on('click', marker => {
        let id = feature.id;
        let delType = feature.delType;
        that.delItem(id, delType);
        that.del(marker, feature);
      });
      gMap.markerLayer.addMarker(gFirstMarker);
    },
    // 增加标注
    addText(feature, text) {
      let that = this;
      let name = feature.id;
      text = text ? text : name;
      let options = this.getPoints(feature);
      const gFirstText = new AILabel.Text(
          name, // id
          {text: text, position: options[0], offset: {x: 0, y: 0}}, // shape
          {name}, // props
          that.textStyle // style
      );
      // gFirstText.events.on('click', text => {
      //   console.log(text);
      // });
      this.gFirstTextLayer.addText(gFirstText);
    },
    // 删除标注
    delText(id) {
      this.gFirstTextLayer.removeTextById(id);
    },
    // 删除 删除按钮
    deIcon() {
      this.gMap.markerLayer.removeAllMarkers();
    },
    // 编辑完要更新数据
    resetItem(feature) {
      let arr = [];
      if (feature.delType === 'coordinateCalibration') {
        arr = this[this[feature.delType]]?.map(i => {
          if (i.name === feature.id) {
            i.point = feature.shape;
          }
          return i;
        });
      } else {
        let options = this.getPoints(feature);
        arr = this[feature.delType].map(i => {
          if (i.id === feature.id) {
            return {
              options,
              id: feature.id,
              props: feature.props,
              shape: feature.shape,
              type: feature.type
            };
          } else {
            return i;
          }
        });
      }
      this[feature.delType] = arr;
    },
    // 增加事件
    addEvent() {
      let that = this;
      let gMap = this.gMap;
      gMap.events.on('drawDone', (type, data) => {
        if (!that.curDraw) {
          return;
        }
        that.getName(type).then(val => {
          if (val) {
            let alreadyHave = that.gFirstFeatureLayer.getFeatureById(val) ? true : false;
            if (alreadyHave) {
              this.$message({
                type: 'info',
                message: '名字重复'
              });
              return;
            }
            // 把上次画的点删除
            if (this.curDrawType === 'coordinateCalibration') {
              let pointId = that[that.curDrawType][that.curEditPointIdx].name;
              this.delLaneLine(pointId, 'coordinateCalibration');
            }
            that.addFeature({type: 'data', data}, type, val, val);
            // 绘制完成的实例
            let feature = this.gFirstFeatureLayer.getFeatureById(val);
            let options = this.getPoints(feature);
            // 画完放到相应的数组
            let o = {
              options,
              id: feature.id,
              props: feature.props,
              shape: feature.shape,
              type: feature.type,
              delType: feature.delType
            };
            // 标定点处理
            if (this.curDrawType === 'coordinateCalibration') {
              that[that.curDrawType][that.curEditPointIdx].point = feature.shape;
              that[that.curDrawType][that.curEditPointIdx].name = feature.id;
            } else {
              that[that.curDrawType].push(o);
            }
            that.addText(feature);
          } else {
            // this.$message({
            //   type: 'info',
            //   message: '请填写名字'
            // });
          }
        });
      });
      gMap.events.on('boundsChanged', data => {
        return '';
      });
      // 双击编辑
      gMap.events.on('featureSelected', feature => {
        that.editId = feature.id;
        // console.log('--map featureSelected--', feature);
        gMap.setActiveFeature(feature);
        // 增加删除按钮
        that.addDeleteIcon(feature);
      });
      // 单机空白取消编辑
      gMap.events.on('featureUnselected', (feature) => {
        console.log(feature);
        // 更新完要更新数据
        that.resetItem(feature);
        // 取消featureSelected
        that.editId = '';
        that.deIcon();
        gMap.setActiveFeature(null);
      });
      // 更新完
      gMap.events.on('featureUpdated', (feature, shape) => {
        console.log(feature);
        // 更新完要更新数据
        that.resetItem(feature);
        // 更新或者移动需要重新设置删除图标
        that.deIcon();
        that.delText(feature.id);
        feature.updateShape(shape);
        that.addDeleteIcon(feature);
        that.addText(feature);
      });
    },
    // 获取坐标
    getPoints(feature) {
      switch (feature.type) {
        case 'RECT':
          return feature.getPoints();
        case 'LINE':
          return [
            feature.shape.start,
            feature.shape.end,
          ];
        case 'POLYLINE':
          return feature.shape.points;
        case 'POLYGON':
          return feature.shape.points;
        case 'POINT':
          return [feature.shape];
        default:
          return [];
      }
    },
    // 初始化
    init() {
      let that = this;
      const gMap = new AILabel.Map(that.id, {
        center: {x: that.imgInfo.width / 2, y: that.imgInfo.height / 2}, // 为了让图片居中
        zoom: that.imgInfo.width + 10,
        mode: that.mode, // 绘制线段
        refreshDelayWhenZooming: true, // 缩放时是否允许刷新延时,性能更优
        zoomWhenDrawing: true,
        panWhenDrawing: true,
        zoomWheelRatio: 5, // 控制滑轮缩放缩率[0, 10), 值越小,则缩放越快,反之越慢
        withHotKeys: true // 关闭快捷键
      });
      that.gMap = gMap;
      this.addEvent();
      // 图片层添加
      const gFirstImageLayer = new AILabel.Layer.Image(
          'first-layer-image', // id
          {
            src: that.imgUrl,
            width: that.imgInfo.width,
            height: that.imgInfo.height,
            crossOrigin: false, // 如果跨域图片,需要设置为true
            position: { // 左上角相对中心点偏移量
              x: 0,
              y: 0
            },
            grid: { // 3 * 3
              columns: [{color: '#9370DB'}, {color: '#FF6347'}],
              rows: [{color: '#9370DB'}, {color: '#FF6347'}]
            }
          }, // imageInfo
          {name: '图片图层'}, // props
          {zIndex: 5} // style
      );
      // 添加到gMap对象
      gMap.addLayer(gFirstImageLayer);
      // 添加矢量图层
      const gFirstFeatureLayer = new AILabel.Layer.Feature(
          'first-layer-feature', // id
          {name: '矢量图层'}, // props
          {zIndex: 10} // style
      );
      this.gFirstFeatureLayer = gFirstFeatureLayer;
      gMap.addLayer(gFirstFeatureLayer);
      // 添加标注
      const gFirstTextLayer = new AILabel.Layer.Text(
          'first-layer-text', // id
          {name: '文本图层'}, // props
          {zIndex: 12, opacity: 1} // style
      );
      this.gFirstTextLayer = gFirstTextLayer;
      this.gMap.addLayer(gFirstTextLayer);
      // 历史
      // this.getHistoryData();
      window.onresize = function () {
        gMap && gMap.resize();
      };
    },
    delAllItem() { //清除所有实例
      this.gFirstFeatureLayer.removeAllFeatures();
      this.gFirstTextLayer.removeAllTexts();
      this.gMap.markerLayer.removeAllMarkers();
    },
    // 历史数据渲染
    drawHistory(data) {
      data.forEach(item => {
        let config = item.extraConfig;
        this.curDrawType = config.delType;// 标注的类型
        this.addFeature({type: 'shape', data: config.shape},
            config.type, item.name, config.id);
        let feature = this.gFirstFeatureLayer.getFeatureById(config.id);
        this.addText(feature, item.name);
      });
    },
    // 获取历史数据
    getHistoryData() {
      let historyDraw = window.localStorage.historyDraw;
      if (!historyDraw) {
        return;
      }
      historyDraw = JSON.parse(historyDraw);
      this.editData = historyDraw.data.filter(i => i.videoId === this.videoId)[0];
      if (!this.editData) {
        return;
      }
      // 先清除所有实例
      this.delAllItem();
      let d = this.editData;
      console.log('当前编辑的数据', d);
      this.lane = this.reFormatData(d.laneLine);
      this.drawHistory(d.laneLine);
      this.lanes = d.lanes;
      this.coordinateCalibration = d.coordinateCalibration;
      d.coordinateCalibration.forEach(item => {
        this.curDrawType = 'coordinateCalibration';// 标注的类型
        this.addFeature({type: 'data', data: item.point},
            'POINT', item.name, item.name);
        let feature = this.gFirstFeatureLayer.getFeatureById(item.name);
        this.addText(feature, item.name);
      });

      this.objectDetection = d.objectDetection;
      this.carAttribute = d.carAttribute;
      this.other = d.other;
      this.codingSwitch = d.codingSwitch;

      this.interestGoal = this.reFormatData(d.objectDetectionDraw.data);
      this.drawHistory(d.objectDetectionDraw.data);
      this.interestGoalRadio = d.objectDetectionDraw.open;

      this.interestLight = this.reFormatData(d.signalLampIdentificationDraw.data);
      this.drawHistory(d.signalLampIdentificationDraw.data);
      this.interestLightRadio = d.signalLampIdentificationDraw.open;

      this.attribute = this.reFormatData(d.attributeAcquisitionLine.data);
      this.drawHistory(d.attributeAcquisitionLine.data);
      this.attributeRadio = d.attributeAcquisitionLine.open;

      this.virtualCoilArea = this.reFormatData(d.virtualCoil.region);
      this.drawHistory(d.virtualCoil.region);
      this.virtualCoilLine = this.reFormatData(d.virtualCoil.acquisitionLine);
      this.drawHistory(d.virtualCoil.acquisitionLine);
      this.virtualCoilRadio = d.virtualCoil.open;
    },
    // 保存
    formatData(data) {
      return this.deepCopy(data).map(i => {
        return {
          name: i.id,
          point: i.options,
          extraConfig: i,
        };
      });
    },
    reFormatData(data) {
      return this.deepCopy(data).map(i => {
        return i.extraConfig;
      });
    },
    formatSubmitData() {
      return {
        id: this.operateId,
        data: [
          {
            videoId: this.videoId,
            videoName: this.videoName,
            laneLine: this.formatData(this.lane),
            lanes: this.lanes,
            coordinateCalibration: this.coordinateCalibration,
            objectDetection: this.objectDetection,
            carAttribute: this.carAttribute,
            other: this.other,
            objectDetectionDraw: {
              open: this.interestGoalRadio,
              data: this.formatData(this.interestGoal),
            },
            signalLampIdentificationDraw: {
              open: this.interestLightRadio,
              data: this.formatData(this.interestLight),
            },
            attributeAcquisitionLine: {
              open: this.attributeRadio,
              data: this.formatData(this.attribute),
            },
            virtualCoil: {
              open: this.virtualCoilRadio,
              region: this.formatData(this.virtualCoilArea),
              acquisitionLine: this.formatData(this.virtualCoilLine),
            },
            codingSwitch: this.codingSwitch,
          }
        ]
      };
    },
    save() {
      // console.log('lane', this.lane);
      // console.log('lanes', this.lanes);
      // console.log('coordinateCalibration', this.coordinateCalibration);
      // console.log('objectDetection', this.objectDetection);
      // console.log('carAttribute', this.carAttribute);
      // console.log('other', this.other);
      // console.log('codingSwitch', this.codingSwitch);
      // console.log('interestGoal', this.interestGoal);
      // console.log('interestLight', this.interestLight);
      // console.log('attribute', this.attribute);
      // console.log('virtualCoilArea', this.virtualCoilArea);
      // console.log('virtualCoilLine', this.virtualCoilLine);
      // console.log(this.editData);
      let o = this.formatSubmitData();
      console.log(11111111111, o);
      secondSave(o).then(res => {
        this.$message({
          type: 'success',
          message: `${this.videoName}保存配置成功`
        });

      });
    },
    temporarySave() {//暂存
      let o = this.formatSubmitData();
      let historyDraw = window.localStorage.historyDraw;
      if (!historyDraw) {
        window.localStorage.setItem('historyDraw', JSON.stringify(o));
      } else {
        historyDraw = JSON.parse(historyDraw);
        let curData = historyDraw.data.filter(i => i.videoId === o.data[0].videoId)[0];
        if (curData) {
          historyDraw.data = historyDraw.data.map(i => {
            if (i.videoId === o.data[0].videoId) {
              return o.data[0];
            } else {
              return i;
            }
          });
        } else {
          historyDraw.data.push(o.data[0]);
        }
        window.localStorage.setItem('historyDraw', JSON.stringify(historyDraw));
      }
    },
    getConfigList() {
      let {size, page} = this.configParams;
      console.log(size, page);
      getConfigList({size, page}).then(({data}) => {
        this.configParams.total = data.total;
        this.configManagerData = data.data;
      });
    },
    delConfig(id) {//删除配置
      delConfig({id}).then(res => {
        this.$message.success('删除成功');
        this.getConfigList();
      });
    },
    readConfig(id) {//读取配置
      getConfig({id}).then(({data}) => {
        console.log('readConfig-----------------', data);
        data.videoName = this.videoName;
        data.videoId = this.videoId;
        let historyDraw = window.localStorage.historyDraw;
        if (!historyDraw) {
          historyDraw = {
            id: this.operateId,
            data: [data]
          };
        } else {
          historyDraw = JSON.parse(historyDraw);
          // 判断是否存在暂存了
          this.hasCurEdit = historyDraw.data.filter(i => i.videoId === this.videoId)[0];
          if (!this.hasCurEdit) {
            historyDraw.data.push(data);
          } else {
            historyDraw.data = historyDraw.data.map(i => {
              if (i.videoId === this.videoId) {
                return data;
              } else {
                return i;
              }
            });
          }
          console.log('readConfighistoryDraw', historyDraw);
        }
        window.localStorage.setItem('historyDraw', JSON.stringify(historyDraw));
        this.getHistoryData();
        this.configManager = false;//加载配置完成关闭弹框
      });
    }
  },
  mounted() {
    this.init();
    this.getHistoryData();
  },
  beforeDestroy() {
    this.gMap && this.gMap.destroy();
    // window.localStorage.removeItem('historyDraw');
  },
};
</script>

<style scoped lang="scss">
.rightOperate {
  width: calc(100% - 800px);
  height: 500px;
  overflow: auto;
  padding-left: 16px;
  padding-right: 6px;

  ::v-deep.el-radio-button__inner {
    width: 64px;
    height: 32px;
    border-radius: 2px;
  }

  //标注的标题
  .title {
    font-size: 12px;
    color: #23A6FF;
    margin: 20px 0 20px 0;
  }

  ::v-deep.fullButton {
    width: 100%;
    color: #B4CDE9;
    margin-bottom: 10px;
  }

  ::v-deep.el-tag.el-tag--info {
    color: #CEDFF4;
    background-color: #223142;
    border-color: #223142;
    margin-right: 6px;
  }

  .leftCon {
    .coordinateCalibration {
      margin-bottom: 16px;
    }
  }
}

.configInfo {
  .configInfoTitle {
    font-size: 14px;
    color: #CEDFF4;
    margin: 8px 0;
  }

  .configInfoCon {

    .text {
      font-size: 14px;
      color: #9BB9DC;
      margin-bottom: 6px;

      .strong {
        color: #E9F3FF;
      }
    }

  }
}

.button-wrap {
  padding-bottom: 10px;
  position: relative;
  z-index: 99;
}
</style>