form-picker.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // components/form-picker/form-picker.ts
  2. interface Option {
  3. label: string;
  4. value: any;
  5. checked?: boolean;
  6. disabled?: boolean;
  7. mutex?: boolean;
  8. };
  9. const filtration = (selected: string[] | Set<string>, options: Option[], option?: Option): string[] => {
  10. const _selected = new Set(selected);
  11. if (option) {
  12. if (_selected.has(option.value)) {
  13. _selected.delete(option.value);
  14. } else {
  15. if (option.mutex) {
  16. _selected.clear();
  17. } else {
  18. for (const option of options) {
  19. if (option.mutex) _selected.delete(option.value);
  20. }
  21. }
  22. _selected.add(option.value);
  23. }
  24. }
  25. return [..._selected];
  26. }
  27. const reset = (values: Option[] | Option | string, options: Option[]) => {
  28. const selected = new Set<string>();
  29. const _values = Array.isArray(values) ? values : values ? [values] : [];
  30. for (const item of _values) {
  31. const value = typeof item === 'object' ? item?.value : item;
  32. if (value && options.find(item => item.value === value)) selected.add(value);
  33. }
  34. for (const option of options) {
  35. if (option.checked) selected.add(option.value);
  36. }
  37. return filtration(selected, options);
  38. }
  39. Component({
  40. options: {
  41. multipleSlots: true,
  42. },
  43. lifetimes: {
  44. attached() {
  45. console.log('attached');
  46. }
  47. },
  48. /**
  49. * 组件的属性列表
  50. */
  51. properties: {
  52. visible: { type: Boolean, value: false },
  53. title: { type: String, value: '' },
  54. value: { type: Array, value: [] },
  55. options: { type: Array, value: [] },
  56. optionsColumns: { type: Number, value: 1 },
  57. itemHeight: { type: Number, value: 64 },
  58. multiple: { type: Boolean, value: true },
  59. closeOnOverlayClick: { type: Boolean, value: true },
  60. },
  61. /**
  62. * 组件的初始数据
  63. */
  64. data: {
  65. containerHeight: 350,
  66. gap: 8,
  67. selected: [] as any[],
  68. replenish: {} as AnyObject,
  69. showReplenish: false,
  70. replenishValue: '',
  71. offset: 0,
  72. },
  73. observers: {
  74. 'options,optionsColumns,itemHeight'(options, columns, height) {
  75. const rows = Math.ceil(options.length / columns);
  76. this.setData({ containerHeight: Math.min(rows * height + (rows - 1) * this.data.gap, 350) })
  77. },
  78. 'value, options'(values: Option[] | Option | string, options: Option[]) {
  79. this.setData({ selected: reset(values, options) });
  80. }
  81. },
  82. /**
  83. * 组件的方法列表
  84. */
  85. methods: {
  86. handle(event: WechatMiniprogram.TouchEvent) {
  87. const index = event.currentTarget.dataset.index;
  88. const option = this.data.options[index];
  89. if (option.disabled) return;
  90. const handle = () => {
  91. if (this.data.multiple) {
  92. this.setData({ selected: filtration(this.data.selected, this.data.options, option) });
  93. } else {
  94. this.setData({ selected: this.data.selected.includes(option.value) ? [] : [option.value] });
  95. }
  96. }
  97. if (option.label === '其他') {
  98. if (this.data.selected.includes(option.value)) {
  99. handle();
  100. this.setData({ [`replenish.${option.value}`]: '' })
  101. } else {
  102. this.setData({ showReplenish: true, replenishValue: '' });
  103. this.onSubConfirm = () => {
  104. if (this.data.replenishValue) {
  105. handle();
  106. this.setData({ [`replenish.${option.value}`]: this.data.replenishValue })
  107. }
  108. this.onSubCancel();
  109. }
  110. }
  111. } else {
  112. handle()
  113. }
  114. },
  115. onConfirm() {
  116. const get = (option: Option) => {
  117. if (!option) return null;
  118. const replenish = this.data.replenish[option.value];
  119. return {
  120. label: replenish ? replenish : option.label,
  121. value: replenish ? `${option.value}:${replenish}` : option.value
  122. }
  123. };
  124. this.setData({ visible: false })
  125. this.triggerEvent('confirm', {
  126. selected: this.data.selected,
  127. options: this.data.selected
  128. .map(value => get(this.data.options.find(item => item.value === value)))
  129. .filter(Boolean),
  130. })
  131. this.triggerEvent('close', { trigger: 'confirm-btn' });
  132. },
  133. onCancel() {
  134. this.setData({ visible: false });
  135. this.triggerEvent('cancel');
  136. this.triggerEvent('close', { trigger: 'cancel-btn' });
  137. },
  138. onSubConfirm() { },
  139. onSubCancel() {
  140. this.setData({ showReplenish: false, offset: 0 });
  141. },
  142. onVisibleChange(event: any) {
  143. if (!event.detail.visible) {
  144. this.triggerEvent('close', { trigger: event.detail.trigger })
  145. setTimeout(() => {
  146. this.setData({
  147. selected: reset(this.data.value, this.data.options),
  148. offset: 0
  149. });
  150. }, 100)
  151. };
  152. },
  153. onBlur() {
  154. this.setData({ offset: 0 })
  155. },
  156. onKeyboardheightchange(event: any) {
  157. const _height = event?.detail?.height;
  158. if (_height !== this.data.offset) this.setData({ offset: _height });
  159. }
  160. }
  161. })