UNPKG

@fesjs/fes-design

Version:
251 lines (245 loc) 7.97 kB
import { defineComponent, inject, watch, createVNode, ref, createTextVNode } from 'vue'; import { isNil } from 'lodash-es'; import Button from '../button'; import { SearchOutlined, RightOutlined, LeftOutlined } from '../icon'; import Empty from '../empty'; import Checkbox from '../checkbox'; import Input from '../input'; import VirtualList from '../virtual-list'; import { COMPONENT_NAME, TRANSFER_INJECT_KEY, COMPONENT_CLASS, COMPONENT_TWO_WAY_CLASS } from './const'; import { cls } from './utils'; import { TransferCheckbox, calcCheckStatus } from './checkbox'; import { useCheckValueWithCheckbox } from './useCheckValueWithCheckbox'; import { useOptionsFilter } from './useOptionsFilter'; const usePanelData = _ref => { let { rootProps, type, filter, handleChange } = _ref; const checkValue = ref([]); const options = ref([]); const { checkboxStatus, handleCheckboxChange, handleCheck } = useCheckValueWithCheckbox({ checkValue, options, onCheckboxChange: () => { if (type === 'source') { return; } handleChange({ nextValue: checkValue.value }); } }); const handleListOptionChange = (optionValue, nextValue) => { const nextCheckValue = [...checkValue.value]; if (nextValue) { // add nextCheckValue.push(optionValue); } else { // remove const index = nextCheckValue.findIndex(v => v === optionValue); if (index === -1) { return; } nextCheckValue.splice(index, 1); } checkValue.value = nextCheckValue; handleCheck({ checkedKeys: nextCheckValue }); }; const updateCheckboxStatus = () => { checkboxStatus.value = calcCheckStatus(checkValue.value.length, options.value.length); }; const { filterText, displayOptions } = useOptionsFilter({ options, filter, rootProps }); return { type, checkValue, options, checkboxStatus, handleCheckboxChange, handleListOptionChange, filterText, displayOptions, updateCheckboxStatus }; }; const TwoWayTransfer = defineComponent({ name: COMPONENT_NAME, setup: () => { const { modelValue, rootProps, rootStyle, filter, renderLabel, handleChange, scrollContentHeight } = inject(TRANSFER_INJECT_KEY); const sourcePanel = usePanelData({ rootProps, handleChange, type: 'source', filter: filter.value }); const targetPanel = usePanelData({ rootProps, handleChange, type: 'target', filter: filter.value }); // options 变化时,重置所有数据 watch(() => rootProps.options, nextOptions => { sourcePanel.checkValue.value = []; targetPanel.checkValue.value = []; sourcePanel.options.value = nextOptions; targetPanel.options.value = []; }); const renderOption = (option, panelData) => createVNode("div", { "class": cls('option') }, [createVNode(Checkbox, { "modelValue": panelData.checkValue.value.includes(option.value), "onChange": nextValue => { panelData.handleListOptionChange(option.value, nextValue); } }, null), createVNode("span", { "class": cls('option-label') }, [renderLabel(option)])]); const renderPanel = function (panelData) { let appendClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; const isEmpty = panelData.options.value.length === 0; let content; if (isEmpty) { content = createVNode(Empty, { "class": cls('empty') }, null); } else if (!isNil(scrollContentHeight.value)) { content = createVNode(VirtualList, { "class": cls('panel-list'), "style": { height: `${scrollContentHeight.value}px` }, "dataSources": panelData.displayOptions.value, "dataKey": option => option.value }, { default: _ref2 => { let { source: option } = _ref2; return renderOption(option, panelData); } }); } else { content = createVNode("div", { "class": cls('panel-list') }, [panelData.displayOptions.value.map(option => renderOption(option, panelData))]); } return createVNode("div", { "class": [cls('panel'), isEmpty && cls('empty-panel'), ...appendClass] }, [createVNode("div", { "class": [cls('panel-header')] }, [createVNode(TransferCheckbox, { "modelValue": panelData.checkboxStatus.value, "onUpdate:modelValue": $event => panelData.checkboxStatus.value = $event, "label": '全选', "disabled": panelData.options.value.length === 0, "onChange": panelData.handleCheckboxChange }, null), createVNode("span", { "class": cls('panel-count') }, [panelData.checkValue.value.length, createTextVNode("\xA0/\xA0"), panelData.options.value.length, createTextVNode("\xA0\u9879")])]), rootProps.filterable && createVNode(Input, { "modelValue": panelData.filterText.value, "onUpdate:modelValue": $event => panelData.filterText.value = $event, "class": cls('panel-filter'), "placeholder": '请输入' }, { suffix: () => createVNode(SearchOutlined, null, null) }), content]); }; // 仅更新 modelValue,panel 的数据更新由 watch 完成 const handleTransfer = from => { let nextValue; if (from === 'source') { nextValue = [...targetPanel.options.value.map(_ref3 => { let { value } = _ref3; return value; }), ...sourcePanel.checkValue.value]; } else { nextValue = targetPanel.options.value.map(_ref4 => { let { value } = _ref4; return value; }).filter(value => !targetPanel.checkValue.value.includes(value)); } modelValue.value = nextValue; handleChange({ nextValue }); }; // modelValue 变化时,更新 panel 的 options 和 checkValue watch(modelValue, nextValue => { sourcePanel.options.value = rootProps.options.filter(_ref5 => { let { value } = _ref5; return !nextValue.includes(value); }); targetPanel.options.value = rootProps.options.filter(_ref6 => { let { value } = _ref6; return nextValue.includes(value); }); sourcePanel.checkValue.value = sourcePanel.checkValue.value.filter(value => !nextValue.includes(value)); targetPanel.checkValue.value = targetPanel.checkValue.value.filter(value => nextValue.includes(value)); sourcePanel.updateCheckboxStatus(); targetPanel.updateCheckboxStatus(); }, { immediate: true, deep: true }); const renderActions = () => { return createVNode("div", { "class": cls('actions') }, [createVNode(Button, { "class": cls('action-button'), "type": "primary", "disabled": sourcePanel.checkValue.value.length === 0, "onClick": () => handleTransfer('source') }, { icon: () => createVNode(RightOutlined, { "class": cls('action-button-icon') }, null) }), createVNode(Button, { "class": cls('action-button'), "type": "primary", "disabled": targetPanel.checkValue.value.length === 0, "onClick": () => handleTransfer('target') }, { icon: () => createVNode(LeftOutlined, { "class": cls('action-button-icon') }, null) })]); }; return () => createVNode("div", { "class": [COMPONENT_CLASS, COMPONENT_TWO_WAY_CLASS], "style": rootStyle.value }, [renderPanel(sourcePanel, [cls('source-panel')]), renderActions(), renderPanel(targetPanel, [cls('target-panel')])]); } }); export { TwoWayTransfer as default };