学习记录:vue2+高德地图+el-cascader懒加载赋值回显

使用高德地图必须要有对应的key和密钥,方法也很简单,直接去官网按照流程申请就可以了

高德地图注册申请流程icon-default.png?t=N7T8https://lbs.amap.com/api/javascript-api-v2/prerequisites

elementUi想必大家也不陌生,这边也不展开说明了,有需要的可以去官网了解

elementUi开发指南icon-default.png?t=N7T8https://element.eleme.cn/#/zh-CN/component/installation

页面功能展示:

直接展示完整代码:(有注释)

index.vue小说明

1、联系人选择我选用的是el-popover弹出层,这样可以自定义内容(比如表格),在复杂情况下可以看到更多的参数,不使用下拉框就是因为这个(下拉框展示数据太少,容易误选)

官方文档 => elementUI el-popover弹出框文档

2、下拉框和级联框中文赋值是因为我这边项目有需求将id和中文都上传,正常情况不需要change事件,只传id即可

3、这里懒加载级联框赋值回显我用的是最简单暴力的方法,因为懒加载的级联框会在组件加载的时候根据绑定的值去搜索,所以我直接用v-if:false让它消失,然后在$nextTick再将它v-if:true,直接回显,这样就不用写一大堆获取节点数据和children赋值的逻辑

除了地图小弹窗之外的CSS部分就不展示了,没啥必要(~OvO~)

<template>
  <basic-container>
    <div class="uploadbox">
      <el-form ref="form" :model="addData" :rules="rules" label-width="120px">
        <el-form-item label="地点名称" prop="name">
          <el-input size="small" v-model="addData.name" placeholder="请输入地点名称"></el-input>
        </el-form-item>
        <el-form-item label="地点类别" prop="category">
          <el-select size="small" v-model="addData.category" placeholder="请选择地点类别" :key="isResouceShow"
            @change="categoryChange">
            <el-option v-for="item in kdTypeList" :value="item.dictKey" :label="item.dictValue"
              :key="item.dictKey"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="联系人" prop="contactMan">
          <el-popover placement="bottom-start" ref="popoverRef" width="550" trigger="manual" v-model="diagnosisPopover">
            <el-table :data="userList" @row-click="diagnosisrowClick" ref="eltableCurrentRow" highlight-current-row
              :header-cell-style="{background:'#ECF5FF',color:'#4c5058',fontWeight: '600', padding: '8px 0',textAlign: 'center'}"
              style="width: 100%;height: auto;user-select: none;">
              <el-table-column align="center" width="150" property="name" label="姓名"
                :show-overflow-tooltip="true"></el-table-column>
              <el-table-column align="center" property="phone" label="手机号"
                :show-overflow-tooltip="true"></el-table-column>
            </el-table>
            <div class="popover_pagination">
              <el-button type="text" @click="handlediagnosisPrev" :disabled="userpage.current === 1">上一页</el-button>
              <el-button type="text" @click="handlediagnosisNext"
                :disabled="userpage.total<= userpage.current*6 ">下一页</el-button>
              <el-button type="text" @click="diagnosisPopover = false">关闭</el-button>
            </div>
            <el-input size="small" v-model="addData.contactMan" placeholder="输入人员姓名搜索(回车确认)" @change="getUserListChange"
              @focus="getUserList" slot="reference">
            </el-input>
          </el-popover>
        </el-form-item>
        <el-form-item label="联系电话" prop="contactPhone">
          <el-input size="small" disabled v-model="addData.contactPhone" placeholder="请选择联系人"></el-input>
        </el-form-item>
        <el-form-item label="详细地址" prop="address">
          <el-button @click="showMap" size="small">地图选择</el-button>
          <el-input disabled size="small" v-model="addData.address" placeholder="地图选择详细地址"></el-input>
        </el-form-item>
        <el-form-item label="地点省市区/县" prop="city" class="cityarr" v-if="isResouceShow">
          <el-cascader ref="cityarr" size="small" v-model="cityarr" :props="props" :show-all-levels="false"
            @change="ciytChange"></el-cascader>
        </el-form-item>
        <el-form-item label="经纬度" prop="contactPhone">
          <template>
            <span>{{addData.longitude?addData.longitude+ ',' +addData.latitude:''}}</span>
          </template>
        </el-form-item>
        <el-form-item label="启用状态">
          <el-select size="small" v-model="addData.status" placeholder="请选择地点类别">
            <el-option :value="0" label="关闭"></el-option>
            <el-option :value="1" label="启用"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
    </div>
    <el-dialog title="地点地址选择" :visible.sync="mapshow" append-to-body width='50%' :close-on-click-modal="false"
      destroy-on-close class="mapDialog" :before-close="mapClose">
      <div class="mapCenter">
        <div class="mapleft">
          <gdMap @showresult="showresult" @setCascader="setCascader" ref="gdmap" :cityName="addData.cityName"
            :address="addData.address" :lng="addData.longitude" :lat="addData.latitude" v-if="mapshow"></gdMap>
        </div>
        <div class="mapright">
          <div style="margin-bottom:15px;text-align:right">
            <el-button size="small" @click="mapClose">取 消</el-button>
            <el-button size="small" type="primary" @click="sureAddress">确 定</el-button>
          </div>
          <div class="rightBottom">
            <div class="maprightPoi" :class="{check:checkedAddressId===item.id}" v-for="item in poiList" :key="item.id"
              @click="poiClick(item)">
              <i class="el-icon-location-outline" style="margin-right:5px;width:15px"></i>
              <div class="addressCenter">
                <div>
                  <div class="poiname">{{item.name}}</div>
                  <el-tooltip effect="dark" :content="item.address" placement="top">
                    <div class="poiaddress">{{item.address}}</div>
                  </el-tooltip>
                </div>
                <div v-if="checkedAddressId===item.id">
                  <i class="el-icon-check" style="margin-left:5px;width:15px"></i>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
  </basic-container>
