模型选择器组件设计文档
组件职责
- 按上游分组展示 AI 模型,根据
category参数过滤 - 只暴露
v-model:aimodel-id,不暴露upstream-id - 内部计算 upstreamId 用于分组显示
- 自动选择首个可用模型(可禁用)
- 通过
defineExpose暴露完整的 Upstream 和 Aimodel 对象
API
Props
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
upstreams | Upstream[] | ✅ | - | 上游配置列表(含 aimodels 子表) |
category | 'chat' | 'image' | 'video' | ✅ | - | 模型分类 |
aimodelId | number | null | ❌ | null | 选中的模型 ID(v-model) |
readOnly | boolean | ❌ | false | 只读模式 |
dropdownWidth | string | ❌ | 'w-80' | 下拉面板宽度 |
listLayout | boolean | ❌ | false | 列表布局(否则 grid) |
noAutoSelect | boolean | ❌ | false | 禁用自动选择 |
alignRight | boolean | ❌ | false | 下拉框右对齐 |
Emits
| 事件 | 参数 | 说明 |
|---|---|---|
update:aimodelId | id: number | null | 模型 ID 变化 |
Expose
| 属性 | 类型 | 说明 |
|---|---|---|
selectedUpstream | ComputedRef<Upstream | undefined> | 当前选中的上游对象 |
selectedAimodel | ComputedRef<Aimodel | undefined> | 当前选中的模型对象 |
使用示例
基础用法
vue
<script setup lang="ts">
const { upstreams } = useUpstreams()
const selectedAimodelId = ref<number | null>(null)
</script>
<template>
<ModelSelector
:upstreams="upstreams"
category="chat"
v-model:aimodel-id="selectedAimodelId"
/>
</template>获取完整对象
vue
<script setup lang="ts">
const { upstreams } = useUpstreams()
const selectedAimodelId = ref<number | null>(null)
const modelSelectorRef = ref()
// 通过 ref 获取完整对象
const selectedAimodel = computed(() => modelSelectorRef.value?.selectedAimodel)
const selectedUpstream = computed(() => modelSelectorRef.value?.selectedUpstream)
function handleSubmit() {
if (!selectedAimodel.value) return
// 使用完整对象的字段
const data = {
aimodelId: selectedAimodel.value.id,
modelType: selectedAimodel.value.modelType,
apiFormat: selectedAimodel.value.apiFormat,
modelName: selectedAimodel.value.modelName,
}
}
</script>
<template>
<ModelSelector
ref="modelSelectorRef"
:upstreams="upstreams"
category="image"
v-model:aimodel-id="selectedAimodelId"
/>
</template>重要说明
数据流向
- 父组件传入:只需传入
upstreams和category - 组件内部:根据
aimodelId自动计算upstreamId(用于分组显示) - 对外暴露:只暴露
aimodelId,不暴露upstreamId
typescript
// 组件内部计算 upstreamId(只用于显示)
const computedUpstreamId = computed(() => {
if (!props.aimodelId) return null
for (const upstream of props.upstreams) {
if (upstream.aimodels?.some(m => m.id === props.aimodelId)) {
return upstream.id
}
}
return null
})不要传递 upstreamId
❌ 错误用法:
vue
<ModelSelector
v-model:upstream-id="selectedUpstreamId"
v-model:aimodel-id="selectedAimodelId"
/>✅ 正确用法:
vue
<ModelSelector
v-model:aimodel-id="selectedAimodelId"
/>获取完整对象的最佳实践
❌ 不推荐:手动查找
typescript
const selectedAimodel = computed(() => {
const upstream = upstreams.value.find(u => u.id === upstreamId)
return upstream?.aimodels.find(m => m.id === aimodelId)
})✅ 推荐:通过 ref
typescript
const modelSelectorRef = ref()
const selectedAimodel = computed(() => modelSelectorRef.value?.selectedAimodel)注意事项
- 必须包含子表:传入的
upstreams必须通过loadUpstreams()加载,确保包含aimodels子表 - 单一数据源:
aimodel.upstreamId是唯一来源,前端不需要维护 upstreamId 状态 - 自动选择:首次加载时自动选择第一个可用模型,除非设置
no-auto-select - 空状态:无模型时自动显示提示信息,引导用户添加配置
- 布局选择:对话场景使用 grid,绘图/视频场景建议使用
list-layout
