UNPKG

@fesjs/fes-design

Version:
401 lines (391 loc) 16 kB
import _defineProperty from '@babel/runtime/helpers/esm/defineProperty'; import { defineComponent, computed, ref, watch, unref, shallowRef, triggerRef, resolveComponent, openBlock, createElementBlock, normalizeClass, createVNode, withCtx, normalizeStyle, Fragment, withDirectives, withModifiers, vShow, createElementVNode, toDisplayString, createBlock } from 'vue'; import { debounce } from 'lodash-es'; import getPrefixCls from '../_util/getPrefixCls'; import { useTheme } from '../_theme/useTheme'; import { useArrayModel, useNormalModel } from '../_util/use/useModel'; import { UPDATE_MODEL_EVENT, CHANGE_EVENT } from '../_util/constants'; import useFormAdaptor from '../_util/use/useFormAdaptor'; import Popper from '../popper'; import SelectTrigger from '../select-trigger'; import Tree from '../tree/tree'; import Scrollbar from '../scrollbar/scrollbar.js'; import { selectProps } from '../select/props'; import { treeProps } from '../tree/props'; import { useLocale } from '../config-provider/useLocale'; import { noop } from '../_util/utils'; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } const prefixCls = getPrefixCls('select-tree'); /** multiple 时,modelValue 的类型 */ const selectTreeProps = _objectSpread(_objectSpread(_objectSpread({}, selectProps), treeProps), {}, { modelValue: { type: [String, Number, Array] }, showPath: { type: Boolean, default: false }, emitPath: { type: Boolean, default: false } }); const COMPONENT_NAME = 'FSelectTree'; var script = defineComponent({ name: COMPONENT_NAME, components: { Popper, SelectTrigger, Tree, Scrollbar }, props: selectTreeProps, emits: [UPDATE_MODEL_EVENT, CHANGE_EVENT, 'update:expandedKeys', 'removeTag', 'visibleChange', 'focus', 'blur', 'clear', 'filter'], setup(props, _ref) { let { emit, attrs } = _ref; useTheme(); const { validate, isError, isFormDisabled } = useFormAdaptor({ valueType: computed(() => props.multiple ? 'array' : 'string') }); const isOpened = ref(false); // 与 props 中 modelValue 类型保持一致 const [currentValue, updateCurrentValue] = props.multiple ? useArrayModel(props, emit) : useNormalModel(props, emit); const [currentExpandedKeys] = useNormalModel(props, emit, { prop: 'expandedKeys' }); const filterText = ref(''); const { t } = useLocale(); const inputPlaceholder = computed(() => props.placeholder || t('select.placeholder')); const listEmptyText = computed(() => props.emptyText || t('select.emptyText')); const innerDisabled = computed(() => props.disabled || isFormDisabled.value); watch(isOpened, () => { emit('visibleChange', unref(isOpened)); // trigger 在mounted 之后可能会改变 if (isOpened.value && triggerDomRef.value) { triggerWidth.value = triggerDomRef.value.$el.offsetWidth; } }); const handleChange = () => { emit(CHANGE_EVENT, currentValue.value); validate(CHANGE_EVENT); }; const nodeList = shallowRef(new Map()); const onChangeNodeList = data => { nodeList.value = data; triggerRef(nodeList); }; const getCurrentValueByKeys = function () { var _nodeList$value$get; let keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; if (props.multiple) { return keys.map(key => { if (props.emitPath) { const node = nodeList.value.get(key); return [...((node === null || node === void 0 ? void 0 : node.indexPath) || [key])]; } return key; }); } return props.emitPath ? [...(((_nodeList$value$get = nodeList.value.get(keys[0])) === null || _nodeList$value$get === void 0 ? void 0 : _nodeList$value$get.indexPath) || [])] : keys[0]; }; const treeSelectable = computed(() => !props.multiple); const treeCheckable = computed(() => props.multiple); const selectedKeys = computed(() => { if (!props.multiple) { if (props.emitPath && Array.isArray(currentValue.value)) { return [currentValue.value[currentValue.value.length - 1]]; } return currentValue.value ? [currentValue.value] : []; } return []; }); const checkedKeys = computed(() => { var _currentValue$value; if (props.multiple && (_currentValue$value = currentValue.value) !== null && _currentValue$value !== void 0 && _currentValue$value.length) { const keys = currentValue.value.map(item => props.emitPath && Array.isArray(item) ? item[item.length - 1] : item); return keys; } return []; }); watch([() => props.checkStrictly, () => props.emitPath], () => { const value = props.multiple && props.cascade || props.emitPath ? [] : null; updateCurrentValue(value); handleChange(); }); const handleClear = () => { const value = props.multiple ? [] : null; if (props.multiple ? currentValue.value.length : currentValue.value !== null) { updateCurrentValue(value); handleChange(); } emit('clear'); }; const handleSelect = data => { if (innerDisabled.value) { return; } filterText.value = ''; if (!props.multiple) { isOpened.value = false; } updateCurrentValue(getCurrentValueByKeys(data.selectedKeys)); handleChange(); }; const handleCheck = data => { if (innerDisabled.value) { return; } filterText.value = ''; if (!props.multiple) { isOpened.value = false; } updateCurrentValue(getCurrentValueByKeys(data.checkedKeys)); handleChange(); }; /** 节点目标值 */ const targetValues = computed(() => { const values = props.multiple ? currentValue.value : [currentValue.value]; if (props.emitPath) { // 获取选中节点 return values.map(item => item[item.length - 1]); // TODO: TreeNodeKey 类型是否不应包含 number } return values; }); const handleRemove = value => { if (!props.multiple) { return; } const findIndex = targetValues.value.indexOf(value); if (findIndex !== -1) { emit('removeTag', value); const values = [...targetValues.value]; values.splice(findIndex, 1); updateCurrentValue(getCurrentValueByKeys(values)); handleChange(); } }; const selectedOptions = computed(() => { const nodeListValue = nodeList.value; return targetValues.value.map(val => { const node = nodeListValue.get(val); if (!node) { return; } if (props.showPath) { var _node$indexPath; return _objectSpread(_objectSpread({}, node), {}, { label: (_node$indexPath = node.indexPath) === null || _node$indexPath === void 0 ? void 0 : _node$indexPath.map(item => { var _nodeListValue$get; return ((_nodeListValue$get = nodeListValue.get(item)) === null || _nodeListValue$get === void 0 ? void 0 : _nodeListValue$get.label) || item; }).join('/') }); } else { return node; } }).filter(Boolean); }); const focus = e => { emit('focus', e); validate('focus'); }; const blur = e => { if (isOpened.value) { isOpened.value = false; } emit('blur', e); validate('blur'); }; const handleFilterTextChange = val => { filterText.value = val; emit('filter', val); }; const refTree = ref(null); watch(filterText, debounce(() => { refTree.value.filter(filterText.value); }, 300)); const filterMethod = computed(() => { if (!props.filterable) { return noop; } if (props.filter) { return props.filter; } if (props.data.some(option => typeof option[props.labelField] !== 'string')) { console.warn(`[${COMPONENT_NAME}]:label 存在自定义渲染,需要传入 filter`); return () => false; } return (value, node) => { return node.label.indexOf(value) !== -1; }; }); const triggerDomRef = ref(); const triggerWidth = ref(0); const dropdownStyle = computed(() => { const style = {}; if (triggerWidth.value) { style['min-width'] = `${triggerWidth.value}px`; } return style; }); return { prefixCls, isOpened, currentValue, currentExpandedKeys, handleRemove, handleClear, selectedOptions, focus, blur, handleFilterTextChange, treeSelectable, selectedKeys, treeCheckable, handleSelect, handleCheck, checkedKeys, refTree, filterMethod, triggerDomRef, dropdownStyle, onChangeNodeList, inputPlaceholder, listEmptyText, isError, attrs, innerDisabled, filterText }; } }); function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_SelectTrigger = resolveComponent("SelectTrigger"); const _component_Tree = resolveComponent("Tree"); const _component_Scrollbar = resolveComponent("Scrollbar"); const _component_Popper = resolveComponent("Popper"); return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.prefixCls) }, [createVNode(_component_Popper, { modelValue: _ctx.isOpened, "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => _ctx.isOpened = $event), trigger: "click", placement: "bottom-start", popperClass: `${_ctx.prefixCls}-popper`, appendToContainer: _ctx.appendToContainer, getContainer: _ctx.getContainer, offset: 4, hideAfter: 0, disabled: _ctx.innerDisabled, lazy: false }, { trigger: withCtx(() => [createVNode(_component_SelectTrigger, { ref: "triggerDomRef", selectedOptions: _ctx.selectedOptions, disabled: _ctx.innerDisabled, clearable: _ctx.clearable, isOpened: _ctx.isOpened, multiple: _ctx.multiple, placeholder: _ctx.inputPlaceholder, filterable: _ctx.filterable, collapseTags: _ctx.collapseTags, collapseTagsLimit: _ctx.collapseTagsLimit, tagBordered: _ctx.tagBordered, class: normalizeClass([{ 'is-error': _ctx.isError }, _ctx.attrs.class]), style: normalizeStyle(_ctx.attrs.style), renderTag: _ctx.$slots.tag, onRemove: _ctx.handleRemove, onClear: _ctx.handleClear, onFocus: _ctx.focus, onBlur: _ctx.blur, onInput: _ctx.handleFilterTextChange }, null, 8 /* PROPS */, ["selectedOptions", "disabled", "clearable", "isOpened", "multiple", "placeholder", "filterable", "collapseTags", "collapseTagsLimit", "tagBordered", "class", "style", "renderTag", "onRemove", "onClear", "onFocus", "onBlur", "onInput"])]), default: withCtx(() => [_ctx.virtualList && !_ctx.inline ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [withDirectives(createVNode(_component_Tree, { ref: "refTree", expandedKeys: _ctx.currentExpandedKeys, "onUpdate:expandedKeys": _cache[0] || (_cache[0] = $event => _ctx.currentExpandedKeys = $event), selectedKeys: _ctx.selectedKeys, checkedKeys: _ctx.checkedKeys, data: _ctx.data, defaultExpandAll: _ctx.defaultExpandAll, accordion: _ctx.accordion, selectable: _ctx.treeSelectable, checkable: _ctx.treeCheckable, checkStrictly: _ctx.checkStrictly, cascade: _ctx.cascade, multiple: _ctx.multiple, childrenField: _ctx.childrenField, valueField: _ctx.valueField, labelField: _ctx.labelField, filterMethod: _ctx.filterMethod, inline: _ctx.inline, remote: _ctx.remote, loadData: _ctx.loadData, virtualList: "", style: normalizeStyle(_ctx.dropdownStyle), class: normalizeClass(`${_ctx.prefixCls}-dropdown is-max-height`), filterText: _ctx.filterText, filterTextHighlight: _ctx.filterTextHighlight, "onUpdate:nodeList": _ctx.onChangeNodeList, onSelect: _ctx.handleSelect, onCheck: _ctx.handleCheck, onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"])) }, null, 8 /* PROPS */, ["expandedKeys", "selectedKeys", "checkedKeys", "data", "defaultExpandAll", "accordion", "selectable", "checkable", "checkStrictly", "cascade", "multiple", "childrenField", "valueField", "labelField", "filterMethod", "inline", "remote", "loadData", "style", "class", "filterText", "filterTextHighlight", "onUpdate:nodeList", "onSelect", "onCheck"]), [[vShow, _ctx.data.length]]), withDirectives(createElementVNode("div", { class: normalizeClass(`${_ctx.prefixCls}-null`), onMousedown: _cache[2] || (_cache[2] = withModifiers(() => {}, ["prevent"])) }, toDisplayString(_ctx.listEmptyText), 35 /* TEXT, CLASS, NEED_HYDRATION */), [[vShow, !_ctx.data.length]])], 64 /* STABLE_FRAGMENT */)) : (openBlock(), createBlock(_component_Scrollbar, { key: 1, containerStyle: _ctx.dropdownStyle, containerClass: `${_ctx.prefixCls}-dropdown`, onMousedown: _cache[4] || (_cache[4] = withModifiers(() => {}, ["prevent"])) }, { default: withCtx(() => [withDirectives(createVNode(_component_Tree, { ref: "refTree", expandedKeys: _ctx.currentExpandedKeys, "onUpdate:expandedKeys": _cache[3] || (_cache[3] = $event => _ctx.currentExpandedKeys = $event), selectedKeys: _ctx.selectedKeys, checkedKeys: _ctx.checkedKeys, data: _ctx.data, defaultExpandAll: _ctx.defaultExpandAll, accordion: _ctx.accordion, selectable: _ctx.treeSelectable, checkable: _ctx.treeCheckable, checkStrictly: _ctx.checkStrictly, cascade: _ctx.cascade, multiple: _ctx.multiple, childrenField: _ctx.childrenField, valueField: _ctx.valueField, labelField: _ctx.labelField, filterMethod: _ctx.filterMethod, inline: _ctx.inline, remote: _ctx.remote, loadData: _ctx.loadData, filterText: _ctx.filterText, filterTextHighlight: _ctx.filterTextHighlight, "onUpdate:nodeList": _ctx.onChangeNodeList, onSelect: _ctx.handleSelect, onCheck: _ctx.handleCheck }, null, 8 /* PROPS */, ["expandedKeys", "selectedKeys", "checkedKeys", "data", "defaultExpandAll", "accordion", "selectable", "checkable", "checkStrictly", "cascade", "multiple", "childrenField", "valueField", "labelField", "filterMethod", "inline", "remote", "loadData", "filterText", "filterTextHighlight", "onUpdate:nodeList", "onSelect", "onCheck"]), [[vShow, _ctx.data.length]]), withDirectives(createElementVNode("div", { class: normalizeClass(`${_ctx.prefixCls}-null`) }, toDisplayString(_ctx.listEmptyText), 3 /* TEXT, CLASS */), [[vShow, !_ctx.data.length]])]), _: 1 /* STABLE */ }, 8 /* PROPS */, ["containerStyle", "containerClass"]))]), _: 1 /* STABLE */ }, 8 /* PROPS */, ["modelValue", "popperClass", "appendToContainer", "getContainer", "disabled"])], 2 /* CLASS */); } script.render = render; script.__file = "components/select-tree/selectTree.vue"; export { script as default, selectTreeProps };