feat:精选资源

This commit is contained in:
小张 2025-10-13 17:59:51 +08:00
parent 42d84ee2fa
commit 3ff9e43877
9 changed files with 534 additions and 184 deletions

View File

@ -19,6 +19,8 @@ export { default as MenuApi } from './modules/menu'
export type { MenuItem } from './modules/menu'
export { SystemApi } from './modules/system'
export type { SystemSettings, DictItem } from './modules/system'
export { ResourceApi } from './modules/resource'
export type { FeaturedResource } from './modules/resource'
// API 基础配置
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/jeecgboot'

View File

@ -708,6 +708,12 @@ const routes: RouteRecordRaw[] = [
component: Resources,
meta: { title: '精选资源' }
},
{
path: '/resource-test',
name: 'ResourceTest',
component: () => import('@/views/ResourceTest.vue'),
meta: { title: '精选资源API测试' }
},
{
path: '/special-training',
name: 'SpecialTraining',

View File

@ -174,28 +174,28 @@ class HttpClient {
* GET请求
*/
get<T = any>(url: string, config?: HttpRequestConfig): Promise<HttpResponse<T>> {
return this.instance.get(url, config)
return this.instance.get(url, config).then(response => response.data)
}
/**
* POST请求
*/
post<T = any>(url: string, data?: any, config?: HttpRequestConfig): Promise<HttpResponse<T>> {
return this.instance.post(url, data, config)
return this.instance.post(url, data, config).then(response => response.data)
}
/**
* PUT请求
*/
put<T = any>(url: string, data?: any, config?: HttpRequestConfig): Promise<HttpResponse<T>> {
return this.instance.put(url, data, config)
return this.instance.put(url, data, config).then(response => response.data)
}
/**
* DELETE请求
*/
delete<T = any>(url: string, config?: HttpRequestConfig): Promise<HttpResponse<T>> {
return this.instance.delete(url, config)
return this.instance.delete(url, config).then(response => response.data)
}
/**

View File

@ -13,22 +13,40 @@
<!-- 精选视频区域 -->
<section class="featured-videos">
<h2 class="section-title">精选视频</h2>
<div class="featured-grid">
<!-- 加载状态 -->
<div v-if="loading" class="loading-container">
<div class="loading-spinner"></div>
<p>正在加载精选资源...</p>
</div>
<!-- 错误状态 -->
<div v-else-if="error" class="error-container">
<p class="error-message">{{ error }}</p>
<button @click="fetchFeaturedResources" class="retry-button">重试</button>
</div>
<!-- 精选视频网格 -->
<div v-else class="featured-grid">
<div v-for="video in featuredVideos" :key="video.id" class="featured-card" @click="handleVideoClick(video)">
<div class="card-image">
<img :src="video.image" :alt="video.title" class="video-thumbnail" />
<div class="duration-badge">
<img
:src="video.thumbnailUrl || video.image"
:alt="video.name || video.title"
class="video-thumbnail"
/>
<div v-if="video.type === 0 && video.duration" class="duration-badge">
<img src="/images/Featured_resources/duration.png" alt="时长" class="duration-icon">
42:52
{{ formatDuration(video.duration) }}
</div>
<div class="play-button">
<div v-if="video.type === 0" class="play-button">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M8 5V19L19 12L8 5Z" fill="white" />
</svg>
</div>
</div>
<div class="card-content">
<h3 class="card-title">{{ video.title }}</h3>
<h3 class="card-title">{{ video.name || video.title }}</h3>
</div>
</div>
</div>
@ -101,7 +119,7 @@
<div v-if="showVideoModal" class="video-modal-overlay" @click="closeVideoModal">
<div class="video-modal" @click.stop>
<div class="video-modal-header">
<h3 class="video-modal-title">{{ currentVideo?.title || '视频播放' }}</h3>
<h3 class="video-modal-title">{{ currentVideo?.name || currentVideo?.title || '视频播放' }}</h3>
<button class="close-btn" @click="closeVideoModal">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
@ -109,8 +127,8 @@
</button>
</div>
<div class="video-modal-body">
<DPlayerVideo ref="videoPlayerRef" :video-url="currentVideoUrl" :placeholder-image="currentVideo?.image"
:placeholder-text="'点击播放视频'" :title="currentVideo?.title || '视频播放'" @play="handleVideoPlay"
<DPlayerVideo ref="videoPlayerRef" :video-url="currentVideoUrl" :placeholder-image="currentVideo?.thumbnailUrl || currentVideo?.image"
:placeholder-text="'点击播放视频'" :title="currentVideo?.name || currentVideo?.title || '视频播放'" @play="handleVideoPlay"
@pause="handleVideoPause" @ended="handleVideoEnded" @error="handleVideoError" />
</div>
</div>
@ -119,8 +137,9 @@
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue'
import { ref, nextTick, onMounted } from 'vue'
import DPlayerVideo from '@/components/course/DPlayerVideo.vue'
import { ResourceApi, type FeaturedResource } from '@/api/modules/resource'
//
const showVideoModal = ref(false)
@ -137,26 +156,9 @@ const VIDEO_CONFIG = {
}
//
const featuredVideos = ref([
{
id: 1,
title: '西安工业大学内部资源之一',
image: '/images/Featured_resources/精选视频1.png',
videoUrl: VIDEO_CONFIG.LOCAL // URL
},
{
id: 2,
title: '华南工业大学内部资源之一',
image: '/images/Featured_resources/精选视频2.png',
videoUrl: VIDEO_CONFIG.LOCAL
},
{
id: 3,
title: '西安工业大学内部资源之一',
image: '/images/Featured_resources/精品视频3.png',
videoUrl: VIDEO_CONFIG.LOCAL
}
])
const featuredVideos = ref<FeaturedResource[]>([])
const loading = ref(false)
const error = ref('')
//
const videoTabs = ref([
@ -276,16 +278,94 @@ const allImages = ref([
}
])
//
const fetchFeaturedResources = async () => {
try {
loading.value = true
error.value = ''
const response = await ResourceApi.getFeaturedResources()
if (response.code === 200 && response.data?.result) {
featuredVideos.value = response.data.result
console.log('✅ 精选资源加载成功:', featuredVideos.value)
} else {
throw new Error(response.message || '获取精选资源失败')
}
} catch (err: any) {
error.value = err.message || '获取精选资源失败'
console.error('❌ 获取精选资源失败:', err)
} finally {
loading.value = false
}
}
//
const formatDuration = (seconds: number): string => {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
} else {
return `${minutes}:${secs.toString().padStart(2, '0')}`
}
}
// URL
const parseVideoUrls = (fileUrl: string): string[] => {
if (!fileUrl) return []
return fileUrl.split(',').map(url => url.trim()).filter(url => url)
}
// URL
const getBestVideoUrl = (fileUrl: string): string => {
console.log('🔍 解析视频URL:', fileUrl)
const urls = parseVideoUrls(fileUrl)
console.log('🔍 解析后的URL数组:', urls)
if (urls.length === 0) {
console.warn('⚠️ 没有有效的视频URL使用本地视频作为备用')
return VIDEO_CONFIG.LOCAL
}
// 720p
const preferredUrl = urls.find(url => url.includes('720p')) || urls[0]
console.log('✅ 选择的视频URL:', preferredUrl)
return preferredUrl
}
//
const handleVideoClick = async (video: any) => {
console.log('点击视频:', video.title)
const handleVideoClick = async (video: FeaturedResource) => {
//
if (video.type !== 0) {
console.log('这是图片资源,不能播放')
return
}
console.log('🎬 点击视频:', video.name)
console.log('🔍 视频数据:', {
id: video.id,
name: video.name,
type: video.type,
fileUrl: video.fileUrl,
thumbnailUrl: video.thumbnailUrl
})
currentVideo.value = video
currentVideoUrl.value = video.videoUrl || VIDEO_CONFIG.LOCAL
// URL
const videoUrl = getBestVideoUrl(video.fileUrl)
currentVideoUrl.value = videoUrl
console.log('🎯 最终使用的视频URL:', currentVideoUrl.value)
showVideoModal.value = true
//
await nextTick()
if (videoPlayerRef.value) {
console.log('🔧 初始化播放器URL:', currentVideoUrl.value)
await videoPlayerRef.value.initializePlayer(currentVideoUrl.value)
}
}
@ -301,6 +381,24 @@ const closeVideoModal = () => {
}
}
//
const handleImageError = (event: Event) => {
const img = event.target as HTMLImageElement
console.error('❌ 图片加载失败:', img.src)
//
img.src = '/images/Featured_resources/default-thumbnail.png'
}
const handleImageLoad = (event: Event) => {
const img = event.target as HTMLImageElement
console.log('✅ 图片加载成功:', img.src)
}
//
onMounted(() => {
fetchFeaturedResources()
})
// DPlayer
const handleVideoPlay = () => {
console.log('视频开始播放')
@ -370,6 +468,53 @@ const handleVideoError = (error: any) => {
margin-bottom: 80px;
}
/* 加载和错误状态 */
.loading-container,
.error-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
text-align: center;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #0088D1;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-message {
color: #ff4757;
font-size: 16px;
margin-bottom: 16px;
}
.retry-button {
background: #0088D1;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.retry-button:hover {
background: #0066A1;
}
.featured-grid {
margin: 0 auto;
width: 1420px;
@ -677,6 +822,11 @@ const handleVideoError = (error: any) => {
.all-videos {
margin-bottom: 60px;
}
.loading-container,
.error-container {
padding: 40px 20px;
}
}
@media (max-width: 480px) {

View File

@ -62,17 +62,17 @@
</div>
<!-- ai助教 - 已注释 -->
<div class="ai-container">
<!-- <div class="ai-container">
<router-link to="/teacher/ai-assistant" class="ai-tab" @mouseenter="isAiHovered = true"
@mouseleave="isAiHovered = false">
<img :src="(isAiActive || isAiHovered) ? '/images/aiAssistant/AI助教1.png' : '/images/aiAssistant/AI助教2.png'"
alt="ai" />
<span>AI助教</span>
</router-link>
</div>
</div> -->
<!-- 智能体编排 - 可展开菜单 - 已注释 -->
<div class="nav-container orchestration-nav">
<!-- <div class="nav-container orchestration-nav">
<div class="nav-item" :class="{ active: activeNavItem === 6 }" @click="toggleOrchestrationMenu">
<img :src="activeNavItem === 6 ? '/images/aiAssistant/AI助教1.png' : '/images/aiAssistant/AI助教2.png'" alt="">
<span>智能体编排</span>
@ -104,7 +104,7 @@
<span>OCR识别</span>
</router-link>
</div>
</div>
</div> -->
</div>
<!-- 右侧路由视图 -->

View File

@ -1,71 +1,60 @@
<template>
<div class="development-page">
<div class="page-header">
<h1>AI应用管理</h1>
<p>管理和配置各种AI应用</p>
</div>
<div class="development-notice">
<div class="notice-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 17L12 22L22 17" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
</svg>
</div>
<h2>功能开发中</h2>
<p>AI应用管理功能正在紧张开发中敬请期待</p>
<div class="redirect-container">
<div class="redirect-message" v-if="!redirected">
<div class="loading-spinner"></div>
<p>正在跳转到AI应用管理页面...</p>
</div>
</div>
</template>
<script setup lang="ts">
// AI
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const redirected = ref(false)
onMounted(() => {
// AI
redirected.value = true
router.replace('/teacher/ai/app')
})
</script>
<style scoped>
.development-page {
.redirect-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
padding: 24px;
max-width: 1200px;
margin: 0 auto;
}
.page-header {
.redirect-message {
text-align: center;
margin-bottom: 48px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.page-header h1 {
font-size: 32px;
color: #1890ff;
margin-bottom: 8px;
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f3f3f3;
border-top: 3px solid #1890ff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.page-header p {
font-size: 16px;
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.redirect-message p {
color: #666;
}
.development-notice {
text-align: center;
padding: 48px 24px;
background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
border-radius: 12px;
border: 1px solid #91d5ff;
}
.notice-icon {
margin-bottom: 24px;
}
.development-notice h2 {
font-size: 24px;
color: #1890ff;
margin-bottom: 12px;
}
.development-notice p {
font-size: 16px;
color: #666;
margin: 0;
}
</style>

View File

@ -1,33 +1,55 @@
<template>
<div class="development-page">
<div class="flow-design-page">
<div class="page-header">
<h1>AI流程设计</h1>
<p>可视化设计AI工作流程</p>
</div>
<div class="development-notice">
<div class="coming-soon-notice">
<div class="notice-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 17L12 22L22 17" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
</svg>
</div>
<h2>功能开发中</h2>
<p>AI流程设计功能正在紧张开发中敬请期待</p>
<h2>即将上线</h2>
<p>AI流程设计功能即将上线将支持</p>
<ul class="feature-list">
<li>拖拽式流程设计器</li>
<li>多种AI节点类型</li>
<li>条件分支和循环</li>
<li>实时流程预览</li>
<li>流程模板库</li>
</ul>
<div class="action-buttons">
<button class="btn-primary" @click="handleNotifyMe">通知我上线</button>
<button class="btn-secondary" @click="handleViewDemo">查看演示</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
// AI
import { useMessage } from 'naive-ui'
const message = useMessage()
const handleNotifyMe = () => {
message.success('已为您开启上线通知,我们会在功能上线时第一时间通知您!')
}
const handleViewDemo = () => {
message.info('演示功能正在准备中,敬请期待!')
}
</script>
<style scoped>
.development-page {
.flow-design-page {
padding: 24px;
max-width: 1200px;
margin: 0 auto;
min-height: 80vh;
}
.page-header {
@ -39,6 +61,7 @@
font-size: 32px;
color: #1890ff;
margin-bottom: 8px;
font-weight: 600;
}
.page-header p {
@ -46,26 +69,117 @@
color: #666;
}
.development-notice {
.coming-soon-notice {
text-align: center;
padding: 48px 24px;
padding: 60px 24px;
background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
border-radius: 12px;
border-radius: 16px;
border: 1px solid #91d5ff;
box-shadow: 0 4px 20px rgba(24, 144, 255, 0.1);
}
.notice-icon {
margin-bottom: 24px;
}
.development-notice h2 {
font-size: 24px;
.coming-soon-notice h2 {
font-size: 28px;
color: #1890ff;
margin-bottom: 12px;
margin-bottom: 16px;
font-weight: 600;
}
.development-notice p {
.coming-soon-notice > p {
font-size: 16px;
color: #666;
margin-bottom: 24px;
}
.feature-list {
text-align: left;
display: inline-block;
margin: 24px 0;
padding: 0;
list-style: none;
}
.feature-list li {
padding: 8px 0;
color: #333;
font-size: 15px;
position: relative;
padding-left: 24px;
}
.feature-list li::before {
content: '✓';
position: absolute;
left: 0;
color: #1890ff;
font-weight: bold;
}
.action-buttons {
margin-top: 32px;
display: flex;
gap: 16px;
justify-content: center;
}
.btn-primary,
.btn-secondary {
padding: 12px 24px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
border: none;
}
.btn-primary {
background: #1890ff;
color: white;
}
.btn-primary:hover {
background: #40a9ff;
transform: translateY(-1px);
}
.btn-secondary {
background: white;
color: #1890ff;
border: 1px solid #1890ff;
}
.btn-secondary:hover {
background: #f0f9ff;
transform: translateY(-1px);
}
/* 响应式设计 */
@media (max-width: 768px) {
.flow-design-page {
padding: 16px;
}
.page-header h1 {
font-size: 24px;
}
.coming-soon-notice {
padding: 40px 16px;
}
.action-buttons {
flex-direction: column;
align-items: center;
}
.btn-primary,
.btn-secondary {
width: 200px;
}
}
</style>

View File

@ -1,71 +1,10 @@
<template>
<div class="development-page">
<div class="page-header">
<h1>AI知识库</h1>
<p>构建和管理AI知识库</p>
</div>
<div class="development-notice">
<div class="notice-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 17L12 22L22 17" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
</svg>
</div>
<h2>功能开发中</h2>
<p>AI知识库功能正在紧张开发中敬请期待</p>
</div>
</div>
<!-- 直接使用真实的AI知识库管理组件 -->
<AiKnowledgeBaseList />
</template>
<script setup lang="ts">
// AI
import AiKnowledgeBaseList from '@/views/teacher/ai-knowledge-naive-ui/AiKnowledgeBaseList.vue'
</script>
<style scoped>
.development-page {
padding: 24px;
max-width: 1200px;
margin: 0 auto;
}
.page-header {
text-align: center;
margin-bottom: 48px;
}
.page-header h1 {
font-size: 32px;
color: #1890ff;
margin-bottom: 8px;
}
.page-header p {
font-size: 16px;
color: #666;
}
.development-notice {
text-align: center;
padding: 48px 24px;
background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
border-radius: 12px;
border: 1px solid #91d5ff;
}
.notice-icon {
margin-bottom: 24px;
}
.development-notice h2 {
font-size: 24px;
color: #1890ff;
margin-bottom: 12px;
}
.development-notice p {
font-size: 16px;
color: #666;
}
</style>

View File

@ -1,26 +1,176 @@
<template>
<div class="development-page">
<div class="model-config-page">
<div class="page-header">
<h1>AI模型配置</h1>
<p>配置和优化AI模型参数</p>
</div>
<div class="development-notice">
<div class="notice-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 17L12 22L22 17" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="#1890ff" stroke-width="2" stroke-linejoin="round"/>
</svg>
</div>
<h2>功能开发中</h2>
<p>AI模型配置功能正在紧张开发中敬请期待</p>
</div>
<n-card class="config-card" title="模型配置中心">
<n-tabs type="line" animated>
<n-tab-pane name="llm" tab="大语言模型">
<div class="model-section">
<n-space vertical size="large">
<n-card title="OpenAI GPT 模型" size="small">
<n-form :model="openaiConfig" label-placement="left" label-width="120px">
<n-form-item label="API Key">
<n-input v-model:value="openaiConfig.apiKey" type="password" placeholder="请输入OpenAI API Key" />
</n-form-item>
<n-form-item label="模型版本">
<n-select v-model:value="openaiConfig.model" :options="gptModels" />
</n-form-item>
<n-form-item label="温度参数">
<n-slider v-model:value="openaiConfig.temperature" :min="0" :max="2" :step="0.1" />
</n-form-item>
</n-form>
</n-card>
<n-card title="本地模型" size="small">
<n-form :model="localConfig" label-placement="left" label-width="120px">
<n-form-item label="模型地址">
<n-input v-model:value="localConfig.endpoint" placeholder="http://localhost:8000" />
</n-form-item>
<n-form-item label="模型名称">
<n-input v-model:value="localConfig.modelName" placeholder="chatglm3-6b" />
</n-form-item>
</n-form>
</n-card>
</n-space>
</div>
</n-tab-pane>
<n-tab-pane name="embedding" tab="向量化模型">
<div class="model-section">
<n-card title="文本向量化配置" size="small">
<n-form :model="embeddingConfig" label-placement="left" label-width="120px">
<n-form-item label="向量维度">
<n-input-number v-model:value="embeddingConfig.dimension" :min="128" :max="4096" />
</n-form-item>
<n-form-item label="批处理大小">
<n-input-number v-model:value="embeddingConfig.batchSize" :min="1" :max="100" />
</n-form-item>
</n-form>
</n-card>
</div>
</n-tab-pane>
<n-tab-pane name="test" tab="模型测试">
<div class="test-section">
<n-space vertical size="large">
<n-input
v-model:value="testInput"
type="textarea"
placeholder="输入测试文本..."
:rows="4"
/>
<n-space>
<n-button type="primary" @click="handleTest" :loading="testing">
测试模型
</n-button>
<n-button @click="handleClear">清空</n-button>
</n-space>
<n-card v-if="testResult" title="测试结果" size="small">
<pre class="test-result">{{ testResult }}</pre>
</n-card>
</n-space>
</div>
</n-tab-pane>
</n-tabs>
<template #action>
<n-space>
<n-button @click="handleReset">重置</n-button>
<n-button type="primary" @click="handleSave" :loading="saving">
保存配置
</n-button>
</n-space>
</template>
</n-card>
</div>
</template>
<script setup lang="ts">
// AI
import { ref, reactive } from 'vue'
import { useMessage } from 'naive-ui'
const message = useMessage()
//
const openaiConfig = reactive({
apiKey: '',
model: 'gpt-3.5-turbo',
temperature: 0.7
})
const localConfig = reactive({
endpoint: '',
modelName: ''
})
const embeddingConfig = reactive({
dimension: 1536,
batchSize: 10
})
// GPT
const gptModels = [
{ label: 'GPT-3.5 Turbo', value: 'gpt-3.5-turbo' },
{ label: 'GPT-4', value: 'gpt-4' },
{ label: 'GPT-4 Turbo', value: 'gpt-4-turbo' }
]
//
const testInput = ref('')
const testResult = ref('')
const testing = ref(false)
const saving = ref(false)
const handleTest = async () => {
if (!testInput.value.trim()) {
message.warning('请输入测试文本')
return
}
testing.value = true
try {
// API
await new Promise(resolve => setTimeout(resolve, 2000))
testResult.value = `模型响应: 这是对"${testInput.value}"的测试响应。\n\n配置信息:\n- 模型: ${openaiConfig.model}\n- 温度: ${openaiConfig.temperature}\n- 向量维度: ${embeddingConfig.dimension}`
message.success('测试完成')
} catch (error) {
message.error('测试失败')
} finally {
testing.value = false
}
}
const handleClear = () => {
testInput.value = ''
testResult.value = ''
}
const handleSave = async () => {
saving.value = true
try {
//
await new Promise(resolve => setTimeout(resolve, 1000))
message.success('配置保存成功')
} catch (error) {
message.error('保存失败')
} finally {
saving.value = false
}
}
const handleReset = () => {
openaiConfig.apiKey = ''
openaiConfig.model = 'gpt-3.5-turbo'
openaiConfig.temperature = 0.7
localConfig.endpoint = ''
localConfig.modelName = ''
embeddingConfig.dimension = 1536
embeddingConfig.batchSize = 10
message.info('配置已重置')
}
</script>
<style scoped>