254 lines
6.2 KiB
Vue
254 lines
6.2 KiB
Vue
<template>
|
||
<el-dialog
|
||
:model-value="modelValue"
|
||
title="发送测试消息"
|
||
width="560px"
|
||
:close-on-click-modal="false"
|
||
@update:model-value="$emit('update:modelValue', $event)"
|
||
@open="onOpen"
|
||
@closed="onClosed"
|
||
>
|
||
<div class="test-message-content">
|
||
<!-- 提供商信息 -->
|
||
<div class="provider-info">
|
||
<span class="provider-label">{{ provider?.providerName }}</span>
|
||
<el-tag size="small">{{ getProviderTypeLabel(provider?.providerType || '') }}</el-tag>
|
||
</div>
|
||
|
||
<!-- 模型选择 -->
|
||
<el-form label-position="top">
|
||
<el-form-item label="选择模型">
|
||
<el-select
|
||
v-model="selectedModel"
|
||
placeholder="请选择要测试的模型"
|
||
style="width: 100%"
|
||
>
|
||
<el-option
|
||
v-for="model in chatModels"
|
||
:key="model.id"
|
||
:label="model.displayName || model.modelName"
|
||
:value="model.modelName"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<!-- 消息内容 -->
|
||
<el-form-item label="测试消息">
|
||
<el-input
|
||
v-model="message"
|
||
type="textarea"
|
||
:rows="2"
|
||
placeholder="留空使用默认消息:你好,请用一句话介绍你自己。"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<!-- 发送按钮 -->
|
||
<div class="send-section">
|
||
<el-button
|
||
type="primary"
|
||
:loading="sending"
|
||
:disabled="!selectedModel"
|
||
@click="handleSend"
|
||
>
|
||
<el-icon class="mr-1"><Promotion /></el-icon>
|
||
{{ sending ? '等待回复...' : '发送测试消息' }}
|
||
</el-button>
|
||
</div>
|
||
|
||
<!-- 测试结果 -->
|
||
<div v-if="testResult" class="result-section">
|
||
<div class="result-header" :class="{ success: testResult.success, error: !testResult.success }">
|
||
<el-icon v-if="testResult.success"><CircleCheckFilled /></el-icon>
|
||
<el-icon v-else><CircleCloseFilled /></el-icon>
|
||
<span>{{ testResult.message }}</span>
|
||
<span v-if="testResult.latency" class="result-meta">
|
||
{{ testResult.latency }}ms
|
||
</span>
|
||
<span v-if="testResult.tokens" class="result-meta">
|
||
{{ testResult.tokens }} tokens
|
||
</span>
|
||
<span v-if="testResult.model" class="result-meta">
|
||
模型: {{ testResult.model }}
|
||
</span>
|
||
</div>
|
||
|
||
<!-- AI 回复内容 -->
|
||
<div v-if="testResult.reply" class="result-reply">
|
||
<div class="reply-label">AI 回复:</div>
|
||
<div class="reply-content">{{ testResult.reply }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<template #footer>
|
||
<el-button @click="$emit('update:modelValue', false)">关闭</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
import { Promotion, CircleCheckFilled, CircleCloseFilled } from '@element-plus/icons-vue'
|
||
import { useProviderStore } from '@/stores/provider'
|
||
|
||
const props = defineProps<{
|
||
modelValue: boolean
|
||
provider?: AIProvider | null
|
||
}>()
|
||
|
||
defineEmits<{
|
||
'update:modelValue': [value: boolean]
|
||
}>()
|
||
|
||
const store = useProviderStore()
|
||
const selectedModel = ref('')
|
||
const message = ref('')
|
||
const sending = ref(false)
|
||
const testResult = ref<SendTestMessageResponse | null>(null)
|
||
|
||
/** 只显示对话类型模型 */
|
||
const chatModels = computed(() => {
|
||
if (!props.provider) return []
|
||
return props.provider.models.filter(m => m.modelType === 'chat' && m.isEnabled)
|
||
})
|
||
|
||
function getProviderTypeLabel(type: string) {
|
||
const labels: Record<string, string> = {
|
||
openai: 'OpenAI / 兼容接口',
|
||
claude: 'Anthropic Claude',
|
||
gemini: 'Google Gemini',
|
||
custom: '自定义接口',
|
||
}
|
||
return labels[type] || type
|
||
}
|
||
|
||
/** 发送测试消息 */
|
||
async function handleSend() {
|
||
if (!props.provider || !selectedModel.value) return
|
||
|
||
sending.value = true
|
||
testResult.value = null
|
||
|
||
const result = await store.sendTestMessageExisting(
|
||
props.provider.id,
|
||
selectedModel.value,
|
||
message.value || undefined,
|
||
)
|
||
|
||
sending.value = false
|
||
|
||
if (result) {
|
||
testResult.value = result
|
||
} else {
|
||
testResult.value = {
|
||
success: false,
|
||
message: '请求失败,请检查网络',
|
||
reply: '',
|
||
model: '',
|
||
latency: 0,
|
||
tokens: 0,
|
||
}
|
||
}
|
||
}
|
||
|
||
/** 对话框打开 */
|
||
function onOpen() {
|
||
testResult.value = null
|
||
message.value = ''
|
||
// 默认选中第一个对话模型
|
||
if (chatModels.value.length > 0) {
|
||
selectedModel.value = chatModels.value[0].modelName
|
||
} else {
|
||
selectedModel.value = ''
|
||
}
|
||
}
|
||
|
||
/** 对话框关闭 */
|
||
function onClosed() {
|
||
testResult.value = null
|
||
sending.value = false
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.test-message-content {
|
||
.provider-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 16px;
|
||
padding: 10px 14px;
|
||
background: var(--el-bg-color-page);
|
||
border-radius: 8px;
|
||
|
||
.provider-label {
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
|
||
.send-section {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.result-section {
|
||
border: 1px solid var(--el-border-color-lighter);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
|
||
.result-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 10px 14px;
|
||
font-size: 13px;
|
||
flex-wrap: wrap;
|
||
|
||
&.success {
|
||
background: var(--el-color-success-light-9);
|
||
color: var(--el-color-success);
|
||
}
|
||
&.error {
|
||
background: var(--el-color-danger-light-9);
|
||
color: var(--el-color-danger);
|
||
}
|
||
|
||
.result-meta {
|
||
font-size: 12px;
|
||
opacity: 0.8;
|
||
margin-left: 4px;
|
||
|
||
&::before {
|
||
content: '·';
|
||
margin-right: 4px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.result-reply {
|
||
padding: 14px;
|
||
|
||
.reply-label {
|
||
font-size: 12px;
|
||
color: var(--el-text-color-secondary);
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.reply-content {
|
||
font-size: 14px;
|
||
line-height: 1.7;
|
||
color: var(--el-text-color-primary);
|
||
background: var(--el-bg-color-page);
|
||
padding: 12px 14px;
|
||
border-radius: 8px;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.mr-1 { margin-right: 4px; }
|
||
</style>
|