fix(ui): refactor model manager layout and cards
- Tab panel: switch to segment style with fixed header - Scroll area: isolate panel scroll from tabs - Card styling: unify text/image/function model cards with rounded corners - Actions: add tooltips for compact icon-only buttons
This commit is contained in:
1 parent
317afcbd67
commit
1b02031d4a
4 files changed
+415
-210
No files matched your search
@@ -1,11 +1,16 @@
|
||||
<template>
|
||||
<div class="function-model-manager">
|
||||
<NSpace vertical :size="16">
|
||||
<!-- 评估模型配置 -->
|
||||
<div class="config-section">
|
||||
<NSpace align="center" :size="8" class="section-header">
|
||||
<NCard embedded size="small" :bordered="false" class="function-config-card">
|
||||
<template #header>
|
||||
<NSpace justify="space-between" align="center" :size="8" class="section-header">
|
||||
<NText strong>{{ t('functionModel.evaluationModel') }}</NText>
|
||||
<NTag size="small" type="default" round :bordered="false">
|
||||
{{ t('modelManager.textModels') }}
|
||||
</NTag>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<NSpace vertical :size="12" class="config-section">
|
||||
<NText depth="3" class="section-hint">
|
||||
{{ t('functionModel.evaluationModelHint') }}
|
||||
</NText>
|
||||
@@ -29,23 +34,23 @@
|
||||
/>
|
||||
<!-- 显示模型源和模型名称标签 -->
|
||||
<template v-if="selectedModelInfo">
|
||||
<NTag v-if="selectedModelInfo.provider" size="small" type="info">
|
||||
<NTag v-if="selectedModelInfo.provider" size="small" type="default" round :bordered="false">
|
||||
{{ selectedModelInfo.provider }}
|
||||
</NTag>
|
||||
<NTag v-if="selectedModelInfo.model" size="small">
|
||||
<NTag v-if="selectedModelInfo.model" size="small" type="info" round :bordered="false">
|
||||
{{ selectedModelInfo.model }}
|
||||
</NTag>
|
||||
</template>
|
||||
</NSpace>
|
||||
</div>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject, onMounted, type Ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { NSpace, NText, NTag } from 'naive-ui'
|
||||
import { NCard, NSpace, NText, NTag } from 'naive-ui'
|
||||
import SelectWithConfig from './SelectWithConfig.vue'
|
||||
import { useFunctionModelManager } from '../composables/model/useFunctionModelManager'
|
||||
import { DataTransformer, OptionAccessors } from '../utils/data-transformer'
|
||||
@@ -164,22 +169,25 @@ defineExpose({ refresh })
|
||||
|
||||
<style scoped>
|
||||
.function-model-manager {
|
||||
padding: 12px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.function-config-card {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.config-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
margin-bottom: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-hint {
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.model-select-row {
|
||||
|
||||
@@ -8,50 +8,55 @@
|
||||
</NEmpty>
|
||||
|
||||
<!-- 模型列表 -->
|
||||
<NSpace v-else vertical :size="12">
|
||||
<NSpace v-else vertical :size="12" class="image-model-stack">
|
||||
<NCard
|
||||
v-for="config in configs"
|
||||
:key="config.id"
|
||||
size="small"
|
||||
hoverable
|
||||
class="image-model-card"
|
||||
:style="{
|
||||
opacity: config.enabled ? 1 : 0.6
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<NSpace justify="space-between" align="center">
|
||||
<NSpace vertical :size="2">
|
||||
<NSpace vertical :size="4" class="image-model-heading">
|
||||
<!-- 配置名称行 -->
|
||||
<NSpace align="center">
|
||||
<NSpace align="center" :size="8">
|
||||
<NText strong>{{ config.name || config.id }}</NText>
|
||||
<NTag
|
||||
v-if="!config.enabled"
|
||||
type="warning"
|
||||
size="small"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
{{ t('modelManager.disabled') }}
|
||||
</NTag>
|
||||
</NSpace>
|
||||
<!-- 标签行:Provider、Model、能力标签合并 -->
|
||||
<NSpace :size="6">
|
||||
<NTag size="small" type="info" :bordered="false">
|
||||
<NSpace :size="6" class="image-model-tags">
|
||||
<NTag size="small" type="default" round :bordered="false">
|
||||
{{ config.provider?.name || config.providerId }}
|
||||
</NTag>
|
||||
<NTag size="small" type="primary" :bordered="false">
|
||||
<NTag size="small" type="info" round :bordered="false">
|
||||
{{ config.model?.name || config.modelId }}
|
||||
</NTag>
|
||||
<NTag
|
||||
v-if="config.provider?.corsRestricted && !isElectronEnv"
|
||||
size="small"
|
||||
type="error"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
{{ t('modelManager.corsRestrictedTag') }}
|
||||
</NTag>
|
||||
<!-- 能力标签移到这里 -->
|
||||
<NTag v-if="config.model?.capabilities?.text2image" size="small" type="success" :bordered="false">
|
||||
<NTag v-if="config.model?.capabilities?.text2image" size="small" type="success" round :bordered="false">
|
||||
{{ t('image.capability.text2image') }}
|
||||
</NTag>
|
||||
<NTag v-if="config.model?.capabilities?.image2image" size="small" type="info" :bordered="false">
|
||||
<NTag v-if="config.model?.capabilities?.image2image" size="small" type="info" round :bordered="false">
|
||||
{{ t('image.capability.image2image') }}
|
||||
</NTag>
|
||||
</NSpace>
|
||||
@@ -60,22 +65,28 @@
|
||||
</template>
|
||||
|
||||
<template #header-extra>
|
||||
<NSpace @click.stop>
|
||||
<NButton
|
||||
@click="testConnection(config.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
:disabled="isTestingConnectionFor(config.id)"
|
||||
:loading="isTestingConnectionFor(config.id)"
|
||||
>
|
||||
<template #icon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
||||
</svg>
|
||||
<NSpace @click.stop :size="4" class="image-model-actions">
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="testConnection(config.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
circle
|
||||
:title="t('modelManager.testConnection')"
|
||||
:disabled="isTestingConnectionFor(config.id)"
|
||||
:loading="isTestingConnectionFor(config.id)"
|
||||
>
|
||||
<template #icon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">{{ t('modelManager.testConnection') }}</span>
|
||||
</NButton>
|
||||
{{ t('modelManager.testConnection') }}
|
||||
</NTooltip>
|
||||
|
||||
<!-- 测试结果缩略图 -->
|
||||
<NImage
|
||||
@@ -89,53 +100,71 @@
|
||||
:alt="t('image.connection.testImagePreview')"
|
||||
/>
|
||||
|
||||
<NButton
|
||||
@click="editConfig(config.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
>
|
||||
<template #icon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
|
||||
</svg>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="editConfig(config.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
circle
|
||||
:title="t('modelManager.editModel')"
|
||||
>
|
||||
<template #icon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">{{ t('modelManager.editModel') }}</span>
|
||||
</NButton>
|
||||
{{ t('modelManager.editModel') }}
|
||||
</NTooltip>
|
||||
|
||||
<NButton
|
||||
@click="toggleConfig(config)"
|
||||
size="small"
|
||||
:type="config.enabled ? 'warning' : 'success'"
|
||||
quaternary
|
||||
>
|
||||
<template #icon>
|
||||
<svg v-if="config.enabled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
||||
<path d="M12 6v.343"/>
|
||||
<path d="M18.218 18.218A7 7 0 0 1 5 15V9a7 7 0 0 1 .782-3.218"/>
|
||||
<path d="M19 13.343V9A7 7 0 0 0 8.56 2.902"/>
|
||||
<path d="M22 22 2 2"/>
|
||||
</svg>
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
||||
<rect x="5" y="2" width="14" height="20" rx="7"/>
|
||||
<path d="M12 6v4"/>
|
||||
</svg>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="toggleConfig(config)"
|
||||
size="small"
|
||||
quaternary
|
||||
circle
|
||||
class="image-model-toggle"
|
||||
:title="config.enabled ? t('common.disable') : t('common.enable')"
|
||||
>
|
||||
<template #icon>
|
||||
<svg v-if="config.enabled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
||||
<path d="M12 6v.343"/>
|
||||
<path d="M18.218 18.218A7 7 0 0 1 5 15V9a7 7 0 0 1 .782-3.218"/>
|
||||
<path d="M19 13.343V9A7 7 0 0 0 8.56 2.902"/>
|
||||
<path d="M22 22 2 2"/>
|
||||
</svg>
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4">
|
||||
<rect x="5" y="2" width="14" height="20" rx="7"/>
|
||||
<path d="M12 6v4"/>
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">{{ config.enabled ? t('common.disable') : t('common.enable') }}</span>
|
||||
</NButton>
|
||||
{{ config.enabled ? t('common.disable') : t('common.enable') }}
|
||||
</NTooltip>
|
||||
|
||||
<NButton
|
||||
@click="deleteConfig(config.id)"
|
||||
size="small"
|
||||
type="error"
|
||||
quaternary
|
||||
>
|
||||
<template #icon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="deleteConfig(config.id)"
|
||||
size="small"
|
||||
type="error"
|
||||
quaternary
|
||||
circle
|
||||
:title="t('common.delete')"
|
||||
>
|
||||
<template #icon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.11 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">{{ t('common.delete') }}</span>
|
||||
</NButton>
|
||||
{{ t('common.delete') }}
|
||||
</NTooltip>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NCard>
|
||||
@@ -148,7 +177,7 @@ import { ref, onMounted, inject, h } from 'vue'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {
|
||||
NSpace, NCard, NText, NTag, NButton, NEmpty, NImage, useDialog
|
||||
NSpace, NCard, NText, NTag, NButton, NEmpty, NImage, NTooltip, useDialog
|
||||
} from 'naive-ui'
|
||||
import { useImageModelManager } from '../composables/model/useImageModelManager'
|
||||
import { useToast } from '../composables/ui/useToast'
|
||||
@@ -354,6 +383,39 @@ defineExpose({
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-model-stack {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-model-card {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.image-model-card :deep(.n-card-header) {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.image-model-card :deep(.n-card__content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.image-model-heading,
|
||||
.image-model-tags {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.image-model-tags {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.image-model-actions {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.image-model-toggle {
|
||||
color: var(--n-text-color);
|
||||
}
|
||||
|
||||
/* 文本截断样式 */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
:show="show"
|
||||
preset="card"
|
||||
:style="{ width: '90vw', maxWidth: '1200px', maxHeight: '90vh' }"
|
||||
content-style="padding: 0; display: flex; flex-direction: column; height: min(75vh, 800px); overflow: hidden;"
|
||||
:title="t('modelManager.title')"
|
||||
size="large"
|
||||
:bordered="false"
|
||||
@@ -63,23 +64,41 @@
|
||||
</NButton>
|
||||
</template>
|
||||
|
||||
<NScrollbar style="max-height: 75vh;">
|
||||
<NTabs v-model:value="activeTab" type="line" size="small" style="margin-bottom: 12px;">
|
||||
<NTabPane name="text" :tab="t('modelManager.textModels')">
|
||||
<TextModelManager ref="textManagerRef" @models-updated="handleTextModelsUpdated" />
|
||||
</NTabPane>
|
||||
<NTabPane name="image" :tab="t('modelManager.imageModels')">
|
||||
<ImageModelManager
|
||||
ref="imageListRef"
|
||||
@edit="handleEditImageModel"
|
||||
@add="handleAddImageModel"
|
||||
/>
|
||||
</NTabPane>
|
||||
<NTabPane name="function" :tab="t('modelManager.functionModels')">
|
||||
<FunctionModelManager ref="functionManagerRef" />
|
||||
</NTabPane>
|
||||
<div class="model-manager-content">
|
||||
<NTabs v-model:value="activeTab" type="segment" size="small" animated class="model-manager-tabs">
|
||||
<NTabPane name="text" :tab="t('modelManager.textModels')" />
|
||||
<NTabPane name="image" :tab="t('modelManager.imageModels')" />
|
||||
<NTabPane name="function" :tab="t('modelManager.functionModels')" />
|
||||
</NTabs>
|
||||
</NScrollbar>
|
||||
|
||||
<div class="model-manager-panel">
|
||||
<NCard
|
||||
embedded
|
||||
size="small"
|
||||
:bordered="false"
|
||||
class="model-manager-shell"
|
||||
content-style="padding: 16px; display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;"
|
||||
>
|
||||
<div class="model-manager-scroll-area">
|
||||
<TextModelManager
|
||||
v-show="activeTab === 'text'"
|
||||
ref="textManagerRef"
|
||||
@models-updated="handleTextModelsUpdated"
|
||||
/>
|
||||
<ImageModelManager
|
||||
v-show="activeTab === 'image'"
|
||||
ref="imageListRef"
|
||||
@edit="handleEditImageModel"
|
||||
@add="handleAddImageModel"
|
||||
/>
|
||||
<FunctionModelManager
|
||||
v-show="activeTab === 'function'"
|
||||
ref="functionManagerRef"
|
||||
/>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</div>
|
||||
</NModal>
|
||||
|
||||
<ImageModelEditModal
|
||||
@@ -95,7 +114,7 @@
|
||||
import { inject, onMounted, onUnmounted, provide, ref, type Ref } from 'vue'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { NButton, NModal, NScrollbar, NTabs, NTabPane } from 'naive-ui'
|
||||
import { NButton, NCard, NModal, NTabs, NTabPane } from 'naive-ui'
|
||||
import ImageModelEditModal from './ImageModelEditModal.vue'
|
||||
import ImageModelManager from './ImageModelManager.vue'
|
||||
import TextModelManager from './TextModelManager.vue'
|
||||
@@ -184,6 +203,57 @@ if (typeof window !== 'undefined') {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.model-manager-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.model-manager-tabs {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.model-manager-tabs :deep(.n-tabs-nav) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.model-manager-tabs :deep(.n-tabs-nav-scroll-wrapper) {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.model-manager-tabs :deep(.n-tabs-content) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.model-manager-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.model-manager-shell {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.model-manager-shell :deep(.n-card__content) {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.model-manager-scroll-area {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.modal-enter-active,
|
||||
.modal-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
<template>
|
||||
<NSpace vertical :size="12">
|
||||
<NSpace vertical :size="12" class="model-list-stack">
|
||||
<NCard
|
||||
v-for="model in models"
|
||||
:key="model.id"
|
||||
size="small"
|
||||
hoverable
|
||||
class="model-config-card"
|
||||
:style="{ opacity: model.enabled ? 1 : 0.6 }"
|
||||
>
|
||||
<template #header>
|
||||
<NSpace justify="space-between" align="center">
|
||||
<NSpace vertical :size="2">
|
||||
<NSpace align="center">
|
||||
<NSpace vertical :size="4" class="model-card-heading">
|
||||
<NSpace align="center" :size="8">
|
||||
<NText strong>{{ model.name }}</NText>
|
||||
<NTag v-if="!model.enabled" type="warning" size="small">
|
||||
<NTag v-if="!model.enabled" type="warning" size="small" round :bordered="false">
|
||||
{{ t('modelManager.disabled') }}
|
||||
</NTag>
|
||||
</NSpace>
|
||||
|
||||
<NSpace :size="6">
|
||||
<NTag size="small" type="info" :bordered="false">
|
||||
<NSpace :size="6" class="model-card-tags">
|
||||
<NTag size="small" type="default" round :bordered="false">
|
||||
{{ model.providerMeta?.name || model.providerMeta?.id }}
|
||||
</NTag>
|
||||
<NTag size="small" type="primary" :bordered="false">
|
||||
<NTag size="small" type="info" round :bordered="false">
|
||||
{{ model.modelMeta?.name || model.modelMeta?.id }}
|
||||
</NTag>
|
||||
<NTag
|
||||
v-if="model.providerMeta?.corsRestricted && !isElectronEnv"
|
||||
size="small"
|
||||
type="error"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
{{ t('modelManager.corsRestrictedTag') }}
|
||||
@@ -35,6 +38,7 @@
|
||||
v-if="model.modelMeta?.capabilities?.supportsTools"
|
||||
size="small"
|
||||
type="success"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
{{ t('modelManager.capabilities.tools') }}
|
||||
@@ -43,132 +47,158 @@
|
||||
v-if="model.modelMeta?.capabilities?.supportsReasoning"
|
||||
size="small"
|
||||
type="warning"
|
||||
round
|
||||
:bordered="false"
|
||||
>
|
||||
{{ t('modelManager.capabilities.reasoning') }}
|
||||
</NTag>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<template #header-extra>
|
||||
<NSpace @click.stop>
|
||||
<NButton
|
||||
@click="emit('test', model.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
:disabled="isTestingConnectionFor(model.id)"
|
||||
:loading="isTestingConnectionFor(model.id)"
|
||||
>
|
||||
<template #icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
<NSpace @click.stop :size="4" class="model-card-actions">
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="emit('test', model.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
circle
|
||||
:title="t('modelManager.testConnection')"
|
||||
:disabled="isTestingConnectionFor(model.id)"
|
||||
:loading="isTestingConnectionFor(model.id)"
|
||||
>
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
||||
</svg>
|
||||
<template #icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
>
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">{{ t('modelManager.testConnection') }}</span>
|
||||
</NButton>
|
||||
{{ t('modelManager.testConnection') }}
|
||||
</NTooltip>
|
||||
|
||||
<NButton @click="emit('edit', model.id)" size="small" quaternary>
|
||||
<template #icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="h-4 w-4"
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="emit('edit', model.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
circle
|
||||
:title="t('modelManager.editModel')"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
<template #icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="h-4 w-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">{{ t('modelManager.editModel') }}</span>
|
||||
</NButton>
|
||||
{{ t('modelManager.editModel') }}
|
||||
</NTooltip>
|
||||
|
||||
<NButton
|
||||
@click="emit(model.enabled ? 'disable' : 'enable', model.id)"
|
||||
size="small"
|
||||
:type="model.enabled ? 'warning' : 'success'"
|
||||
quaternary
|
||||
>
|
||||
<template #icon>
|
||||
<svg
|
||||
v-if="model.enabled"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="emit(model.enabled ? 'disable' : 'enable', model.id)"
|
||||
size="small"
|
||||
quaternary
|
||||
circle
|
||||
class="model-card-toggle"
|
||||
:title="model.enabled ? t('common.disable') : t('common.enable')"
|
||||
>
|
||||
<path d="M12 6v.343" />
|
||||
<path d="M18.218 18.218A7 7 0 0 1 5 15V9a7 7 0 0 1 .782-3.218" />
|
||||
<path d="M19 13.343V9A7 7 0 0 0 8.56 2.902" />
|
||||
<path d="M22 22 2 2" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
>
|
||||
<rect x="5" y="2" width="14" height="20" rx="7" />
|
||||
<path d="M12 6v4" />
|
||||
</svg>
|
||||
<template #icon>
|
||||
<svg
|
||||
v-if="model.enabled"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
>
|
||||
<path d="M12 6v.343" />
|
||||
<path d="M18.218 18.218A7 7 0 0 1 5 15V9a7 7 0 0 1 .782-3.218" />
|
||||
<path d="M19 13.343V9A7 7 0 0 0 8.56 2.902" />
|
||||
<path d="M22 22 2 2" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-4 w-4"
|
||||
>
|
||||
<rect x="5" y="2" width="14" height="20" rx="7" />
|
||||
<path d="M12 6v4" />
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">
|
||||
{{ model.enabled ? t('common.disable') : t('common.enable') }}
|
||||
</span>
|
||||
</NButton>
|
||||
{{ model.enabled ? t('common.disable') : t('common.enable') }}
|
||||
</NTooltip>
|
||||
|
||||
<NButton
|
||||
v-if="!isDefaultModel(model.id)"
|
||||
@click="emit('delete', model.id)"
|
||||
size="small"
|
||||
type="error"
|
||||
quaternary
|
||||
>
|
||||
<template #icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="h-4 w-4"
|
||||
<NTooltip v-if="!isDefaultModel(model.id)" trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
@click="emit('delete', model.id)"
|
||||
size="small"
|
||||
type="error"
|
||||
quaternary
|
||||
circle
|
||||
:title="t('common.delete')"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
<template #icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="h-4 w-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.11 0 0 0-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<span class="hidden md:inline">{{ t('common.delete') }}</span>
|
||||
</NButton>
|
||||
{{ t('common.delete') }}
|
||||
</NTooltip>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NCard>
|
||||
@@ -179,7 +209,7 @@
|
||||
import { type PropType } from 'vue'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { NButton, NCard, NTag, NText, NSpace } from 'naive-ui'
|
||||
import { NButton, NCard, NTag, NText, NSpace, NTooltip } from 'naive-ui'
|
||||
import { isRunningInElectron, type TextModelConfig } from '@prompt-optimizer/core'
|
||||
|
||||
const { models, isTestingConnectionFor, isDefaultModel } = defineProps({
|
||||
@@ -203,3 +233,38 @@ const { t } = useI18n()
|
||||
|
||||
const isElectronEnv = isRunningInElectron()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.model-list-stack {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.model-config-card {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.model-config-card :deep(.n-card-header) {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.model-config-card :deep(.n-card__content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.model-card-heading,
|
||||
.model-card-tags {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.model-card-tags {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.model-card-actions {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.model-card-toggle {
|
||||
color: var(--n-text-color);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user