草庐IT

vue3封装城市联动组件

晨曦的杂货铺 2023-03-28 原文

前言

本文将带你实现地址的选择,将其注册为全局组件,进行三级联动后选定地址。

一.准备

1.axios

利用axios发送请求,拿到全部城市数据。

  • 在项目根目录下打开任意终端,执行yarn add axios命令。
  • 项目中需要发送请求统一统一放在src/api/index.js
import axios from 'axios'
// 获取城市数据
// 1. 数据在哪里?https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json
// 2. 何时获取?打开城市列表的时候,做个内存中缓存
// 3. 怎么使用数据?定义计算属性,根据点击的省份城市展示
export const getCityList = async () => {
  // 添加缓存,防止频繁加载列表数据
  if ((window as any).cityList) {
    // 缓存中已经存在数据了
    return(window as any).cityList
  }
  const ret = await axios.get('https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json')
  // 给window对象添加了一个属性cityList
  if (ret.data) {
    (window as any).cityList = ret.data
  }
  // 把数据返回
  return ret.data
}

2.vueuse/core

利用vueuse/core里的onClickOutside,判断点击是否是外部组件,来帮助我们关闭弹层。
终端中执行yarn add @vueuse/core@5.3.0,这里安装指定版本,各位按需选择。

二.代码实现

1.封装

将其封装为局部组件,文件放在src/libs目录下,新建city.vue组件。
代码如下(示例):

<template>
  <div class="city" ref="target">
    <div class="select" @click="toggle" :class="{ active: isShow }">
      <span v-if="!fullLocation" class="placeholder">请选择你所在的城市</span>
      <span v-else class="value">{{ fullLocation }}</span>
      <svg class="icon" aria-hidden="true">
        <use xlink:href="#icon-xiajiantou"></use>
      </svg>
    </div>
    <div class="option" v-show="isShow">
      <div class="loading" v-if="loading"></div>
      <template v-else>
        <span
          @click="changeCity(item)"
          class="ellipsis"
          v-for="item in cityList"
          :key="item.code"
          >{{ item.name }}</span
        >
      </template>
    </div>
  </div>
