areapicker.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import type { Area } from '../../../../utils/area'
  2. Component({
  3. //@ts-ignore
  4. codeList: [],
  5. options: {
  6. multipleSlots: true,
  7. },
  8. properties: {
  9. show: {
  10. type: Boolean,
  11. observer(show) {
  12. if (!show) return
  13. this.setData({ pickerValue: [] }, () => {
  14. this.codeList = this.splitCode(this.data.value)
  15. this.updateDivisions()
  16. })
  17. },
  18. },
  19. title: {
  20. type: String,
  21. value: '选择地区',
  22. },
  23. value: {
  24. type: String,
  25. value: '',
  26. },
  27. areaData: {
  28. type: Array,
  29. value: [],
  30. observer() {
  31. if (!this.data.show) return
  32. this.updateDivisions()
  33. },
  34. },
  35. columns: {
  36. type: Number,
  37. value: 3,
  38. },
  39. columnsName: {
  40. type: Array,
  41. value: ['省市/地区', '城市', '区'],
  42. },
  43. useLoadingSlot: Boolean,
  44. },
  45. data: {
  46. pickerValue: [] as Area[],
  47. pickerColumnData: [] as Area[],
  48. hightLightIndex: [] as number[],
  49. loading: false,
  50. scrollTop: 0,
  51. },
  52. methods: {
  53. splitCode(value: string) {
  54. const codeList = []
  55. for (let i = 1; i <= this.data.columns; i++) {
  56. let code = value.slice(0, i * 2)
  57. if (code.length < i * 2) {
  58. code = `${value.slice(0, (i - 1) * 2)}00`
  59. }
  60. while (code.length < this.data.value.length) {
  61. code += '0'
  62. }
  63. codeList.push(code)
  64. }
  65. return codeList
  66. },
  67. updateDivisions() {
  68. const { pickerValue } = this.data
  69. const area = pickerValue[pickerValue.length - 1]
  70. let currentAreaData = []
  71. if (pickerValue.length === 0) {
  72. currentAreaData = this.data.areaData
  73. } else {
  74. if (area && area.children && area.children.length > 0) {
  75. currentAreaData = [...area.children]
  76. } else if (area && !area.children) {
  77. const newArea = this.getNewAreaByPickerValue(pickerValue).area
  78. if (newArea && newArea.children) {
  79. area.children = newArea.children
  80. currentAreaData = [...(area.children || [])]
  81. }
  82. }
  83. }
  84. if (currentAreaData.length > 0) {
  85. const pickerColumnData = currentAreaData[0].code
  86. ? [{ name: '', children: currentAreaData }]
  87. : currentAreaData
  88. let oldPickedIndexes = [-1, -1]
  89. if (this.codeList.length > pickerValue.length) {
  90. for (const gi in pickerColumnData) {
  91. for (const ai in pickerColumnData[gi].children) {
  92. //@ts-ignore
  93. if (pickerColumnData[gi].children[ai].code === this.codeList[pickerValue.length]) {
  94. oldPickedIndexes = [+gi, +ai]
  95. break
  96. }
  97. }
  98. if (oldPickedIndexes[0] > -1) break
  99. }
  100. }
  101. this.setData({ loading: false, pickerColumnData }, () => {
  102. return new Promise<number>((resolve) => {
  103. if (oldPickedIndexes[0] > -1) {
  104. this.createSelectorQuery()
  105. .select('#area-item-0-0')
  106. .boundingClientRect()
  107. .select(`#area-item-${oldPickedIndexes.join('-')}`)
  108. .boundingClientRect()
  109. .exec((res) => {
  110. if (res[0] && res[1]) {
  111. const offsetTop = res[1].top - res[0].top
  112. const diff = res[1].height
  113. resolve(offsetTop > diff ? offsetTop - diff : 0)
  114. } else {
  115. resolve(0)
  116. }
  117. })
  118. } else resolve(0)
  119. }).then((scrollTop) => {
  120. this.setData({
  121. hightLightIndex: oldPickedIndexes,
  122. scrollTop,
  123. })
  124. })
  125. })
  126. } else {
  127. this.setData({ loading: true, pickerColumnData: [] }, () => {
  128. if (area && area.code) {
  129. this.triggerEvent('pullchildren', { parentCode: area.code })
  130. } else {
  131. this.triggerEvent('pullchildren', {})
  132. }
  133. })
  134. }
  135. },
  136. getNewAreaByPickerValue(pickerValue: Area[]) {
  137. let { areaData } = this.data as any
  138. const areas = []
  139. for (const pickedArea of pickerValue) {
  140. const _areaData =
  141. areaData.length > 0 && areaData[0].code ? [{ name: '', children: areaData }] : areaData
  142. let newArea
  143. for (const gi in _areaData) {
  144. for (const ai in _areaData[gi].children) {
  145. if (_areaData[gi].children[ai].code === pickedArea.code) {
  146. newArea = _areaData[gi].children[ai]
  147. break
  148. }
  149. }
  150. if (newArea) break
  151. }
  152. if (!newArea) break
  153. areaData = newArea.children || []
  154. areas.push(newArea)
  155. }
  156. return { area: areas[areas.length - 1], areas }
  157. },
  158. onChange(e: WechatMiniprogram.CustomEvent<{}, {}, { gi: number; ai: number }>) {
  159. const { gi, ai } = e.currentTarget.dataset
  160. const area = this.data.pickerColumnData[gi].children![ai]
  161. const pickerValue = this.data.pickerValue.concat(area)
  162. if (pickerValue.length < this.data.columns) {
  163. this.setData({ pickerValue }, () => {
  164. this.updateDivisions()
  165. })
  166. this.triggerEvent('change', { value: area.code, areas: pickerValue })
  167. } else {
  168. this.setData({ show: false })
  169. this.triggerEvent('confirm', { value: area.code, areas: pickerValue })
  170. }
  171. },
  172. onPickerClick(e: WechatMiniprogram.CustomEvent<{}, {}, { index: number }>) {
  173. const { index } = e.currentTarget.dataset
  174. if (index > this.data.pickerValue.length - 1) return
  175. const pickerValue = this.data.pickerValue.slice(0, index)
  176. this.setData({ pickerValue }, () => {
  177. this.updateDivisions()
  178. })
  179. this.triggerEvent('change', {
  180. value: pickerValue[pickerValue.length - 1]?.code,
  181. areas: pickerValue,
  182. })
  183. },
  184. onClose() {
  185. this.setData({ show: false })
  186. this.triggerEvent('close')
  187. },
  188. },
  189. })