(component): 修复 select 无法异步加载的问题

This commit is contained in:
就眠儀式 2022-09-30 17:06:09 +08:00
parent 1819e88ff0
commit 954e14bff6
4 changed files with 236 additions and 227 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@layui/layui-vue", "name": "@layui/layui-vue",
"version": "1.5.0", "version": "1.5.1-alpha.1",
"author": "就眠儀式", "author": "就眠儀式",
"license": "MIT", "license": "MIT",
"description": "a component library for Vue 3 base on layui-vue", "description": "a component library for Vue 3 base on layui-vue",

View File

@ -1,220 +1,231 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: "LaySelect", name: "LaySelect",
}; };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import "./index.less"; import "./index.less";
import { import {
provide, provide,
computed, computed,
ref, ref,
Ref, Ref,
useSlots, useSlots,
onMounted, onMounted,
VNode, VNode,
Component, Component,
watch, watch,
} from "vue"; nextTick,
import { LayIcon } from "@layui/icons-vue"; onUnmounted
import LayInput from "../input/index.vue"; } from "vue";
import LayTagInput from "../tagInput/index.vue"; import { LayIcon } from "@layui/icons-vue";
import LayDropdown from "../dropdown/index.vue"; import LayInput from "../input/index.vue";
import LaySelectOption, { import LayTagInput from "../tagInput/index.vue";
LaySelectOptionProps, import LayDropdown from "../dropdown/index.vue";
} from "../selectOption/index.vue"; import LaySelectOption, {
LaySelectOptionProps,
} from "../selectOption/index.vue";
export interface LaySelectProps { export interface LaySelectProps {
name?: string; name?: string;
disabled?: boolean; disabled?: boolean;
placeholder?: string; placeholder?: string;
searchPlaceholder?: string; searchPlaceholder?: string;
showEmpty?: boolean; showEmpty?: boolean;
emptyMessage?: string; emptyMessage?: string;
modelValue?: any; modelValue?: any;
multiple?: boolean; multiple?: boolean;
items?: LaySelectOptionProps[]; items?: LaySelectOptionProps[];
size?: "lg" | "md" | "sm" | "xs"; size?: "lg" | "md" | "sm" | "xs";
collapseTagsTooltip?: boolean; collapseTagsTooltip?: boolean;
minCollapsedNum?: number; minCollapsedNum?: number;
allowClear?: boolean; allowClear?: boolean;
showSearch?: boolean; showSearch?: boolean;
}
export interface SelectEmits {
(e: "update:modelValue", value: string): void;
(e: "change", value: string): void;
(e: "search", value: string): void;
}
const props = withDefaults(defineProps<LaySelectProps>(), {
modelValue: null,
placeholder: "请选择",
showEmpty: true,
emptyMessage: "请选择",
searchPlaceholder: "请搜索",
collapseTagsTooltip: true,
minCollapsedNum: 3,
disabled: false,
multiple: false,
allowClear: false,
showSearch: false,
size: "md",
});
const slots = useSlots();
const searchValue = ref("");
const singleValue = ref("");
const multipleValue = ref([]);
const emits = defineEmits<SelectEmits>();
const openState: Ref<boolean> = ref(false);
const selectedItem: Ref<any> = ref([]);
const options = ref<any>([]);
onMounted(() => {
if (slots.default) {
getOption(slots.default());
} }
Object.assign(options.value, props.items); export interface SelectEmits {
(e: "update:modelValue", value: string): void;
(e: "change", value: string): void;
(e: "search", value: string): void;
}
watch( const props = withDefaults(defineProps<LaySelectProps>(), {
selectedValue, modelValue: null,
() => { placeholder: "请选择",
if (multiple.value) { showEmpty: true,
multipleValue.value = selectedValue.value.map((value: any) => { emptyMessage: "请选择",
return options.value.find((item: any) => { searchPlaceholder: "请搜索",
item.disabled == "" || item.disabled == true collapseTagsTooltip: true,
? (item.closable = false) minCollapsedNum: 3,
: (item.closable = true); disabled: false,
return item.value === value; multiple: false,
}); allowClear: false,
}); showSearch: false,
} else { size: "md",
searchValue.value = "";
singleValue.value = options.value.find((item: any) => {
return item.value === selectedValue.value;
})?.label;
}
},
{ immediate: true }
);
});
const getOption = function (nodes: VNode[]) {
nodes?.map((item: VNode) => {
let component = item.type as Component;
if (component.name === LaySelectOption.name) {
options.value.push(item.props);
} else {
getOption(item.children as VNode[]);
}
}); });
};
const selectedValue = computed({ const slots = useSlots();
get() { const searchValue = ref("");
return props.modelValue; const singleValue = ref("");
}, const multipleValue = ref([]);
set(value) { const emits = defineEmits<SelectEmits>();
emits("update:modelValue", value); const openState: Ref<boolean> = ref(false);
emits("change", value); const options = ref<any>([]);
}, var timer: any;
});
const multiple = computed(() => { const getOption = (nodes: VNode[]) => {
return props.multiple; nodes?.map((item: VNode) => {
}); let component = item.type as Component;
if (component.name === LaySelectOption.name) {
options.value.push(item.props);
} else {
getOption(item.children as VNode[]);
}
});
};
const handleSearch = (value: string) => { const intOption = () => {
emits("search", value); if (slots.default) {
searchValue.value = value; getOption(slots.default());
}; }
Object.assign(options.value, props.items);
const handleClear = () => {
if (multiple.value) {
selectedValue.value = [];
} else {
selectedValue.value = "";
} }
};
provide("openState", openState); onMounted(() => {
provide("selectedItem", selectedItem);
provide("selectedValue", selectedValue);
provide("searchValue", searchValue);
provide("multiple", multiple);
</script>
<template> intOption();
<div class="layui-select layui-unselect"> timer = setInterval(intOption, 500);
<lay-dropdown
:disabled="disabled" watch(
:update-at-scroll="true" [selectedValue, options],
@show="openState = true" () => {
@hide="openState = false" if (multiple.value) {
> multipleValue.value = selectedValue.value.map((value: any) => {
<lay-tag-input return options.value.find((item: any) => {
v-if="multiple" item.disabled == "" || item.disabled == true
v-model="multipleValue" ? (item.closable = false)
:allow-clear="allowClear" : (item.closable = true);
:placeholder="placeholder" return item.value === value;
:collapseTagsTooltip="collapseTagsTooltip" });
:minCollapsedNum="minCollapsedNum" });
:disabledInput="true" } else {
:size="size" searchValue.value = "";
@clear="handleClear" singleValue.value = options.value.find((item: any) => {
return item.value === selectedValue.value;
})?.label;
}
},
{ immediate: true, deep: true }
);
});
onUnmounted(() => {
clearInterval(timer);
})
const selectedValue = computed({
get() {
return props.modelValue;
},
set(value) {
emits("update:modelValue", value);
emits("change", value);
},
});
const multiple = computed(() => {
return props.multiple;
});
const handleSearch = (value: string) => {
emits("search", value);
searchValue.value = value;
};
const handleClear = () => {
if (multiple.value) {
selectedValue.value = [];
} else {
selectedValue.value = "";
}
};
provide("openState", openState);
provide("selectedValue", selectedValue);
provide("searchValue", searchValue);
provide("multiple", multiple);
</script>
<template>
<div class="layui-select layui-unselect">
<lay-dropdown
:disabled="disabled"
:update-at-scroll="true"
@show="openState = true"
@hide="openState = false"
> >
<template #suffix> <lay-tag-input
<lay-icon v-if="multiple"
type="layui-icon-triangle-d" v-model="multipleValue"
:class="{ triangle: openState }" :allow-clear="allowClear"
></lay-icon> :placeholder="placeholder"
</template> :collapseTagsTooltip="collapseTagsTooltip"
</lay-tag-input> :minCollapsedNum="minCollapsedNum"
<lay-input :disabledInput="true"
v-else :size="size"
v-model="singleValue" @clear="handleClear"
:placeholder="placeholder" >
:allow-clear="allowClear" <template #suffix>
:readonly="!showSearch" <lay-icon
:size="size" type="layui-icon-triangle-d"
@Input="handleSearch" :class="{ triangle: openState }"
@clear="handleClear" ></lay-icon>
>
<template #suffix>
<lay-icon
type="layui-icon-triangle-d"
:class="{ triangle: openState }"
></lay-icon>
</template>
</lay-input>
<template #content>
<dl class="layui-select-content">
<div class="layui-select-search" v-if="multiple && showSearch">
<lay-input
v-model="searchValue"
:placeholder="searchPlaceholder"
prefix-icon="layui-icon-search"
size="sm"
></lay-input>
</div>
<lay-select-option
v-if="showEmpty && !multiple"
:label="emptyMessage"
value=""
></lay-select-option>
<template v-if="items">
<lay-select-option
v-for="(item, index) in items"
v-bind="item"
:key="index"
></lay-select-option>
</template> </template>
<slot></slot> </lay-tag-input>
</dl> <lay-input
</template> v-else
</lay-dropdown> v-model="singleValue"
</div> :placeholder="placeholder"
</template> :allow-clear="allowClear"
:readonly="!showSearch"
:size="size"
@Input="handleSearch"
@clear="handleClear"
>
<template #suffix>
<lay-icon
type="layui-icon-triangle-d"
:class="{ triangle: openState }"
></lay-icon>
</template>
</lay-input>
<template #content>
<dl class="layui-select-content">
<div class="layui-select-search" v-if="multiple && showSearch">
<lay-input
v-model="searchValue"
:placeholder="searchPlaceholder"
prefix-icon="layui-icon-search"
size="sm"
></lay-input>
</div>
<lay-select-option
v-if="showEmpty && !multiple"
:label="emptyMessage"
value=""
></lay-select-option>
<template v-if="items">
<lay-select-option
v-for="(item, index) in items"
v-bind="item"
:key="index"
></lay-select-option>
</template>
<slot></slot>
</dl>
</template>
</lay-dropdown>
</div>
</template>

