u-area-picker.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <template>
  2. <u-picker
  3. ref="areaPicker"
  4. keyName="label"
  5. valueName="value"
  6. :show="show"
  7. :columns="columns"
  8. :showToolbar="showToolbar"
  9. :title="title"
  10. :loading="loading"
  11. :itemHeight="itemHeight"
  12. :cancelText="cancelText"
  13. :confirmText="confirmText"
  14. :cancelColor="cancelColor"
  15. :confirmColor="confirmColor"
  16. :visibleItemCount="visibleItemCount"
  17. :closeOnClickOverlay="closeOnClickOverlay"
  18. :defaultIndex="innerDefaultIndex"
  19. :immediateChange="immediateChange"
  20. :round="round"
  21. @close="close"
  22. @cancel="cancel"
  23. @confirm="confirm"
  24. @change="change"
  25. >
  26. <template v-slot:trigger>
  27. <slot name="trigger">
  28. <u-input
  29. v-if="showInput"
  30. :value="inputValue"
  31. :clearable="inputProps.clearable"
  32. :placeholder="inputProps.placeholder"
  33. :disabled="inputProps.disabled"
  34. :border="inputProps.border"
  35. :round="inputProps.round"
  36. :backgroundColor="inputProps.disabled ? '' : inputProps.backgroundColor"
  37. :disabledColor="inputProps.disabled ? inputProps.backgroundColor : ''"
  38. :placeholderClass="inputProps.placeholderClass"
  39. :placeholderStyle="inputProps.placeholderStyle"
  40. :confirmType="inputProps.confirmType"
  41. :focus="inputProps.focus"
  42. :inputAlign="inputProps.inputAlign"
  43. :fontSize="inputProps.fontSize"
  44. :color="inputProps.color"
  45. :borderColor="inputProps.borderColor"
  46. :prefixIcon="inputProps.prefixIcon"
  47. :suffixIcon="inputProps.suffixIcon"
  48. :suffixIconStyle="inputProps.suffixIconStyle"
  49. :prefixIconStyle="inputProps.prefixIconStyle"
  50. :shape="inputProps.shape"
  51. :customStyle="inputProps.customStyle"
  52. />
  53. </slot>
  54. </template>
  55. </u-picker>
  56. </template>
  57. <script>
  58. import area from '../../libs/util/area.js'
  59. import props from './props.js'
  60. import mixin from '../../libs/mixin/mixin'
  61. import mpMixin from '../../libs/mixin/mpMixin'
  62. export default {
  63. name: 'u-region-picker',
  64. mixins: [mpMixin, mixin, props],
  65. data() {
  66. return {
  67. inputValue: '',
  68. columns: [],
  69. innerDefaultIndex: [],
  70. selectedValue:[]
  71. }
  72. },
  73. watch: {
  74. // #ifdef VUE2
  75. value(newValue) {
  76. if(newValue){
  77. this.init()
  78. }
  79. },
  80. // #endif
  81. // #ifdef VUE3
  82. modelValue(newValue) {
  83. if(newValue){
  84. this.init()
  85. }
  86. },
  87. // #endif
  88. },
  89. mounted() {
  90. this.init();
  91. },
  92. // #ifdef VUE3
  93. emits: ['update:modelValue','init', 'confirm', 'change', 'close', 'cancel'],
  94. // #endif
  95. methods: {
  96. // 初始化数据
  97. init() {
  98. const columns = [];
  99. const innerDefaultIndex = [];
  100. const innerDefaultLabel = []
  101. // 获取当前值
  102. const currentValue = this.getCurrentValue();
  103. // 处理省份列
  104. let selectedProvince;
  105. if (this.province) {
  106. const province = this.getProvinceList();
  107. columns.push(province);
  108. selectedProvince = this.getSelectedValue(0, province);
  109. let provinceIndex = province.findIndex(item => item.value == selectedProvince);
  110. innerDefaultIndex.push(provinceIndex >= 0 ? provinceIndex : 0);
  111. innerDefaultLabel.push(province[provinceIndex].label)
  112. }
  113. // 处理城市列
  114. let selectedCity;
  115. if (this.city) {
  116. const city = this.getCityList(selectedProvince);
  117. columns.push(city);
  118. const cityIndex = this.province ? 1 : 0;
  119. selectedCity = this.getSelectedValue(cityIndex, city);
  120. let cityIndexValue = city.findIndex(item => item.value == selectedCity);
  121. innerDefaultIndex.push(cityIndexValue >= 0 ? cityIndexValue : 0);
  122. innerDefaultLabel.push(city[cityIndexValue].label)
  123. }
  124. // 处理县区列
  125. if (this.county && selectedCity) {
  126. const county = this.getCountyList(selectedCity);
  127. columns.push(county);
  128. const countyIndex = (this.province ? 1 : 0) + (this.city ? 1 : 0);
  129. const selectedCounty = this.getSelectedValue(countyIndex, county);
  130. let countyIndexValue = county.findIndex(item => item.value == selectedCounty);
  131. innerDefaultIndex.push(countyIndexValue >= 0 ? countyIndexValue : 0);
  132. innerDefaultLabel.push(county[countyIndexValue].label)
  133. }
  134. // 设置列数据
  135. this.columns = columns;
  136. this.innerDefaultIndex = innerDefaultIndex;
  137. if(this.showInput && currentValue.length > 0){
  138. this.inputValue = innerDefaultLabel.join(this.inputProps.separator)
  139. }
  140. },
  141. // 获取选中值
  142. getSelectedValue(index, list) {
  143. const currentValue = this.getCurrentValue();
  144. // 如果有默认值且列表不为空,则使用默认值,否则使用第一个值
  145. return (currentValue && currentValue[index] && list.length > 0)
  146. ? Number(currentValue[index])
  147. : Number(list[0].value);
  148. },
  149. // 获取当前值,兼容 Vue2 和 Vue3
  150. getCurrentValue() {
  151. // #ifdef VUE2
  152. return this.value;
  153. // #endif
  154. // #ifdef VUE3
  155. return this.modelValue;
  156. // #endif
  157. },
  158. // 获取省份数据
  159. getProvinceList() {
  160. let list = [];
  161. Object.entries(area.province_list).forEach(([value, label],idx) => {
  162. list.push({ value, label });
  163. });
  164. return list;
  165. },
  166. // 获取城市数据
  167. getCityList(cityCode) {
  168. let list = [];
  169. Object.entries(area.city_list).forEach(([value, label],idx) => {
  170. if(cityCode){
  171. if (value.startsWith(String(cityCode).substring(0, 2))) {
  172. list.push({ value, label });
  173. }
  174. }else{
  175. list.push({ value, label });
  176. }
  177. });
  178. return list;
  179. },
  180. // 获取区县数据
  181. getCountyList(countyCode) {
  182. let list = [];
  183. Object.entries(area.county_list).forEach(([value, label],idx) => {
  184. if(countyCode){
  185. if (value.startsWith(String(countyCode).substring(0, 4))) {
  186. list.push({ value, label });
  187. }
  188. }else{
  189. list.push({ value, label });
  190. }
  191. });
  192. return list;
  193. },
  194. // 选择器变化事件
  195. change(e) {
  196. const { columnIndex, value } = e;
  197. // 计算实际的列索引(考虑可能跳过的列)
  198. let actualColumnIndex = columnIndex;
  199. let valueIndex = columnIndex;
  200. // 省份列变化
  201. if ((this.province && actualColumnIndex === 0) && this.city) {
  202. // 选择省份时更新城市列表
  203. const cityList = this.getCityList(value[valueIndex].value);
  204. const cityColumnIndex = this.province ? 1 : 0;
  205. this.$refs.areaPicker.setColumnValues(cityColumnIndex, cityList);
  206. // 如果显示区县列,同时更新区县列表
  207. if (this.county && cityList.length > 0) {
  208. const countyList = this.getCountyList(cityList[0].value);
  209. const countyColumnIndex = (this.province ? 1 : 0) + (this.city ? 1 : 0);
  210. this.$refs.areaPicker.setColumnValues(countyColumnIndex, countyList);
  211. }
  212. }
  213. // 城市列变化
  214. else if (this.city &&
  215. ((this.province && actualColumnIndex === 1) ||
  216. (!this.province && actualColumnIndex === 0)) &&
  217. this.county) {
  218. // 选择城市时更新区县列表
  219. const cityValueIndex = this.province ? 1 : 0;
  220. let countyList = this.getCountyList(value[cityValueIndex].value);
  221. const countyColumnIndex = (this.province ? 1 : 0) + (this.city ? 1 : 0) - 1;
  222. this.$refs.areaPicker.setColumnValues(countyColumnIndex + 1, countyList);
  223. }
  224. this.$emit('change', e);
  225. },
  226. // 关闭选择器
  227. close() {
  228. if (this.closeOnClickOverlay) {
  229. this.$emit('close')
  230. }
  231. },
  232. // 确认选择
  233. confirm(e) {
  234. this.$emit('confirm', e);
  235. let value = e.value.map(item => item.value)
  236. if(this.showInput){
  237. let label = e.value.map(item => item.label)
  238. this.inputValue = label.join(this.inputProps.separator)
  239. }
  240. // #ifdef VUE2
  241. this.$emit('input', value)
  242. // #endif
  243. // #ifdef VUE3
  244. this.$emit('update:modelValue', value)
  245. // #endif
  246. },
  247. // 取消选择
  248. cancel() {
  249. this.$emit('cancel');
  250. }
  251. },
  252. }
  253. </script>
  254. <style lang="scss" scoped>
  255. @import "../../libs/css/components.scss";
  256. </style>