AI_Painting_V2.0/src/platforms/painting/modelSelector.vue
WangLeo 1d07cdc907 feat: 输入框宽度62%、发送按钮改为圆形灰度切换、绘画平台默认选文生图首个模型
- 输入框从75%缩至62%
- 发送按钮去掉文字改为44px正圆形,无提示词时浅灰底+深色箭头,有提示词时稍深灰底+白色箭头
- 绘画平台 getDefaultModel() 返回空字符串,modelSelector 加载后优先选 text tag 模型
- CLAUDE.md 新增 Display Store/Canvas/视图层架构章节,移除已删除的 model-configs 目录,修正环境变量表
2026-06-17 18:22:30 +08:00

169 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Select
v-model="selectValue"
:grouped-options="modelGroups"
class="model-select"
position="top"
>
<template #prefix>
<img src="@/assets/dialog/model.svg" alt="" style="width: 16px;">
</template>
</Select>
</template>
<script setup>
import Select from '@/components/Select/index.vue'
import { fetchPlatformModels, getPlatformCode } from '@/utils/modelApi'
const props = defineProps({
modelValue: { type: String, default: 'Flux 2' },
typeValue: { type: String, default: 'text' }
})
const emit = defineEmits(['update:modelValue', 'update:typeValue'])
const platformModels = ref([])
const categoryMap = [
{ tag: 'text', label: '生成模型', inputType: 'text' },
{ tag: 'edit', label: '编辑模型', inputType: 'image' },
{ tag: 'vision', label: '视觉理解模型', inputType: 'vision' }
]
function parseValue(encoded) {
if (!encoded) return null
const idx = encoded.indexOf('::')
if (idx === -1) return null
return { tag: encoded.substring(0, idx), modelName: encoded.substring(idx + 2) }
}
function encodeValue(tag, modelName) {
return `${tag}::${modelName}`
}
function findTagForModel(modelName) {
for (const cat of categoryMap) {
const model = platformModels.value.find((m) => (m.display_name || m.name) === modelName && m.tags?.includes(cat.tag))
if (model) return cat.tag
}
return 'text'
}
function tagToInputType(tag) {
const cat = categoryMap.find((c) => c.tag === tag)
return cat?.inputType || 'text'
}
// Select 双向绑定值(内部编码)
const selectValue = computed({
get: () => {
if (!props.modelValue) return ''
const tag = findTagForModel(props.modelValue)
return encodeValue(tag, props.modelValue)
},
set: (encoded) => {
const parsed = parseValue(encoded)
if (!parsed) return
emit('update:modelValue', parsed.modelName)
emit('update:typeValue', tagToInputType(parsed.tag))
}
})
// 从 API 加载模型列表
const loadModels = async () => {
try {
const code = getPlatformCode('Painting')
const models = await fetchPlatformModels(code)
platformModels.value = models || []
} catch (error) {
console.error('加载平台模型列表失败:', error)
}
}
loadModels()
// 按固定分类分组value 编码为 tag::displayName
const modelGroups = computed(() => {
const models = platformModels.value
if (models.length === 0) return []
return categoryMap
.filter((cat) => models.some((m) => m.tags?.includes(cat.tag)))
.map((cat) => ({
label: cat.label,
options: models
.filter((m) => m.tags?.includes(cat.tag))
.map((m) => ({
value: `${cat.tag}::${m.display_name || m.name}`,
label: m.display_name || m.name,
disabled: m.disabled || false
}))
.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))
}))
})
// 模型列表加载后自动纠正不可用模型优先选文生图text tag第一个
watch(platformModels, (models) => {
if (models.length === 0) return
const currentModel = models.find((m) => (m.display_name || m.name) === props.modelValue || m.id === props.modelValue)
if (!currentModel || currentModel.disabled) {
const firstText = models.find((m) => !m.disabled && m.tags?.includes('text'))
const fallback = firstText || models.find((m) => !m.disabled)
if (fallback) {
emit('update:modelValue', fallback.display_name || fallback.name)
emit('update:typeValue', tagToInputType(findTagForModel(fallback.display_name || fallback.name)))
}
}
}, { immediate: true })
// 外部改变 modelValue 时校验是否可用
watch(() => props.modelValue, (newValue) => {
if (!newValue) return
const models = platformModels.value
if (models.length === 0) return
const currentModel = models.find((m) => (m.display_name || m.name) === newValue)
if (currentModel && currentModel.disabled) {
const firstEnabled = models.find((m) => !m.disabled)
if (firstEnabled) {
emit('update:modelValue', firstEnabled.display_name || firstEnabled.name)
emit('update:typeValue', tagToInputType(findTagForModel(firstEnabled.display_name || firstEnabled.name)))
}
}
})
</script>
<style lang="less" scoped>
.model-select {
:deep(.select-header) {
height: 40px;
padding: 0 15px;
border-radius: 10px;
border: 1px solid #E8E9EB;
background: #f5f6f7;
&:hover {
background: #e9eaeb;
}
}
:deep(.select-text) {
font-size: 14px;
}
:deep(.dropdown-menu) {
max-height: 510px;
overflow-y: auto;
}
:deep(.dropdown-item) {
min-width: 120px;
&.active {
background: rgba(0, 15, 51, 0.10);
color: #000F33;
font-weight: 600;
}
}
}
</style>