View File

@ -28,7 +28,6 @@ const props = withDefaults(defineProps<LaySelectOptionProps>(), {
label: "", label: "",
}); });
const selectedItem: Ref<any> = inject("selectedItem") as Ref<any>;
const openState: Ref<boolean> = inject("openState") as Ref<boolean>; const openState: Ref<boolean> = inject("openState") as Ref<boolean>;
const selectedValue: WritableComputedRef<any> = inject( const selectedValue: WritableComputedRef<any> = inject(
"selectedValue" "selectedValue"
@ -53,21 +52,14 @@ const selected = computed(() => {
}); });
const select = () => { const select = () => {
const info = {
label: props.label,
value: props.value,
dispabled: props.disabled,
keyword: props.keyword,
};
if (multiple.value) { if (multiple.value) {
if (Array.isArray(selectedItem.value)) { if (Array.isArray(selectedValue.value)) {
if (notChecked.value) selectedItem.value.push(info); if (notChecked.value) selectedValue.value.push(props.value);
} else { } else {
selectedItem.value = [info]; selectedValue.value = [props.value];
} }
} else { } else {
selectedItem.value = info; selectedValue.value = props.value;
} }
}; };
@ -80,8 +72,8 @@ const display = computed(() => {
const notChecked = computed(() => { const notChecked = computed(() => {
return ( return (
selectedItem.value.find((item: any) => { selectedValue.value.find((item: any) => {
return item.value === props.value; return item === props.value;
}) === undefined }) === undefined
); );
}); });

View File

@ -183,7 +183,7 @@ export default {
::: demo ::: demo
<template> <template>
<lay-select v-model="selected2" :multiple="true"> <lay-select v-model="selected2" :multiple="true">
<lay-select-option v-for="index of 200" :value="index" :label="index"></lay-select-option> <lay-select-option v-for="index of count2" :value="index" :label="index"></lay-select-option>
</lay-select> </lay-select>
</template> </template>
@ -193,9 +193,15 @@ import { ref } from 'vue'
export default { export default {
setup() { setup() {
const count2 = ref(0)
const selected2 = ref([1]) const selected2 = ref([1])
setTimeout(() => {
count2.value = 100;
}, 2000);
return { return {
count2,
selected2 selected2
} }
} }