</template>
<script lang="ts">
import { ref, reactive, computed } from "vue";
import { onClickOutside } from "@vueuse/core";
import { getCityList } from "../api/index";
export default {
  name: "City",
  props: {
    fullLocation: {
      type: String,
      default: "",
    },
  },
  setup(props, { emit }) {
    const isShow = ref(false);
    const loading = ref(false);
    // 城市列表原始数据
    const list = ref([]);
    //选中的省市区
    const changeResult = reactive({
      provinceCode: "",
      provinceName: "",
      cityCode: "",
      cityName: "",
      countyCode: "",
      countyName: "",
      fullLocation: "",
    });
    //选择的城市操作
    const changeCity = (city) => {
      if (city.level === 0) {
        // 点击的是省级单位
        changeResult.provinceCode = city.code;
        changeResult.provinceName = city.name;
      } else if (city.level === 1) {
        // 点击的是市级单位
        changeResult.cityCode = city.code;
        changeResult.cityName = city.name;
      } else if (city.level === 2) {
        // 点击的县级单位:选中最终的省市区数据,并且传递给父组件
        changeResult.countyCode = city.code;
        changeResult.countyName = city.name;
        //组合完整的省区名称
        changeResult.fullLocation = `${changeResult.provinceName}${changeResult.cityName}${changeResult.countyName}`;
        //关闭弹层
        isShow.value = false;
        //把选中的数据传递给子组件
        emit("change-city", changeResult);
      }
    };

    //通过计算属性计算当前显示的数据:省级,市级,县级

    const cityList = computed(() => {
      let result = list.value;
      //当前点击的是省,那么就计算市集列表
      if (changeResult.provinceCode && changeResult.provinceName) {
        result = result.find(
          (item) => item.code === changeResult.provinceCode
        ).areaList;
      }
      // 当前点击的是市,那么计算县级列表
      if (changeResult.cityCode && changeResult.cityCode) {
        result = result.find(
          (item) => item.code === changeResult.cityCode
        ).areaList;
      }
      return result;
    });
    // 控制城市弹窗的显示和隐藏
    const toggle = () => {
      isShow.value = !isShow.value;
      if (isShow.value) {
        loading.value = true;
        //调用接口之前,把之前的数据置空
        for (const key in changeResult) {
          changeResult[key] = "";
        }
        //弹层显示了,调用接口
        getCityList().then((ret) => {
          list.value = ret;
          loading.value = false;
        });
      }
    };
    //控制点击区域外,隐藏弹层
    const target = ref(null);
    onClickOutside(target, () => {
      isShow.value = false;
    });
    return { isShow, toggle, target, cityList, loading, changeCity };
  },
};
</script>
<style lang="scss">
.city {
  display: inline-block;
  position: relative;
  z-index: 400;
  .select {
    border: 1px solid #e4e4e4;
    min-height: 60px;
    text-align: center;
    padding: 0 20px;
    width: 350px;
    line-height: 60px;
    cursor: pointer;
    &.active {
      background: #fff;
    }
    .placeholder {
      color: #999;
      font-size: 30px;
    }
    .value {
      color: #999;
      font-size: 30px;
    }
    svg {
      font-size: 28px;
      margin-left: 5px;
    }
  }
  .option {
    width: 600px;
    border: 1px solid #e4e4e4;
    position: absolute;
    left: 0;
    top: 60px;
    background: #fff;
    min-height: 30px;
    line-height: 30px;
    display: flex;
    flex-wrap: wrap;
    padding: 10px;
    .loading {
      height: 290px;
      width: 100%;
      background: url(https://code-1307161657.cos.ap-beijing.myqcloud.com/images%2Fload.gif)
        no-repeat center;
    }
    span {
      width: 166px;
      cursor: pointer;
      text-align: center;
      border-radius: 4px;
      padding: 0 3px;
      &:hover {
        background: #f5f5f5;
      }
    }
  }
}
</style>

2. 使用

在任意.vue结尾的文件中引入city.vue就可以使用。
如果有默认数据的话,将默认数据传递给子组件。
代码如下(示例):

<template>
  <h1>City组件示例</h1>
  <div class="demo">
    <h2>常规使用</h2>
    <div class="demo__component">
      <h3>fullLocation:默认显示的城市; change事件可以获得选择的城市</h3>
      <City :fullLocation="fullLocation" @change-city="changeCity" />
    </div>
    <div class="demo__actions">
      <Button>隐藏代码</Button>
    </div>
    <div v-if="false" class="demo__code">
      <pre>代码</pre>
    </div>
  </div>
</template>
<script>
import { ref } from "vue";
import City from "../libs/City.vue";
import Button from "../libs/Button.vue";
export default {
  components: {
    City,
    Button,
  },
  setup() {
    //默认地址的获取,如果有默认地址就获取默认地址即可,
    const provinceCode = ref("110000");
    const cityCode = ref("119900");
    const countyCode = ref("110101");
    const fullLocation = ref("北京市市辖区东城区");
    // 更新选中的省市区数据
    const changeCity = (cityInfo) => {
      provinceCode.value = cityInfo.provinceCode;
      cityCode.value = cityInfo.cityCode;
      countyCode.value = cityInfo.countyCode;
      fullLocation.value = cityInfo.fullLocation;
    };
    return { fullLocation, changeCity };
  },
};
</script>
<style lang="scss">
</style>

总结

把时间用在进步上,其实很多时候,你并不需要做什么,真诚即可。

有关vue3封装城市联动组件的更多相关文章

  1. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  2. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  3. ruby-on-rails - 如何在 Ruby 中封装包含的模块方法? - 2

    我希望能够在包含该模块的类无法访问的模块中拥有方法。给定以下示例:classFooincludeBardefdo_stuffcommon_method_nameendendmoduleBardefdo_stuffcommon_method_nameendprivatedefcommon_method_name#blahblahendend我希望Foo.new.do_stuff爆炸,因为它试图访问模块试图对其隐藏的方法。不过,在上面的代码中,Foo.new.do_stuff可以正常工作:(有没有办法在Ruby中实现我想做的事情?更新-真正的代码classPlace"Place"}has_

  4. vue 实现内容超出两行显示展开更多功能,可依据需求自定义任意行数! - 2

    平时开发中我们经常会遇到这样的需求,在一个不限高度的盒子中会有很多内容,如果全部显示用户体验会非常不好,所以可以先折叠起来,当内容达到一定高度时,显示展开更多按钮,点击即可显示全部内容,先来看看效果图: 这样做用户体验瞬间得到提升,接下来看看具体细节。0">主要操作在内容这里{{item.username}},……展开更多样式大家可依据自己项目需求进行设计,这里就不贴了,主要说几个关键的。1、在data中定义三个属性isShowMore:false, //控制展开更多的显示与隐藏textHeight:null, //框中内容的高度status:false, //内容状态是否打开2.计算内容是否

  5. vue3.0 + vite2.0+如何兼容低版本浏览器 - 2

    这里写自定义目录标题一、问题二、解决三、解决方案四、打包预览一、问题在使用vue3.2和vite2开发一个移动端或者钉钉端H5微服务iosapp内置浏览器打开没问题安卓app内置浏览器打开空白页面vconsole打印出现报错globalthisundefind二、解决内置浏览器版本比较低打印出来是63vue3代码不兼容低版本浏览器三、解决方案步骤一:vite.config.ts里build.target配置项指定构建目标为es2015或者步骤二:安装@vitejs/plugin-legacy安装完报错也还在指定版本可以解决“@vitejs/plugin-legacy”:“1.8.0”,步骤三:

  6. ruby-on-rails - 使用 Faker gem 生成相关的城市、邮政编码、国家代码值 - 2

    有没有办法得到Fakergem生成“相关的”城市和国家/地区代码值?例如,加利福尼亚州温哥华明尼苏达州明尼阿波利斯我这样做:FactoryGirl.definedofactory:locationdo...city{Faker::Address.city}country_code{['US','CA'].sample}...endend但不能保证city实际位于country_code。我会满足于这样的事情:postal_code{Faker::Address.postcode(['US','CA'].sample)}然后我可以对其进行地理编码以获得其他值。

  7. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  8. Vue3的新特性 - 2

    Vue3的新特性包括:CompositionAPI:一种新的API风格,可将有关组件功能的代码逻辑封装在单独的函数中,从而更好地管理和重用代码。Teleport:可以让组件在DOM层次结构中的任何位置渲染。Suspense:一种新的异步渲染模式,可以优化应用程序的性能。更快的渲染速度:Vue3使用了新的虚拟DOM算法,并且对渲染过程进行了优化,因此在渲染大型应用时性能更高。更小的包大小:Vue3的打包大小比Vue2更小,因为它不再需要依赖像vue-template-compiler这样的工具。其他改进:Vue3还具有其他一些改进,例如更好的TypeScript支持、更好的错误提示和更好的调试工

  9. ruby - 如何在 Ruby Gem 中封装 Ruby C 扩展? - 2

    我找不到很多这方面的文档。如何打包gem,以便在安装gem时编译C扩展?特别是我想这样做:在Linux和MacOSX上,我想在安装gem时编译C扩展在Windows上,我想简单地安装一个预编译的.so对此的任何帮助,特别是示例源代码,都将非常有用:) 最佳答案 LuisLavena创造了rake-compiler只是为了这个目的。Homepage/GitHubGitHubWiki但是,您确定需要C扩展吗?关于C扩展的事情是,每个Ruby实现都有自己的C扩展API(而非基于C的API,如XRuby、JRuby、Ruby.NET、Iro

  10. ruby - Ruby 的 "Open Classes"会破坏封装吗? - 2

    在Ruby中,程序员可以更改预定义的类。所以一个非常糟糕的程序员可能会做这样的事情:classStringdef==(other)returntrueendend显然,几乎没有人会这么蠢,但是对预定义类进行更细微的更改可能会导致已经运行的代码出现问题的想法在我看来违反了封装原则。四个问题:首先,这是否实际上违反了面向对象的封装原则?其次,作为一名程序员,有没有一种方法可以保证我正在使用类的未修改版本的代码?第三,我应该在我的代码中“打开”类吗?原因?最后,在大规模生产编码环境中如何处理这类事情?换句话说,编程行业的人真的用其他人的代码来做这件事吗?将使用?或者即使他们不这样做,你如何确

随机推荐