LocationPicker.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <template>
  2. <!-- <view class="form-item"> -->
  3. <!-- <view class="item-label">
  4. <text class="label-text">{{ label }}</text>
  5. <text v-if="required" class="required">*</text>
  6. </view> -->
  7. <!-- 编辑模式:输入框 -->
  8. <uni-data-picker
  9. v-if="mode === 'edit'"
  10. v-model="innerValue"
  11. :localdata="localData"
  12. :popup-title="popupTitle"
  13. @change="onChange"
  14. >
  15. <u-input
  16. :value="getLocationLabel(innerValue)"
  17. :placeholder="placeholder"
  18. readonly
  19. suffix-icon="arrow-down"
  20. >
  21. <!-- 清除按钮 -->
  22. <template slot="suffix">
  23. <view
  24. v-if="innerValue"
  25. @click.stop="clearAddress"
  26. style="padding: 0 8rpx; display: flex; align-items: center;"
  27. >
  28. <uni-icons type="close" color="#999" size="20"></uni-icons>
  29. </view>
  30. </template>
  31. </u-input>
  32. </uni-data-picker>
  33. <!-- 展示模式:纯文本 -->
  34. <text v-else class="location-text">
  35. {{ getLocationLabel(innerValue) || "无" }}
  36. </text>
  37. <!-- </view> -->
  38. </template>
  39. <script>
  40. import cityRows from '@/utils/data.json';
  41. export default {
  42. name: "LocationPicker",
  43. props: {
  44. mode: { // 新增模式属性
  45. type: String,
  46. default: "edit" // edit / view
  47. },
  48. value: { // v-model
  49. type: [String, Number],
  50. default: ""
  51. },
  52. label: { // 左侧文字
  53. type: String,
  54. default: "所在地"
  55. },
  56. placeholder: {
  57. type: String,
  58. default: "请选择省市区"
  59. },
  60. popupTitle: {
  61. type: String,
  62. default: "请选择省市区"
  63. },
  64. required: {
  65. type: Boolean,
  66. default: false
  67. }
  68. },
  69. data() {
  70. return {
  71. innerValue: this.value,
  72. localData: [] // 省市区树数据
  73. }
  74. },
  75. watch: {
  76. value(newVal) {
  77. this.innerValue = newVal
  78. },
  79. innerValue(newVal) {
  80. this.$emit("input", newVal)
  81. }
  82. },
  83. created() {
  84. this.localData = this.get_city_tree()
  85. },
  86. methods: {
  87. /** 点击选择后的回调 */
  88. onChange(e) {
  89. const lastNode = e.detail.value[e.detail.value.length - 1]
  90. this.innerValue = lastNode.value // 只存最底层的 code
  91. },
  92. /** 清空地址 */
  93. clearAddress() {
  94. this.innerValue = ""
  95. this.$emit("clear")
  96. },
  97. /** 回显文字(递归找路径) */
  98. getLocationLabel(value) {
  99. if (!value) return ""
  100. let label = ""
  101. const traverse = (nodes) => {
  102. for (const node of nodes) {
  103. if (node.value === value) {
  104. label = node.text
  105. return true
  106. }
  107. if (node.children && traverse(node.children)) {
  108. label = node.text + " - " + label
  109. return true
  110. }
  111. }
  112. return false
  113. }
  114. traverse(this.localData)
  115. return label
  116. },
  117. /** 生成树数据 */
  118. get_city_tree() {
  119. let res = []
  120. if (cityRows.length) {
  121. res = this.handleTree(cityRows)
  122. }
  123. return res
  124. },
  125. /** 递归组装树 */
  126. handleTree(data, parent_code = null) {
  127. let res = []
  128. let keys = {
  129. id: "code",
  130. pid: "parent_code",
  131. children: "children",
  132. text: "name",
  133. value: "code"
  134. }
  135. for (let item of data) {
  136. if (parent_code === null) {
  137. // 顶级
  138. if (!item.hasOwnProperty(keys.pid) || item[keys.pid] == parent_code) {
  139. let node = {
  140. text: item[keys.text],
  141. value: item[keys.value],
  142. children: this.handleTree(data, item[keys.id])
  143. }
  144. res.push(node)
  145. }
  146. } else {
  147. // 子级
  148. if (item.hasOwnProperty(keys.pid) && item[keys.pid] == parent_code) {
  149. let node = {
  150. text: item[keys.text],
  151. value: item[keys.value],
  152. children: this.handleTree(data, item[keys.id])
  153. }
  154. res.push(node)
  155. }
  156. }
  157. }
  158. return res
  159. }
  160. }
  161. }
  162. </script>
  163. <style scoped>
  164. .form-item {
  165. display: flex;
  166. flex-direction: column;
  167. /* margin-bottom: 20rpx; */
  168. }
  169. .item-label {
  170. display: flex;
  171. align-items: center;
  172. margin-bottom: 10rpx;
  173. }
  174. .label-text {
  175. font-size: 28rpx;
  176. color: #333;
  177. }
  178. .required {
  179. color: red;
  180. margin-left: 5rpx;
  181. }
  182. .location-text {
  183. /* font-size: 28rpx;
  184. color: #333; */
  185. /* padding: 16rpx 0; */
  186. }
  187. </style>