@fesjs/fes-design
Version:
fes-design for PC
251 lines (245 loc) • 7.97 kB
JavaScript
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 };