408 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			408 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | |
|   <div v-if="popupVisible" class="region-select-overlay">
 | |
|     <div class="region-select-popup">
 | |
|       <div class="popup-header">
 | |
|         <h3 class="popup-title">
 | |
|           {{ reselected ? '修改' : '选择' }}收益区域({{ typeEnumMap[area] }})
 | |
|         </h3>
 | |
|       </div>
 | |
|       <div class="popup-content">
 | |
|         <picker-view
 | |
|           v-if="popupVisible"
 | |
|           class="picker-view"
 | |
|           :value="pickerValue"
 | |
|           @change="handlePickerChange"
 | |
|         >
 | |
|           <picker-view-column>
 | |
|             <div v-for="p in provinceList" :key="p.id" class="picker-item">
 | |
|               {{ p.name }}
 | |
|             </div>
 | |
|           </picker-view-column>
 | |
|           <picker-view-column
 | |
|             v-if="
 | |
|               cityList &&
 | |
|               cityList.length &&
 | |
|               ['city', 'county'].includes(this.area)
 | |
|             "
 | |
|           >
 | |
|             <div v-for="c in cityList" :key="c.id" class="picker-item">
 | |
|               {{ c.name }}
 | |
|             </div>
 | |
|           </picker-view-column>
 | |
|           <picker-view-column
 | |
|             v-if="countyList && countyList.length && this.area == 'county'"
 | |
|           >
 | |
|             <div v-for="d in countyList" :key="d.id" class="picker-item">
 | |
|               {{ d.name }}
 | |
|             </div>
 | |
|           </picker-view-column>
 | |
|         </picker-view>
 | |
|       </div>
 | |
|       <div class="popup-footer">
 | |
|         <button
 | |
|           v-if="showCancelButton"
 | |
|           class="popup-btn popup-cancel"
 | |
|           @click="handleClose"
 | |
|         >
 | |
|           取消
 | |
|         </button>
 | |
|         <button class="popup-btn popup-confirm" @click="handleConfirm">
 | |
|           确认
 | |
|         </button>
 | |
|       </div>
 | |
|     </div>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import { getRegionSelect, setRegion, getRegionAreaTree } from '@/config/mine.js'
 | |
| 
 | |
