Browse Source

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

- 新增多选模式,可选择多个城市或区县
- 添加"选择整个城市/省份"功能
- 优化地址选择确认逻辑,支持多种选择模式
- 更新样式以区分选中状态
- 添加确认按钮用于多选模式
- 确保与父组件的数据格式一致性
master
前端-胡立永 2 days 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', url: '/employ/config/queryConfigList',
method: 'GET', method: 'GET',
}, },
// 查看配置信息
queryConfig: {
url: '/employ/config/queryConfig',
method: 'GET',
},
} }
export default api export default api

+ 216
- 26
components/AddressPicker.vue View File

@ -29,12 +29,33 @@
<scroll-view scroll-y class="scroll-view"> <scroll-view scroll-y class="scroll-view">
<!-- 二级地址 --> <!-- 二级地址 -->
<template v-if="selectedProvince && !selectedCity"> <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 <view
class="address-item" class="address-item"
:class="{
'selected': multiple && isCitySelected(item),
'active': !multiple && selectedCity && selectedCity.id === item.id
}"
v-for="item in cityList" v-for="item in cityList"
:key="item.id" :key="item.id"
@click="selectCity(item)"> @click="selectCity(item)">
{{ item.adress }} {{ 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> </view>
</template> </template>
@ -46,12 +67,34 @@
<uv-icon name="arrow-left" size="30rpx"></uv-icon> <uv-icon name="arrow-left" size="30rpx"></uv-icon>
返回{{ selectedProvince.adress }} 返回{{ selectedProvince.adress }}
</view> </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 <view
class="address-item" class="address-item"
:class="{
'selected': multiple && isDistrictSelected(item),
'active': !multiple && selectedDistrict && selectedDistrict.id === item.id
}"
v-for="item in districtList" v-for="item in districtList"
:key="item.id" :key="item.id"
@click="selectDistrict(item)"> @click="selectDistrict(item)">
{{ item.adress }} {{ 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> </view>
</template> </template>
</scroll-view> </scroll-view>
@ -71,6 +114,16 @@
onlyCity: { onlyCity: {
type: Boolean, type: Boolean,
default: false default: false
},
//
multiple: {
type: Boolean,
default: false
},
// ""
showSelectWholeCity: {
type: Boolean,
default: false
} }
}, },
data() { data() {
@ -78,6 +131,8 @@
selectedProvince: null, // selectedProvince: null, //
selectedCity: null, // selectedCity: null, //
selectedDistrict: null, // selectedDistrict: null, //
selectedCities: [], //
selectedDistricts: [], //
cityList: [], // cityList: [], //
districtList: [], // districtList: [], //
} }
@ -101,6 +156,8 @@
this.selectedProvince = null this.selectedProvince = null
this.selectedCity = null this.selectedCity = null
this.selectedDistrict = null this.selectedDistrict = null
this.selectedCities = []
this.selectedDistricts = []
this.cityList = [] this.cityList = []
this.districtList = [] this.districtList = []
}, },
@ -129,33 +186,96 @@
// //
async selectCity(city) { 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() this.confirm()
} }
} catch (error) {
console.error('获取区县列表失败:', error)
this.districtList = []
//
this.confirm()
} }
}, },
// //
selectDistrict(district) { 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() this.confirm()
}, },
@ -171,7 +291,9 @@
const result = { const result = {
province: this.selectedProvince, province: this.selectedProvince,
city: this.selectedCity, city: this.selectedCity,
district: this.selectedDistrict
district: this.selectedDistrict,
cities: this.selectedCities,
districts: this.selectedDistricts
} }
// //
@ -179,15 +301,36 @@
if (this.selectedProvince) { if (this.selectedProvince) {
fullAddress += this.selectedProvince.adress 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 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.fullAddress = fullAddress
result.selectedAddress = this.selectedDistrict || this.selectedCity || this.selectedProvince
this.$emit('confirm', result) this.$emit('confirm', result)
this.close() this.close()
@ -274,6 +417,53 @@
margin-right: 10rpx; 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", "selectedIconPath": "/static/image/tabbar/center-a.png",
"iconPath": "/static/image/tabbar/center.png", "iconPath": "/static/image/tabbar/center.png",
"pagePath": "/pages/index/center", "pagePath": "/pages/index/center",
"title": "会员中心"
"title": "个人中心"
} }
] ]
}; };


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

@ -18,12 +18,19 @@
} }
}, },
onShow(){ onShow(){
},
},
methods: { methods: {
// //
open(key){ 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) setTimeout(this.initData, 500)
}, },
methods: { methods: {
init(){
this.$refs.dropDown.init()
},
initData(){ initData(){
if(this.addressList.length == 0){ if(this.addressList.length == 0){
@ -371,8 +374,8 @@
value: addressResult.selectedAddress.adress, 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> <text class="grid-text">考证咨询</text>
</uv-grid-item> </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> <image class="image" src="/static/image/center/5.png" mode=""></image>
<text class="grid-text">兑换码</text> <text class="grid-text">兑换码</text>
</uv-grid-item>
</uv-grid-item> -->
<uv-grid-item @click="isLoggedIn ? $utils.navigateTo('/pages_order/contract/contract') : $utils.toLogin()"> <uv-grid-item @click="isLoggedIn ? $utils.navigateTo('/pages_order/contract/contract') : $utils.toLogin()">
<image class="image" src="/static/image/center/5.png" mode=""></image> <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) return this.banner.filter(n => n.type == 0)
}, },
}, },
onPageScroll(e){
this.$refs.screenWork.init()
},
data() { data() {
return { return {
productList: [], productList: [],
@ -136,7 +139,11 @@
this.$refs.workList.getData(this.screenWorkList) this.$refs.workList.getData(this.screenWorkList)
this.$store.commit('getBanner') this.$store.commit('getBanner')
}, },
onLoad(){
onLoad(query) {
if (query.shareId) {
uni.setStorageSync('shareId', query.shareId)
this.$store.commit('login')
}
uni.$on('initConfig', (e) => { uni.$on('initConfig', (e) => {
this.config_other_job = e.config_other_job this.config_other_job = e.config_other_job
}) })
@ -190,6 +197,7 @@
} }
this.$store.commit('setRole', !this.role) this.$store.commit('setRole', !this.role)
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.screenWork.init()
this.$refs.workList.getData(this.screenWorkList) this.$refs.workList.getData(this.screenWorkList)
}) })
} }


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

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


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

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


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

@ -12,7 +12,9 @@
:key="item.id" :key="item.id"
v-for="(item, index) in formList"> v-for="(item, index) in formList">
<view class="label"> <view class="label">
{{ item.title }}<text>{{ item.descrip }}</text>
{{ item.title }}
<br>
<text>{{ item.descrip }}</text>
</view> </view>
<view class="input" <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({ uni.showLoading({
title: '登录中...' title: '登录中...'
}) })
@ -55,10 +55,16 @@ const store = new Vuex.Store({
if(res.errMsg != "login:ok"){ if(res.errMsg != "login:ok"){
return 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() uni.hideLoading()
@ -66,9 +72,13 @@ const store = new Vuex.Store({
return return
} }
state.userInfo = res.result.userInfo state.userInfo = res.result.userInfo
uni.setStorageSync('token', res.result.token) uni.setStorageSync('token', res.result.token)
if(is){
return
}
if(!state.userInfo.nickName || !state.userInfo.headImage if(!state.userInfo.nickName || !state.userInfo.headImage
|| !state.userInfo.phone){ || !state.userInfo.phone){
uni.navigateTo({ uni.navigateTo({


Loading…
Cancel
Save