Browse Source

feat(地址选择器): 增强地址选择器功能,支持多选和选择整个城市

- 新增多选模式,可选择多个城市或区县
- 添加"选择整个城市/省份"功能
- 优化地址选择确认逻辑,支持多种选择模式
- 更新样式以区分选中状态
- 添加确认按钮用于多选模式
- 确保与父组件的数据格式一致性
master
前端-胡立永 1 day ago
parent
commit
85a96de3c0
11 changed files with 269 additions and 43 deletions
  1. +5
    -0
      api/model/config.js
  2. +216
    -26
      components/AddressPicker.vue
  3. +1
    -1
      components/base/tabbar.vue
  4. +10
    -3
      components/config/configPopup.vue
  5. +5
    -2
      components/screen/screenWork.vue
  6. +2
    -2
      pages/index/center.vue
  7. +9
    -1
      pages/index/index.vue
  8. +1
    -1
      pages_order/auth/wxLogin.vue
  9. +3
    -2
      pages_order/work/addResume.vue
  10. +3
    -1
      pages_order/work/postConsult.vue
  11. +14
    -4
      store/store.js

+ 5
- 0
api/model/config.js View File

@ -7,6 +7,11 @@ const api = {
url: '/employ/config/queryConfigList',
method: 'GET',
},
// 查看配置信息
queryConfig: {
url: '/employ/config/queryConfig',
method: 'GET',
},
}
export default api

+ 216
- 26
components/AddressPicker.vue View File

