123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- import { computed, reactive, ref, watch, watchEffect } from "vue";
- type Item<T> = { id: string; children?: Item<T>[] } & T;
- export type TreeOption = {
- label: string;
- level?: number;
- value: any;
- children?: TreeOption[];
- };
- export type Props = {
- modelValue: string;
- label?: string;
- hideAll?: boolean;
- allText?: string;
- notDefault?: boolean;
- disabled?: boolean;
- };
- export type Mapper = {
- label: string;
- level: string;
- };
- const treeSelectOptionCover = <T>(
- data: Item<T>[],
- mapping: Mapper
- ): TreeOption[] =>
- data.map(
- (item) =>
- ({
- label: (item as any)[mapping.label],
- value: item.id,
- level: (item as any)[mapping.level],
- children:
- item.children && item.children.length
- ? treeSelectOptionCover(item.children, mapping)
- : null,
- } as any)
- );
- const getTreePath = (options: TreeOption[], value: string): null | string[] => {
- for (const option of options) {
- if (option.value === value) {
- return [option.value];
- } else if (option.children) {
- const cpath = getTreePath(option.children, value);
- if (cpath) {
- return [option.value, ...cpath];
- }
- }
- }
- return null;
- };
- const getTreeSelect = (
- options: TreeOption[],
- value: string
- ): null | TreeOption => {
- for (const option of options) {
- if (option.value === value) {
- return option;
- } else if (option.children) {
- const coption = getTreeSelect(option.children, value);
- if (coption) {
- return coption;
- }
- }
- }
- return null;
- };
- const allOption: TreeOption = { label: "全部", value: "" };
- export const useTreeSelect = <T>(
- props: Props,
- request: () => Promise<Item<T>[]>,
- update: (val: string) => void,
- mapping: Mapper = { label: "name", level: "level" }
- ) => {
- // 原始数据
- const optionsRaw = ref<Item<T>[]>([]);
- // 级联控件需要数据
- const options = computed<TreeOption[]>(() => {
- const dataOptions = treeSelectOptionCover(optionsRaw.value, mapping);
- return props.hideAll ? dataOptions : [allOption, ...dataOptions];
- });
- // 级联控件续高亮value
- const path = computed({
- get: () => getTreePath(options.value, props.modelValue)!,
- set: (path) => {
- update(path ? path[path.length - 1] : "");
- },
- });
- const currentOption = computed(() =>
- getTreeSelect(options.value, props.modelValue)
- );
- const label = computed(
- () =>
- options.value.find((option) => option.value === props.modelValue)
- ?.label || ""
- );
- watch(
- () => props.modelValue,
- () => {
- if (!props.notDefault && !props.modelValue && options.value.length) {
- update(options.value[0].value);
- }
- },
- { immediate: true }
- );
- request().then((data) => (optionsRaw.value = data as any));
- return reactive({
- label,
- path,
- options,
- raw: optionsRaw,
- currentOption,
- });
- };
|