</template>

<script>
// 这是我自己的接口引用,可以忽略
import { getDictionary, getCityList } from '@/api/ExaminationPoints/index';
import { getListkd } from '@/api/administrators/index';
// 引入地图组件
import gdMap from './MapContainer.vue';
import { mapGetters } from 'vuex';
export default {
  name: '',
  components: { gdMap },
  props: {},
  data() {
    return {
      isResouceShow: true,
      checkedAddressId: '',
      checkedData: {},
      poiList: [],
      mapshow: false,
      diagnosisPopover: false,
      userList: [],
      cityarr: [],
      options: [],
      kdTypeList: [],
      // 级联框懒加载
      props: {
        lazy: true,
        lazyLoad: (node, resolve) => {
          const { value, level } = node;
          let code = value || '';
          getCityList(code).then((res) => {
            res.data.data.forEach((i) => {
              i.value = i.code;
              i.label = i.name;
              i.leaf = level === 2;
            });
            resolve(res.data.data);
          });
        },
      },
      addData: {
        name: '',
        category: '',
        categoryText: '',
        fullArea: '',
        contactMan: '',
        contactPhone: '',
        province: '', // 省
        city: '', //市
        cityName: '',
        district: '', //区
        longitude: '', //经度
        latitude: '', //纬度
        address: '',
        status: 1,
      },
      rules: {
        name: [{ required: true, message: '请输入地点名称', trigger: 'blur' }],
        category: [
          { required: true, message: '请选择地点类别', trigger: 'change' },
        ],
        contactMan: [
          { required: true, message: '请输入地点联系人', trigger: 'change' },
        ],
        contactPhone: [
          { required: true, message: '请输入联系电话', trigger: 'blur' },
        ],
        city: [{ required: true, message: '请选择省市区', trigger: 'blur' }],
        address: [
          { required: true, message: '请输入详细地址', trigger: 'blur' },
        ],
      },
      userpage: {
        size: 6,
        current: 1,
        total: 0,
      },
    };
  },
  computed: {
    ...mapGetters(['userInfo']),
  },
  watch: {},
  created() {
    this.getDicData();
  },
  mounted() {},
  destroyed() {},
  methods: {
    // 确认地址
    sureAddress() {
      if (!this.checkedAddressId) {
        this.$message.error('请选择地点');
        return;
      }
      this.addData.address = this.checkedData.name;
      this.addData.longitude = this.checkedData.location.lng;
      this.addData.latitude = this.checkedData.location.lat;
      this.$refs.gdmap.getAddress(
        this.checkedData.location.lng,
        this.checkedData.location.lat
      );
    },
    // 更据地址获取省市区
    setCascader(data) {
      this.cityarr = [
        data.adcode.slice(0, 2),
        data.adcode.slice(0, 4),
        data.adcode,
      ];
      this.addData.fullArea =
        data.province +
        (data.city[0] ? data.city : data.province) +
        data.district;
      this.addData.cityName = data.city[0] ? data.city : data.province;
      this.addData.province = data.adcode.slice(0, 2);
      this.addData.city = data.adcode.slice(0, 4);
      this.addData.district = data.adcode;
      console.log(this.$refs['cityarr']);
      // 重新加载省市区级联
      this.isResouceShow = false;
      this.$nextTick(() => {
        this.isResouceShow = true;
      });
      this.mapClose();
    },
    // 展示地图
    showMap() {
      this.mapshow = true;
    },
    // 展示搜索地址列表
    showresult(val) {
      this.poiList = val;
    },
    // 点击搜索结果
    poiClick(data) {
      this.checkedData = data;
      this.checkedAddressId = data.id;
      this.$refs.gdmap.searchText = data.name;
      this.$refs.gdmap.markerResult(data);
    },
    // 关闭地图
    mapClose() {
      this.checkedAddressId = '';
      this.poiList = [];
      this.mapshow = false;
    },
    // 获取人员列表
    getUserList() {
      const obj = {
        realName: this.addData.contactMan,
      };
      getListkd(
        this.userpage.current,
        this.userpage.size,
        obj,
        this.userInfo.dept_id
      ).then((res) => {
        if (res.data.code === 200) {
          this.userList = res.data.data.records;
          this.userpage.total = res.data.data.total;
          this.diagnosisPopover = true;
        }
      });
    },
    // 人员弹出层搜索值改变
    getUserListChange() {
      this.userpage.current = 1;
      this.userpage.total = 0;
      this.addData.contactPhone = '';
      this.getUserList();
    },
    // 人员弹出层点击事件
    diagnosisrowClick(row) {
      this.addData.contactMan = row.name;
      this.addData.contactPhone = row.phone;
      this.diagnosisPopover = false;
      this.userpage.current = 1;
      this.userpage.total = 0;
    },
    // 人员弹出层上一页下一页
    handlediagnosisPrev() {
      this.userpage.current--;
      this.getUserList();
    },
    handlediagnosisNext() {
      this.userpage.current++;
      this.getUserList();
    },
    // 省市区级联修改
    ciytChange(val) {
      let e = this.$refs['cityarr'].getCheckedNodes();
      console.log(e);
      this.addData.fullArea =
        e[0].pathLabels[0] + e[0].pathLabels[1] + e[0].pathLabels[2];
      this.addData.cityName = e[0].pathLabels[1];
      this.addData.province = val[0];
      this.addData.city = val[1];
      this.addData.district = val[2];
      console.log(this.cityarr);
    },
    // 修改地点类型,将类型中文赋值给data
    categoryChange(val) {
      const thisLabel = this.kdTypeList.find(
        (item) => item.dictKey === val
      ).dictValue;
      this.addData.categoryText = thisLabel;
    },
    //获取地点类型字典
    getDicData() {
      getDictionary({ code: 'test_center_category' }).then((res) => {
        this.kdTypeList = res.data.data;
      });
    },
  },
};
</script>

