持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
从零开始使用Vue.js实现中国三级城市查询
一 简介
项目需求是实现一个展示和查询中国三级城市(省->市->县(区))的功能。
主要界面如下:
二 开发环境部署
2.1 建立vue.js工程项目
我们这里采用HbuilderX开发工具,来直接创建一个Vue.js 2.x的项目,省去安装node.js及npm下载的过程。
菜单栏:文件->新建->项目,然后选择普通项目中的vue项目(2.6.10),并给项目起一个名字:“myApp”,然后点击创建,等待HbuilderX下载相关依赖。
项目构建完成后,项目目录如下图,其中src是源码目录,也就是我们的主要开发工作都在此目录里进行。
2.2 创建页面及组件
2.2.1 创建主页面
在src目录下,创建pages目录,用于存放vue页面,并在其目录下,创建首页index、省级页province和市级页city,最后我们再创建一个找不到页面时转向的404页面。
2.2.2 创建组件页面
在src目录下的components目录中,创建Header顶部组件和Serach搜索组件。
2.3 安装Vue工具及插件
2.3.1 Axios - ajax工具
Axios是vue官方推荐的一个基于promise的ajax库,通过它我们来与数据接口进行请求,获得响应数据。我们利用Hbuilder自带的终端及npm工具来进行下载:
-
Axios 安装
选择项目目录,右键弹出菜单中,选择使用命令行窗口打开所在目录。或者直接使用快捷键“ALT+C”,来打开底部终端,其本质是调用了windows的PowerShell工具。
在终端里,我们执行npm安装Axios命令:
npm i axios
0. Axios 配置
在src目录下,创建axios目录,并创建index.js。
```
// index.js
import axios from 'axios'
// 定义一个全局的路径
axios.defaults.baseURL = 'https://www.mxnzp.com/api/'; // api接口地址
// 定义了一个全局请求参数 app_id及app_secret 自行到https://www.mxnzp.com申请
axios.defaults.params = {
app_id: 'xx',
app_secret: 'xxxx',
}
export default axios
```
在src目录下的main.js文件中,加入代码:
```
import Vue from 'vue'
import App from './App.vue'
import axios from '@/axios'
// vue 原型链增加axios 在项目中可用this.axios
Vue.prototype.axios = axios
new Vue({
render: h => h(App),
}).$mount('#app')
```
2.3.2 Vue-Router - 路由
-
Vue-Router 安装
在终端里,我们执行npm安装vue-router 3.x命令。
npm i vue-router@3 -
Vue-Router 配置
在src目录下,创建router目录,并创建index.js,这里我们定义了4个路由规则,并指向了pages目录下的vue页面,
// index.js import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // 解决 vue 重复点击一个路由报错的问题 const originalPush = VueRouter.prototype.push VueRouter.prototype.push = function push(location) { return originalPush.call(this, location).catch(err => err) } // 定义路由规则 const routes = [{ name: 'index', path: '/', component: () => import("@/pages/index") }, { name: 'province', path: '/province', component: () => import("@/pages/province") }, { name: 'city', path: '/city', component: () => import("@/pages/city") }, { name: '404', path: '*', component: () => import("@/pages/404") } ] // 实例化vuerouter const router = new VueRouter({ routes }) export default router 在src目录下的main.js文件中,加入代码:
import Vue from 'vue' import App from './App.vue' import axios from '@/axios' // 导入router import router from '@/router' Vue.prototype.axios = axios Vue.config.productionTip = false new Vue({ router, // 使用router render: h => h(App), }).$mount('#app')
2.3.3 ElementUI
-
ElementUI安装
npm i element-ui -
在main.js 导入 ElementUI
import Vue from 'vue' import App from './App.vue' import axios from '@/axios' import ElementUI from 'element-ui' Vue.use(ElementUI) import 'element-ui/lib/theme-chalk/index.css'; import 'element-ui/lib/theme-chalk/display.css'; import router from '@/router' Vue.prototype.axios = axios Vue.config.productionTip = false new Vue({ router, render: h => h(App), }).$mount('#app')
三 数据接口
3.1 全国城市列表
- 接口地址: www.mxnzp.com/api/address…
- 返回格式: JSON
- 请求方式: GET
- 请求示例: www.mxnzp.com/api/address…
- 接口备注: 获取全国城市列表信息
请求参数说明:
| 名称 | 类型 | 说明 |
|---|---|---|
| 无 | 无 | 无 |
返回参数说明:
| 名称 | 类型 | 说明 |
|---|---|---|
| code | 整形 | 省/市/区编号 |
| name | 字符串 | 省/市/区名称 |
| pchilds | Array | 市列表 |
| cchilds | Array | 区列表 |
返回示例:
{
"code":1,
"msg":"数据返回成功",
"data":[
{
"code":"130000",
"name":"河北省",
"pchilds":[
{
"code":"130100",
"name":"石家庄市",
"cchilds":[
{
"code":"130101",
"name":"市辖区"
},
{
"code":"130102",
"name":"长安区"
},
...这里只显示了两个区...
]
},
{
"code":"130200",
"name":"唐山市",
"cchilds":[
{
"code":"130201",
"name":"市辖区"
},
{
"code":"130202",
"name":"路南区"
},
...这里只显示了两个区...
]
},
...这里只显示了两个市...
]
}
...这里只显示了一个省...
]
}
3.2 搜索全国城市列表
- 接口地址: www.mxnzp.com/api/address…
- 返回格式: JSON
- 请求方式: GET
- 请求示例: www.mxnzp.com/api/address…深圳&app_id=nuetvmiuspttrsgo&app_secret=d2F1cGl4L2F3ZVhJdzRQaExHNlVEdz09
- 接口备注: 搜索全国城市列表信息
请求参数说明:
| 名称 | 类型 | 说明 |
|---|---|---|
| type | 整形 | 类型 0-查询省份 1-查询城市 |
| value | 字符串 | 被查询的省份或者城市名称 |
返回参数说明:
| 名称 | 类型 | 说明 |
|---|---|---|
| code | 整形 | 省/市/区编号 |
| name | 字符串 | 省/市/区名称 |
| pchilds | Array | 市列表 |
| cchilds | Array | 区列表 |
返回示例:
{
"code": 1,
"msg": "数据返回成功",
"data": [
{
"code": "440000",
"name": "广东省",
"pchilds": [
{
"code": "440300",
"name": "深圳市",
"cchilds": [
{
"code": "440301",
"name": "市辖区"
},
{
"code": "440303",
"name": "罗湖区"
},
...这里只显示了两个区...
]
}
]
}
]
}
四 代码实现
4.1 主要页面
4.1.1 首页 index.vue
<template>
<div>
<el-row>
<el-col :xs="12" :sm="6" :md="6" :lg="6" :xl="6" :gutter="20" v-for="item,index in provinceList">
<div class="grid-content" @click="goProvince(item.name)">{{ item.name }}</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
provinceList: []
}
},
created() {
this.getList()
},
methods: {
getList() {
this.axios.get('address/list').then((res) => {
this.provinceList = res.data.data
// console.log(res);
})
},
goProvince(provinceName) {
this.$router.push('/province?value=' + provinceName)
}
},
}
</script>
<style>
</style>
4.1.2 省级页 privince.vue
<template>
<div>
<el-row>
<div class="title">
{{provinceName}}
</div>
<el-col :xs="12" :sm="6" :md="6" :lg="6" :xl="6" :gutter="20" v-for="item,index in cityList">
<div class="grid-content" @click="goCity(item.name)">{{ item.name }}</div>
</el-col>
</el-row>
</div>
</template>
<script>
/**
* 使用test方法实现模糊查询
* @param {Array} list 原数组
* @param {String} keyWord 查询的关键词
* @return {Array} 查询的结果
*/
function fuzzyQuery(list, keyWord) {
var reg = new RegExp(keyWord);
var arr = [];
for (var i = 0; i < list.length; i++) {
if (reg.test(list[i])) {
arr.push(list[i]);
}
}
if (arr.length > 0) {
return true
} else {
return false
}
}
export default {
data() {
return {
provinceName:'',
cityList: [],
// 直辖市或特别自治区: 只有直辖市/地区->县区 两级的地方
specialProvinces: ['北京市', '天津市', '重庆市', '上海市', '中国台湾', '中国香港', '澳门']
}
},
created() {
this.getList()
},
watch: {
// 观察$route发生变化时,重新加载dom
'$route.query.value'(to, from) {
this.getList()
}
},
methods: {
getList() {
this.axios.get('address/search', {
params: {
type: 1,
value: this.$route.query.value
}
}).then((res) => {
console.log(res);
// 无省份信息时,跳转404
if (res.data.data.length === 0) {
this.$router.replace('/404')
}
this.provinceName = res.data.data[0].name
// 判断特殊地区 处理
fuzzyQuery(this.specialProvinces, this.$route.query.value) ?
this.cityList = res.data.data[0].pchilds[0].cchilds :
this.cityList = res.data.data[0].pchilds
})
},
goCity(cityName) {
// 判断特殊地区 处理
fuzzyQuery(this.specialProvinces, this.$route.query.value) ?
this.$message.error('直辖市或特别自治区,没有往下一级了') :
this.$router.push('/city?value=' + cityName)
}
},
}
</script>
<style>
.title {
background: #E6A23C;
text-align: center;
line-height: 36px;
margin: 10px auto;
width: 200px;
padding: 10px;
border-radius: 4px;
min-height: 36px;
color: white;
}
</style>
4.1.3 市级页 city.vue
<template>
<div>
<div class="title">
当前市:{{this.$route.query.value}}
</div>
<el-row>
<el-col :xs="12" :sm="6" :md="6" :lg="6" :xl="6" :gutter="20" v-for="item,index in countyList">
<div class="grid-content" @click="giveNotice()">{{ item.name }}</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
countyList: []
}
},
created() {
this.getList()
},
methods: {
getList() {
this.axios.get('address/search',{
params:{
type:1,
value:this.$route.query.value
}
}).then((res) => {
this.countyList = res.data.data[0].pchilds[0].cchilds
this.countyList.shift()
console.log(this.countyList);
})
},
giveNotice(){
this.$message.error('没有往下一级了')
}
},
}
</script>
<style>
.title {
background: #E6A23C;
text-align: center;
line-height: 36px;
margin: 10px auto;
width: 200px;
padding: 10px;
border-radius: 4px;
min-height: 36px;
color: white;
}
</style>
4.1.4 404页 404.vue
<template>
<div>
<router-link to="/">
<img src="../assets/images/404.png" width="100%" alt="">
</router-link>
</div>
</template>
<script>
</script>
<style>
</style>
4.2 子组件页面
4.2.1 顶部组件 Header.vue
<template>
<el-row>
<div class="header">
<el-col :xs="{span:24,offset:0}" :sm="{span:16,offset:4}" :md="{span:12,offset:6}">
<el-row style="margin-bottom: 0;">
<el-col :span="8">
<div v-if="$route.name !== 'index'" class="back"><i class="el-icon-arrow-left"
@click="$router.back()"></i></div>
<div v-if="$route.name == 'index'"> </div>
</el-col>
<el-col :span="8" style="text-align: center; over">
<router-link to="/" class="webtitle">
中国城市查询
</router-link>
</el-col>
<el-col :span="8">
<search-com class="search-com"></search-com>
</el-col>
</el-row>
</el-col>
</div>
</el-row>
</template>
<script>
export default {
components: {
'search-com': () => import('@/components/Search')
},
}
</script>
<style scoped>
.header {
width: 100%;
position: fixed;
top: 0;
background: rgb(41, 175, 276);
height: 70px;
line-height: 70px;
color: white;
z-index: 9;
}
.webtitle {
font-size: 1.5rem;
color: white;
text-decoration: none;
}
.back {
font-size: 1.5rem;
cursor: pointer;
margin-left: 10px;
}
.search-com {
float: right;
margin-right: 10px;
}
@media screen and (max-width: 600px) {
.header {
height: 50px;
line-height: 50px;
}
.webtitle {
font-size: 0.8rem;
}
.back {
font-size: 1.2rem;
}
}
</style>
4.2.2 搜索组件 Serach.vue
<template>
<div>
<el-input @keydown.native.enter="goProvince(provinceName)" size="mini" prefix-icon="el-icon-search"
v-model.trim="provinceName" @focus="focusIt" @blur="focusIt" placeholder="请输入省份名称,可模糊查询"
:style="isFocus?'width:100%':'width:5rem'">
</el-input>
</div>
</template>
<script>
/**
* 使用test方法实现模糊查询
* @param {Array} list 原数组
* @param {String} keyWord 查询的关键词
* @return {Array} 查询的结果
*/
function fuzzyQuery(list, keyWord) {
var reg = new RegExp(keyWord);
var arr = [];
for (var i = 0; i < list.length; i++) {
if (reg.test(list[i].name)) {
arr.push(list[i]);
}
}
if (arr.length > 0) {
return true
} else {
return false
}
}
export default {
data() {
return {
provinceName: '',
isFocus: false
}
},
methods: {
goProvince(provinceName) {
if (!provinceName.length) {
return this.$message.error('请输入省份名称')
}
if (provinceName.length<2) {
return this.$message.error('请输入两个字以上')
}
this.axios.get('address/list').then((res) => {
this.provinceList = res.data.data
let find = fuzzyQuery(this.provinceList, provinceName)
if (find) {
this.$router.push('/province?value=' + provinceName)
} else {
this.$message.error('找不到您所输入的省份')
}
})
},
focusIt() {
this.isFocus = !this.isFocus
}
},
}
</script>
<style scoped>
@media screen and (max-width: 600px) {
.el-input {
width: 140px;
}
}
</style>
4.3 其他资源
4.3.1 main.css
* {
padding: 0;
margin: 0;
}
#app {
height: 100%;
}
.content {
margin-top: 70px;
}
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
.el-col {
border-radius: 4px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
background: #909399;
text-align: center;
line-height: 36px;
margin: 10px;
padding: 10px;
border-radius: 4px;
min-height: 36px;
color: white;
}
.grid-content:hover {
background: #E6A23C;
text-align: center;
line-height: 36px;
margin: 10px;
padding: 10px;
border-radius: 4px;
min-height: 36px;
color: white;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
4.3.2 404图
五、项目发布:
我们在菜单栏运行->运行到终端-> npm run build 打包生成html代码,打包完成后,会在项目的dist目录中,将该目录下的所有文件及文件夹上传到服务器,即可运行。
预览地址:area.0315e.com/#/