You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
7.6 KiB

7 months ago
<template>
<a-radio-group option-type="button"
v-bind="getBindValue" :options="getOptions" v-model:value="modelValue"
>
</a-radio-group>
<template v-if="tips">
<p class="tips">{{tips}}</p>
</template>
</template>
<script lang="ts">
import {computed, defineComponent, nextTick, PropType, ref, unref, watch} from 'vue';
import { PropTypes, tuple } from '/nerv-lib/util/type';
import {HttpRequestConfig, useApi} from "/nerv-lib/use";
import {cloneDeep, get, isArray, isEqual, isFunction, isString, isUndefined} from "lodash-es";
type ChangeValue = string | number | undefined | string[] | number[];
export default defineComponent({
name: 'NsRadioGroupApi',
props: {
radioType: PropTypes.oneOf(tuple('radio', 'radioButton')).def('radioButton'),
prefixCls: PropTypes.string,
defaultValue: PropTypes.any,
value: PropTypes.any,
size: PropTypes.oneOf(tuple('large', 'default', 'small')).def('default'),
// options: PropTypes.array,
disabled: PropTypes.looseBool,
name: PropTypes.string,
buttonStyle: PropTypes.oneOf(tuple('outline', 'solid')).def('solid'),
onChange: PropTypes.func,
api: {
type: [String, Object, Function] as PropType<string | Function | HttpRequestConfig>,
required: true,
},
field: String,
params: {
type: Object,
default: () => ({}),
},
resultField: {
type: String,
default: 'data.data',
},
firstOption: {
type: Object,
},
numberToString: {
type: Boolean,
default: false,
},
paramsRequired: {
type: [Boolean, Object],
default: false,
},
labelField: {
type: [String, Function],
default: 'label',
},
valueField: {
type: String,
default: 'value',
},
autoSelectFirst: {
type: Boolean,
default: false,
},
//数据筛选函数
filterData: {
type: Function,
},
tips: {
type: String,
default: '',
},
},
emits: ['change', 'validateChange'],
// emits: ['update:value', 'change'],
setup(props, { attrs, emit }) {
let isLoading = false;
const isLoad = ref(false);
const options = ref([]);
let isFirstLoad = !!props.api; // 是否第一次加载
const changeValue = ref<any>(undefined);
const filterFiledRef = ref<string | undefined>(undefined);
const getBindValue = computed(() => {
return {
...props,
...attrs,
};
});
/**
* 根据请求得到的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;
}
console.log(acc)
}, sourceOptions);
});
/**
* 获取数据
*/
const fetch = () => {
isLoading = true;
const requestConfig: HttpRequestConfig = { method: 'get' };
const {
api,
params: _params,
resultField,
filterData,
} = props;
const params: Recordable = cloneDeep(_params);
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);
}
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;
});
};
fetch();
let lastParams: any = undefined;
/**
** 绑定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 } = props;
// 首次加载如果有值则选中值
if (isFirstLoad && !isUndefined(value)) {
modelValue.value = value;
}else if (!filterFiledRef.value) {
if (autoSelectFirst) {
modelValue.value = getOptions.value[0]?.value;
}
}
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 {
getBindValue,
getOptions,
modelValue,
};
},
});
</script>
<style lang="less" scoped>
.tips {
color: #7f7e7e;
line-height: 20px;
margin-bottom: 0;
}
</style>