移动端
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

220 rader
5.3 KiB

  1. <!-- 下拉列表组件 zhao -->
  2. <template>
  3. <div>
  4. <slot/>
  5. <van-popup v-model="popupVisible" position="bottom">
  6. <van-picker
  7. ref="picker"
  8. :title="label"
  9. show-toolbar
  10. :columns="options"
  11. :readonly="readonly"
  12. :value-key="labelKey"
  13. :loading="loading"
  14. @confirm="onConfirm"
  15. @cancel="onCancel"
  16. @change="onChanged"
  17. />
  18. </van-popup>
  19. </div>
  20. </template>
  21. <script>
  22. import request from "@/utils/request";
  23. export default {
  24. name: "Selector",
  25. props: {
  26. value: {
  27. default: null,
  28. },
  29. readonly: {
  30. type: Boolean,
  31. default: false,
  32. },
  33. label: {
  34. type: String,
  35. default: '',
  36. },
  37. columns: { // 列表数据 Array
  38. type: Array,
  39. default: () => [],
  40. },
  41. labelKey: { // 名称键名 String
  42. type: String,
  43. default: 'label',
  44. },
  45. valueKey: { // 值键名 String
  46. type: String,
  47. default: 'value',
  48. },
  49. remoteUrl: { // 远程列表加载地址 String, 函数, 或 Promise
  50. type: [String, Function, Object],
  51. default: null,
  52. },
  53. onRemoteResponse: { // 远程获取到结果的处理回调 String|Function 如果是函数需返回数组, 如果是字符串支持.分割
  54. type: [String, Function],
  55. default: null,
  56. },
  57. clear: { // 点击取消时清空绑定值
  58. type: Boolean,
  59. default: false,
  60. },
  61. visible: { // 打开状态
  62. type: Boolean,
  63. default: false,
  64. },
  65. },
  66. watch: {
  67. value: function (newVal, oldVal) {
  68. this.internalValue = newVal;
  69. this.visibleValue = newVal;
  70. this.syncIndex();
  71. },
  72. columns: function (newVal, oldVal) {
  73. this.syncIndex();
  74. },
  75. remoteUrl: function (newVal, oldVal) {
  76. this.requestRemote();
  77. },
  78. onRemoteResponse: function (newVal, oldVal) {
  79. this.parseRemote();
  80. },
  81. visible: function (newVal, oldVal) {
  82. if(newVal != this.popupVisible)
  83. this.popupVisible = newVal;
  84. },
  85. popupVisible: function (newVal, oldVal) {
  86. if(newVal != this.visible)
  87. this.$emit('update:visible', newVal);
  88. }
  89. },
  90. created() {
  91. if(this.remoteUrl)
  92. this.requestRemote();
  93. },
  94. data() {
  95. return {
  96. popupVisible: false,
  97. internalValue: this.value,
  98. visibleValue: '',
  99. defaultIndex: 0,
  100. remoteColumns: null,
  101. loading: false,
  102. remoteResponse: null,
  103. };
  104. },
  105. methods: {
  106. openPopup() {
  107. if(!this.readonly)
  108. {
  109. this.popupVisible = true;
  110. this.$nextTick(() => {
  111. this.$refs.picker.setIndexes([this.defaultIndex]);
  112. })
  113. }
  114. },
  115. closePopup() {
  116. this.popupVisible = false;
  117. },
  118. onChanged(data) {
  119. this.$emit('change', data);
  120. },
  121. onConfirm(data) {
  122. this.syncValue(data);
  123. this.$emit('input', this.internalValue);
  124. this.$emit('confirm', this.internalValue);
  125. this.closePopup();
  126. },
  127. onCancel() {
  128. this.closePopup();
  129. if(this.clear)
  130. {
  131. this.visibleValue = '';
  132. this.internalValue = null;
  133. this.$emit('input', this.internalValue);
  134. }
  135. this.$emit('cancel');
  136. },
  137. getValue(data) {
  138. return typeof(data) === 'object' && this.valueKey ? data[this.valueKey] : data;
  139. },
  140. getLabel(data) {
  141. return typeof(data) === 'object' && this.labelKey ? data[this.labelKey] : data;
  142. },
  143. syncValue(data) {
  144. this.internalValue = this.getValue(data);
  145. this.visibleValue = this.getLabel(data);
  146. },
  147. syncIndex() {
  148. let columns = this.getColumns();
  149. if(!columns)
  150. return -1;
  151. for(let i in columns)
  152. {
  153. if(this.getValue(columns[i]) == this.internalValue) {
  154. this.defaultIndex = i;
  155. this.visibleValue = this.getLabel(columns[i]);
  156. this.onChanged(columns[i]);
  157. return i;
  158. }
  159. }
  160. if(1) // 不存在
  161. {
  162. this.defaultIndex = -1;
  163. this.visibleValue = this.internalValue;
  164. this.onChanged(null);
  165. }
  166. return -1;
  167. },
  168. getColumns() {
  169. return this.columns ? this.columns : this.remoteColumns;
  170. },
  171. requestRemote() {
  172. if(!this.remoteUrl)
  173. return;
  174. this.loading = true;
  175. this.remoteColumns = [];
  176. let promise = typeof(this.remoteUrl) === 'function' ? this.remoteUrl() : (this.remoteUrl instanceof Promise ? this.remoteUrl : request(this.remoteUrl));
  177. promise.then((resp) => {
  178. this.remoteResponse = resp;
  179. this.parseRemote();
  180. this.syncIndex();
  181. }).catch((e) => {
  182. console.error(e);
  183. }).finally(() => {
  184. this.loading = false;
  185. })
  186. },
  187. parseRemote() {
  188. if(!this.remoteResponse)
  189. return;
  190. let type = typeof(this.onRemoteResponse);
  191. if(type === 'function')
  192. this.remoteColumns = this.onRemoteResponse(this.remoteResponse);
  193. else if(type === 'string')
  194. {
  195. let arr = this.onRemoteResponse.split('.');
  196. let ptr = this.remoteResponse;
  197. for(let i in arr)
  198. {
  199. ptr = this.remoteResponse[arr[i]];
  200. }
  201. this.remoteColumns = ptr;
  202. }
  203. else
  204. this.remoteColumns = this.remoteResponse;
  205. },
  206. },
  207. computed: {
  208. options() {
  209. return this.columns ? this.columns : (this.remoteColumns || []);
  210. }
  211. }
  212. }
  213. </script>
  214. <style scoped>
  215. </style>