This commit is contained in:
xuziqiang
2024-05-15 17:29:42 +08:00
commit d0155dbe3c
7296 changed files with 1832517 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
import type { App } from 'vue';
import nsSelect from './select.vue';
import nsSelectApi from './select-api.vue';
import nsSelectOption from './select-option.vue';
import nsSelectOptGroup from './select-opt-group.vue';
import nsSelectTreeApi from './select-tree-api.vue';
import nsSelectApiV2 from './select-api-v2.vue';
import nsSelectTree from './selectTree.vue';
import { Select } from 'ant-design-vue';
export const NsSelect = function (app: App) {
app.component(nsSelect.name, nsSelect);
app.component(nsSelectApi.name, nsSelectApi);
app.component(nsSelectApiV2.name, nsSelectApiV2);
app.component(nsSelectTree.name, nsSelectTree);
app.component(nsSelectTreeApi.name, nsSelectTreeApi);
app.component(nsSelectOption.name, nsSelect.Option);
app.component(nsSelectOptGroup.name, Select.OptGroup);
return app;
};

View File

@@ -0,0 +1,440 @@
<!-- @format -->
<template>
<a-select
v-bind="getBindValues"
:options="getOptions"
@dropdown-visible-change="dropdownHandle"
@popupScroll="onPopupScroll"
v-model:value="modelValue"
@search="onSearch"
@clear="onClear"
:filterOption="onFilterOption">
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</a-select>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, PropType, ref, unref, watch } from 'vue';
import {
cloneDeep,
get,
isArray,
isEqual,
isFunction,
isString,
isUndefined,
isEmpty,
} from 'lodash-es';
import { HttpRequestConfig, useApi } from '/nerv-lib/use/use-api';
import { useParams } from '/nerv-lib/use/use-params';
type ChangeValue = string | number | undefined | string[] | number[];
declare type Recordable<T = any> = Record<string, T>;
export default defineComponent({
name: 'NsSelectApiV2',
props: {
api: {
type: [String, Object, Function] as PropType<string | Function | HttpRequestConfig>,
required: true,
},
value: [String, Object, Array, Number],
field: String,
params: {
type: Object,
default: () => ({}),
},
resultField: {
type: String,
default: 'data.data',
},
firstOption: {
type: Object,
},
numberToString: {
type: Boolean,
default: false,
},
immediate: {
type: Boolean,
default: false,
},
labelField: {
type: [String, Function],
default: 'label',
},
valueField: {
type: String,
default: 'value',
},
customRequest: {
type: [Function],
},
autoSelectFirst: {
// 与autoClearValue同时使用只会执行autoSelectFirst
type: Boolean,
default: false,
},
autoClearValue: {
type: Boolean,
default: false,
},
//数据筛选函数
filterData: {
type: Function,
},
// 开启前端筛选默认label包含
autoSearch: {
type: Boolean,
default: false,
},
//后端筛选字段
filterFiled: {
type: String,
},
//是否开启滚动加载
scrollLoad: {
type: Boolean,
default: false,
},
//分页字段
pageField: {
type: String,
default: 'page',
},
//第一页
defaultPage: {
type: Number,
default: 0,
},
dropdownReload: {
type: Boolean,
default: false,
},
dynamicParams: {
type: String || Array || Function || Object,
},
formModel: {
type: Object as PropType<Recordable>,
default: () => ({}),
},
},
emits: ['change', 'validateChange'],
setup(props, { attrs, emit }) {
const options = ref([]);
const isLoad = ref(false);
const changeValue = ref<any>(undefined);
const filterFiledRef = ref<string | undefined>(undefined);
let isFirstLoad = !!props.api; // 是否第一次加载
const { getParams } = useParams();
// eslint-disable-next-line vue/no-setup-props-destructure
let page = props.defaultPage; //
let isLoading = false;
const getBindValues = computed(() => {
const selectProps: Recordable = {};
if (props.filterFiled || props.autoSearch) {
selectProps['showSearch'] = true;
}
return {
...attrs,
...selectProps,
};
});
/**
* 设置filterFiled时注册search事件
*/
const onSearch = computed(() => {
if (props.filterFiled) {
return (input: string) => {
if (filterFiledRef.value !== input) {
filterFiledRef.value = input;
page = props.defaultPage;
fetch();
}
};
} else {
return undefined;
}
});
/**
* 设置filterFiled时注册filterOption事件
*/
const onFilterOption = computed(() => {
if (props.autoSearch) {
return (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
} else {
return false;
}
});
/**
* 根据请求得到的data获取options
*/
const getOptions = computed(() => {
const { firstOption, labelField, valueField, numberToString } = props;
const sourceOptions = [];
firstOption && sourceOptions.push(firstOption);
return unref(options).reduce((acc, next: Recordable) => {
if (isString(next)) {
//值是字符串类型
const option = {
label: next,
value: next,
};
acc.push(option);
return acc;
} else {
const value = get(next, valueField);
const option = {
...next,
label: isFunction(labelField) ? labelField(next) : get(next, labelField as string),
value: numberToString ? `${value}` : value,
};
acc.push(option);
return acc;
}
}, sourceOptions);
});
/**
* 获取数据
*/
const fetch = () => {
console.log('requiredParams', getBindValues.value.checkRequiredParams);
if (getBindValues.value.checkRequiredParams === false) {
options.value = [];
return;
}
isLoading = true;
const requestConfig: HttpRequestConfig = { method: 'get' };
const {
api,
params: _params,
resultField,
filterData,
filterFiled,
pageField,
scrollLoad,
defaultPage,
dynamicParams,
customRequest,
} = props;
let params: Recordable = cloneDeep(_params);
if (dynamicParams) {
params = getParams(attrs.record, dynamicParams, { ...params });
}
if (filterFiled && filterFiledRef.value) {
params[filterFiled] = filterFiledRef.value;
// options.value = [];
}
if (scrollLoad) {
params[pageField] = page;
if (page === defaultPage) {
options.value = [];
}
}
const success = (res: Recordable) => {
emit('validateChange', { help: undefined });
if (resultField) {
let data = get(res, resultField) || [];
// data = data.splice(Math.floor(Math.random() * 3), Math.floor(Math.random() * 5 + 5));
if (isFunction(filterData)) {
data = data.filter(filterData);
}
if (scrollLoad) {
options.value = [...options.value, ...data];
} else {
options.value = data;
}
}
isLoad.value = true;
isLoading = false;
};
const errror = (error: any) => {
if (error?.response?.status === 403) {
emit('validateChange', { help: '暂无权限', validateStatus: 'error' });
nextTick(() => {
//清空编辑初始值
modelValue.value = undefined;
});
}
options.value = [];
isLoading = false;
};
if (customRequest) {
customRequest({ api, params, requestConfig })
.then((res: Recordable) => {
success(res);
})
.catch((error: any) => {
errror(error);
});
} else {
const { httpRequest } = useApi();
httpRequest({ api, params, requestConfig })
.then((res: Recordable) => {
success(res);
})
.catch((error: any) => {
errror(error);
});
}
};
let lastParams: any = undefined;
/**
** 延迟获取数据
*/
async function dropdownHandle(open: boolean) {
if (!open) return;
if ((!props.immediate && !isLoad.value) || props.dropdownReload) {
await fetch();
}
}
/**
** 绑定value
*/
const modelValue = computed({
set(value: ChangeValue) {
if (isEqual(value, changeValue.value)) return;
changeValue.value = value;
triggerChange(value);
},
get() {
return changeValue.value;
},
});
/**
* 传入值需要
* 前提option已获取到数据
*/
watch(
() => props.value,
() => {
if (isLoad.value) {
modelValue.value = props.value;
}
},
{
immediate: true,
},
);
watch(
() => getOptions.value,
() => {
const { value, autoSelectFirst, autoClearValue } = props;
// 首次加载如果有值则选中值
if (isFirstLoad && !isUndefined(value)) {
modelValue.value = value;
} else if (!filterFiledRef.value) {
if (autoSelectFirst) {
modelValue.value = getOptions.value[0]?.value;
} else if (autoClearValue) {
modelValue.value = undefined;
}
}
isFirstLoad = false;
},
);
/**
* 重写change ant 初始化数据、删除数据时不触发change
* @param value
*/
function triggerChange(value: ChangeValue) {
if (isUndefined(value)) {
emit('change', value, undefined);
} else if (isArray(value)) {
const options: Record<string, any>[] = [];
value.forEach((v) => {
getOptions.value.forEach((option) => {
if (option.value === v) {
options.push(option);
}
});
});
emit('change', value, options);
} else {
let op = {};
getOptions.value.forEach((option) => {
if (option.value === value) {
op = option;
}
});
emit('change', value, op);
}
}
/**
** 联动 immediate 为是否主动获取数据
*/
watch(
() => attrs.record,
async (value: object) => {
if (isEmpty(value)) return;
let { params, dynamicParams, defaultParams = {} } = props;
params = getParams(value, dynamicParams, { ...params, ...defaultParams });
if (!isEqual(lastParams, params)) {
if (props.immediate || true) {
//todo 暂时全设为主动获取
lastParams = cloneDeep(params);
await fetch();
}
isLoad.value = false; // 设置成false 点击下拉才会触发
}
},
{ deep: true, immediate: props.immediate },
);
/**
** 联动 immediate 为是否主动获取数据
*/
watch(
[() => props.params, () => props.api],
async () => {
const { params } = props;
if (!isEqual(lastParams, params)) {
if (props.immediate || true) {
//todo 暂时全设为主动获取
lastParams = cloneDeep(params);
page = props.defaultPage;
await fetch();
}
isLoad.value = false; // 设置成false 点击下拉才会触发
}
},
{ deep: true, immediate: props.immediate },
);
const onPopupScroll = computed(() => {
if (props.scrollLoad) {
return (e) => {
const { scrollHeight, scrollTop, clientHeight } = e.target;
if (scrollHeight - scrollTop - 80 < clientHeight && !isLoading) {
page++;
fetch();
}
};
} else {
return undefined;
}
});
const onClear = () => {
filterFiledRef.value = undefined;
};
return {
onPopupScroll,
dropdownHandle,
getOptions,
getBindValues,
modelValue,
onSearch,
onFilterOption,
onClear,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,371 @@
<!-- @format -->
<template>
<a-select
v-bind="getBindValues"
:options="getOptions"
:getPopupContainer="(triggerNode) => triggerNode.parentNode"
@dropdown-visible-change="dropdownHandle"
@popupScroll="onPopupScroll"
v-model:value="modelValue"
@search="onSearch"
@clear="onClear"
:filterOption="onFilterOption">
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</a-select>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, PropType, ref, unref, watch } from 'vue';
import { cloneDeep, get, isArray, isEqual, isFunction, isString, isUndefined } from 'lodash-es';
import { HttpRequestConfig, useApi } from '/nerv-lib/use/use-api';
type ChangeValue = string | number | undefined | string[] | number[];
export default defineComponent({
name: 'NsSelectApi',
props: {
api: {
type: [String, Object, Function] as PropType<string | Function | HttpRequestConfig>,
required: true,
},
value: [String, Object, Array, Number],
field: String,
params: {
type: Object,
default: () => ({}),
},
resultField: {
type: String,
default: 'data.data',
},
firstOption: {
type: Object,
},
numberToString: {
type: Boolean,
default: false,
},
immediate: {
type: Boolean,
default: false,
},
labelField: {
type: [String, Function],
default: 'label',
},
valueField: {
type: String,
default: 'value',
},
autoSelectFirst: {
// 与autoClearValue同时使用只会执行autoSelectFirst
type: Boolean,
default: false,
},
autoClearValue: {
type: Boolean,
default: false,
},
//数据筛选函数
filterData: {
type: Function,
},
// 开启前端筛选默认label包含
autoSearch: {
type: Boolean,
default: false,
},
//后端筛选字段
filterFiled: {
type: String,
},
//是否开启滚动加载
scrollLoad: {
type: Boolean,
default: false,
},
//分页字段
pageField: {
type: String,
default: 'page',
},
//第一页
defaultPage: {
type: Number,
default: 0,
},
dropdownReload: {
type: Boolean,
default: false,
},
},
emits: ['change', 'validateChange'],
setup(props, { attrs, emit }) {
const options = ref([]);
const isLoad = ref(false);
const changeValue = ref<any>(undefined);
const filterFiledRef = ref<string | undefined>(undefined);
let isFirstLoad = !!props.api; // 是否第一次加载
// eslint-disable-next-line vue/no-setup-props-destructure
let page = props.defaultPage; //
let isLoading = false;
const getBindValues = computed(() => {
const selectProps: Recordable = {};
if (props.filterFiled || props.autoSearch) {
selectProps['showSearch'] = true;
}
return {
...attrs,
...selectProps,
};
});
/**
* 设置filterFiled时注册search事件
*/
const onSearch = computed(() => {
if (props.filterFiled) {
return (input: string) => {
if (filterFiledRef.value !== input) {
filterFiledRef.value = input;
page = props.defaultPage;
fetch();
}
};
} else {
return undefined;
}
});
/**
* 设置filterFiled时注册filterOption事件
*/
const onFilterOption = computed(() => {
if (props.autoSearch) {
return (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
} else {
return false;
}
});
/**
* 根据请求得到的data获取options
*/
const getOptions = computed(() => {
const { firstOption, labelField, valueField, numberToString } = props;
const sourceOptions = [];
firstOption && sourceOptions.push(firstOption);
return unref(options).reduce((acc, next: Recordable) => {
if (isString(next)) {
//值是字符串类型
const option = {
label: next,
value: next,
};
acc.push(option);
return acc;
} else {
const value = get(next, valueField);
const option = {
...next,
label: isFunction(labelField) ? labelField(next) : get(next, labelField as string),
value: numberToString ? `${value}` : value,
};
acc.push(option);
return acc;
}
}, sourceOptions);
});
/**
* 获取数据
*/
const fetch = () => {
console.log('requiredParams', getBindValues.value.checkRequiredParams);
if (getBindValues.value.checkRequiredParams === false) {
options.value = [];
return;
}
isLoading = true;
const requestConfig: HttpRequestConfig = { method: 'get' };
const {
api,
params: _params,
resultField,
filterData,
filterFiled,
pageField,
scrollLoad,
defaultPage,
} = props;
const params: Recordable = cloneDeep(_params);
if (filterFiled && filterFiledRef.value) {
params[filterFiled] = filterFiledRef.value;
}
if (scrollLoad) {
params[pageField] = page;
if (page === defaultPage) {
options.value = [];
}
}
const { httpRequest } = useApi();
httpRequest({ api, params, requestConfig })
.then((res: Recordable) => {
emit('validateChange', { help: undefined });
if (resultField) {
let data = get(res, resultField) || [];
// data = data.splice(Math.floor(Math.random() * 3), Math.floor(Math.random() * 5 + 5));
if (isFunction(filterData)) {
data = data.filter(filterData);
}
if (scrollLoad) {
options.value = [...options.value, ...data];
} else {
options.value = data;
}
}
isLoad.value = true;
isLoading = false;
})
.catch((error: any) => {
if (error?.response?.status === 403) {
emit('validateChange', { help: '暂无权限', validateStatus: 'error' });
nextTick(() => {
//清空编辑初始值
modelValue.value = undefined;
});
}
options.value = [];
isLoading = false;
});
};
let lastParams: any = undefined;
/**
** 延迟获取数据
*/
async function dropdownHandle(open: boolean) {
if (!open) return;
if ((!props.immediate && !isLoad.value) || props.dropdownReload) {
await fetch();
}
}
/**
** 绑定value
*/
const modelValue = computed({
set(value: ChangeValue) {
if (isEqual(value, changeValue.value)) return;
changeValue.value = value;
triggerChange(value);
},
get() {
return changeValue.value;
},
});
/**
* 传入值需要
* 前提option已获取到数据
*/
watch(
() => props.value,
() => {
if (isLoad.value) {
modelValue.value = props.value;
}
},
{
immediate: true,
},
);
watch(
() => getOptions.value,
() => {
const { value, autoSelectFirst, autoClearValue } = props;
// 首次加载如果有值则选中值
if (isFirstLoad && !isUndefined(value)) {
modelValue.value = value;
} else if (!filterFiledRef.value) {
if (autoSelectFirst) {
modelValue.value = getOptions.value[0]?.value;
} else if (autoClearValue) {
modelValue.value = undefined;
}
}
isFirstLoad = false;
},
);
/**
* 重写change ant 初始化数据、删除数据时不触发change
* @param value
*/
function triggerChange(value: ChangeValue) {
if (isUndefined(value)) {
emit('change', value, undefined);
} else if (isArray(value)) {
const options: Record<string, any>[] = [];
value.forEach((v) => {
getOptions.value.forEach((option) => {
if (option.value === v) {
options.push(option);
}
});
});
emit('change', value, options);
} else {
let op = {};
getOptions.value.forEach((option) => {
if (option.value === value) {
op = option;
}
});
emit('change', value, op);
}
}
/**
** 联动 immediate 为是否主动获取数据
*/
watch(
[() => props.params, () => props.api],
async () => {
const { params } = props;
if (!isEqual(lastParams, params)) {
if (props.immediate || true) {
//todo 暂时全设为主动获取
lastParams = cloneDeep(params);
await fetch();
}
isLoad.value = false; // 设置成false 点击下拉才会触发
}
},
{ deep: true, immediate: props.immediate },
);
const onPopupScroll = computed(() => {
if (props.scrollLoad) {
return (e) => {
const { scrollHeight, scrollTop, clientHeight } = e.target;
if (scrollHeight - scrollTop - 80 < clientHeight && !isLoading) {
page++;
fetch();
}
};
} else {
return undefined;
}
});
const onClear = () => {
filterFiledRef.value = undefined;
};
return {
onPopupScroll,
dropdownHandle,
getOptions,
getBindValues,
modelValue,
onSearch,
onFilterOption,
onClear,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,14 @@
<template>
<a-select-opt-group>
<slot></slot>
</a-select-opt-group>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'NsSelectOptGroup',
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,12 @@
<template>
<a-select-option><slot></slot></a-select-option>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'NsSelectOption',
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,375 @@
<!-- @format -->
<template>
<a-tree-select
v-bind="getBindValues"
:tree-data="getOptions"
@dropdown-visible-change="dropdownHandle"
v-model:value="modelValue"
@search="onSearch"
:filterOption="onFilterOption"
:multiple="multiple"
:treeDefaultExpandedKeys="treeDefaultExpandedKeys">
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</a-tree-select>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, PropType, ref, unref, watch } from 'vue';
import { cloneDeep, get, isArray, isEqual, isFunction, isString, isUndefined } from 'lodash-es';
import { HttpRequestConfig, useApi } from '/nerv-lib/use/use-api';
type ChangeValue = string | number | undefined | string[] | number[];
export default defineComponent({
name: 'NsSelectTreeApi',
props: {
api: {
type: [String, Object, Function] as PropType<string | Function | HttpRequestConfig>,
required: true,
},
value: [String, Object, Array, Number],
field: String,
params: {
type: Object,
default: () => ({}),
},
resultField: {
type: String,
default: 'data.data',
},
firstOption: {
type: Object,
},
numberToString: {
type: Boolean,
default: false,
},
immediate: {
type: Boolean,
default: false,
},
requiredParams: {
type: [Boolean, Object],
default: false,
},
labelField: {
type: [String, Function],
default: 'label',
},
valueField: {
type: String,
default: 'value',
},
autoSelectFirst: {
// 与autoClearValue同时使用只会执行autoSelectFirst
type: Boolean,
default: false,
},
autoClearValue: {
type: Boolean,
default: false,
},
//数据筛选函数
filterData: {
type: Function,
},
// 开启前端筛选默认label包含
autoSearch: {
type: Boolean,
default: false,
},
//后端筛选字段
filterFiled: {
type: String,
},
dropdownReload: {
type: Boolean,
default: false,
},
// 多选模式
multiple: {
type: Boolean,
default: false,
},
unSelectable: {
type: Object,
default: () => ({}),
},
},
emits: ['change', 'validateChange'],
setup(props, { attrs, emit }) {
const options = ref([]);
const isLoad = ref(false);
const changeValue = ref<any>(undefined);
const filterFiledRef = ref<string | undefined>(undefined);
let isFirstLoad = !!props.api; // 是否第一次加载
let treeDefaultExpandedKeys = ref<any>([]); //展开默认选中值
const getBindValues = computed(() => {
const selectProps: Recordable = {};
if (props.filterFiled || props.autoSearch) {
selectProps['showSearch'] = true;
}
return {
...attrs,
...selectProps,
};
});
/**
* 设置filterFiled时注册search事件
*/
const onSearch = computed(() => {
if (props.filterFiled) {
return (input: string) => {
if (filterFiledRef.value !== input) {
filterFiledRef.value = input;
fetch();
}
};
} else {
return undefined;
}
});
/**
* 设置filterFiled时注册filterOption事件
*/
const onFilterOption = computed(() => {
if (props.autoSearch) {
return (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
} else {
return false;
}
});
/**
* 根据请求得到的data获取options
*/
const getOptions = computed(() => {
const { firstOption, labelField, valueField, numberToString } = props;
const sourceOptions = [];
firstOption && sourceOptions.push(firstOption);
let acc: any[] = [];
unref(options).forEach((next: Recordable) => {
const args = { labelField, valueField };
transferTreeData(next, null, acc, { ...args });
});
return acc;
});
/** 转换树形数据 */
function transferTreeData(next: Recordable, parent: any, acc: any, args: any) {
const { labelField, valueField, numberToString } = args;
let node = null;
if (isString(next)) {
//值是字符串类型
node = {
title: next,
value: next,
key: next,
children: [],
};
} else {
const value = get(next, valueField);
node = {
title: isFunction(labelField) ? labelField(next) : get(next, labelField as string),
value: numberToString ? `${value}` : value,
key: value,
children: [],
};
}
if (props.unSelectable) {
const level = props.unSelectable.level;
if (next['level'] == level) {
node['selectable'] = false;
}
}
if (!parent) {
// 根节点
acc.push(node);
} else {
parent.children?.push(node);
}
if (next.children?.length > 0) {
for (const el of next['children']) {
transferTreeData(el, node, acc, { ...args });
}
} else {
return;
}
}
/**
* 获取数据
*/
const fetch = () => {
const requestConfig: HttpRequestConfig = { method: 'get' };
const { api, params: _params, resultField, filterData } = props;
const params: Recordable = cloneDeep(_params);
if (props.filterFiled && filterFiledRef.value) {
params[props.filterFiled] = filterFiledRef.value;
}
const { httpRequest } = useApi();
httpRequest({ api, params, requestConfig })
.then((res: Recordable) => {
emit('validateChange', { help: undefined });
let data = [];
if (resultField) {
data = get(res, resultField) || [];
// data = data.splice(Math.floor(Math.random() * 3), Math.floor(Math.random() * 5 + 5));
} else {
data = res;
}
if (isFunction(filterData)) {
options.value = data.filter(filterData);
} else {
options.value = data;
}
isLoad.value = true;
})
.catch((error: any) => {
if (error?.response?.status === 403) {
emit('validateChange', { help: '暂无权限', validateStatus: 'error' });
nextTick(() => {
//清空编辑初始值
modelValue.value = undefined;
});
}
options.value = [];
});
};
let lastParams: any = undefined;
/**
** 延迟获取数据
*/
async function dropdownHandle(open: boolean) {
if (!open) return;
if ((!props.immediate && !isLoad.value) || props.dropdownReload) {
await fetch();
}
}
/**
** 绑定value
*/
const modelValue = computed({
set(value: ChangeValue) {
if (isEqual(value, changeValue.value)) return;
changeValue.value = value;
triggerChange(value);
},
get() {
return changeValue.value;
},
});
/**
* 传入值需要
* 前提option已获取到数据
*/
watch(
() => props.value,
() => {
if (isLoad.value) {
modelValue.value = props.value;
}
// 默认选中值
if (props.multiple) {
treeDefaultExpandedKeys.value = props.value;
} else {
treeDefaultExpandedKeys.value = [props.value];
}
},
{
immediate: true,
},
);
watch(
() => getOptions.value,
() => {
const { value, autoSelectFirst, autoClearValue } = props;
// 首次加载如果有值则选中值
if (isFirstLoad && !isUndefined(value)) {
modelValue.value = value;
} else if (!filterFiledRef.value) {
if (autoSelectFirst) {
modelValue.value = getOptions.value[0]?.value;
} else if (autoClearValue) {
modelValue.value = undefined;
}
}
isFirstLoad = false;
},
);
/**
* 重写change ant 初始化数据、删除数据时不触发change
* @param value
*/
function triggerChange(value: ChangeValue) {
if (isUndefined(value)) {
emit('change', value, undefined);
} else if (isArray(value)) {
const options: Record<string, any>[] = [];
value.forEach((v) => {
getOptions.value.forEach((option) => {
if (option.value === v) {
options.push(option);
}
});
});
emit('change', value, options);
} else {
let op = {};
getOptions.value.forEach((option) => {
if (option.value === value) {
op = option;
}
});
emit('change', value, op);
}
}
/**
** 联动 immediate 为是否主动获取数据
*/
watch(
[() => props.params, () => props.api],
async () => {
const { params } = props;
if (!isEqual(lastParams, params)) {
if (props.immediate || true) {
//todo 暂时全设为主动获取
lastParams = cloneDeep(params);
await fetch();
}
isLoad.value = false; // 设置成false 点击下拉才会触发
}
},
{ deep: true, immediate: props.immediate },
);
return {
dropdownHandle,
getOptions,
getBindValues,
modelValue,
onSearch,
onFilterOption,
treeDefaultExpandedKeys,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,18 @@
<template>
<a-select :getPopupContainer="(triggerNode) => triggerNode.parentNode">
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</a-select>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'NsSelect',
props: {},
setup(props) {},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,18 @@
<template>
<a-tree-select>
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</a-tree-select>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'NsSelectTree',
props: {},
setup(props) {},
});
</script>
<style lang="less" scoped></style>