form-picker.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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. },
  69. observers: {
  70. 'options,optionsColumns,itemHeight'(options, columns, height) {
  71. const rows = Math.ceil(options.length / columns);
  72. this.setData({ containerHeight: Math.min(rows * height + (rows - 1) * this.data.gap, 350) })
  73. },
  74. 'value, options'(values: Option[] | Option | string, options: Option[]) {
  75. this.setData({ selected: reset(values, options) });
  76. }
  77. },
  78. /**
  79. * 组件的方法列表
  80. */
  81. methods: {
  82. handle(event: WechatMiniprogram.TouchEvent) {
  83. const index = event.currentTarget.dataset.index;
  84. const option = this.data.options[index];
  85. if (option.disabled) return;
  86. if (this.data.multiple) {
  87. this.setData({ selected: filtration(this.data.selected, this.data.options, option) });
  88. } else {
  89. this.setData({ selected: this.data.selected.includes(option.value) ? [] : [option.value] });
  90. }
  91. },
  92. onConfirm() {
  93. const get = (option: Option) => option ? ({ label: option.label, value: option.value }) : null;
  94. this.setData({ visible: false })
  95. this.triggerEvent('confirm', {
  96. selected: this.data.selected,
  97. options: this.data.selected
  98. .map(value => get(this.data.options.find(item => item.value === value)))
  99. .filter(Boolean),
  100. })
  101. this.triggerEvent('close', { trigger: 'confirm-btn' });
  102. },
  103. onCancel() {
  104. this.setData({ visible: false });
  105. this.triggerEvent('cancel');
  106. this.triggerEvent('close', { trigger: 'cancel-btn' });
  107. },
  108. onVisibleChange(event: any) {
  109. if (!event.detail.visible) {
  110. this.triggerEvent('close', { trigger: event.detail.trigger })
  111. setTimeout(() => { this.setData({ selected: reset(this.data.value, this.data.options) }); }, 100)
  112. };
  113. }
  114. }
  115. })