| export default {
 | |
|   name: 'RegionSelect',
 | |
|   props: {
 | |
|     autoTrigger: {
 | |
|       type: Boolean,
 | |
|       default: true,
 | |
|     },
 | |
|     showCancelButton: {
 | |
|       type: Boolean,
 | |
|       default: false,
 | |
|     },
 | |
|     reselected: {
 | |
|       type: Boolean,
 | |
|       default: false,
 | |
|     },
 | |
|   },
 | |
|   data() {
 | |
|     return {
 | |
|       popupVisible: false,
 | |
|       areaTree: [],
 | |
|       pickerValue: [0, 0, 0],
 | |
|       // indicatorStyle: `height: 50px;`,
 | |
|       selectedList: [],
 | |
|       regionList: [],
 | |
|       provinceList: [],
 | |
|       cityList: [],
 | |
|       countyList: [],
 | |
|       area: '',
 | |
|       typeMap: {
 | |
|         province: 1,
 | |
|         city: 2,
 | |
|         county: 3,
 | |
|       },
 | |
|       typeEnumMap: {
 | |
|         province: '省',
 | |
|         city: '市',
 | |
|         county: '区',
 | |
|       },
 | |
|     }
 | |
|   },
 | |
|   async created() {
 | |
|     if (!this.autoTrigger) {
 | |
|       return
 | |
|     }
 | |
|     this.init()
 | |
|   },
 | |
|   methods: {
 | |
|     init() {
 | |
|       uni.showLoading({
 | |
|         title: '加载中...',
 | |
|       })
 | |
|       this.getRegionSelect()
 | |
|         .then(selectedList => {
 | |
|           this.selectedList = selectedList
 | |
|           const area = selectedList.shift()
 | |
|           this.open(area)
 | |
|         })
 | |
|         .catch(() => {
 | |
|           this.$emit('success')
 | |
|           uni.hideLoading()
 | |
|         })
 | |
|     },
 | |
|     async getRegionSelect() {
 | |
|       return new Promise(async (resolve, reject) => {
 | |
|         try {
 | |
|           const res = await getRegionSelect()
 | |
|           if (res?.code === 200) {
 | |
|             const needSelected = Object.keys(res?.data || {})
 | |
|               .filter(key => key !== 'data')
 | |
|               .filter(key => res.data[key])
 | |
|               .filter(key => !res.data?.data?.[`${key}Data`])
 | |
|               .reverse()
 | |
|             if (
 | |
|               needSelected?.length &&
 | |
|               Object.keys(res.data?.data || {}).length < needSelected?.length
 | |
|             ) {
 | |
|               resolve(needSelected)
 | |
|             } else {
 | |
|               reject(false)
 | |
|             }
 | |
|           }
 | |
|         } catch (error) {
 | |
|           reject(false)
 | |
|         }
 | |
|       })
 | |
|     },
 | |
|     async open(area) {
 | |
|       this.area = area
 | |
|       uni.showLoading({
 | |
|         title: '加载中...',
 | |
|       })
 | |
|       return new Promise(async (resolve, reject) => {
 | |
|         this.popupVisible = true
 | |
| 
 | |
|         await this.loadAreaTree(area)
 | |
|         resolve()
 | |
|         uni.hideLoading()
 | |
|       })
 | |
|     },
 | |
|     async loadAreaTree(selectedArea) {
 | |
|       try {
 | |
|         const res = await getRegionAreaTree({
 | |
|           type: this.typeMap[selectedArea],
 | |
|         })
 | |
|         if (res.code === 200 && res.data) {
 | |
|           this.provinceList = res.data.filter(item => item.parent === 0)
 | |
|           this.cityList = res.data.filter(item =>
 | |
|             this.provinceList.find(province => province.pkId === item.parent)
 | |
|           )
 | |
|           this.countyList = res.data.filter(item =>
 | |
|             this.cityList.find(city => city.pkId === item.parent)
 | |
|           )
 | |
|           this.getProvinceFilterList()
 | |
|           this.getCityFilterList()
 | |
|           this.generateRegionTree()
 | |
|           return true
 | |
|         }
 | |
|         return false
 | |
|       } catch (error) {
 | |
|         console.error('Failed to load area tree:', error)
 | |
|       }
 | |
|     },
 | |
|     generateRegionTree() {
 | |
|       this.countyList.forEach(item => {
 | |
|         const city = this.cityList.find(city => city.pkId === item.parent)
 | |
|         if (city) {
 | |
|           if (!city.children) {
 | |
|             city.children = [item]
 | |
|           } else {
 | |
|             city.children.push(item)
 | |
|           }
 | |
|         }
 | |
|       })
 | |
| 
 | |
|       this.cityList.forEach(item => {
 | |
|         const province = this.provinceList.find(
 | |
|           province => province.pkId === item.parent
 | |
|         )
 | |
|         if (province) {
 | |
|           if (!province.children) {
 | |
|             console.log(item)
 | |
|             province.children = [item]
 | |
|           } else {
 | |
|             province.children.push(item)
 | |
|           }
 | |
|         }
 | |
|       })
 | |
|       this.provinceList = this.provinceList.filter(item => item.children)
 | |
|       this.cityList = this.cityList.filter(item => item.children)
 | |
|       this.cityList = this.provinceList[this.pickerValue[0]].children
 | |
|       this.countyList = this.cityList[this.pickerValue[1]].children
 | |
|     },
 | |
|     getProvinceFilterList() {
 | |
|       if (!this.cityList.length) {
 | |
|         this.provinceList = []
 | |
|       }
 | |
|       this.provinceList = this.provinceList.filter(
 | |
|         item => !!this.cityList.find(city => city.parent === item.pkId)
 | |
|       )
 | |
|     },
 | |
|     getCityFilterList() {
 | |
|       if (!this.countyList.length) {
 | |
|         this.cityList = []
 | |
|       }
 | |
|       this.cityList = this.cityList.filter(
 | |
|         item => !!this.countyList.find(county => county.parent === item.pkId)
 | |
|       )
 | |
|     },
 | |
|     handlePickerChange(e) {
 | |
|       const [pIndex, cIndex] = e.detail.value
 | |
|       const oldPIndex = this.pickerValue[0]
 | |
|       const oldCIndex = this.pickerValue[1]
 | |
|       if (oldPIndex !== pIndex) {
 | |
|         this.cityList = this.provinceList[pIndex].children
 | |
|         this.countyList = this.cityList[0].children
 | |
|         this.$set(this.pickerValue, 1, 0)
 | |
|         this.$set(this.pickerValue, 2, 0)
 | |
|       } else if (oldCIndex !== cIndex) {
 | |
|         this.countyList = this.cityList[cIndex].children
 | |
|         this.$set(this.pickerValue, 2, 0)
 | |
|       }
 | |
|       this.pickerValue = e.detail.value
 | |
|     },
 | |
|     handleClose() {
 | |
|       this.popupVisible = false
 | |
|       this.$emit('cancel')
 | |
|     },
 | |
|     async handleConfirm() {
 | |
|       const [pIndex, cIndex, dIndex] = this.pickerValue
 | |
|       const province = this.provinceList[pIndex]
 | |
|       const city = this.cityList[cIndex]
 | |
|       const county = this.countyList[dIndex]
 | |
| 
 | |
|       try {
 | |
|         const params = {
 | |
|           type: this.typeMap[this.area],
 | |
|           province: province.pkId,
 | |
|           city: 0,
 | |
|           county: 0,
 | |
|         }
 | |
|         if (this.area == 'city') {
 | |
|           params.city = city.pkId
 | |
|         } else if (this.area == 'county') {
 | |
|           params.city = city.pkId
 | |
|           params.county = county.pkId
 | |
|         }
 | |
|         const res = await setRegion(params)
 | |
|         if (res.code === 200) {
 | |
|           this.handleClose()
 | |
|           if (this.selectedList.length) {
 | |
|             this.area = this.selectedList.shift()
 | |
|             this.pickerValue = [0, 0, 0]
 | |
|             this.open(this.area)
 | |
|           } else {
 | |
|             this.$emit('success')
 | |
|           }
 | |
|         } else {
 | |
|           throw new Error(res.message || 'Failed to set region')
 | |
|         }
 | |
|       } catch (error) {
 | |
|         this.$emit('success')
 | |
|         console.error('Failed to set region:', error)
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <style scoped>
 | |
| .region-select-overlay {
 | |
|   position: fixed;
 | |
|   top: 0;
 | |
|   left: 0;
 | |
|   width: 100%;
 | |
|   height: 100%;
 | |
|   background-color: rgba(0, 0, 0, 0.5);
 | |
|   display: flex;
 | |
|   align-items: center;
 | |
|   justify-content: center;
 | |
|   z-index: 1000;
 | |
|   transition: opacity 0.3s ease;
 | |
| }
 | |
| 
 | |
| .region-select-popup {
 | |
|   width: 90%;
 | |
|   max-width: 680rpx;
 | |
|   background-color: white;
 | |
|   border-radius: 24rpx;
 | |
|   /* animation: scale-up 0.3s ease-out; */
 | |
|   max-height: 80vh;
 | |
|   display: flex;
 | |
|   flex-direction: column;
 | |
|   overflow: hidden;
 | |
|   box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.1);
 | |
| }
 | |
| 
 | |
| .popup-header {
 | |
|   display: flex;
 | |
|   justify-content: center;
 | |
|   align-items: center;
 | |
|   padding: 30rpx;
 | |
|   border-bottom: 1rpx solid #f5f5f5;
 | |
|   flex-shrink: 0;
 | |
| }
 | |
| 
 | |
| .popup-title {
 | |
|   font-size: 32rpx;
 | |
|   font-weight: 600;
 | |
|   color: #1c1c1e;
 | |
|   margin: 0;
 | |
| }
 | |
| 
 | |
| .popup-btn {
 | |
|   border: none;
 | |
|   background: none;
 | |
|   cursor: pointer;
 | |
| }
 | |
| 
 | |
| .popup-content {
 | |
|   overflow: hidden;
 | |
|   flex-grow: 1;
 | |
| }
 | |
| 
 | |
| .picker-view {
 | |
|   width: 100%;
 | |
|   height: 600rpx;
 | |
|   /* margin-top: 20rpx; */
 | |
| }
 | |
| 
 | |
| .picker-item {
 | |
|   display: flex;
 | |
|   align-items: center;
 | |
|   justify-content: center;
 | |
|   font-size: 24rpx;
 | |
|   line-height: 100rpx;
 | |
|   color: #666;
 | |
|   white-space: nowrap;
 | |
|   overflow: hidden;
 | |
|   text-overflow: ellipsis;
 | |
| }
 | |
| 
 | |
| .popup-footer {
 | |
|   display: flex;
 | |
|   flex-shrink: 0;
 | |
|   padding: 24rpx;
 | |
|   gap: 24rpx;
 | |
|   background-color: #f7f7f7;
 | |
|   border-top: 1rpx solid #efefef;
 | |
| }
 | |
| 
 | |
| .popup-footer .popup-btn {
 | |
|   flex: 1;
 | |
|   text-align: center;
 | |
|   padding: 14rpx 0;
 | |
|   font-size: 28rpx;
 | |
|   border-radius: 40rpx;
 | |
|   font-weight: 500;
 | |
|   transition:
 | |
|     transform 0.1s ease,
 | |
|     box-shadow 0.2s ease;
 | |
| }
 | |
| 
 | |
| .popup-footer .popup-btn:active {
 | |
|   transform: scale(0.97);
 | |
| }
 | |
| 
 | |
| .popup-footer .popup-cancel {
 | |
|   background-color: #eee;
 | |
|   color: #555;
 | |
| }
 | |
| 
 | |
| .popup-footer .popup-confirm {
 | |
|   background: linear-gradient(135deg, #007aff, #0056b3);
 | |
|   color: white;
 | |
|   box-shadow: 0 4rpx 12rpx rgba(0, 122, 255, 0.25);
 | |
| }
 | |
| 
 | |
| /* @keyframes scale-up {
 | |
|   from {
 | |
|     transform: scale(0.8);
 | |
|     opacity: 0;
 | |
|   }
 | |
|   to {
 | |
|     transform: scale(1);
 | |
|     opacity: 1;
 | |
|   }
 | |
| } */
 | |
| </style>
 |