2025-06-05 17:40:58 +08:00
|
|
|
|
<!-- components/UrlSelect.vue -->
|
|
|
|
|
<template>
|
|
|
|
|
<el-select
|
|
|
|
|
v-bind="$attrs"
|
|
|
|
|
v-model="internalValue"
|
|
|
|
|
@change="$emit('change', $event)"
|
|
|
|
|
@blur="$emit('blur', $event)"
|
|
|
|
|
@focus="$emit('focus', $event)"
|
|
|
|
|
@clear="$emit('clear', $event)"
|
|
|
|
|
@visible-change="$emit('visible-change', $event)"
|
|
|
|
|
@remove-tag="$emit('remove-tag', $event)"
|
|
|
|
|
@scroll="$emit('scroll', $event)"
|
|
|
|
|
>
|
|
|
|
|
<!-- 1. 原生 prefix 插槽 -->
|
|
|
|
|
<slot name="prefix" />
|
|
|
|
|
<!-- 2. 如果要实现分组插槽,也照搬 el-option-group 的结构 -->
|
|
|
|
|
<slot name="option-group">
|
|
|
|
|
<!-- 默认选项渲染 -->
|
|
|
|
|
<el-option v-for="item in options" :key="item[valueKey]" :label="item[labelKey]" :value="item[valueKey]" />
|
|
|
|
|
</slot>
|
|
|
|
|
<!-- 3. 其他自定义插槽,不指定 name 的默认为 default slot -->
|
|
|
|
|
<slot />
|
|
|
|
|
<!-- 4. 下面这几个插槽是 el-select 也常见的: -->
|
|
|
|
|
<slot name="empty" />
|
|
|
|
|
<slot name="no-match" />
|
|
|
|
|
<slot name="footer" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, watch, onMounted } from 'vue';
|
|
|
|
|
import request from '@/utils/axios';
|
|
|
|
|
|
|
|
|
|
// -------------- ① 定义要继承/自定义的 Props --------------
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
modelValue: {
|
|
|
|
|
type: [String, Number, Array],
|
|
|
|
|
default: null,
|
|
|
|
|
},
|
|
|
|
|
url: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: null,
|
|
|
|
|
},
|
|
|
|
|
params: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: () => ({}),
|
|
|
|
|
},
|
|
|
|
|
// 可按需让上层指定 label 字段名、value 字段名
|
|
|
|
|
labelKey: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: 'label',
|
|
|
|
|
},
|
|
|
|
|
valueKey: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: 'value',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// -------------- ② 定义要透传回父组件的 Events --------------
|
|
|
|
|
const emit = defineEmits(['update:modelValue', 'change', 'blur', 'focus', 'clear', 'visible-change', 'remove-tag', 'scroll']);
|
|
|
|
|
|
|
|
|
|
// -------------- ③ 内部双向绑定 --------------
|
|
|
|
|
// 用一个 ref 存储内部的值
|
|
|
|
|
const internalValue = ref(props.modelValue);
|
|
|
|
|
// 当 prop 变化时,同步到 internalValue
|
|
|
|
|
watch(
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
(newVal) => {
|
|
|
|
|
internalValue.value = newVal;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
// 当 internalValue 变化时,emit update:modelValue
|
|
|
|
|
watch(internalValue, (newVal) => {
|
|
|
|
|
emit('update:modelValue', newVal);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// -------------- ④ 选项列表 --------------
|
|
|
|
|
const options = ref([]);
|
|
|
|
|
|
|
|
|
|
// -------------- ⑤ 请求数据的函数 --------------
|
|
|
|
|
async function fetchOptions() {
|
2025-06-08 20:18:35 +08:00
|
|
|
|
// console.log('fetchOptions :>> ', props.url, props.params);
|
2025-06-05 17:40:58 +08:00
|
|
|
|
if (!props.url) return;
|
2025-06-08 20:18:35 +08:00
|
|
|
|
const res = await request.get(props.url, { params: props.params });
|
|
|
|
|
|
|
|
|
|
const records = res.data.records;
|
|
|
|
|
if (Array.isArray(records)) {
|
|
|
|
|
options.value = records;
|
|
|
|
|
// console.log('option', options.value);
|
|
|
|
|
} else {
|
|
|
|
|
options.value = [];
|
|
|
|
|
console.log('UrlSelect:接口返回数据格式不是数组,无法解析成 options');
|
2025-06-05 17:40:58 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -------------- ⑥ 生命周期:组件挂载后立即拉一次数据 --------------
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
fetchOptions();
|
|
|
|
|
});
|
|
|
|
|
</script>
|