特易招,招聘小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
5.9 KiB

3 weeks ago
  1. <template>
  2. <uv-popup ref="popup" mode="bottom" :round="30"
  3. :safeAreaInsetBottom="false" @close="handleClose">
  4. <view class="address-picker">
  5. <view class="header">
  6. <view class="title">选择地址</view>
  7. <view class="close-btn" @click="close">
  8. <uv-icon name="close" size="40rpx"></uv-icon>
  9. </view>
  10. </view>
  11. <view class="content">
  12. <!-- 左侧一级地址列表 -->
  13. <view class="left-panel">
  14. <scroll-view scroll-y class="scroll-view">
  15. <view
  16. class="address-item"
  17. :class="{ active: selectedProvince && selectedProvince.id === item.id }"
  18. v-for="item in addressList"
  19. :key="item.id"
  20. @click="selectProvince(item)">
  21. {{ item.adress }}
  22. </view>
  23. </scroll-view>
  24. </view>
  25. <!-- 右侧二三级地址列表 -->
  26. <view class="right-panel">
  27. <scroll-view scroll-y class="scroll-view">
  28. <!-- 二级地址 -->
  29. <template v-if="selectedProvince && !selectedCity">
  30. <view
  31. class="address-item"
  32. v-for="item in cityList"
  33. :key="item.id"
  34. @click="selectCity(item)">
  35. {{ item.adress }}
  36. </view>
  37. </template>
  38. <!-- 三级地址 -->
  39. <template v-if="selectedCity">
  40. <view
  41. class="address-item back-item"
  42. @click="backToCity">
  43. <uv-icon name="arrow-left" size="30rpx"></uv-icon>
  44. 返回{{ selectedProvince.adress }}
  45. </view>
  46. <view
  47. class="address-item"
  48. v-for="item in districtList"
  49. :key="item.id"
  50. @click="selectDistrict(item)">
  51. {{ item.adress }}
  52. </view>
  53. </template>
  54. </scroll-view>
  55. </view>
  56. </view>
  57. </view>
  58. </uv-popup>
  59. </template>
  60. <script>
  61. import { mapState } from 'vuex'
  62. export default {
  63. name: 'AddressPicker',
  64. props: {
  65. // 是否只选择到市级,不选择区县
  66. onlyCity: {
  67. type: Boolean,
  68. default: false
  69. }
  70. },
  71. data() {
  72. return {
  73. selectedProvince: null, // 选中的省份
  74. selectedCity: null, // 选中的城市
  75. selectedDistrict: null, // 选中的区县
  76. cityList: [], // 城市列表
  77. districtList: [], // 区县列表
  78. }
  79. },
  80. computed: {
  81. ...mapState(['addressList'])
  82. },
  83. methods: {
  84. // 打开弹窗
  85. open() {
  86. this.$refs.popup.open()
  87. },
  88. // 关闭弹窗
  89. close() {
  90. this.$refs.popup.close()
  91. },
  92. // 弹窗关闭时重置状态
  93. handleClose() {
  94. this.selectedProvince = null
  95. this.selectedCity = null
  96. this.selectedDistrict = null
  97. this.cityList = []
  98. this.districtList = []
  99. },
  100. // 选择省份
  101. async selectProvince(province) {
  102. this.selectedProvince = province
  103. this.selectedCity = null
  104. this.selectedDistrict = null
  105. this.districtList = []
  106. // 获取城市列表
  107. try {
  108. this.cityList = await this.$store.dispatch('getChildAddressList', province.id)
  109. // 如果没有下级城市,直接确认选择
  110. if (this.cityList.length === 0) {
  111. this.confirm()
  112. }
  113. } catch (error) {
  114. console.error('获取城市列表失败:', error)
  115. this.cityList = []
  116. // 获取失败时也直接确认
  117. this.confirm()
  118. }
  119. },
  120. // 选择城市
  121. async selectCity(city) {
  122. this.selectedCity = city
  123. this.selectedDistrict = null
  124. // 如果只选择到市级,直接确认
  125. if (this.onlyCity) {
  126. this.confirm()
  127. return
  128. }
  129. // 获取区县列表
  130. try {
  131. this.districtList = await this.$store.dispatch('getChildAddressList', city.id)
  132. // 如果没有下级地址,直接确认
  133. if (this.districtList.length === 0) {
  134. this.confirm()
  135. }
  136. } catch (error) {
  137. console.error('获取区县列表失败:', error)
  138. this.districtList = []
  139. // 获取失败时也直接确认
  140. this.confirm()
  141. }
  142. },
  143. // 选择区县
  144. selectDistrict(district) {
  145. this.selectedDistrict = district
  146. this.confirm()
  147. },
  148. // 返回城市选择
  149. backToCity() {
  150. this.selectedCity = null
  151. this.selectedDistrict = null
  152. this.districtList = []
  153. },
  154. // 确认选择
  155. confirm() {
  156. const result = {
  157. province: this.selectedProvince,
  158. city: this.selectedCity,
  159. district: this.selectedDistrict
  160. }
  161. // 生成完整地址文本
  162. let fullAddress = ''
  163. if (this.selectedProvince) {
  164. fullAddress += this.selectedProvince.adress
  165. }
  166. if (this.selectedCity) {
  167. fullAddress += this.selectedCity.adress
  168. }
  169. if (this.selectedDistrict) {
  170. fullAddress += this.selectedDistrict.adress
  171. }
  172. result.fullAddress = fullAddress
  173. result.selectedAddress = this.selectedDistrict || this.selectedCity || this.selectedProvince
  174. this.$emit('confirm', result)
  175. this.close()
  176. }
  177. }
  178. }
  179. </script>
  180. <style scoped lang="scss">
  181. .address-picker {
  182. height: 70vh;
  183. background: #fff;
  184. .header {
  185. display: flex;
  186. justify-content: space-between;
  187. align-items: center;
  188. padding: 30rpx;
  189. border-bottom: 1px solid #eee;
  190. .title {
  191. font-size: 32rpx;
  192. font-weight: bold;
  193. color: #333;
  194. }
  195. .close-btn {
  196. padding: 10rpx;
  197. }
  198. }
  199. .content {
  200. display: flex;
  201. height: calc(70vh - 160rpx);
  202. .left-panel {
  203. width: 240rpx;
  204. border-right: 1px solid #eee;
  205. background: #f8f8f8;
  206. }
  207. .right-panel {
  208. flex: 1;
  209. }
  210. .scroll-view {
  211. height: 100%;
  212. }
  213. .address-item {
  214. padding: 30rpx 20rpx;
  215. font-size: 28rpx;
  216. color: #333;
  217. border-bottom: 1px solid #f0f0f0;
  218. &:last-child {
  219. border-bottom: none;
  220. }
  221. &.active {
  222. background: #fff;
  223. color: $uni-color;
  224. font-weight: bold;
  225. position: relative;
  226. &::after {
  227. content: '';
  228. position: absolute;
  229. right: 0;
  230. top: 0;
  231. bottom: 0;
  232. width: 6rpx;
  233. background: $uni-color;
  234. }
  235. }
  236. &.back-item {
  237. display: flex;
  238. align-items: center;
  239. color: $uni-color;
  240. background: #f8f8f8;
  241. uv-icon {
  242. margin-right: 10rpx;
  243. }
  244. }
  245. }
  246. }
  247. }
  248. </style>