vue 实现仿京东、淘宝省市区三级联动
在做电商或其他项目的时候,物流我们肯定会集成进去,那么我们肯定需要集成地址管理,地址管理除了最基本的省、市、区外,我们还要单独添加一个详细地址字段,不然我们无法精确到详细地址,那么最后我们的详细地址就是省市区加上我们的详细地址字段,好了,我们先来看一下省市区三级联动的效果图
其实这个功能可以封装成一个组件呢,我这里面集成了VUX移动端组件,你用的时候html里面加入,代码本来是对齐规范的,但是贴上来就变形了
1、html代码
<
cell
is-link
title
=
"所属地区"
class
=
"require"
@click.native
=
"
choseAdd
()
"
value-align
=
"left"
>
<
p
style
=
"margin-left: 20px"
>
{{
receptAddtess
.
receiptProvince
===
''
?
"请选择"
:
receptAddtess
.
receiptProvince
}} {{
receptAddtess
.
receiptCity
}} {{
receptAddtess
.
receiptArea
}}
</
p
>
</
cell
>
<!-- 省市区三级联动 -->
<
section
class
=
"myAddress"
>
<!-- 居住地址三级联动选项 -->
<
section
class
=
"showChose"
v-show
=
"
showChose
"
>
<
section
class
=
"address"
>
<
section
class
=
"titleTop"
>
<
div
class
=
"titleTopLeft"
>
<
span
>
选择地址
</
span
>
</
div
>
<
div
class
=
"titleTopRight"
>
<
span
@click
=
"
closeAdd
()
"
>
×
</
span
>
</
div
>
</
section
>
<
hr
style
=
"height:1px;border:none;border-top:1px solid #EEEEEE;"
>
<
section
class
=
"title"
>
<
div
class
=
"area"
@click
=
"
provinceSelected
()
"
>
<
span
>
{{
Province
?
Province
:
info
[
province
-
1
].
name
}}
</
span
>
</
div
>
<
div
class
=
"area"
@click
=
"
citySelected
()
"
:class
=
"
City
?
''
:
'active'
"
>
{{
City
?
City
:
'请选择'
}}
</
div
>
<
div
class
=
"area"
@click
=
"
districtSelected
()
"
:class
=
"
District
?
''
:
'active'
"
v-show
=
"
City
"
>
{{
District
?
District
:
'请选择'
}}
</
div
>
</
section
>
<
hr
style
=
"height:1px;border:none;border-top:1px solid #EEEEEE;"
>
<
ul
>
<
li
class
=
"addList"
v-for
=
"
(
v
,
k
)
in
info
"
:key
=
"
(
v
,
k
).
index
"
@click
=
"
getProvinceId
(
v
.
id
,
v
.
name
,
k
)
"
v-show
=
"
showProvince
"
:class
=
"
v
.
selected
===
true
?
'active'
:
''
"
>
{{
v
.
name
}}
</
li
>
<
li
class
=
"addList"
v-for
=
"
(
v
,
k
)
in
showCityList
"
:key
=
"
(
v
,
k
).
index
"
@click
=
"
getCityId
(
v
.
id
,
v
.
name
,
k
)
"
v-show
=
"
showCity
"
:class
=
"
v
.
selected
?
'active'
:
''
"
>
{{
v
.
name
}}
</
li
>
<
li
class
=
"addList"
v-for
=
"
(
v
,
k
)
in
showDistrictList
"
:key
=
"
(
v
,
k
).
index
"
@click
=
"
getDistrictId
(
v
.
id
,
v
.
name
,
k
)
"
v-show
=
"
showDistrict
"
:class
=
"
v
.
selected
?
'active'
:
''
"
>
{{
v
.
name
}}
</
li
>
</
ul
>
</
section
>
</
section
>
</
section
>
2、js方法如下
choseAdd
() {
this
.
showChose
=
true
},
closeAdd
() {
this
.
showChose
=
false
},
_filter
(
add
,
name
,
code
) {
let
result
=
[]
for
(
let
i
=
0
;
i
<
add
.
length
;
i
++
) {
if
(
code
===
add
[
i
].
id
) {
result
=
add
[
i
][
name
]
}
}
return
result
},
getProvinceId
:
function
(
code
,
input
,
index
) {
this
.
province
=
code
this
.
Province
=
input
this
.
showProvince
=
false
this
.
showCity
=
true
this
.
showDistrict
=
false
this
.
showCityList
=
this
.
_filter
(
this
.
info
,
'city'
,
this
.
province
)
// 点击选择当前
this
.
info
.
map
(
a
=>
{
a
.
selected
=
false
})
this
.
info
[
index
].
selected
=
true
this
.
receptAddtess
.
receiptProvince
=
input
},
provinceSelected
:
function
() {
// 清除市级和区级列表
this
.
showCityList
=
false
this
.
showDistrictList
=
false
// 清除市级和区级选项
this
.
City
=
false
this
.
District
=
false
// 选项页面的切换
this
.
showProvince
=
true
this
.
showCity
=
false
this
.
showDistrict
=
false
},
getCityId
:
function
(
code
,
input
,
index
) {
this
.
city
=
code
this
.
City
=
input
this
.
showProvince
=
false
this
.
showCity
=
false
this
.
showDistrict
=
true
this
.
showDistrictList
=
this
.
_filter
(
this
.
showCityList
,
'district'
,
this
.
city
)
// 选择当前添加active
this
.
showCityList
.
map
(
a
=>
{
a
.
selected
=
false
})
this
.
showCityList
[
index
].
selected
=
true
this
.
receptAddtess
.
receiptCity
=
input
},
citySelected
:
function
() {
this
.
showProvince
=
false
this
.
showCity
=
true
this
.
showDistrict
=
false
},
getDistrictId
:
function
(
code
,
input
,
index
) {
this
.
district
=
code
this
.
District
=
input
// 选择当前添加active
this
.
showDistrictList
.
map
(
a
=>
{
a
.
selected
=
false
})
this
.
showDistrictList
[
index
].
selected
=
true
// 选取市区选项之后关闭弹层
this
.
showChose
=
false
this
.
receptAddtess
.
receiptArea
=
input
},
districtSelected
:
function
() {
this
.
showProvince
=
false
this
.
showCity
=
false
this
.
showDistrict
=
true
}
3、css代码如下
// 省市区三级联动CSS
.
myAddress
.
cont
{
border-bottom:
1px
solid
rgba
(245,245,245,0.8);
}
.
myAddress
.
cont
span
{
display:
inline-block
;
font-size:
0.28rem;
color:
#333;
line-height:
0.88rem;
margin-left:
0.32rem;
}
.
myAddress
.
cont
section
{
float:left
;
}
.
myAddress
.
cont
p
{
display:
inline-block
;
font-size:
0.28rem;
color:
#333333;
line-height:
0.88rem;
margin-left:
1rem;
}
.
myAddress
.
cont
.
pic
2{
float:
right
;
width:
0.14rem;
height:
0.24rem;
margin:
0.32rem 0.32rem 0.32rem 0;
}
.
myAddress
.
cont
p
.
text
{
margin-left:
0.72rem;
}
.
showChose
{
width:
100%;
height:
100%;
position:fixed
;
top:
0;
left:
0;
z-index:
120;
background:rgba
(77,82,113,0.8);
}
.
address
{
.
titleTop
{
display
inline
text-align
center
.
titleTopLeft
{
padding-top
5px
padding-bottom
5px
span
{
font-size
18px
}
}
.
titleTopRight
{
span
{
position
absolute
right
10px
top
-5px
font-size
30px
color:
#929292
}
}
}
position:absolute
;
bottom:
0;
left:
0;
z-index:
121;
background:
#
fff
;
width:
100%;
height:
430px
}
.
title
{
padding-top
4px
padding-bottom
5px
}
.
title
h
4{
display:inline-block
;
font-size:
18px;
font-weight:normal
;
}
.
area
{
display:inline-block
;
font-size:
15px;
margin-left:
25px;
}
.
addList
{
width:
85%;
padding-left:
0.32rem;
font-size:
15px;
line-height:
32px;
}
/* 修改的格式 */
.
address
ul
{
width:
95%;
height:
78%;
overflow:auto
;
}
.
address
ul
li
{
margin-left:
20px;
}
.
address
.
title
.
active
{
color:red
;
border-bottom:
0.02rem
solid
red
;
}
.
address
ul
.
active
{
color:red
;
}
完成的一个界面如下,我这里面还调了接口,但是利用上面的代码就可以集成了,我的界面完成代码如下
<
template
>
<
div
class
=
"modifyAddress"
>
<
v-header
title
=
"添加地址"
ref
=
"header"
>
<
div
slot
=
"right"
class
=
"headerRight"
style
=
"top: 2px; right: 5px;"
@click
=
"
handleSaveInfo
"
>
<
span
>
保存
</
span
>
</
div
>
</
v-header
>
<
div
class
=
"inputWrapper"
>
<
x-input
placeholder
=
"请输入收货人姓名"
v-model
=
"
receptAddtess
.
receiptName
"
>
<
p
slot
=
"label"
style
=
"margin-right:20px"
>
<
font
color
=
"red"
>
*
</
font
>
昵称
</
p
>
</
x-input
>
<
x-input
:max
=
"
13
"
is-type
=
"china-mobile"
placeholder
=
"请输入手机号码"
v-model
=
"
receptAddtess
.
receiptPhone
"
>
<
p
slot
=
"label"
style
=
"margin-right:20px"
>
<
font
color
=
"red"
>
*
</
font
>
手机号码
</
p
>
</
x-input
>
<
cell
is-link
title
=
"所属地区"
class
=
"require"
@click.native
=
"
choseAdd
()
"
value-align
=
"left"
>
<
p
style
=
"margin-left: 20px"
>
{{
receptAddtess
.
receiptProvince
===
''
?
"请选择"
:
receptAddtess
.
receiptProvince
}} {{
receptAddtess
.
receiptCity
}} {{
receptAddtess
.
receiptArea
}}
</
p
>
</
cell
>
<!-- 省市区三级联动 -->
<
section
class
=
"myAddress"
>
<!-- 居住地址三级联动选项 -->
<
section
class
=
"showChose"
v-show
=
"
showChose
"
>
<
section
class
=
"address"
>
<
section
class
=
"titleTop"
>
<
div
class
=
"titleTopLeft"
>
<
span
>
选择地址
</
span
>
</
div
>
<
div
class
=
"titleTopRight"
>
<
span
@click
=
"
closeAdd
()
"
>
×
</
span
>
</
div
>
</
section
>
<
hr
style
=
"height:1px;border:none;border-top:1px solid #EEEEEE;"
>
<
section
class
=
"title"
>
<
div
class
=
"area"
@click
=
"
provinceSelected
()
"
>
<
span
>
{{
Province
?
Province
:
info
[
province
-
1
].
name
}}
</
span
>
</
div
>
<
div
class
=
"area"
@click
=
"
citySelected
()
"
:class
=
"
City
?
''
:
'active'
"
>
{{
City
?
City
:
'请选择'
}}
</
div
>
<
div
class
=
"area"
@click
=
"
districtSelected
()
"
:class
=
"
District
?
''
:
'active'
"
v-show
=
"
City
"
>
{{
District
?
District
:
'请选择'
}}
</
div
>
</
section
>
<
hr
style
=
"height:1px;border:none;border-top:1px solid #EEEEEE;"
>
<
ul
>
<
li
class
=
"addList"
v-for
=
"
(
v
,
k
)
in
info
"
:key
=
"
(
v
,
k
).
index
"
@click
=
"
getProvinceId
(
v
.
id
,
v
.
name
,
k
)
"
v-show
=
"
showProvince
"
:class
=
"
v
.
selected
===
true
?
'active'
:
''
"
>
{{
v
.
name
}}
</
li
>
<
li
class
=
"addList"
v-for
=
"
(
v
,
k
)
in
showCityList
"
:key
=
"
(
v
,
k
).
index
"
@click
=
"
getCityId
(
v
.
id
,
v
.
name
,
k
)
"
v-show
=
"
showCity
"
:class
=
"
v
.
selected
?
'active'
:
''
"
>
{{
v
.
name
}}
</
li
>
<
li
class
=
"addList"
v-for
=
"
(
v
,
k
)
in
showDistrictList
"
:key
=
"
(
v
,
k
).
index
"
@click
=
"
getDistrictId
(
v
.
id
,
v
.
name
,
k
)
"
v-show
=
"
showDistrict
"
:class
=
"
v
.
selected
?
'active'
:
''
"
>
{{
v
.
name
}}
</
li
>
</
ul
>
</
section
>
</
section
>
</
section
>
<
x-textarea
placeholder
=
"街道、楼牌号等"
:show-counter
=
"
false
"
:rows
=
"
1
"
autosize
v-model
=
"
receptAddtess
.
detailAddress
"
>
<
p
slot
=
"label"
style
=
"margin-right:20px"
>
<
font
color
=
"red"
>
*
</
font
>
详细地址
</
p
>
</
x-textarea
>
</
div
>
<!-- 是否默认地址 -->
<
div
class
=
"recommededWrapper"
>
<
x-switch
title
=
"设为默认地址"
:value-map
=
"
[
0
,
1
]
"
v-model
=
"
receptAddtess
.
defaultAddress
"
@on-change
=
"
ifDefault
"
></
x-switch
>
</
div
>
</
div
>
</
template
>
<
script
>
import
VHeader
from
'@/components/v-header/v-header'
import
{
XInput
,
Group
,
Cell
,
XTextarea
,
XSwitch
}
from
'vux'
import
axios
from
'@/api/axiosApi'
import
info
from
'@/assets/json/address.json'
import
{
localUser
}
from
'@/assets/js/local'
export
default
{
data
() {
return
{
pageTitle:
'居住地址'
,
address:
''
,
showChose:
false
,
showProvince:
true
,
showCity:
false
,
showDistrict:
false
,
showCityList:
false
,
showDistrictList:
false
,
province:
1
,
city:
3
,
district:
57
,
GetProvinceId:
2
,
District:
false
,
Province:
false
,
City:
false
,
// areaProvince: '',
// areaCity: '',
// areaDistrict: '',
// v-for循环判断是否为当前
selected:
false
,
info
,
receptAddtess:
{
userId:
''
,
receiptName:
''
,
receiptPhone:
''
,
detailAddress:
''
,
defaultAddress:
0
,
receiptProvince:
''
,
receiptCity:
''
,
receiptArea:
''
}
}
},
mounted
() {
},
created
() {
},
methods:
{
handleSaveInfo
() {
if
(
this
.
receptAddtess
.
receiptName
===
''
) {
this
.
$vux
.
toast
.
text
(
'收货人不能为空'
,
'middle'
)
return
}
if
(
this
.
receptAddtess
.
receiptPhone
===
''
) {
this
.
$vux
.
toast
.
text
(
'电话号码不能为空'
,
'middle'
)
return
}
if
(
this
.
receptAddtess
.
receiptProvince
===
''
) {
this
.
$vux
.
toast
.
text
(
'所属地区不能为空'
,
'middle'
)
return
}
if
(
this
.
receptAddtess
.
detailAddress
===
''
) {
this
.
$vux
.
toast
.
text
(
'详细地址不能为空'
,
'middle'
)
return
}
this
.
handleAddReceptAddress
()
},
ifDefault
(
value
) {
this
.
receptAddtess
.
defaultAddress
=
value
},
// 添加收货地址,每个用户不超过20个地址
handleAddReceptAddress
() {
this
.
receptAddtess
.
userId
=
localUser
.
get
(
'id'
)
axios
.
post
(
this
,
'/v1/receiptAddress'
,
this
.
receptAddtess
, (
data
)
=>
{
this
.
$vux
.
confirm
.
show
({
title:
'温馨提示'
,
content:
'添加地址成功'
})
})
},
choseAdd
() {
this
.
showChose
=
true
},
closeAdd
() {
this
.
showChose
=
false
},
_filter
(
add
,
name
,
code
) {
let
result
=
[]
for
(
let
i
=
0
;
i
<
add
.
length
;
i
++
) {
if
(
code
===
add
[
i
].
id
) {
result
=
add
[
i
][
name
]
}
}
return
result
},
getProvinceId
:
function
(
code
,
input
,
index
) {
this
.
province
=
code
this
.
Province
=
input
this
.
showProvince
=
false
this
.
showCity
=
true
this
.
showDistrict
=
false
this
.
showCityList
=
this
.
_filter
(
this
.
info
,
'city'
,
this
.
province
)
// 点击选择当前
this
.
info
.
map
(
a
=>
{
a
.
selected
=
false
})
this
.
info
[
index
].
selected
=
true
this
.
receptAddtess
.
receiptProvince
=
input
},
provinceSelected
:
function
() {
// 清除市级和区级列表
this
.
showCityList
=
false
this
.
showDistrictList
=
false
// 清除市级和区级选项
this
.
City
=
false
this
.
District
=
false
// 选项页面的切换
this
.
showProvince
=
true
this
.
showCity
=
false
this
.
showDistrict
=
false
},
getCityId
:
function
(
code
,
input
,
index
) {
this
.
city
=
code
this
.
City
=
input
this
.
showProvince
=
false
this
.
showCity
=
false
this
.
showDistrict
=
true
this
.
showDistrictList
=
this
.
_filter
(
this
.
showCityList
,
'district'
,
this
.
city
)
// 选择当前添加active
this
.
showCityList
.
map
(
a
=>
{
a
.
selected
=
false
})
this
.
showCityList
[
index
].
selected
=
true
this
.
receptAddtess
.
receiptCity
=
input
},
citySelected
:
function
() {
this
.
showProvince
=
false
this
.
showCity
=
true
this
.
showDistrict
=
false
},
getDistrictId
:
function
(
code
,
input
,
index
) {
this
.
district
=
code
this
.
District
=
input
// 选择当前添加active
this
.
showDistrictList
.
map
(
a
=>
{
a
.
selected
=
false
})
this
.
showDistrictList
[
index
].
selected
=
true
// 选取市区选项之后关闭弹层
this
.
showChose
=
false
this
.
receptAddtess
.
receiptArea
=
input
},
districtSelected
:
function
() {
this
.
showProvince
=
false
this
.
showCity
=
false
this
.
showDistrict
=
true
}
},
components:
{
VHeader
,
XInput
,
Group
,
Cell
,
XTextarea
,
axios
,
XSwitch
,
localUser
}
}
</
script
>
<
style
lang
=
"stylus"
scoped
>
@
import
'~@/assets/css/variable.styl'
.
modifyAddress
{
position:
fixed
;
top:
0;
left:
0;
right:
0;
bottom:
0;
background-color:
$
color-background
;
z-index:
101;
height:
100%;
}
.
inputWrapper
{
background-color
white
}
// 省市区三级联动CSS
.
myAddress
.
cont
{
border-bottom:
1px
solid
rgba
(245,245,245,0.8);
}
.
myAddress
.
cont
span
{
display:
inline-block
;
font-size:
0.28rem;
color:
#333;
line-height:
0.88rem;
margin-left:
0.32rem;
}
.
myAddress
.
cont
section
{
float:left
;
}
.
myAddress
.
cont
p
{
display:
inline-block
;
font-size:
0.28rem;
color:
#333333;
line-height:
0.88rem;
margin-left:
1rem;
}
.
myAddress
.
cont
.
pic
2{
float:
right
;
width:
0.14rem;
height:
0.24rem;
margin:
0.32rem 0.32rem 0.32rem 0;
}
.
myAddress
.
cont
p
.
text
{
margin-left:
0.72rem;
}
.
showChose
{
width:
100%;
height:
100%;
position:fixed
;
top:
0;
left:
0;
z-index:
120;
background:rgba
(77,82,113,0.8);
}
.
address
{
.
titleTop
{
display
inline
text-align
center
.
titleTopLeft
{
padding-top
5px
padding-bottom
5px
span
{
font-size
18px
}
}
.
titleTopRight
{
span
{
position
absolute
right
10px
top
-5px
font-size
30px
color:
#929292
}
}
}
position:absolute
;
bottom:
0;
left:
0;
z-index:
121;
background:
#
fff
;
width:
100%;
height:
430px
}
.
title
{
padding-top
4px
padding-bottom
5px
}
.
title
h
4{
display:inline-block
;
font-size:
18px;
font-weight:normal
;
}
.
area
{
display:inline-block
;
font-size:
15px;
margin-left:
25px;
}
.
addList
{
width:
85%;
padding-left:
0.32rem;
font-size:
15px;
line-height:
32px;
}
/* 修改的格式 */
.
address
ul
{
width:
95%;
height:
78%;
overflow:auto
;
}
.
address
ul
li
{
margin-left:
20px;
}
.
address
.
title
.
active
{
color:red
;
border-bottom:
0.02rem
solid
red
;
}
.
address
ul
.
active
{
color:red
;
}
.
recommededWrapper
{
margin-top
10px
background-color
white
}
</
style
>
<
style
>
.modifyAddress .require .vux-label:before
{
content
:
'*'
;
display
:
inline-block
;
color
:
red
;
}
</
style
>
哈哈,效果就实现了