index.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <template>
  2. <view class="ssq adffc">
  3. <view class="box">
  4. <view class="box-top adfac">
  5. <view class="box-top-btn qx" @click="handleCancel">取消</view>
  6. <view class="box-top-title">{{ title }}</view>
  7. <view class="box-top-btn qd" @click="handleConfirm">确定</view>
  8. </view>
  9. <picker-view :value="pickerValue" @change="handlePickerChange" class="picker-view"
  10. item-height="88rpx" :indicator-style="{'height':'88rpx'}" mask-style="background-color: transparent;">
  11. <!-- 省份列 -->
  12. <picker-view-column>
  13. <view class="picker-item" :class="{ 'selected-item': pickerValue[0] === index }" v-for="(province, index) in provinces" :key="province.id">
  14. {{ province.name }}
  15. </view>
  16. </picker-view-column>
  17. <!-- 城市列 -->
  18. <picker-view-column>
  19. <view class="picker-item" :class="{ 'selected-item': pickerValue[1] === index }" v-for="(city, index) in cities" :key="city.id">
  20. {{ city.name }}
  21. </view>
  22. </picker-view-column>
  23. <!-- 区县列 -->
  24. <picker-view-column>
  25. <view class="picker-item" :class="{ 'selected-item': pickerValue[2] === index }" v-for="(area, index) in areas" :key="area.id">
  26. {{ area.name }}
  27. </view>
  28. </picker-view-column>
  29. </picker-view>
  30. </view>
  31. </view>
  32. </template>
  33. <script>
  34. export default {
  35. props: {
  36. title: {
  37. typeof: String,
  38. default: '所属地区'
  39. }
  40. },
  41. data() {
  42. return {
  43. provinces: [],
  44. cities: [],
  45. areas: [],
  46. pickerValue: [0, 0, 0]
  47. };
  48. },
  49. computed: {
  50. selectedProvince: function () {
  51. return this.provinces[this.pickerValue[0]] || {};
  52. },
  53. selectedCity: function () {
  54. return this.cities[this.pickerValue[1]] || {};
  55. },
  56. selectedArea: function () {
  57. return this.areas[this.pickerValue[2]] || {};
  58. }
  59. },
  60. mounted() {
  61. this.getProvinceCityAreaData();
  62. },
  63. methods: {
  64. getProvinceCityAreaData() {
  65. this.$api.get('/sys/region/tree').then(({ data: res }) => {
  66. if (res.code !== 0) return this.$showToast(res.msg);
  67. this.provinces = this.arrayToTreeOptimized(res.data);
  68. this.initialize();
  69. });
  70. },
  71. //方法一:递归
  72. arrayToTree(list, rootPid = '0') {
  73. const tree = [];
  74. // 1. 首先找到所有根节点
  75. list.forEach((item) => {
  76. if (item.pid === rootPid) {
  77. // 2. 找到根节点后,递归寻找它的子节点
  78. const children = this.arrayToTree(list, item.id);
  79. if (children.length > 0) {
  80. item.children = children;
  81. }
  82. tree.push(item);
  83. }
  84. });
  85. return tree;
  86. },
  87. //方法二:Map映射,性能提升
  88. arrayToTreeOptimized(list, rootPid = '0') {
  89. const map = new Map();
  90. const tree = [];
  91. list.forEach((item) => {
  92. map.set(item.id, { ...item, children: [] });
  93. });
  94. // 2. 再次遍历列表,建立父子关系
  95. map.forEach((node) => {
  96. const parent = map.get(node.pid);
  97. if (parent) {
  98. // 如果找到父节点,则将当前节点添加到父节点的 children 数组中
  99. parent.children.push(node);
  100. } else if (node.pid === rootPid) {
  101. // 如果没有父节点,并且 pid 是根 pid,则为根节点
  102. tree.push(node);
  103. }
  104. });
  105. // 清理空 children 数组(可选步骤,如果不需要空数组)
  106. map.forEach((node) => {
  107. if (node.children.length === 0) {
  108. delete node.children;
  109. }
  110. });
  111. return tree;
  112. },
  113. handlePickerChange(e) {
  114. const newPickerValue = e.detail.value;
  115. const [provinceIndex, cityIndex, areaIndex] = newPickerValue;
  116. const oldProvinceIndex = this.pickerValue[0];
  117. const oldCityIndex = this.pickerValue[1];
  118. if (provinceIndex !== oldProvinceIndex) {
  119. this.cities = this.provinces[provinceIndex].children;
  120. this.areas = this.cities[0]?.children || [];
  121. this.pickerValue = [provinceIndex, 0, 0];
  122. } else if (cityIndex !== oldCityIndex) {
  123. // 更新地区列表,并将地区索引重置为0
  124. this.areas = this.cities[cityIndex]?.children || [];
  125. this.pickerValue = [provinceIndex, cityIndex, 0];
  126. } else {
  127. this.pickerValue = newPickerValue;
  128. }
  129. },
  130. initialize() {
  131. this.cities = this.provinces[0]?.children || [];
  132. this.areas = this.cities[0]?.children || [];
  133. },
  134. handleCancel() {
  135. this.$emit('cancel');
  136. },
  137. handleConfirm() {
  138. this.$emit('confirm', {
  139. provinceId:this.provinces[this.pickerValue[0]].id,
  140. cityId:this.cities[this.pickerValue[1]].id,
  141. districtId:this.areas[this.pickerValue[2]].id,
  142. provinceName:this.provinces[this.pickerValue[0]].name,
  143. cityName:this.cities[this.pickerValue[1]].name,
  144. districtName:this.areas[this.pickerValue[2]].name,
  145. });
  146. }
  147. }
  148. };
  149. </script>
  150. <style scoped lang="scss">
  151. .ssq {
  152. position: fixed;
  153. left: 0;
  154. right: 0;
  155. top: 0;
  156. bottom: 0;
  157. background: rgba(0, 0, 0, 0.5);
  158. justify-content: flex-end;
  159. z-index: 1001;
  160. .box {
  161. background: #ffffff;
  162. border-radius: 24rpx 24rpx 0 0;
  163. &-top {
  164. width: 100%;
  165. height: 84rpx;
  166. &-btn {
  167. width: 120rpx;
  168. font-size: 30rpx;
  169. text-align: center;
  170. line-height: 42rpx;
  171. &.qx {
  172. color: #909193;
  173. }
  174. &.qd {
  175. color: #3c9cff;
  176. }
  177. }
  178. &-title {
  179. flex: 1;
  180. font-size: 32rpx;
  181. color: #303133;
  182. text-align: center;
  183. line-height: 42rpx;
  184. }
  185. }
  186. }
  187. }
  188. .picker-view {
  189. width: 100%;
  190. height: 580rpx;
  191. margin: 100rpx 0;
  192. }
  193. .picker-item {
  194. height: 88rpx;
  195. line-height: 88rpx;
  196. display: flex;
  197. align-items: center;
  198. justify-content: center;
  199. font-size: 36rpx;
  200. color: #303133;
  201. transition: all 0.2s;
  202. &.selected-item {
  203. font-size: 36rpx;
  204. font-weight: bold;
  205. color: #303133;
  206. }
  207. }
  208. </style>