// components/form-picker/form-picker.ts interface Option { label: string; value: any; checked?: boolean; disabled?: boolean; mutex?: boolean; }; const filtration = (selected: string[] | Set, options: Option[], option?: Option): string[] => { const _selected = new Set(selected); if (option) { if (_selected.has(option.value)) { _selected.delete(option.value); } else { if (option.mutex) { _selected.clear(); } else { for (const option of options) { if (option.mutex) _selected.delete(option.value); } } _selected.add(option.value); } } return [..._selected]; } const reset = (values: Option[] | Option | string, options: Option[]) => { const selected = new Set(); const _values = Array.isArray(values) ? values : values ? [values] : []; for (const item of _values) { const value = typeof item === 'object' ? item?.value : item; if (value && options.find(item => item.value === value)) selected.add(value); } for (const option of options) { if (option.checked) selected.add(option.value); } return filtration(selected, options); } Component({ options: { multipleSlots: true, }, lifetimes: { attached() { } }, /** * 组件的属性列表 */ properties: { visible: { type: Boolean, value: false }, title: { type: String, value: '' }, value: { type: Array, value: [] }, options: { type: Array, value: [] }, optionsColumns: { type: Number, value: 1 }, itemHeight: { type: Number, value: 64 }, multiple: { type: Boolean, value: true }, closeOnOverlayClick: { type: Boolean, value: true }, }, /** * 组件的初始数据 */ data: { containerHeight: 350, gap: 8, selected: [] as any[], replenish: {} as AnyObject, showReplenish: false, replenishValue: '', offset: 0, }, observers: { 'options,optionsColumns,itemHeight'(options, columns, height) { const rows = Math.ceil(options.length / columns); this.setData({ containerHeight: Math.min(rows * height + (rows - 1) * this.data.gap, 350) }) }, 'value, options'(values: Option[] | Option | string, options: Option[]) { this.setData({ selected: reset(values, options) }); } }, /** * 组件的方法列表 */ methods: { handle(event: WechatMiniprogram.TouchEvent) { const index = event.currentTarget.dataset.index; const option = this.data.options[index]; if (option.disabled) return; const handle = () => { if (this.data.multiple) { this.setData({ selected: filtration(this.data.selected, this.data.options, option) }); } else { this.setData({ selected: this.data.selected.includes(option.value) ? [] : [option.value] }); } } if (option.label === '其他') { if (this.data.selected.includes(option.value)) { handle(); this.setData({ [`replenish.${option.value}`]: '' }) } else { this.setData({ showReplenish: true, replenishValue: '' }); this.onSubConfirm = () => { if (this.data.replenishValue) { handle(); this.setData({ [`replenish.${option.value}`]: this.data.replenishValue }) } this.onSubCancel(); } } } else { handle() } }, onConfirm() { const get = (option: Option) => { if (!option) return null; const replenish = this.data.replenish[option.value]; return { label: replenish ? replenish : option.label, value: replenish ? `${option.value}:${replenish}` : option.value } }; this.setData({ visible: false }) this.triggerEvent('confirm', { selected: this.data.selected, options: this.data.selected .map(value => get(this.data.options.find(item => item.value === value))) .filter(Boolean), }) this.triggerEvent('close', { trigger: 'confirm-btn' }); }, onCancel() { this.setData({ visible: false }); this.triggerEvent('cancel'); this.triggerEvent('close', { trigger: 'cancel-btn' }); }, onSubConfirm() { }, onSubCancel() { this.setData({ showReplenish: false, offset: 0 }); }, onVisibleChange(event: any) { if (!event.detail.visible) { this.triggerEvent('close', { trigger: event.detail.trigger }) setTimeout(() => { this.setData({ selected: reset(this.data.value, this.data.options), offset: 0 }); }, 100) }; }, onBlur() { this.setData({ offset: 0 }) }, onKeyboardheightchange(event: any) { const _height = event?.detail?.height; if (_height !== this.data.offset) this.setData({ offset: _height }); } } })