@ -29,12 +29,33 @@
<scroll-view scroll-y class="scroll-view">
<!-- 二级地址 -->
<template v-if="selectedProvince && !selectedCity">
<!-- 选择整个省份选项 -->
<view
v-if="showSelectWholeCity"
class="address-item whole-city-item"
@click="selectWholeProvince">
<uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
选择整个{{ selectedProvince.adress }}
</view>
<view
class="address-item"
:class="{
'selected': multiple && isCitySelected(item),
'active': !multiple && selectedCity && selectedCity.id === item.id
}"
v-for="item in cityList"
:key="item.id"
@click="selectCity(item)">
{{ item.adress }}
<uv-icon v-if="multiple && isCitySelected(item)"
name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
</view>
<!-- 多选时的确认按钮 -->
<view v-if="multiple && selectedCities.length > 0"
class="confirm-btn" @click="confirmMultipleCitySelection">
<button class="confirm-button">确认选择城市 ({{ selectedCities.length }})</button>
</view>
</template>
@ -46,12 +67,34 @@
<uv-icon name="arrow-left" size="30rpx"></uv-icon>
返回{{ selectedProvince.adress }}
</view>
<!-- 选择整个城市选项 -->
<view
v-if="showSelectWholeCity"
class="address-item whole-city-item"
@click="selectWholeCity">
<uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
选择整个{{ selectedCity.adress }}
</view>
<view
class="address-item"
:class="{
'selected': multiple && isDistrictSelected(item),
'active': !multiple && selectedDistrict && selectedDistrict.id === item.id
}"
v-for="item in districtList"
:key="item.id"
@click="selectDistrict(item)">
{{ item.adress }}
<uv-icon v-if="multiple && isDistrictSelected(item)"
name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
</view>
<!-- 多选时的确认按钮 -->
<view v-if="multiple && selectedDistricts.length > 0"
class="confirm-btn" @click="confirmMultipleSelection">
<button class="confirm-button">确认选择 ({{ selectedDistricts.length }})</button>
</view>
</template>
</scroll-view>
@ -71,6 +114,16 @@
onlyCity: {
type: Boolean,
default: false
},
//
multiple: {
type: Boolean,
default: false
},
// ""
showSelectWholeCity: {
type: Boolean,
default: false
}
},
data() {
@ -78,6 +131,8 @@
selectedProvince: null, //
selectedCity: null, //
selectedDistrict: null, //
selectedCities: [], //
selectedDistricts: [], //
cityList: [], //
districtList: [], //
}
@ -101,6 +156,8 @@
this.selectedProvince = null
this.selectedCity = null
this.selectedDistrict = null
this.selectedCities = []
this.selectedDistricts = []
this.cityList = []
this.districtList = []
},
@ -129,33 +186,96 @@
//
async selectCity(city) {
this.selectedCity = city
this.selectedDistrict = null
//
if (this.onlyCity) {
this.confirm()
return
}
//
try {
this.districtList = await this.$store.dispatch('getChildAddressList', city.id)
//
if (this.districtList.length === 0) {
if (this.multiple) {
//
const index = this.selectedCities.findIndex(item => item.id === city.id)
if (index > -1) {
//
this.selectedCities.splice(index, 1)
} else {
//
this.selectedCities.push(city)
}
} else {
//
this.selectedCity = city
this.selectedDistrict = null
//
if (this.onlyCity) {
this.confirm()
return
}
//
try {
this.districtList = await this.$store.dispatch('getChildAddressList', city.id)
//
if (this.districtList.length === 0) {
this.confirm()
}
} catch (error) {
console.error('获取区县列表失败:', error)
this.districtList = []
//
this.confirm()
}
} catch (error) {
console.error('获取区县列表失败:', error)
this.districtList = []
//
this.confirm()
}
},
//
selectDistrict(district) {
this.selectedDistrict = district
if (this.multiple) {
//
const index = this.selectedDistricts.findIndex(item => item.id === district.id)
if (index > -1) {
//
this.selectedDistricts.splice(index, 1)
} else {
//
this.selectedDistricts.push(district)
}
} else {
//
this.selectedDistrict = district
this.confirm()
}
},
//
isCitySelected(city) {
return this.selectedCities.some(item => item.id === city.id)
},
//
isDistrictSelected(district) {
return this.selectedDistricts.some(item => item.id === district.id)
},
//
selectWholeProvince() {
this.selectedCity = null
this.selectedCities = []
this.selectedDistrict = null
this.selectedDistricts = []
this.confirm()
},
//
selectWholeCity() {
this.selectedDistrict = null
this.selectedDistricts = []
this.confirm()
},
//
confirmMultipleCitySelection() {
//
this.confirm()
},
//
confirmMultipleSelection() {
this.confirm()
},
@ -171,7 +291,9 @@
const result = {
province: this.selectedProvince,
city: this.selectedCity,
district: this.selectedDistrict
district: this.selectedDistrict,
cities: this.selectedCities,
districts: this.selectedDistricts
}
//
@ -179,15 +301,36 @@
if (this.selectedProvince) {
fullAddress += this.selectedProvince.adress
}
if (this.selectedCity) {
//
if (this.multiple && this.selectedCities.length > 0) {
const cityNames = this.selectedCities.map(item => item.adress).join(',')
fullAddress += cityNames
result.selectedAddress = this.selectedProvince //
result.selectedCities = this.selectedCities
} else if (this.selectedCity) {
fullAddress += this.selectedCity.adress
}
if (this.selectedDistrict) {
fullAddress += this.selectedDistrict.adress
//
if (this.multiple && this.selectedDistricts.length > 0) {
const districtNames = this.selectedDistricts.map(item => item.adress).join(',')
fullAddress += districtNames
result.selectedAddress = this.selectedCity //
result.selectedDistricts = this.selectedDistricts
} else if (this.selectedDistrict) {
//
fullAddress += this.selectedDistrict.adress
result.selectedAddress = this.selectedDistrict
} else {
//
result.selectedAddress = this.selectedCity
}
} else {
//
result.selectedAddress = this.selectedProvince
}
result.fullAddress = fullAddress
result.selectedAddress = this.selectedDistrict || this.selectedCity || this.selectedProvince
this.$emit('confirm', result)
this.close()
@ -274,6 +417,53 @@
margin-right: 10rpx;
}
}
&.whole-city-item {
display: flex;
align-items: center;
color: #3796F8;
background: rgba(#3796F8, 0.1);
font-weight: bold;
uv-icon {
margin-right: 10rpx;
}
}
&.selected {
background: rgba($uni-color, 0.1);
color: $uni-color;
font-weight: bold;
position: relative;
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 6rpx;
background: $uni-color;
}
}
}
.confirm-btn {
position: sticky;
bottom: 0;
background: #fff;
padding: 20rpx;
border-top: 1px solid #eee;
.confirm-button {
width: 100%;
background: $uni-color;
color: #fff;
border: none;
border-radius: 10rpx;
padding: 20rpx;
font-size: 28rpx;
}
}
}
}

+ 1
- 1
components/base/tabbar.vue View File

@ -54,7 +54,7 @@
"selectedIconPath": "/static/image/tabbar/center-a.png",
"iconPath": "/static/image/tabbar/center.png",
"pagePath": "/pages/index/center",
"title": "会员中心"
"title": "个人中心"
}
]
};


+ 10
- 3
components/config/configPopup.vue View File

@ -18,12 +18,19 @@
}
},
onShow(){
},
},
methods: {
//
open(key){
this.content = this.configList[key]
this.$refs.popup.open('bottom');
this.$api('queryConfig', {
paramCode : 'config_vip_service',
})
.then(res => {
// this.content = this.configList[key]
this.content = res.result.paramValueText
this.$refs.popup.open('bottom');
})
this.$store.commit('initConfig')
}
},