<style lang='scss' scoped>
.mapDialog {
  height: 700px;
}
.mapCenter {
  display: flex;
  .mapright {
    flex: 1;
    padding: 0 15px;
    .rightBottom {
      height: 400px;
      display: flex;
      justify-content: start;
      flex-direction: column;
    }
    .check {
      color: #6b9df2;
    }
    .maprightPoi {
      width: 100%;
      height: 40px;
      display: flex;
      align-items: center;
      cursor: default;
      .addressCenter {
        flex: 1;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      .poiaddress {
        max-width: 300px;
        overflow: hidden; /* 超出一行文字自动隐藏 */
        text-overflow: ellipsis; /*文字隐藏后添加省略号*/
        white-space: nowrap; /*强制不换行*/
      }
    }
  }
}
</style>

MapContainer.vue小说明

1、key和对应的密钥一定要配置!(最重要的)

2、因为涉及到地图回显,所以在初始化地图的时候做个一个判断,有值的时候就是按照之前选择的地点回显,没有值就是正常初始化

3、搜索可以有搜索提示组件AutoComplete,我注册了但是没有用,觉得麻烦(0.0),反正我的右边有列表展示,有需要的可以自己试试, 官方文档=>高德地图JS API2.0 输入提示与 POI 搜索

<template>
  <div>
    <el-input id="tipInput" size="small" class="mapinput" v-model="searchText" placeholder="为提高精准度请输入(城市名+搜索地点关键字)"
      @keyup.enter.native="searchKeyWord"></el-input>
    <div id="container"></div>
  </div>
</template>

<script>
import AMapLoader from '@amap/amap-jsapi-loader';
export default {
  name: '',
  components: {},
  props: ['address', 'lat', 'lng', 'cityName'],
  data() {
    return {
      map: null, //地图实例
      searchText: '', //搜索关键词
      mapModule: null, // AMap
      placeSearchComponent: null,
      poiList: [],
    };
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.initAMap();
    window._AMapSecurityConfig = {
      securityJsCode: '', // 申请key对应的秘钥 -> 注意了,如果不搭配密钥使用,搜索将没有结果
    };
  },
  beforeDestroy() {
    this.map.clearMap(); // 清除所有覆盖物(点标志)
    this.searchText = '';
    this.map = null;
    this.mapModule = null;
  },
  destroyed() {},
  methods: {
    initAMap() {
      const _this = this;
      AMapLoader.load({
        key: '', // 申请好的Web端开发者Key,首次调用 load 时必填
        version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
        //在初始化函数中有一个plugins的配置给他穿入插件的名字即可
        //如
        plugins: [
          'AMap.ToolBar',
          'AMap.Scale',
          'AMap.HawkEye',
          'AMap.MapType',
          'AMap.Geolocation',
          'AMap.AutoComplete',
          'AMap.PlaceSearch',
          'AMap.Geocoder',
        ], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
      })
        .then((AMap) => {
          _this.mapModule = AMap;
          if (this.lng) {
            this.searchText = this.address;
            this.map = new AMap.Map('container', {
              // 设置地图容器id
              // viewMode: '3D', // 是否为3D地图模式
              zoom: 17, // 初始化地图级别
              center: [this.lng, this.lat], // 初始化地图中心点位置
            });
            var marker = new AMap.Marker({
              position: [Number(this.lng), Number(this.lat)],
            });
            this.map.add(marker);
          } else {
            this.map = new AMap.Map('container', {
              // 设置地图容器id
              // viewMode: '3D', // 是否为3D地图模式
              zoom: 12, // 初始化地图级别
              // center: [116.397428, 39.90923], // 初始化地图中心点位置 不设置则为ip的中心点
            });
          }
          var toolbar = new AMap.ToolBar(); //创建工具条插件实例
          this.map.addControl(toolbar); //添加工具条插件到页面
          var scale = new AMap.Scale();
          this.map.addControl(scale);
          this.mapSearchInit(AMap);
        })
        .catch((e) => {
          console.log(e);
        });
    },
    /** 初始化搜索 */
    mapSearchInit(AMap) {
    // 注册AutoComplete组件
      let autoOptions = {
        input: 'tipInput',
      };
      let autoCompleteComponent = new AMap.AutoComplete(autoOptions);
      this.autoCompleteComponent = autoCompleteComponent;
      // 注册placeSearch组件
      this.placeSearchComponent = new AMap.PlaceSearch();
    },
    //根据输入内容查询
    searchKeyWord() {
      let that = this;
      that.placeSearchComponent.search(
        that.cityName + that.searchText,
        function (status, result) {
          if (status === 'complete' && result.info === 'OK') {
            that.poiList = result.poiList.pois;
            that.$emit('showresult', that.poiList);
          } else {
            that.poiList = [];
            that.$message({
              message: '没有查到结果',
              type: 'warning',
            });
          }
        }
      );
    },

    // 显示地图点标记
    markerResult(data) {
      var marker = new this.mapModule.Marker({
        position: [Number(data.location.lng), Number(data.location.lat)],
      });
      this.map.clearMap(); // 清除所有覆盖物(点标志)
      this.map.add(marker); // 添加点标志
      setTimeout(() => {
        this.map.setCenter(data.location);
        this.map.setZoom(17);
      }, 50);
    },

    // 逆推详细地址行政区
    async getAddress(lng, lat) {
      let that = this;
      var geocoder = new this.mapModule.Geocoder({
        city: '010', //城市设为北京,默认:“全国”
        radius: 1000, //范围,默认:500
      });
      geocoder.getAddress([lng, lat], (status, result) => {
        if (status === 'complete' && result.regeocode) {
          var address = result.regeocode.addressComponent;
          console.log(address);
          that.$emit('setCascader', address);
        } else {
          log.error('根据经纬度查询地址失败');
        }
      });
    },
  },
};
</script>

<style lang='scss' scoped>
#container {
  padding: 0px;
  margin: 0px;
  width: 400px;
  height: 400px;
}
.mapinput {
  margin-bottom: 15px;
  width: 400px;
}
</style>

  • 18
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3 中,可以使用 `<script lang="ts" setup>` 来编写组件,并使用 `ref` 来获取组件中的数据和方法。下面是 el-cascader 动态Vue3 + TypeScript + `<script lang="ts" setup>` 的实现方式: ```vue <template> <el-cascader v-model="defaultValue" :options="options" :props="props" @change="handleChange" ></el-cascader> </template> <script lang="ts" setup> import { ref, onMounted } from 'vue' interface CascaderOption { label: string; value: string; children?: CascaderOption[]; } const defaultValue = ref<string[]>([]); const options = ref<CascaderOption[]>([ { label: 'Option 1', value: '1', children: [] } ]); const props = { lazy: true, lazyLoad: loadNode }; function handleChange(value: string[]) { console.log('Selected: ', value); } async function loadNode(node: CascaderOption, resolve: (nodes: CascaderOption[]) => void) { const children = await fetchChildren(node.value); const options = children.map(item => { return { label: item.name, value: item.id, children: item.children ? [] : undefined } }) resolve(options); } async function fetchChildren(value: string) { // 根据 value 的值,通过接口获取子节点数据 const response = await fetch('url?id=' + value); const data = await response.json(); return data.children; } onMounted(async () => { // 获取数据 const response = await fetch('url/echo'); const data = await response.json(); defaultValue.value = data; }); // 将 defaultValue、options 和 props 暴露出去,以便在模板中使用 export default { defaultValue, options, props, handleChange, loadNode }; </script> ``` 在上面的代码中,我们使用 `ref` 来定义 `defaultValue` 和 `options` 变量,分别用于存储数据和级联选择器的选项数据。然后,我们定义了 `loadNode` 方法,用于动态子节点数据,并将其作为 `lazyLoad` 属性传递给 el-cascader 组件。在 `onMounted` 钩子函数中,我们通过接口获取数据,并将其赋值给 `defaultValue` 变量。最后,我们将 `defaultValue`、`options` 和 `props` 暴露出去,以便在模板中使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值