fix: select deep watch options, close #5398

pull/5407/head
tangjinzhou 2022-03-25 16:40:06 +08:00
parent e146b4847d
commit 3613eceff8
10 changed files with 92 additions and 88 deletions

View File

@ -42,7 +42,7 @@ import { toArray } from './utils/commonUtil';
import useFilterOptions from './hooks/useFilterOptions'; import useFilterOptions from './hooks/useFilterOptions';
import useCache from './hooks/useCache'; import useCache from './hooks/useCache';
import type { Key, VueNode } from '../_util/type'; import type { Key, VueNode } from '../_util/type';
import { computed, defineComponent, ref, toRef, watchEffect } from 'vue'; import { computed, defineComponent, ref, shallowRef, toRef, watchEffect } from 'vue';
import type { ExtractPropTypes, PropType } from 'vue'; import type { ExtractPropTypes, PropType } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { initDefaultProps } from '../_util/props-util'; import { initDefaultProps } from '../_util/props-util';
@ -314,13 +314,15 @@ export default defineComponent({
}; };
// Fill tag as option if mode is `tags` // Fill tag as option if mode is `tags`
const filledTagOptions = computed(() => { const filledTagOptions = shallowRef();
watchEffect(() => {
if (props.mode !== 'tags') { if (props.mode !== 'tags') {
return mergedOptions.value; filledTagOptions.value = mergedOptions.value;
return;
} }
// >>> Tag mode // >>> Tag mode
const cloneOptions = [...mergedOptions.value]; const cloneOptions = mergedOptions.value.slice();
// Check if value exist in options (include new patch item) // Check if value exist in options (include new patch item)
const existOptions = (val: RawValueType) => valueOptions.value.has(val); const existOptions = (val: RawValueType) => valueOptions.value.has(val);
@ -336,7 +338,7 @@ export default defineComponent({
} }
}); });
return cloneOptions; filledTagOptions.value = cloneOptions;
}); });
const filteredOptions = useFilterOptions( const filteredOptions = useFilterOptions(

View File

@ -7,15 +7,15 @@ import type {
BaseOptionType, BaseOptionType,
} from '../Select'; } from '../Select';
import { injectPropsWithOption } from '../utils/valueUtil'; import { injectPropsWithOption } from '../utils/valueUtil';
import type { Ref } from 'vue'; import type { Ref, ShallowRef } from 'vue';
import { toRaw, computed } from 'vue'; import { computed } from 'vue';
function includes(test: any, search: string) { function includes(test: any, search: string) {
return toArray(test).join('').toUpperCase().includes(search); return toArray(test).join('').toUpperCase().includes(search);
} }
export default ( export default (
options: Ref<DefaultOptionType[]>, options: ShallowRef<DefaultOptionType[]>,
fieldNames: Ref<FieldNames>, fieldNames: Ref<FieldNames>,
searchValue?: Ref<string>, searchValue?: Ref<string>,
filterOption?: Ref<SelectProps['filterOption']>, filterOption?: Ref<SelectProps['filterOption']>,
@ -55,7 +55,7 @@ export default (
? opt => injectPropsWithOption(opt) ? opt => injectPropsWithOption(opt)
: opt => opt; : opt => opt;
toRaw(options.value).forEach(item => { options.value.forEach(item => {
// Group should check child options // Group should check child options
if (item[fieldOptions]) { if (item[fieldOptions]) {
// Check group first // Check group first

View File

@ -1,5 +1,5 @@
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { toRaw, shallowRef, watchEffect } from 'vue'; import { toRaw, shallowRef, watchEffect, watch } from 'vue';
import type { FieldNames, RawValueType } from '../Select'; import type { FieldNames, RawValueType } from '../Select';
import { convertChildrenToData } from '../utils/legacyUtil'; import { convertChildrenToData } from '../utils/legacyUtil';
@ -15,13 +15,20 @@ export default function useOptions<OptionType>(
const mergedOptions = shallowRef(); const mergedOptions = shallowRef();
const valueOptions = shallowRef(); const valueOptions = shallowRef();
const labelOptions = shallowRef(); const labelOptions = shallowRef();
watchEffect(() => { const tempMergedOptions = shallowRef([]);
let newOptions = toRaw(options.value); watch(
const childrenAsData = !options.value; [options, children],
() => {
if (childrenAsData) { if (options.value) {
newOptions = convertChildrenToData(children.value); tempMergedOptions.value = toRaw(options.value).slice();
} else {
tempMergedOptions.value = convertChildrenToData(children.value);
} }
},
{ immediate: true, deep: true },
);
watchEffect(() => {
const newOptions = tempMergedOptions.value;
const newValueOptions = new Map<RawValueType, OptionType>(); const newValueOptions = new Map<RawValueType, OptionType>();
const newLabelOptions = new Map<any, OptionType>(); const newLabelOptions = new Map<any, OptionType>();

View File

@ -98,7 +98,7 @@ export default defineComponent({
); );
const mergedExpandedKeys = computed(() => { const mergedExpandedKeys = computed(() => {
if (legacyContext.treeExpandedKeys) { if (legacyContext.treeExpandedKeys) {
return toRaw(legacyContext.treeExpandedKeys).slice(); return legacyContext.treeExpandedKeys.slice();
} }
return baseProps.searchValue ? searchExpandedKeys.value : expandedKeys.value; return baseProps.searchValue ? searchExpandedKeys.value : expandedKeys.value;
}); });

View File

@ -1,5 +1,5 @@
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { toRaw, computed, shallowRef } from 'vue'; import { watch, toRaw, computed, shallowRef } from 'vue';
import type { LabeledValueType, RawValueType } from '../TreeSelect'; import type { LabeledValueType, RawValueType } from '../TreeSelect';
/** /**
@ -10,12 +10,19 @@ export default (values: Ref<LabeledValueType[]>): [Ref<LabeledValueType[]>] => {
const cacheRef = shallowRef({ const cacheRef = shallowRef({
valueLabels: new Map<RawValueType, any>(), valueLabels: new Map<RawValueType, any>(),
}); });
const mergedValues = shallowRef();
watch(
values,
() => {
mergedValues.value = toRaw(values.value);
},
{ immediate: true },
);
const newFilledValues = computed(() => { const newFilledValues = computed(() => {
const { valueLabels } = cacheRef.value; const { valueLabels } = cacheRef.value;
const valueLabelsCache = new Map<RawValueType, any>(); const valueLabelsCache = new Map<RawValueType, any>();
const filledValues = toRaw(values.value).map(item => { const filledValues = mergedValues.value.map(item => {
const { value } = item; const { value } = item;
const mergedLabel = item.label ?? valueLabels.get(value); const mergedLabel = item.label ?? valueLabels.get(value);

View File

@ -3,11 +3,11 @@ import type { DataEntity } from '../../vc-tree/interface';
import { conductCheck } from '../../vc-tree/utils/conductUtil'; import { conductCheck } from '../../vc-tree/utils/conductUtil';
import type { LabeledValueType, RawValueType } from '../TreeSelect'; import type { LabeledValueType, RawValueType } from '../TreeSelect';
import type { Ref, ShallowRef } from 'vue'; import type { Ref, ShallowRef } from 'vue';
import { toRaw, shallowRef, watchEffect } from 'vue'; import { shallowRef, watchEffect } from 'vue';
export default ( export default (
rawLabeledValues: Ref<LabeledValueType[]>, rawLabeledValues: ShallowRef<LabeledValueType[]>,
rawHalfCheckedValues: Ref<LabeledValueType[]>, rawHalfCheckedValues: ShallowRef<LabeledValueType[]>,
treeConduction: Ref<boolean>, treeConduction: Ref<boolean>,
keyEntities: Ref<Record<Key, DataEntity>>, keyEntities: Ref<Record<Key, DataEntity>>,
maxLevel: Ref<number>, maxLevel: Ref<number>,
@ -17,10 +17,8 @@ export default (
const newRawHalfCheckedValues = shallowRef<RawValueType[]>([]); const newRawHalfCheckedValues = shallowRef<RawValueType[]>([]);
watchEffect(() => { watchEffect(() => {
let checkedKeys: RawValueType[] = toRaw(rawLabeledValues.value).map(({ value }) => value); let checkedKeys: RawValueType[] = rawLabeledValues.value.map(({ value }) => value);
let halfCheckedKeys: RawValueType[] = toRaw(rawHalfCheckedValues.value).map( let halfCheckedKeys: RawValueType[] = rawHalfCheckedValues.value.map(({ value }) => value);
({ value }) => value,
);
const missingValues = checkedKeys.filter(key => !keyEntities.value[key]); const missingValues = checkedKeys.filter(key => !keyEntities.value[key]);

View File

@ -3,16 +3,16 @@ import type { DataEntity } from '../../vc-tree/interface';
import type { FieldNames, RawValueType } from '../TreeSelect'; import type { FieldNames, RawValueType } from '../TreeSelect';
import { isNil } from '../utils/valueUtil'; import { isNil } from '../utils/valueUtil';
import type { Ref } from 'vue'; import type { Ref, ShallowRef } from 'vue';
import { toRaw, ref, watchEffect } from 'vue'; import { shallowRef, watchEffect } from 'vue';
import { warning } from '../../vc-util/warning'; import { warning } from '../../vc-util/warning';
export default (treeData: Ref<any>, fieldNames: Ref<FieldNames>) => { export default (treeData: ShallowRef<any>, fieldNames: Ref<FieldNames>) => {
const valueEntities = ref<Map<RawValueType, DataEntity>>(new Map()); const valueEntities = shallowRef<Map<RawValueType, DataEntity>>(new Map());
const keyEntities = ref<Record<string, DataEntity>>({}); const keyEntities = shallowRef<Record<string, DataEntity>>({});
watchEffect(() => { watchEffect(() => {
const fieldNamesValue = fieldNames.value; const fieldNamesValue = fieldNames.value;
const collection = convertDataToEntities(toRaw(treeData.value), { const collection = convertDataToEntities(treeData.value, {
fieldNames: fieldNamesValue, fieldNames: fieldNamesValue,
initWrapper: wrapper => ({ initWrapper: wrapper => ({
...wrapper, ...wrapper,

View File

@ -1,5 +1,5 @@
import type { Ref } from 'vue'; import type { Ref, ShallowRef } from 'vue';
import { toRaw, computed } from 'vue'; import { computed } from 'vue';
import type { DefaultOptionType, InternalFieldName, TreeSelectProps } from '../TreeSelect'; import type { DefaultOptionType, InternalFieldName, TreeSelectProps } from '../TreeSelect';
import { fillLegacyProps } from '../utils/legacyUtil'; import { fillLegacyProps } from '../utils/legacyUtil';
@ -7,7 +7,7 @@ type GetFuncType<T> = T extends boolean ? never : T;
type FilterFn = GetFuncType<TreeSelectProps['filterTreeNode']>; type FilterFn = GetFuncType<TreeSelectProps['filterTreeNode']>;
export default ( export default (
treeData: Ref<DefaultOptionType[]>, treeData: ShallowRef<DefaultOptionType[]>,
searchValue: Ref<string>, searchValue: Ref<string>,
{ {
treeNodeFilterProp, treeNodeFilterProp,
@ -56,24 +56,8 @@ export default (
} }
} }
return res; return res;
// return list
// .map(dataNode => {
// const children = dataNode[fieldChildren];
// const match = keepAll || filterOptionFunc(searchValueVal, fillLegacyProps(dataNode));
// const childList = dig(children || [], match);
// if (match || childList.length) {
// return {
// ...dataNode,
// [fieldChildren]: childList,
// };
// }
// return null;
// })
// .filter(node => node);
} }
return dig(toRaw(treeData.value)); return dig(treeData.value);
}); });
}; };

View File

@ -1,5 +1,5 @@
import type { Ref } from 'vue'; import type { Ref, ShallowRef } from 'vue';
import { toRaw, computed } from 'vue'; import { shallowRef, watch, toRaw } from 'vue';
import type { DataNode, SimpleModeConfig } from '../interface'; import type { DataNode, SimpleModeConfig } from '../interface';
import { convertChildrenToData } from '../utils/legacyUtil'; import { convertChildrenToData } from '../utils/legacyUtil';
import type { DefaultOptionType } from '../TreeSelect'; import type { DefaultOptionType } from '../TreeSelect';
@ -49,20 +49,26 @@ export default function useTreeData(
treeData: Ref<DataNode[]>, treeData: Ref<DataNode[]>,
children: Ref<VueNode[]>, children: Ref<VueNode[]>,
simpleMode: Ref<boolean | SimpleModeConfig>, simpleMode: Ref<boolean | SimpleModeConfig>,
): Ref<DefaultOptionType[]> { ): ShallowRef<DefaultOptionType[]> {
return computed(() => { const mergedTreeData = shallowRef<DefaultOptionType[]>();
watch(
[simpleMode, treeData, children],
() => {
const simpleModeValue = simpleMode.value; const simpleModeValue = simpleMode.value;
if (treeData.value) { if (treeData.value) {
return simpleMode.value mergedTreeData.value = simpleMode.value
? parseSimpleTreeData(toRaw(treeData.value), { ? parseSimpleTreeData(toRaw(treeData.value), {
id: 'id', id: 'id',
pId: 'pId', pId: 'pId',
rootPId: null, rootPId: null,
...(simpleModeValue !== true ? simpleModeValue : {}), ...(simpleModeValue !== true ? simpleModeValue : {}),
}) })
: treeData.value; : toRaw(treeData.value);
} else {
mergedTreeData.value = convertChildrenToData(toRaw(children.value));
} }
},
return convertChildrenToData(toRaw(children.value)); { immediate: true, deep: true },
}); );
return mergedTreeData;
} }

View File

@ -104,12 +104,16 @@ export default defineComponent({
dragOverNodeKey: null, dragOverNodeKey: null,
}); });
const treeData = shallowRef([]); const treeData = shallowRef([]);
watchEffect(() => { watch(
[() => props.treeData, () => props.children],
() => {
treeData.value = treeData.value =
props.treeData !== undefined props.treeData !== undefined
? toRaw(props.treeData) ? toRaw(props.treeData)
: convertTreeToData(toRaw(props.children)); : convertTreeToData(toRaw(props.children));
}); },
{ immediate: true, deep: true },
);
const keyEntities = shallowRef({}); const keyEntities = shallowRef({});
const focused = ref(false); const focused = ref(false);
@ -143,7 +147,7 @@ export default defineComponent({
watchEffect(() => { watchEffect(() => {
if (treeData.value) { if (treeData.value) {
const entitiesMap = convertDataToEntities(toRaw(treeData.value), { const entitiesMap = convertDataToEntities(treeData.value, {
fieldNames: fieldNames.value, fieldNames: fieldNames.value,
}); });
keyEntities.value = { keyEntities.value = {
@ -190,19 +194,15 @@ export default defineComponent({
// ================ flattenNodes ================= // ================ flattenNodes =================
const flattenNodes = shallowRef([]); const flattenNodes = shallowRef([]);
watchEffect(() => { watchEffect(() => {
flattenNodes.value = flattenTreeData( flattenNodes.value = flattenTreeData(treeData.value, expandedKeys.value, fieldNames.value);
toRaw(treeData.value),
toRaw(expandedKeys.value),
fieldNames.value,
);
}); });
// ================ selectedKeys ================= // ================ selectedKeys =================
watchEffect(() => { watchEffect(() => {
if (props.selectable) { if (props.selectable) {
if (props.selectedKeys !== undefined) { if (props.selectedKeys !== undefined) {
selectedKeys.value = calcSelectedKeys(toRaw(props.selectedKeys), props); selectedKeys.value = calcSelectedKeys(props.selectedKeys, props);
} else if (!init && props.defaultSelectedKeys) { } else if (!init && props.defaultSelectedKeys) {
selectedKeys.value = calcSelectedKeys(toRaw(props.defaultSelectedKeys), props); selectedKeys.value = calcSelectedKeys(props.defaultSelectedKeys, props);
} }
} }
}); });
@ -213,12 +213,12 @@ export default defineComponent({
let checkedKeyEntity; let checkedKeyEntity;
if (props.checkedKeys !== undefined) { if (props.checkedKeys !== undefined) {
checkedKeyEntity = parseCheckedKeys(toRaw(props.checkedKeys)) || {}; checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {};
} else if (!init && props.defaultCheckedKeys) { } else if (!init && props.defaultCheckedKeys) {
checkedKeyEntity = parseCheckedKeys(toRaw(props.defaultCheckedKeys)) || {}; checkedKeyEntity = parseCheckedKeys(props.defaultCheckedKeys) || {};
} else if (treeData.value) { } else if (treeData.value) {
// If `treeData` changed, we also need check it // If `treeData` changed, we also need check it
checkedKeyEntity = parseCheckedKeys(toRaw(props.checkedKeys)) || { checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {
checkedKeys: checkedKeys.value, checkedKeys: checkedKeys.value,
halfCheckedKeys: halfCheckedKeys.value, halfCheckedKeys: halfCheckedKeys.value,
}; };