vue.js+mint-ui的Popup组件和Picker组件实现省市县三级联动功能
前言
在工作中遇到省市县三级联动的需求,传统的pc端大部分是用3个HTML <select>标签根据选中的一级地址去获取二级地址,然后根据二级地址去获取三级地址。实现省市县三级地址选择,需要2次服务端请求。这里使用mint--ui的Popup弹出框组件和picker选择器组件,无需请求服务端,通过遍历本地的json文件,实现本地输出三级地址,同时给每级地址绑定一个code,从而满足与后台交互时,传递code代码,这个需求非常常见。
vue.js官网:https://cn.vuejs.org/
mint-ui官网:http://mint-ui.github.io/#!/zh-cn
步骤一:使用vue-cli快速构建demo项目
vue-cli是vue提供的官方命令行工具,用于快速搭建大型单页应用。
- 新建一个空文件夹demo,安装node.js,安装方法请谷歌或者百度,非常简单。
- 用IDEA开发工具打开demo文件夹,打开Terminal终端,输入下面的命令行全局安装vue-cli
npm install vue-cli -g
- 安装成功后输入npm -v可以查看npm的版本
- 输入以下命令行回车,初始化项目配置
vue init webpack demo
- 输入Project name, Project description , Author, Install vue-router?输入Y, Use ESLint to lint your code?输入N, Set up unit tests 输入N, Setup e2e tests with Nightwatch?输入N,回车初始化安装,初始化成功后可看到以下界面
- 终端输入以下命令行,浏览器打开http://localhost:8080,demo项目构建完毕
-
cd demo
-
npm run dev
步骤二:新建一个ThreeLevelAddress.vue文件,并引入mint-ui
- 在demo/src/components路径下新建ThreeLevelAddress.vue文件
1 <template> 2 <div class="three-level-address"> 3 hello 4 </div> 5 </template> 6 7 <script> 8 </script> 9 10 <style scoped> 11 </style>
- 在demo/src/router/index.js进行路由定义
-
import ThreeLevelAddress from '@/components/ThreeLevelAddress' export default new Router({ routes: [ { path:'/ThreeLevelAddress', name:'ThreeLevelAddress', component:ThreeLevelAddress } ] })
- 在浏览器内输入http://localhost:8080/#/ThreeLevelAddress可预览到下图
- 在终端输入以下命令行,引入mint-ui
npm i mint-ui -S
- 在demo/src/main.js路径下写入以下代码即可使用mint-ui
-
import MintUI from 'mint-ui' import 'mint-ui/lib/style.css' Vue.use(MintUI)
- 接下来测试一下是否引入成功,这里已mint-ui的button按钮组件为例。
-
<template> <div class="three-level-address"> <mt-button type="default" size="large">default</mt-button> <mt-button type="primary" size="large">primary</mt-button> <mt-button type="danger" size="large">danger</mt-button> </div> </template> <script> import Vue from 'vue' import { Button } from 'mint-ui'; Vue.component(Button.name, Button); </script> <style scoped> </style>
效果图:
步骤三:引入Popup组件
1 import { Popup } from 'mint-ui'; 2 Vue.component(Popup.name, Popup);
Popup的API
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
position | popup 的位置。省略则居中显示 |
String | 'top' 'right' 'bottom' 'left' |
|
pop-transition | 显示/隐藏时的动效,仅在省略 position 时可配置 |
String | 'popup-fade' | |
modal | 是否创建一个 modal 层 | Boolean | true | |
closeOnClickModal | 是否可以通过点击 modal 层来关闭 popup |
Boolean | true |
API中比较常用的是position,可配置弹出框的位置,四种效果都不太一样,可自行体验官网的demo。这里我使用的是位置为bottom的Popup组件,其他均使用默认配置。
<mt-popup v-model="popupVisible" position="bottom"> </mt-popup>
步骤四:引入Picker组件
import { Picker } from 'mint-ui';
Vue.component(Picker.name, Picker);
picker组件比如Popup组件复杂许多。已官方给出的demo为例,:slots绑定的组件的插槽,传入的是一个对象数组,用于配置picker组件的内容。@change绑定的是picker被选中的值发生变化时的change事件,在这里可以做你想做事情,比如给组件的插槽赋值,获取插槽的值,或者处理其他业务逻辑。
<mt-picker :slots="slots" @change="onValuesChange"></mt-picker>
我在本例中传入的插槽对象数组如下:
myAddressSlots: [ { flex: 1, values: this.getProvinceArr(), //省份数组 className: 'slot1', textAlign: 'center' }, { divider: true, content: '', className: 'slot2' }, { flex: 1, values: this.getCityArr("北京市"), className: 'slot3', textAlign: 'center' }, { divider: true, content: '', className: 'slot4' }, { flex: 1, values: this.getCountyArr("北京市","北京市"), className: 'slot5', textAlign: 'center' } ]
slots对象数组键值对解释:
key | 描述 |
---|---|
divider | 分隔符 |
content | 显示的文本 |
values | slot 的备选值数组。若为对象数组,则需设置 value-key 属性来指定显示的字段名 |
defaultIndex | picker初始选中值,需传入其在 values 数组中的序号,默认为 0 |
textAlign | 文本对齐方式 |
flex | slot CSS 的 flex 值 |
className |
slot 的类名,可以自定义你的样式 |
比较重要的是values,必须传入数组。因为本例中需要输出地址代码,所以这里必须传入对象数组,形如:
[ { "code": "110000", "name": "北京市", "children": [ { "code": "110100", "name": "北京市", "children": [ { "code": "110101", "name": "东城区" }, { "code": "110102", "name": "西城区" }, { "code": "110105", "name": "朝阳区" }, { "code": "110106", "name": "丰台区" }, { "code": "110107", "name": "石景山区" }, { "code": "110108", "name": "海淀区" }, { "code": "110109", "name": "门头沟区" }, { "code": "110111", "name": "房山区" }, { "code": "110112", "name": "通州区" }, { "code": "110113", "name": "顺义区" }, { "code": "110114", "name": "昌平区" }, { "code": "110115", "name": "大兴区" }, { "code": "110116", "name": "怀柔区" }, { "code": "110117", "name": "平谷区" }, { "code": "110128", "name": "密云县" }, { "code": "110129", "name": "延庆县" } ] } ] } ]
并在picker组件里设置value-key属性,这样picker就只会显示json对象中name属性。
value-key="name"
这里再学习一下picker组件的API
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
:slots | 插槽数组 | Array | [] | |
valueKey | 当 slots插槽的values 为对象数组时,作为文本显示 在 Picker 中的对应字段的字段名 |
String | ||
:showToolbar | 是否在组件顶部显示一个 toolbar,内容自定义 | Boolean | false | |
:visibleItemCount | picker组件中可显示的slot个数 | Number | 5 | |
:itemHeight | 每个 slot 的高度 | Number |
36 |
change事件中还给我们暴露了许多有用的方法:
- getSlotValue(index):获取给定 slot 目前被选中的值
- setSlotValue(index, value):设定给定 slot 被选中的值,该值必须存在于该 slot 的备选值数组中
- getSlotValues(index):获取给定 slot 的备选值数组
- setSlotValues(index, values):设定给定 slot 的备选值数组
- getValues():获取所有 slot 目前被选中的值(分隔符 slot 除外)
- setValues(values):设定所有 slot 被选中的值(分隔符 slot 除外),该值必须存在于对应 slot 的备选值数组中
在本例中,我们可以打印上述函数的输出结果:
console.log(picker.getSlotValue(0));//获取被选中的省,单个slot console.table(picker.getSlotValues(0));//获取被选中的省份备选数组 console.table(picker.getValues());//获取被选中的省市县,全部slot,分隔符除外
setSlot相关方法就不再一一举例,你可以在本地控制台自行实验查看效果。
步骤五:实现省市县三级联动并输出地址和关联code
HTML代码:
<template> <div class="three-level-address" id="three_level_address"> <div class="region-div"> <span class="input-icon"><i class="iconfont icon-dizhi"></i></span> <input type="text" placeholder="请选择三级地址" v-model="region" maxlength="80" readonly="readonly" @click="showAddressPicker" /> <mt-popup v-model="regionVisible" position="bottom" class="region-popup"> <mt-picker :slots="myAddressSlots" valueKey="name" :visibleItemCount ="5" @change="addressChange" :itemHeight="40"></mt-picker> </mt-popup> <div class="data-show-div"> <p><span>三级地址:</span>{{region}}</p> <p><span>省:</span>{{province}}</p> <p><span>市:</span>{{city}}</p> <p><span>县:</span>{{county}}</p> <p><span>省级代码:</span>{{provinceCode}}</p> <p><span>市级代码:</span>{{cityCode}}</p> <p><span>县级代码:</span>{{countyCode}}</p> </div> </div> </div> </template>
JavaScript代码:
<script> import Vue from 'vue' import { Popup } from 'mint-ui'; Vue.component(Popup.name, Popup); import { Picker } from 'mint-ui'; Vue.component(Picker.name, Picker); //引入省市区数据json文件 import threeLevelAddress from '../assets/commom/json/threeLevelAddress.json' export default { data(){ return{ region:'',//三级地址 province:'',//省 city:'',//市 county:'',//县 provinceCode:'',//省级代码 cityCode:'', //市级代码 countyCode:'',//县级代码 regionVisible:false,//弹出框是否可见 regionInit:false,//禁止地区选择器自动初始化,picker组件会默认进行初始化,导致一进入页面就会默认选中一个初始3级地址 //picker组件插槽 myAddressSlots: [ //省 { flex: 1, values: this.getProvinceArr(), //省份数组 className: 'slot1', textAlign: 'center' }, //分隔符 { divider: true, content: '', className: 'slot2' }, //市 { flex: 1, values: this.getCityArr("北京市"), className: 'slot3', textAlign: 'center' }, { divider: true, content: '', className: 'slot4' }, //县 { flex: 1, values: this.getCountyArr("北京市","北京市"), className: 'slot5', textAlign: 'center' } ], } }, methods:{ //打开地址选择器 showAddressPicker(){ this.regionVisible = true; }, //picker组件的change事件,进行取值赋值 addressChange(picker, values){ console.log(picker); console.table(values); if (this.regionInit){ //取值并赋值 this.region = values[0]["name"] + values[1]["name"] + values[2]["name"]; this.province = values[0]["name"]; this.city = values[1]["name"]; this.county = values[2]["name"]; this.provinceCode = values[0]["code"]; this.cityCode = values[1]["code"]; this.countyCode = values[2]["code"]; console.log(picker.getSlotValue(0)); console.table(picker.getSlotValues(0)); console.table(picker.getValues()); //给市、县赋值 picker.setSlotValues(1, this.getCityArr(values[0]["name"])); picker.setSlotValues(2, this.getCountyArr(values[0]["name"], values[1]["name"])); }else { this.regionInit = true; } }, //遍历json,返回省级对象数组 getProvinceArr() { let provinceArr = []; threeLevelAddress.forEach(function (item) { let obj = {}; obj.name = item.name; obj.code = item.code; provinceArr.push(obj); }); return provinceArr; }, //遍历json,返回市级对象数组 getCityArr(province) { // console.log("省:" + province); let cityArr = []; threeLevelAddress.forEach(function (item) { if (item.name === province) { item.children.forEach(function (args) { let obj = {}; obj.name = args.name; obj.code = args.code; cityArr.push(obj); }); } }); return cityArr; }, //遍历json,返回县级对象数组 getCountyArr(province,city){ let countyArr = []; threeLevelAddress.forEach(function(item){ if (item.name === province){ item.children.forEach(function (args) { if (args.name === city){ args.children.forEach(function (param) { let obj = {}; obj.name=param.name; obj.code=param.code; countyArr.push(obj); }) } }); } }); // console.log(countyArr); return countyArr; }, }, mounted(){ //初始化设备高度为设备高度height 100% let orderHeight = window.innerHeight; document.getElementById("three_level_address").style.height = orderHeight + 'px'; } } </script>
CSS样式:
<style scoped> .three-level-address{ width: 100%; text-align: left; background: black; color: #ffffff; } .region-div{ width: 100%; padding-top: 1rem; } .input-icon{ display: inline-block; vertical-align: middle; } .input-icon i{ font-size: 2rem; } .region-div input{ width: 70%; font-size: 1rem; line-height: 2rem; border-radius: 5px; outline: none; text-align: right; color: black; } .region-popup{ width: 100%; } .data-show-div{ margin-top: 1rem; margin-left: 1rem; color: #45C473; } .data-show-div span{ color: #ffffff; font-size: 0.8rem; } </style>
threeLevelAddress.json部分结构
[ { "code": "110000", "name": "北京市", "children": [ { "code": "110100", "name": "北京市", "children": [ { "code": "110101", "name": "东城区" }, { "code": "110102", "name": "西城区" }, { "code": "110105", "name": "朝阳区" }, { "code": "110106", "name": "丰台区" }, { "code": "110107", "name": "石景山区" }, { "code": "110108", "name": "海淀区" }, { "code": "110109", "name": "门头沟区" }, { "code": "110111", "name": "房山区" }, { "code": "110112", "name": "通州区" }, { "code": "110113", "name": "顺义区" }, { "code": "110114", "name": "昌平区" }, { "code": "110115", "name": "大兴区" }, { "code": "110116", "name": "怀柔区" }, { "code": "110117", "name": "平谷区" }, { "code": "110128", "name": "密云县" }, { "code": "110129", "name": "延庆县" } ] } ] }, { "code": "120000", "name": "天津市", "children": [ { "code": "120100", "name": "天津市", "children": [ { "code": "120101", "name": "和平区" }, { "code": "120102", "name": "河东区" }, { "code": "120103", "name": "河西区" }, { "code": "120104", "name": "南开区" }, { "code": "120105", "name": "河北区" }, { "code": "120106", "name": "红桥区" }, { "code": "120110", "name": "东丽区" }, { "code": "120111", "name": "西青区" }, { "code": "120112", "name": "津南区" }, { "code": "120113", "name": "北辰区" }, { "code": "120114", "name": "武清区" }, { "code": "120115", "name": "宝坻区" }, { "code": "120116", "name": "滨海新区" }, { "code": "120121", "name": "宁河县" }, { "code": "120123", "name": "静海县" }, { "code": "120125", "name": "蓟县" } ] } ] } ]
中华人民共和国行政区划:省级(省份直辖市自治区)、 地级(城市)、 县级(区县)、 乡级(乡镇街道)、 村级(村委会居委会) ,中国省市区镇村三级四级五级联动地址数据可见GitHub:https://github.com/modood/Administrative-divisions-of-China
最后,上效果图:
如果你觉得这篇博客不错的话,记得收藏关注一波哦。我会不定时更新,分享自己学习的有趣的前端知识。
我的gitbook地址是https://legacy.gitbook.com/@lzweife,不定时更新学习笔记。
限于本人水平有限,难免出现纰漏错误,欢迎指正,相互学习。首次尝试写博客,语言不当不通顺之处,请先凑合看。
学海无涯,生命不息。
版权声明:本文为博主原创文章,转载请先征求博主同意并附上原文链接。 https://blog.csdn.net/lzw_1994/article/details/79981166