feat: 组件MsTreeSelect支持选择当前

This commit is contained in:
teukkk 2024-11-13 17:51:13 +08:00 committed by Craftsman
parent 407639f7e2
commit 1d08e57664
4 changed files with 190 additions and 56 deletions

View File

@ -171,6 +171,7 @@ export default function useTreeSelection(selectedModuleProps: SelectedModuleProp
Object.keys(selectedModulesMaps.value).forEach((key) => {
delete selectedModulesMaps.value[key];
});
checkedKeys.value = [];
isCheckedAll.value = false;
}

View File

@ -322,7 +322,7 @@
filterTreeData.value = data.value;
}
nextTick(() => {
treeRef.value?.expandAll(false);
treeRef.value?.expandAll(props.defaultExpandAll ?? false);
});
} else {
updateDebouncedSearch();

View File

@ -1,31 +1,38 @@
<template>
<a-tooltip
:content="getTreeSelectTooltip()"
:disabled="!(modelValue ?? []).length"
:disabled="!(checkedKeys ?? []).length"
position="top"
content-class="tree-select-content"
:mouse-enter-delay="300"
>
<a-trigger
v-model:popup-visible="viewSelectOptionVisible"
:trigger="['click']"
:click-to-close="false"
:popup-translate="[0, 4]"
content-class="arco-trigger-menu tree-select-trigger-content"
:content-style="{ width: `${triggerWidth}px` }"
>
<a-tree-select
:id="treeSelectId"
ref="treeSelectRef"
v-model:model-value="selectValue"
v-model:model-value="checkedKeys"
v-model:input-value="inputValue"
:data="props.data"
:disabled="props.disabled"
:multiple="props.multiple"
:field-names="props.fieldNames"
allow-search
:filter-tree-node="filterTreeNode"
:tree-props="{
virtualListProps: {
height: 200,
threshold: 200,
},
}"
v-bind="$attrs"
:data="treeData"
:max-tag-count="maxTagCount"
@input-value-change="handleInputValueChange"
:multiple="props.multiple"
allow-search
disable-filter
allow-clear
:tree-props="{
virtualListProps: virtualListProps,
}"
:field-names="props.fieldNames"
:placeholder="t('common.pleaseSelect')"
:trigger-props="{ contentClass: 'view-select-trigger' }"
@popup-visible-change="handlePopupVisibleChange"
@input-value-change="handleInputValueChange"
@change="handleChange"
@keyup="handleKeyup"
@clear="handleClear"
@ -33,48 +40,129 @@
<template #label="{ data: slotData }">
<div class="one-line-text">{{ slotData.label }}</div>
</template>
<template #tree-slot-title="node">
<a-tooltip :content="`${node[props?.fieldNames?.title || 'title']}`" position="tr">
<div class="one-line-text max-w-[170px]">{{ node[props?.fieldNames?.title || 'title'] }}</div>
</a-tooltip>
</template>
</a-tree-select>
<template #content>
<MsTree
v-model:checked-keys="checkedKeys"
v-model:halfCheckedKeys="halfCheckedKeys"
:selectable="false"
:data="treeData"
:keyword="inputValue"
:empty-text="t('common.noData')"
:virtual-list-props="virtualListProps"
:field-names="props.fieldNames as MsTreeFieldNames"
default-expand-all
block-node
title-tooltip-position="tr"
:multiple="props.multiple"
:checkable="props.treeCheckable"
:check-strictly="props.treeCheckStrictly"
v-bind="$attrs"
@check="checkNode"
>
<template #title="nodeData">
<div
class="one-line-text w-full cursor-pointer text-[var(--color-text-1)]"
@click="checkNode(checkedKeys, { checked: !checkedKeys.includes(nodeData.id), node: nodeData })"
>
{{ nodeData.name }}
</div>
</template>
<template #extra="nodeData">
<MsButton
v-if="nodeData.children && nodeData.children.length"
@click="selectParent(nodeData, !!checkedKeys.includes(nodeData.id))"
>
{{
checkedKeys.includes(nodeData.id)
? t('ms.case.associate.cancelCurrent')
: t('ms.case.associate.selectCurrent')
}}
</MsButton>
</template>
</MsTree>
</template>
</a-trigger>
</a-tooltip>
</template>
<script lang="ts" setup>
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
import { isEqual } from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue';
import useTreeSelection from '@/components/business/ms-associate-case/useTreeSelection';
import MsTree from '@/components/business/ms-tree/index.vue';
import type { MsTreeFieldNames, MsTreeNodeData } from '@/components/business/ms-tree/types';
import { useI18n } from '@/hooks/useI18n';
import useSelect from '@/hooks/useSelect';
import { filterTreeNode, findNodeByKey } from '@/utils';
import { findNodeByKey, getGenerateId } from '@/utils';
import type { TreeFieldNames, TreeNodeData } from '@arco-design/web-vue';
const props = withDefaults(
defineProps<{
data: TreeNodeData[];
fieldNames?: TreeFieldNames;
disabled?: boolean;
fieldNames?: TreeFieldNames | MsTreeFieldNames;
multiple?: boolean;
shouldCalculateMaxTag?: boolean;
treeCheckStrictly?: boolean;
treeCheckable?: boolean;
}>(),
{
shouldCalculateMaxTag: true,
}
);
const selectValue = defineModel<any>('modelValue', { required: true });
const inputValue = ref('');
const tempInputValue = ref('');
const { t } = useI18n();
const treeSelectId = ref(getGenerateId()); //
const viewSelectOptionVisible = ref(false);
const selectValue = defineModel<Array<string | number>>('modelValue', { required: true, default: [] });
const treeData = defineModel<TreeNodeData[]>('data', { required: true });
const selectedModuleProps = ref({
modulesTree: treeData.value,
moduleCount: {},
});
const { selectedModulesMaps, checkedKeys, halfCheckedKeys, selectParent, checkNode, clearSelector } =
useTreeSelection(selectedModuleProps.value);
const skipSelectValueWatch = ref(false);
watch(
() => selectValue.value,
(newValue) => {
if (!skipSelectValueWatch.value && !isEqual(checkedKeys.value, newValue)) {
clearSelector();
(newValue ?? []).forEach((id) => {
selectedModulesMaps.value[id] = {
selectAll: true,
selectIds: new Set(),
excludeIds: new Set(),
count: 0,
};
});
}
skipSelectValueWatch.value = false;
},
{
immediate: true,
}
);
const treeSelectRef = ref();
const { maxTagCount, calculateMaxTag } = useSelect({
selectRef: treeSelectRef,
selectVal: selectValue,
selectVal: checkedKeys,
});
watch(
() => selectValue.value,
() => {
() => checkedKeys.value,
(newValue) => {
if (!isEqual(selectValue.value, newValue)) {
// selectValuewatch selectValue
skipSelectValueWatch.value = true;
selectValue.value = [...newValue];
}
if (props.shouldCalculateMaxTag !== false && props.multiple) {
calculateMaxTag();
}
@ -95,10 +183,10 @@
const getTreeSelectTooltip = computed(() => {
return () => {
let treeSelectTooltip = '';
const values = Array.isArray(selectValue.value) ? selectValue.value : [selectValue.value];
const values = Array.isArray(checkedKeys.value) ? checkedKeys.value : [checkedKeys.value];
if (props.data) {
treeSelectTooltip = values
?.map((valueItem: string) => {
?.map((valueItem: string | number) => {
const optItem = findNodeByKey<MsTreeNodeData>(
props.data as MsTreeNodeData[],
valueItem,
@ -114,22 +202,32 @@
};
});
const inputValue = ref('');
const tempInputValue = ref('');
const isPopupVisibleChanging = ref(false);
function handlePopupVisibleChange() {
isPopupVisibleChanging.value = true;
setTimeout(() => {
isPopupVisibleChanging.value = false;
}, 0);
}
/**
* 处理输入框搜索值变化
* @param val 搜索值
*/
function handleInputValueChange(val: string) {
async function handleInputValueChange(val: string) {
// treeSelectpopupVisibleChangeinputValueinputValue
if (isPopupVisibleChanging.value) {
inputValue.value = tempInputValue.value;
} else {
inputValue.value = val;
if (val !== '') {
// arco-tree-select
//
tempInputValue.value = val;
}
}
function handlePopupVisibleChange(val: boolean) {
if (!val) {
inputValue.value = '';
tempInputValue.value = '';
}
}
function handleChange() {
if (props.multiple) {
@ -145,7 +243,37 @@
}
function handleClear() {
tempInputValue.value = '';
clearSelector();
}
const triggerWidth = ref<number>(280); //
function updateTriggerWidth() {
const treeSelectInput = document.getElementById(treeSelectId.value) as HTMLElement;
if (treeSelectInput) {
triggerWidth.value = treeSelectInput.offsetWidth; //
}
}
watch(
() => viewSelectOptionVisible.value,
(val: boolean) => {
if (!val) {
inputValue.value = '';
tempInputValue.value = '';
} else {
updateTriggerWidth();
}
}
);
const virtualListProps = computed(() => {
return {
threshold: 300,
height: 280,
fixedSize: true,
buffer: 15, // 10 padding
};
});
</script>
<style lang="less">
@ -154,4 +282,8 @@
max-height: 150px;
.ms-scroll-bar();
}
.tree-select-trigger-content {
max-height: 300px;
.ms-scroll-bar();
}
</style>

View File

@ -31,6 +31,7 @@
:data="moduleTree"
allow-clear
:multiple="true"
tree-check-strictly
:tree-checkable="true"
:placeholder="t('common.pleaseSelect')"
:field-names="{ title: 'name', key: 'id', children: 'children' }"