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