+ 5
- 2
components/screen/screenWork.vue View File

@ -201,6 +201,9 @@
setTimeout(this.initData, 500)
},
methods: {
init(){
this.$refs.dropDown.init()
},
initData(){
if(this.addressList.length == 0){
@ -371,8 +374,8 @@
value: addressResult.selectedAddress.adress,
})
//
this.$refs.dropDown.close()
// -
uni.$emit(`${this.sign}_CLOSEPOPUP`)
},
}
}


+ 2
- 2
pages/index/center.vue View File

@ -226,10 +226,10 @@
<text class="grid-text">考证咨询</text>
</uv-grid-item>
<uv-grid-item @click="isLoggedIn ? $utils.navigateTo('/pages_order/mine/redeemCode') : $utils.toLogin()">
<!-- <uv-grid-item @click="isLoggedIn ? $utils.navigateTo('/pages_order/mine/redeemCode') : $utils.toLogin()">
<image class="image" src="/static/image/center/5.png" mode=""></image>
<text class="grid-text">兑换码</text>
</uv-grid-item>
</uv-grid-item> -->
<uv-grid-item @click="isLoggedIn ? $utils.navigateTo('/pages_order/contract/contract') : $utils.toLogin()">
<image class="image" src="/static/image/center/5.png" mode=""></image>


+ 9
- 1
pages/index/index.vue View File

@ -124,6 +124,9 @@
return this.banner.filter(n => n.type == 0)
},
},
onPageScroll(e){
this.$refs.screenWork.init()
},
data() {
return {
productList: [],
@ -136,7 +139,11 @@
this.$refs.workList.getData(this.screenWorkList)
this.$store.commit('getBanner')
},
onLoad(){
onLoad(query) {
if (query.shareId) {
uni.setStorageSync('shareId', query.shareId)
this.$store.commit('login')
}
uni.$on('initConfig', (e) => {
this.config_other_job = e.config_other_job
})
@ -190,6 +197,7 @@
}
this.$store.commit('setRole', !this.role)
this.$nextTick(() => {
this.$refs.screenWork.init()
this.$refs.workList.getData(this.screenWorkList)
})
}


+ 1
- 1
pages_order/auth/wxLogin.vue View File

@ -70,7 +70,7 @@
icon:'none'
})
}
this.$store.commit('login')
this.$store.commit('login', true)
},
qux(){
uni.reLaunch({


+ 3
- 2
pages_order/work/addResume.vue View File

@ -127,7 +127,7 @@
@confirm="pickerConfirm"></uv-picker>
<!-- 地址选择组件 -->
<AddressPicker ref="addressPicker" @confirm="onAddressConfirm" />
<AddressPicker ref="addressPicker" :multiple="true" :showSelectWholeCity="true" :onlyCity="false" @confirm="onAddressConfirm" />
</view>
</template>
@ -303,6 +303,7 @@
this.list.forEach((n, i) => {
//
if(n.useAddressPicker && this.form[n.type]) {
// 使
this.selectedAddress = this.form[n.type]
} else {
n.tag.forEach((e, index) => {
@ -322,8 +323,8 @@
},
//
onAddressConfirm(addressResult) {
// 使AddressPickerfullAddress
this.selectedAddress = addressResult.fullAddress
// ID
this.form.expectAddress = addressResult.fullAddress
},
},


+ 3
- 1
pages_order/work/postConsult.vue View File

@ -12,7 +12,9 @@
:key="item.id"
v-for="(item, index) in formList">
<view class="label">
{{ item.title }}<text>{{ item.descrip }}</text>
{{ item.title }}
<br>
<text>{{ item.descrip }}</text>
</view>
<view class="input"


+ 14
- 4
store/store.js View File

@ -46,7 +46,7 @@ const store = new Vuex.Store({
// })
// })
},
login(state){
login(state, is){
uni.showLoading({
title: '登录中...'
})
@ -55,10 +55,16 @@ const store = new Vuex.Store({
if(res.errMsg != "login:ok"){
return
}
let data = {
code: res.code,
}
if (uni.getStorageSync('shareId')) {
data.shareId = uni.getStorageSync('shareId')
}
api('wxLogin', {
code : res.code
}, res => {
api('wxLogin', data, res => {
uni.hideLoading()
@ -66,9 +72,13 @@ const store = new Vuex.Store({
return
}
state.userInfo = res.result.userInfo
uni.setStorageSync('token', res.result.token)
if(is){
return
}
if(!state.userInfo.nickName || !state.userInfo.headImage
|| !state.userInfo.phone){
uni.navigateTo({


Loading…
Cancel
Save