mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
* feat(ElTreeSelect): add tree select base component * refactor(ElTreeSelect): use render function and move select/tree props to them self module * fix(ElTreeSelect): init value not checked * fix(ElTreeSelect): `toArray` ignores valid values * fix(ElTreeSelect): expose not working when defined on mounted * fix(ElTreeSelect): watch `modelValue` deep * test(ElTreeSelect): add base unit test * perf(ElTreeSelect): default slot should be a function * fix(ElTreeSelect): `onNodeClick` can not call, * test(ElTreeSelect): update unit test * fix(ElTreeSelect): `onNodeClick` can not call, * fix(ElTreeSelect): remove folder node when `checkStrictly` is false * feat(ElTreeSelect): export `ElTreeSelect` * fix(ElTreeSelect): `filterMethod` conflicts with `filterNodeMethod` * docs(ElTreeSelect): add component docs * fix(ElTreeSelect): fix lint * docs(ElTreeSelect): fix lazy loading requires non-leaf nodes, and change mock labels * docs(ElTreeSelect): the link address of the attributes is incorrect * docs(ElTreeSelect): `dropdown` doesn't need the `-` symbol * refactor(ElTreeSelect): use alias path and make sure vue is above to components * refactor(ElTreeSelect): use a unified namespace for styles * docs(ElTreeSelect): change option labels in default slots * refactor(ElTreeSelect): import `ElOption` using unified entry and change the way to override the select click event * style(ElTreeSelect): sort imports * docs(ElTreeSelect): update the documentation for special codes * refactor(ElTreeSelect): keep it consistent with the select style * refactor(ElTreeSelect): use `isFunction` from `@element-plus/utils` * refactor(ElTreeSelect): use single closing tag when no subset * docs(ElTreeSelect): set `TreeSelect` promotion as `2.1.8`
137 lines
3.7 KiB
TypeScript
137 lines
3.7 KiB
TypeScript
import { computed, nextTick, toRefs, watch } from 'vue'
|
|
import { isEqual, pick } from 'lodash-unified'
|
|
import { UPDATE_MODEL_EVENT } from '@element-plus/constants'
|
|
import { isFunction } from '@element-plus/utils'
|
|
import ElTree from '@element-plus/components/tree'
|
|
import TreeSelectOption from './tree-select-option'
|
|
import type { Ref } from 'vue'
|
|
import type ElSelect from '@element-plus/components/select'
|
|
import type Node from '@element-plus/components/tree/src/model/node'
|
|
import type { TreeNodeData } from '@element-plus/components/tree/src/tree.type'
|
|
|
|
export const useTree = (
|
|
props,
|
|
{ attrs, slots, emit },
|
|
{
|
|
select,
|
|
tree,
|
|
key,
|
|
}: {
|
|
select: Ref<InstanceType<typeof ElSelect> | undefined>
|
|
tree: Ref<InstanceType<typeof ElTree> | undefined>
|
|
key: Ref<string>
|
|
}
|
|
) => {
|
|
watch(
|
|
() => props.modelValue,
|
|
() => {
|
|
if (props.showCheckbox) {
|
|
nextTick(() => {
|
|
const treeInstance = tree.value
|
|
if (
|
|
treeInstance &&
|
|
!isEqual(
|
|
treeInstance.getCheckedKeys(),
|
|
toValidArray(props.modelValue)
|
|
)
|
|
) {
|
|
treeInstance.setCheckedKeys(toValidArray(props.modelValue))
|
|
}
|
|
})
|
|
}
|
|
},
|
|
{
|
|
immediate: true,
|
|
deep: true,
|
|
}
|
|
)
|
|
|
|
const propsMap = computed(() => ({
|
|
value: key.value,
|
|
...props.props,
|
|
}))
|
|
|
|
const getNodeValByProp = (
|
|
prop: 'value' | 'label' | 'children' | 'disabled' | 'isLeaf',
|
|
data: TreeNodeData
|
|
) => {
|
|
const propVal = propsMap.value[prop]
|
|
if (isFunction(propVal)) {
|
|
return propVal(
|
|
data,
|
|
tree.value?.getNode(getNodeValByProp('value', data)) as Node
|
|
)
|
|
} else {
|
|
return data[propVal as string]
|
|
}
|
|
}
|
|
|
|
return {
|
|
...pick(toRefs(props), Object.keys(ElTree.props)),
|
|
...attrs,
|
|
nodeKey: key,
|
|
defaultExpandedKeys: computed(() =>
|
|
props.defaultExpandedKeys
|
|
? props.defaultExpandedKeys.concat(props.modelValue)
|
|
: toValidArray(props.modelValue)
|
|
),
|
|
renderContent: (h, { node, data, store }) => {
|
|
return h(
|
|
TreeSelectOption,
|
|
{
|
|
value: getNodeValByProp('value', data),
|
|
label: getNodeValByProp('label', data),
|
|
disabled: getNodeValByProp('disabled', data),
|
|
},
|
|
props.renderContent
|
|
? () => props.renderContent(h, { node, data, store })
|
|
: slots.default
|
|
? () => slots.default({ node, data, store })
|
|
: undefined
|
|
)
|
|
},
|
|
filterNodeMethod: (value, data, node) => {
|
|
if (props.filterNodeMethod)
|
|
return props.filterNodeMethod(value, data, node)
|
|
if (!value) return true
|
|
return getNodeValByProp('label', data)?.includes(value)
|
|
},
|
|
onNodeClick: (data, node, e) => {
|
|
attrs.onNodeClick?.(data, node, e)
|
|
|
|
if (props.checkStrictly || node.isLeaf) {
|
|
if (!getNodeValByProp('disabled', data)) {
|
|
const option = select.value?.options.get(
|
|
getNodeValByProp('value', data)
|
|
)
|
|
select.value?.handleOptionSelect(option, true)
|
|
}
|
|
} else {
|
|
e.ctx.handleExpandIconClick()
|
|
}
|
|
},
|
|
onCheck: (data, params) => {
|
|
attrs.onCheck?.(data, params)
|
|
|
|
// remove folder node when `checkStrictly` is false
|
|
const checkedKeys = !props.checkStrictly
|
|
? tree.value?.getCheckedKeys(true)
|
|
: params.checkedKeys
|
|
|
|
const value = getNodeValByProp('value', data)
|
|
emit(
|
|
UPDATE_MODEL_EVENT,
|
|
props.multiple
|
|
? checkedKeys
|
|
: checkedKeys.includes(value)
|
|
? value
|
|
: undefined
|
|
)
|
|
},
|
|
}
|
|
}
|
|
|
|
function toValidArray(val: any) {
|
|
return Array.isArray(val) ? val : val || val === 0 ? [val] : []
|
|
}
|