mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor(guide): 重构教程页面为管理员视角
- 重新规划教程结构:概览、供应商管理、模型管理、用户与密钥、高级功能、常见问题 - 新增详细的功能解释和配置说明 - 删除旧的 CLI/SDK 用户指南页面 - 修复路由配置,添加 meta.requiresAuth: false
This commit is contained in:
@@ -15,9 +15,46 @@ const routes: RouteRecordRaw[] = [
|
||||
|
||||
{
|
||||
path: '/guide',
|
||||
name: 'Guide',
|
||||
component: () => importWithRetry(() => import('@/views/public/Guide.vue')),
|
||||
meta: { requiresAuth: false }
|
||||
component: () => importWithRetry(() => import('@/views/public/guide/GuideLayout.vue')),
|
||||
meta: { requiresAuth: false },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'GuideOverview',
|
||||
component: () => importWithRetry(() => import('@/views/public/guide/Overview.vue')),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: 'provider',
|
||||
name: 'GuideProvider',
|
||||
component: () => importWithRetry(() => import('@/views/public/guide/ProviderGuide.vue')),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: 'model',
|
||||
name: 'GuideModel',
|
||||
component: () => importWithRetry(() => import('@/views/public/guide/ModelGuide.vue')),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: 'user-key',
|
||||
name: 'GuideUserKey',
|
||||
component: () => importWithRetry(() => import('@/views/public/guide/UserKeyGuide.vue')),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: 'advanced',
|
||||
name: 'GuideAdvanced',
|
||||
component: () => importWithRetry(() => import('@/views/public/guide/AdvancedGuide.vue')),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: 'faq',
|
||||
name: 'GuideFaq',
|
||||
component: () => importWithRetry(() => import('@/views/public/guide/GuideFaq.vue')),
|
||||
meta: { requiresAuth: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/logo-demo',
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
<template>
|
||||
<div class="min-h-screen literary-grid literary-paper">
|
||||
<!-- Header -->
|
||||
<header class="sticky top-0 z-50 border-b border-[#cc785c]/10 dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/90 dark:bg-[#191714]/95 backdrop-blur-xl transition-all">
|
||||
<div class="h-14 sm:h-16 flex items-center px-3 sm:px-4 md:px-8">
|
||||
<!-- Left: Logo & Brand -->
|
||||
<RouterLink
|
||||
to="/"
|
||||
class="flex items-center gap-2 sm:gap-3 group/logo cursor-pointer shrink-0"
|
||||
>
|
||||
<HeaderLogo
|
||||
size="h-7 w-7 sm:h-9 sm:w-9"
|
||||
class-name="text-[#191919] dark:text-white"
|
||||
/>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="text-base sm:text-lg font-bold text-[#191919] dark:text-white leading-none">
|
||||
Aether
|
||||
</h1>
|
||||
<span class="text-[9px] sm:text-[10px] text-[#91918d] dark:text-muted-foreground leading-none mt-1 sm:mt-1.5 font-medium tracking-wide">AI Gateway</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
|
||||
<!-- Spacer -->
|
||||
<div class="flex-1" />
|
||||
|
||||
<!-- Right: Actions -->
|
||||
<div class="flex items-center gap-1 sm:gap-2 shrink-0">
|
||||
<!-- Theme Toggle + GitHub Icons -->
|
||||
<div class="flex items-center gap-0.5 sm:gap-1">
|
||||
<button
|
||||
class="flex h-8 w-8 sm:h-9 sm:w-9 items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition"
|
||||
:title="themeMode === 'system' ? '跟随系统' : themeMode === 'dark' ? '深色模式' : '浅色模式'"
|
||||
@click="toggleDarkMode"
|
||||
>
|
||||
<SunMoon
|
||||
v-if="themeMode === 'system'"
|
||||
class="h-3.5 w-3.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
<Sun
|
||||
v-else-if="themeMode === 'light'"
|
||||
class="h-3.5 w-3.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
<Moon
|
||||
v-else
|
||||
class="h-3.5 w-3.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
href="https://github.com/fawney19/Aether"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex h-8 w-8 sm:h-9 sm:w-9 items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition"
|
||||
title="GitHub 仓库"
|
||||
>
|
||||
<GithubIcon class="h-3.5 w-3.5 sm:h-4 sm:w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="relative z-10 px-4 sm:px-8 md:px-16 lg:px-20 py-12 md:py-20">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<!-- Page Title -->
|
||||
<div class="text-center mb-12">
|
||||
<div
|
||||
class="inline-flex items-center gap-1.5 md:gap-2 rounded-full bg-[#cc785c]/10 dark:bg-[#cc785c]/20 border border-[#cc785c]/20 dark:border-[#cc785c]/40 px-3 md:px-4 py-1.5 md:py-2 text-xs md:text-sm font-medium text-[#cc785c] dark:text-[#d4a27f] mb-4 md:mb-6 backdrop-blur-sm"
|
||||
>
|
||||
<BookOpen class="h-3.5 w-3.5 md:h-4 md:w-4" />
|
||||
配置教程
|
||||
</div>
|
||||
<h1 class="text-3xl sm:text-4xl md:text-5xl font-bold text-[#191919] dark:text-white mb-4">
|
||||
配置指南
|
||||
</h1>
|
||||
<p class="text-base md:text-lg text-[#666663] dark:text-[#c9c3b4] max-w-2xl mx-auto">
|
||||
快速上手 Aether AI Gateway 的完整配置教程
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Wiki Content Placeholder -->
|
||||
<div class="bg-white/70 dark:bg-[#262624]/80 backdrop-blur-sm rounded-2xl p-8 md:p-12 border border-[#e5e4df] dark:border-[rgba(227,224,211,0.16)]">
|
||||
<div class="text-center py-16">
|
||||
<div class="flex h-16 w-16 items-center justify-center rounded-2xl bg-[#cc785c]/10 dark:bg-[#cc785c]/15 mx-auto mb-6">
|
||||
<FileText class="h-8 w-8 text-[#cc785c] dark:text-[#d4a27f]" />
|
||||
</div>
|
||||
<h2 class="text-xl md:text-2xl font-bold text-[#191919] dark:text-white mb-3">
|
||||
教程文档即将上线
|
||||
</h2>
|
||||
<p class="text-[#666663] dark:text-[#c9c3b4] max-w-md mx-auto mb-8">
|
||||
我们正在努力编写详细的配置教程,包括 Claude Code、Codex CLI 和 Gemini CLI 的完整配置指南。
|
||||
</p>
|
||||
<div class="flex flex-wrap items-center justify-center gap-3">
|
||||
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-amber-500/10 text-amber-600 dark:text-amber-400 text-sm font-medium">
|
||||
<Loader2 class="h-4 w-4 animate-spin" />
|
||||
文档编写中
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Coming Soon Sections -->
|
||||
<div class="mt-12 pt-8 border-t border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)]">
|
||||
<h3 class="text-lg font-semibold text-[#191919] dark:text-white mb-6 text-center">
|
||||
即将推出的内容
|
||||
</h3>
|
||||
<div class="grid md:grid-cols-3 gap-4">
|
||||
<div class="p-4 rounded-xl bg-[#fafaf7] dark:bg-[#1f1d1a] border border-[#e5e4df]/50 dark:border-[rgba(227,224,211,0.08)]">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-amber-500/10">
|
||||
<Code2 class="h-4 w-4 text-amber-500" />
|
||||
</div>
|
||||
<span class="font-medium text-[#191919] dark:text-white">Claude Code</span>
|
||||
</div>
|
||||
<p class="text-sm text-[#666663] dark:text-[#c9c3b4]">
|
||||
IDE 集成配置教程
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-4 rounded-xl bg-[#fafaf7] dark:bg-[#1f1d1a] border border-[#e5e4df]/50 dark:border-[rgba(227,224,211,0.08)]">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-emerald-500/10">
|
||||
<Terminal class="h-4 w-4 text-emerald-500" />
|
||||
</div>
|
||||
<span class="font-medium text-[#191919] dark:text-white">Codex CLI</span>
|
||||
</div>
|
||||
<p class="text-sm text-[#666663] dark:text-[#c9c3b4]">
|
||||
命令行工具配置教程
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-4 rounded-xl bg-[#fafaf7] dark:bg-[#1f1d1a] border border-[#e5e4df]/50 dark:border-[rgba(227,224,211,0.08)]">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10">
|
||||
<Sparkles class="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<span class="font-medium text-[#191919] dark:text-white">Gemini CLI</span>
|
||||
</div>
|
||||
<p class="text-sm text-[#666663] dark:text-[#c9c3b4]">
|
||||
多模态 AI 配置教程
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
import {
|
||||
BookOpen,
|
||||
Code2,
|
||||
FileText,
|
||||
Loader2,
|
||||
Moon,
|
||||
Sparkles,
|
||||
Sun,
|
||||
SunMoon,
|
||||
Terminal
|
||||
} from 'lucide-vue-next'
|
||||
import GithubIcon from '@/components/icons/GithubIcon.vue'
|
||||
import HeaderLogo from '@/components/HeaderLogo.vue'
|
||||
import { useDarkMode } from '@/composables/useDarkMode'
|
||||
|
||||
const { themeMode, toggleDarkMode } = useDarkMode()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Typography */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--serif);
|
||||
letter-spacing: -0.02em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: var(--serif);
|
||||
letter-spacing: 0.01em;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
button, nav, a, .inline-flex {
|
||||
font-family: var(--sans-serif);
|
||||
}
|
||||
</style>
|
||||
@@ -402,7 +402,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-6 md:mt-12 transition-all duration-700 flex items-center justify-center gap-4"
|
||||
class="mt-6 md:mt-12 transition-all duration-700 flex items-center justify-center gap-4 relative z-30"
|
||||
:style="getButtonsStyle(SECTIONS.FEATURES)"
|
||||
>
|
||||
<RouterLink
|
||||
|
||||
367
frontend/src/views/public/guide/AdvancedGuide.vue
Normal file
367
frontend/src/views/public/guide/AdvancedGuide.vue
Normal file
@@ -0,0 +1,367 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { ArrowRight, Shuffle, FileCode, Globe, Shield, Check, Info, AlertTriangle, Settings } from 'lucide-vue-next'
|
||||
import { panelClasses } from './guide-config'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
baseUrl?: string
|
||||
}>(),
|
||||
{
|
||||
baseUrl: typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com'
|
||||
}
|
||||
)
|
||||
|
||||
// 格式转换示例
|
||||
const conversionExamples = [
|
||||
{
|
||||
from: 'OpenAI SDK',
|
||||
to: 'Claude API',
|
||||
description: '用 OpenAI SDK 调用 Claude 模型',
|
||||
icon: 'openai'
|
||||
},
|
||||
{
|
||||
from: 'Anthropic SDK',
|
||||
to: 'OpenAI API',
|
||||
description: '用 Claude SDK 调用 GPT 模型',
|
||||
icon: 'claude'
|
||||
},
|
||||
{
|
||||
from: 'Claude Code',
|
||||
to: 'OpenAI API',
|
||||
description: 'Claude Code 使用 GPT 模型',
|
||||
icon: 'cli'
|
||||
}
|
||||
]
|
||||
|
||||
// 请求头规则类型
|
||||
const headerRuleTypes = [
|
||||
{ type: 'add', name: '添加', description: '添加新的请求头,如果已存在则跳过' },
|
||||
{ type: 'set', name: '设置', description: '设置请求头的值,会覆盖已有值' },
|
||||
{ type: 'remove', name: '删除', description: '删除指定的请求头' }
|
||||
]
|
||||
|
||||
// 系统设置分类
|
||||
const systemSettings = [
|
||||
{
|
||||
category: '基础设置',
|
||||
items: [
|
||||
{ name: '系统名称', description: '显示在页面标题和 Logo 旁边的名称' },
|
||||
{ name: '注册开关', description: '是否允许新用户注册' },
|
||||
{ name: '默认用户角色', description: '新注册用户的默认角色' }
|
||||
]
|
||||
},
|
||||
{
|
||||
category: 'API 设置',
|
||||
items: [
|
||||
{ name: '格式转换', description: '是否启用跨格式 API 调用' },
|
||||
{ name: '请求超时', description: '默认的 API 请求超时时间' },
|
||||
{ name: '流式响应', description: '是否默认启用流式响应' }
|
||||
]
|
||||
},
|
||||
{
|
||||
category: '安全设置',
|
||||
items: [
|
||||
{ name: 'IP 限流', description: '单 IP 的请求频率限制' },
|
||||
{ name: 'Key 限流', description: '单 Key 的请求频率限制' },
|
||||
{ name: 'IP 黑名单', description: '全局 IP 黑名单' }
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-8">
|
||||
<!-- 标题 -->
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-3xl font-bold text-[#262624] dark:text-[#f1ead8]">
|
||||
高级功能
|
||||
</h1>
|
||||
<p class="text-lg text-[#666663] dark:text-[#a3a094]">
|
||||
Aether 提供了一些高级功能,帮助你实现更灵活的 API 管理。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 格式转换 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
格式转换
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2 rounded-lg bg-purple-500/10">
|
||||
<Shuffle class="h-5 w-5 text-purple-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">什么是格式转换?</h3>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mb-4">
|
||||
格式转换允许你用一种 SDK 调用另一种格式的 API。例如用 OpenAI SDK 调用 Claude 模型,
|
||||
系统会自动转换请求格式和响应格式。
|
||||
</p>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-3">
|
||||
<div
|
||||
v-for="example in conversionExamples"
|
||||
:key="example.from"
|
||||
class="p-4 rounded-lg bg-[#f5f5f0]/50 dark:bg-[#1f1d1a]/50"
|
||||
>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span :class="panelClasses.badgeBlue">{{ example.from }}</span>
|
||||
<ArrowRight class="h-4 w-4 text-[#999]" />
|
||||
<span :class="panelClasses.badgeGreen">{{ example.to }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-[#666663] dark:text-[#a3a094]">{{ example.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 如何启用 -->
|
||||
<div :class="[panelClasses.section, 'p-5 space-y-4']">
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">如何启用格式转换</h3>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">开启系统设置</p>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
在「系统设置」中开启 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">ENABLE_API_FORMAT_CONVERSION</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">配置端点</p>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
在端点配置中,启用「格式接受配置」并选择接受的入站格式
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">使用</p>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
用户现在可以用 OpenAI SDK 调用配置在 Claude 格式端点上的模型
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4']">
|
||||
<div class="flex items-start gap-3">
|
||||
<Info class="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">注意事项</p>
|
||||
<p class="mt-1">
|
||||
格式转换会增加少量延迟(通常 <10ms)。部分特有功能(如 Claude 的 thinking、OpenAI 的 function calling)
|
||||
可能无法完美转换,建议在实际场景中测试。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 请求头规则 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
请求头规则
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2 rounded-lg bg-green-500/10">
|
||||
<FileCode class="h-5 w-5 text-green-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">什么是请求头规则?</h3>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mb-4">
|
||||
请求头规则允许你在转发请求时修改 HTTP 请求头。可以添加、修改或删除特定的请求头。
|
||||
</p>
|
||||
|
||||
<div :class="[panelClasses.section, 'overflow-hidden']">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50">
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">类型</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="rule in headerRuleTypes"
|
||||
:key="rule.type"
|
||||
class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] last:border-0"
|
||||
>
|
||||
<td class="px-4 py-3 font-medium text-[#262624] dark:text-[#f1ead8]">
|
||||
<span :class="panelClasses.badgeBlue">{{ rule.name }}</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-[#666663] dark:text-[#a3a094]">
|
||||
{{ rule.description }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4']">
|
||||
<div class="flex items-start gap-3">
|
||||
<Info class="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">使用场景</p>
|
||||
<ul class="mt-1 space-y-1">
|
||||
<li>• 添加额外的认证信息(如 API 版本号)</li>
|
||||
<li>• 添加跟踪标记(如请求 ID、来源标识)</li>
|
||||
<li>• 删除敏感信息(如用户原始 IP)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 代理设置 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
代理设置
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2 rounded-lg bg-blue-500/10">
|
||||
<Globe class="h-5 w-5 text-blue-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">HTTP 代理</h3>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mb-4">
|
||||
如果你的服务器无法直接访问某些 API(如在国内访问 OpenAI),可以在端点配置中设置代理。
|
||||
</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="p-4 rounded-lg bg-[#f5f5f0]/50 dark:bg-[#1f1d1a]/50">
|
||||
<h4 class="font-medium text-[#262624] dark:text-[#f1ead8]">端点级代理</h4>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
在端点配置中填写代理地址,格式如 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">http://proxy:8080</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 rounded-lg bg-[#f5f5f0]/50 dark:bg-[#1f1d1a]/50">
|
||||
<h4 class="font-medium text-[#262624] dark:text-[#f1ead8]">全局代理</h4>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
也可以通过环境变量 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">HTTP_PROXY</code> 设置全局代理
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 系统设置概览 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
系统设置
|
||||
</h2>
|
||||
|
||||
<p class="text-[#666663] dark:text-[#a3a094]">
|
||||
在「系统设置」页面可以配置全局参数:
|
||||
</p>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div
|
||||
v-for="category in systemSettings"
|
||||
:key="category.category"
|
||||
:class="[panelClasses.section, 'p-4']"
|
||||
>
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<Settings class="h-4 w-4 text-[#cc785c]" />
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">{{ category.category }}</h3>
|
||||
</div>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="item in category.items"
|
||||
:key="item.name"
|
||||
class="text-sm"
|
||||
>
|
||||
<span class="font-medium text-[#262624] dark:text-[#f1ead8]">{{ item.name }}</span>
|
||||
<span class="text-[#666663] dark:text-[#a3a094]"> - {{ item.description }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 健康监控 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
健康监控
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2 rounded-lg bg-green-500/10">
|
||||
<Shield class="h-5 w-5 text-green-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">端点健康检查</h3>
|
||||
</div>
|
||||
|
||||
<ul class="space-y-3 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>系统会定期检测端点的可用性</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>不健康的端点会被自动跳过,请求会路由到其他可用端点</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>可以在「健康监控」页面查看所有端点的实时状态</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>支持手动触发健康检查</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4']">
|
||||
<div class="flex items-start gap-3">
|
||||
<AlertTriangle class="h-5 w-5 text-yellow-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">端点显示不健康?</p>
|
||||
<p class="mt-1">
|
||||
检查:1) API URL 是否正确;2) API Key 是否有效;3) 网络是否可达;4) 是否需要配置代理。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 下一步 -->
|
||||
<section class="pt-4">
|
||||
<RouterLink
|
||||
to="/guide/faq"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">常见问题</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">查看使用中的常见问题和解答</div>
|
||||
</div>
|
||||
<ArrowRight class="h-5 w-5 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</RouterLink>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
200
frontend/src/views/public/guide/GuideFaq.vue
Normal file
200
frontend/src/views/public/guide/GuideFaq.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { Search, ChevronDown, ExternalLink, HelpCircle } from 'lucide-vue-next'
|
||||
import { faqItems, panelClasses } from './guide-config'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
baseUrl?: string
|
||||
}>(),
|
||||
{
|
||||
baseUrl: typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com'
|
||||
}
|
||||
)
|
||||
|
||||
// 搜索关键词
|
||||
const searchQuery = ref('')
|
||||
|
||||
// 展开的 FAQ
|
||||
const expandedIds = ref<Set<string>>(new Set())
|
||||
|
||||
// 分类列表
|
||||
const categories = computed(() => {
|
||||
const cats = new Set(faqItems.map(item => item.category))
|
||||
return Array.from(cats)
|
||||
})
|
||||
|
||||
// 过滤后的 FAQ
|
||||
const filteredFaqs = computed(() => {
|
||||
if (!searchQuery.value.trim()) {
|
||||
return faqItems
|
||||
}
|
||||
const query = searchQuery.value.toLowerCase()
|
||||
return faqItems.filter(
|
||||
item =>
|
||||
item.question.toLowerCase().includes(query) ||
|
||||
item.answer.toLowerCase().includes(query)
|
||||
)
|
||||
})
|
||||
|
||||
// 按分类分组
|
||||
const faqsByCategory = computed(() => {
|
||||
const grouped: Record<string, typeof faqItems> = {}
|
||||
for (const item of filteredFaqs.value) {
|
||||
if (!grouped[item.category]) {
|
||||
grouped[item.category] = []
|
||||
}
|
||||
grouped[item.category].push(item)
|
||||
}
|
||||
return grouped
|
||||
})
|
||||
|
||||
// 切换展开状态
|
||||
function toggleExpand(id: string) {
|
||||
if (expandedIds.value.has(id)) {
|
||||
expandedIds.value.delete(id)
|
||||
} else {
|
||||
expandedIds.value.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
// 全部展开/收起
|
||||
function toggleAll() {
|
||||
if (expandedIds.value.size === filteredFaqs.value.length) {
|
||||
expandedIds.value.clear()
|
||||
} else {
|
||||
expandedIds.value = new Set(filteredFaqs.value.map(item => item.id))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-8">
|
||||
<!-- 标题 -->
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-3xl font-bold text-[#262624] dark:text-[#f1ead8]">
|
||||
常见问题
|
||||
</h1>
|
||||
<p class="text-lg text-[#666663] dark:text-[#a3a094]">
|
||||
关于 Aether 使用和配置的常见问题解答。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<div :class="[panelClasses.section, 'p-4']">
|
||||
<div class="flex items-center gap-3">
|
||||
<Search class="h-5 w-5 text-[#999]" />
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="搜索问题..."
|
||||
class="flex-1 bg-transparent border-none outline-none text-[#262624] dark:text-[#f1ead8] placeholder:text-[#999]"
|
||||
/>
|
||||
<button
|
||||
v-if="filteredFaqs.length > 0"
|
||||
class="text-sm text-[#cc785c] hover:underline"
|
||||
@click="toggleAll"
|
||||
>
|
||||
{{ expandedIds.size === filteredFaqs.length ? '全部收起' : '全部展开' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FAQ 列表 -->
|
||||
<div v-if="filteredFaqs.length > 0" class="space-y-6">
|
||||
<div
|
||||
v-for="category in Object.keys(faqsByCategory)"
|
||||
:key="category"
|
||||
class="space-y-3"
|
||||
>
|
||||
<h2 class="text-lg font-semibold text-[#262624] dark:text-[#f1ead8] flex items-center gap-2">
|
||||
<HelpCircle class="h-5 w-5 text-[#cc785c]" />
|
||||
{{ category }}
|
||||
</h2>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="faq in faqsByCategory[category]"
|
||||
:key="faq.id"
|
||||
:class="[panelClasses.section, 'overflow-hidden']"
|
||||
>
|
||||
<button
|
||||
class="w-full px-4 py-3 flex items-center justify-between text-left hover:bg-[#f5f5f0]/50 dark:hover:bg-[#1f1d1a]/50 transition-colors"
|
||||
@click="toggleExpand(faq.id)"
|
||||
>
|
||||
<span class="font-medium text-[#262624] dark:text-[#f1ead8] pr-4">
|
||||
{{ faq.question }}
|
||||
</span>
|
||||
<ChevronDown
|
||||
class="h-5 w-5 text-[#999] flex-shrink-0 transition-transform"
|
||||
:class="{ 'rotate-180': expandedIds.has(faq.id) }"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
v-show="expandedIds.has(faq.id)"
|
||||
class="px-4 pb-4 text-sm text-[#666663] dark:text-[#a3a094] whitespace-pre-line"
|
||||
>
|
||||
{{ faq.answer }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无结果 -->
|
||||
<div
|
||||
v-else
|
||||
:class="[panelClasses.section, 'p-8 text-center']"
|
||||
>
|
||||
<HelpCircle class="h-12 w-12 text-[#999] mx-auto mb-4" />
|
||||
<p class="text-[#666663] dark:text-[#a3a094]">
|
||||
没有找到匹配的问题,请尝试其他关键词
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 更多帮助 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
需要更多帮助?
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<a
|
||||
href="https://github.com/your-repo/aether"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<div class="p-2 rounded-lg bg-gray-500/10">
|
||||
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">GitHub</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">查看源码、提交 Issue</div>
|
||||
</div>
|
||||
<ExternalLink class="h-4 w-4 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/your-repo/aether/discussions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<div class="p-2 rounded-lg bg-blue-500/10">
|
||||
<svg class="h-5 w-5 text-blue-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">Discussions</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">社区讨论、功能建议</div>
|
||||
</div>
|
||||
<ExternalLink class="h-4 w-4 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
258
frontend/src/views/public/guide/GuideLayout.vue
Normal file
258
frontend/src/views/public/guide/GuideLayout.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="min-h-screen literary-grid literary-paper">
|
||||
<!-- Header -->
|
||||
<header class="sticky top-0 z-50 border-b border-[#cc785c]/10 dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/90 dark:bg-[#191714]/95 backdrop-blur-xl transition-all">
|
||||
<div class="h-14 sm:h-16 flex items-center px-3 sm:px-4 md:px-8">
|
||||
<!-- Left: Logo & Brand -->
|
||||
<RouterLink
|
||||
to="/"
|
||||
class="flex items-center gap-2 sm:gap-3 group/logo cursor-pointer shrink-0"
|
||||
>
|
||||
<HeaderLogo
|
||||
size="h-7 w-7 sm:h-9 sm:w-9"
|
||||
class-name="text-[#191919] dark:text-white"
|
||||
/>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="text-base sm:text-lg font-bold text-[#191919] dark:text-white leading-none">
|
||||
Aether
|
||||
</h1>
|
||||
<span class="text-[9px] sm:text-[10px] text-[#91918d] dark:text-muted-foreground leading-none mt-1 sm:mt-1.5 font-medium tracking-wide">AI Gateway</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
|
||||
<!-- Center: Breadcrumb -->
|
||||
<div class="hidden md:flex items-center gap-2 ml-8">
|
||||
<RouterLink
|
||||
to="/guide"
|
||||
class="text-sm text-[#666663] dark:text-muted-foreground hover:text-[#191919] dark:hover:text-white transition"
|
||||
>
|
||||
教程文档
|
||||
</RouterLink>
|
||||
<ChevronRight
|
||||
v-if="currentNavItem && currentNavItem.id !== 'overview'"
|
||||
class="h-4 w-4 text-[#91918d]"
|
||||
/>
|
||||
<span
|
||||
v-if="currentNavItem && currentNavItem.id !== 'overview'"
|
||||
class="text-sm font-medium text-[#191919] dark:text-white"
|
||||
>
|
||||
{{ currentNavItem.name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Spacer -->
|
||||
<div class="flex-1" />
|
||||
|
||||
<!-- Right: Actions -->
|
||||
<div class="flex items-center gap-1 sm:gap-2 shrink-0">
|
||||
<!-- Mobile menu button -->
|
||||
<button
|
||||
class="md:hidden flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition"
|
||||
@click="showMobileNav = !showMobileNav"
|
||||
>
|
||||
<Menu class="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
<!-- Theme Toggle + GitHub Icons -->
|
||||
<div class="flex items-center gap-0.5 sm:gap-1">
|
||||
<button
|
||||
class="flex h-8 w-8 sm:h-9 sm:w-9 items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition"
|
||||
:title="themeMode === 'system' ? '跟随系统' : themeMode === 'dark' ? '深色模式' : '浅色模式'"
|
||||
@click="toggleDarkMode"
|
||||
>
|
||||
<SunMoon
|
||||
v-if="themeMode === 'system'"
|
||||
class="h-3.5 w-3.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
<Sun
|
||||
v-else-if="themeMode === 'light'"
|
||||
class="h-3.5 w-3.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
<Moon
|
||||
v-else
|
||||
class="h-3.5 w-3.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
href="https://github.com/fawney19/Aether"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex h-8 w-8 sm:h-9 sm:w-9 items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition"
|
||||
title="GitHub 仓库"
|
||||
>
|
||||
<GithubIcon class="h-3.5 w-3.5 sm:h-4 sm:w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Mobile Nav Overlay -->
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-if="showMobileNav"
|
||||
class="fixed inset-0 z-40 bg-black/50 md:hidden"
|
||||
@click="showMobileNav = false"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<!-- Mobile Nav Drawer -->
|
||||
<Transition name="slide-left">
|
||||
<div
|
||||
v-if="showMobileNav"
|
||||
class="fixed left-0 top-14 bottom-0 z-50 w-64 bg-[#fafaf7] dark:bg-[#191714] border-r border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] md:hidden overflow-y-auto"
|
||||
>
|
||||
<nav class="p-4 space-y-1">
|
||||
<RouterLink
|
||||
v-for="item in guideNavItems"
|
||||
:key="item.id"
|
||||
:to="item.path"
|
||||
class="flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors"
|
||||
:class="isActive(item.path)
|
||||
? 'bg-[#cc785c]/10 text-[#cc785c] dark:text-[#d4a27f]'
|
||||
: 'text-[#666663] dark:text-muted-foreground hover:bg-[#f0f0eb] dark:hover:bg-[#262624]'"
|
||||
@click="showMobileNav = false"
|
||||
>
|
||||
<component
|
||||
:is="item.icon"
|
||||
class="h-4 w-4 shrink-0"
|
||||
/>
|
||||
<span class="text-sm font-medium">{{ item.name }}</span>
|
||||
</RouterLink>
|
||||
</nav>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex">
|
||||
<!-- Desktop Sidebar -->
|
||||
<aside class="hidden md:block w-64 shrink-0 border-r border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#191714]/50">
|
||||
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-auto">
|
||||
<nav class="p-4 space-y-1">
|
||||
<RouterLink
|
||||
v-for="item in guideNavItems"
|
||||
:key="item.id"
|
||||
:to="item.path"
|
||||
class="flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors group"
|
||||
:class="isActive(item.path)
|
||||
? 'bg-[#cc785c]/10 text-[#cc785c] dark:text-[#d4a27f]'
|
||||
: 'text-[#666663] dark:text-muted-foreground hover:bg-[#f0f0eb] dark:hover:bg-[#262624]'"
|
||||
>
|
||||
<component
|
||||
:is="item.icon"
|
||||
class="h-4 w-4 shrink-0"
|
||||
/>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm font-medium">{{ item.name }}</span>
|
||||
<span
|
||||
v-if="item.description"
|
||||
class="text-xs text-[#91918d] dark:text-muted-foreground/70"
|
||||
>
|
||||
{{ item.description }}
|
||||
</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</nav>
|
||||
|
||||
<!-- Base URL Input -->
|
||||
<div class="p-4 border-t border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)]">
|
||||
<label class="block text-xs font-medium text-[#666663] dark:text-muted-foreground mb-2">
|
||||
Aether Base URL
|
||||
</label>
|
||||
<input
|
||||
v-model="baseUrl"
|
||||
type="text"
|
||||
class="w-full px-3 py-2 text-sm rounded-lg border border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-white dark:bg-[#1f1d1a] text-[#191919] dark:text-white placeholder-[#91918d] focus:outline-none focus:ring-2 focus:ring-[#cc785c]/30"
|
||||
placeholder="https://your-aether.com"
|
||||
/>
|
||||
<p class="mt-1.5 text-xs text-[#91918d]">
|
||||
代码示例将使用此 URL
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Page Content -->
|
||||
<main class="flex-1 min-w-0">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8 md:py-12">
|
||||
<RouterView v-slot="{ Component }">
|
||||
<component :is="Component" :base-url="baseUrl" />
|
||||
</RouterView>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { RouterLink, RouterView, useRoute } from 'vue-router'
|
||||
import {
|
||||
ChevronRight,
|
||||
Menu,
|
||||
Moon,
|
||||
Sun,
|
||||
SunMoon
|
||||
} from 'lucide-vue-next'
|
||||
import GithubIcon from '@/components/icons/GithubIcon.vue'
|
||||
import HeaderLogo from '@/components/HeaderLogo.vue'
|
||||
import { useDarkMode } from '@/composables/useDarkMode'
|
||||
import { guideNavItems } from './guide-config'
|
||||
|
||||
const route = useRoute()
|
||||
const { themeMode, toggleDarkMode } = useDarkMode()
|
||||
|
||||
const showMobileNav = ref(false)
|
||||
const baseUrl = ref(typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com')
|
||||
|
||||
const currentNavItem = computed(() => {
|
||||
return guideNavItems.find(item => item.path === route.path)
|
||||
})
|
||||
|
||||
function isActive(path: string): boolean {
|
||||
if (path === '/guide') {
|
||||
return route.path === '/guide'
|
||||
}
|
||||
return route.path.startsWith(path)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Typography */
|
||||
h1, h2, h3 {
|
||||
font-family: var(--serif);
|
||||
letter-spacing: -0.02em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: var(--serif);
|
||||
letter-spacing: 0.01em;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
button, nav, a, .inline-flex, input, label {
|
||||
font-family: var(--sans-serif);
|
||||
}
|
||||
|
||||
/* Transitions */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-left-enter-active,
|
||||
.slide-left-leave-active {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.slide-left-enter-from,
|
||||
.slide-left-leave-to {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
</style>
|
||||
317
frontend/src/views/public/guide/ModelGuide.vue
Normal file
317
frontend/src/views/public/guide/ModelGuide.vue
Normal file
@@ -0,0 +1,317 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { ArrowRight, Layers, Check, Info, Shuffle, TrendingUp, Gauge, Clock } from 'lucide-vue-next'
|
||||
import { loadBalanceModes, panelClasses } from './guide-config'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
baseUrl?: string
|
||||
}>(),
|
||||
{
|
||||
baseUrl: typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com'
|
||||
}
|
||||
)
|
||||
|
||||
// 模型配置字段
|
||||
const modelFields = [
|
||||
{ name: '模型名称', description: '用户调用时使用的模型名,如 gpt-4、claude-3', required: true },
|
||||
{ name: '别名', description: '模型的其他名称,用户可以用别名调用', required: false },
|
||||
{ name: '关联端点', description: '该模型使用的端点列表,可多选', required: true },
|
||||
{ name: '负载均衡模式', description: '多端点时的请求分发策略', required: false },
|
||||
{ name: '目标模型名', description: '发送给端点的实际模型名(如端点模型名不同)', required: false },
|
||||
{ name: '是否启用', description: '禁用后用户无法使用该模型', required: false }
|
||||
]
|
||||
|
||||
// 负载均衡图标
|
||||
const lbIcons = {
|
||||
priority: TrendingUp,
|
||||
random: Shuffle,
|
||||
round_robin: Clock,
|
||||
weighted: Gauge,
|
||||
latency: Clock
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-8">
|
||||
<!-- 标题 -->
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-3xl font-bold text-[#262624] dark:text-[#f1ead8]">
|
||||
模型管理
|
||||
</h1>
|
||||
<p class="text-lg text-[#666663] dark:text-[#a3a094]">
|
||||
模型是用户实际调用的对象。通过模型配置,你可以定义模型名称、关联端点、配置负载均衡策略。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 模型概念 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
什么是模型?
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2 rounded-lg bg-purple-500/10">
|
||||
<Layers class="h-5 w-5 text-purple-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">模型 (Model)</h3>
|
||||
</div>
|
||||
|
||||
<ul class="space-y-3 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>模型是用户在请求中指定的 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">model</code> 参数值</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>一个模型可以关联多个端点,实现负载均衡和故障转移</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>可以设置别名,让用户用多个名称访问同一个模型</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>支持模型名映射,请求中的模型名可以与发给端点的不同</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4']">
|
||||
<div class="flex items-start gap-3">
|
||||
<Info class="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">示例场景</p>
|
||||
<p class="mt-1">
|
||||
用户请求模型 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">gpt-4</code>,
|
||||
你可以配置它同时使用 OpenAI 官方端点和某个代理端点。
|
||||
系统会根据负载均衡策略选择一个端点,如果失败则自动尝试其他端点。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 创建模型 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
创建模型
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5 space-y-4']">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">进入模型管理页面</h3>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
在管理后台左侧菜单点击「模型管理」
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">添加模型</h3>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
点击「添加模型」,填写模型名称(这是用户调用时使用的名称)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">关联端点</h3>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
选择该模型要使用的端点,可以选择多个。如果端点的模型名与你定义的不同,需要设置目标模型名映射。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
4
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">配置负载均衡</h3>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
如果选择了多个端点,可以配置负载均衡策略
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 模型配置字段 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
配置字段说明
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'overflow-hidden']">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50">
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">字段</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">说明</th>
|
||||
<th class="px-4 py-3 text-center font-medium text-[#666663] dark:text-[#a3a094]">必填</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="field in modelFields"
|
||||
:key="field.name"
|
||||
class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] last:border-0"
|
||||
>
|
||||
<td class="px-4 py-3 font-medium text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ field.name }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-[#666663] dark:text-[#a3a094]">
|
||||
{{ field.description }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span v-if="field.required" :class="panelClasses.badgeGreen">必填</span>
|
||||
<span v-else class="text-[#999]">可选</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 负载均衡模式 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
负载均衡模式
|
||||
</h2>
|
||||
<p class="text-[#666663] dark:text-[#a3a094]">
|
||||
当模型关联多个端点时,系统会根据负载均衡模式选择端点:
|
||||
</p>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
v-for="lb in loadBalanceModes"
|
||||
:key="lb.mode"
|
||||
:class="[panelClasses.section, 'p-4']"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<component
|
||||
:is="lbIcons[lb.mode as keyof typeof lbIcons] || Shuffle"
|
||||
class="h-5 w-5 text-[#cc785c]"
|
||||
/>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">{{ lb.name }}</h3>
|
||||
</div>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094]">{{ lb.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4']">
|
||||
<div class="flex items-start gap-3">
|
||||
<Info class="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">推荐配置</p>
|
||||
<p class="mt-1">
|
||||
<strong>主备切换场景</strong>:使用「优先级」模式,将官方端点设为高优先级,代理端点设为低优先级。<br />
|
||||
<strong>分摊负载场景</strong>:使用「轮询」或「加权」模式,将多个同质端点平均分配请求。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 模型名映射 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
模型名映射
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mb-4">
|
||||
有时候你希望用户调用的模型名与实际发给端点的模型名不同。例如:
|
||||
</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
<div class="px-3 py-2 rounded-lg bg-purple-500/10 border border-purple-500/20">
|
||||
<span class="text-purple-600 dark:text-purple-400">用户请求</span>
|
||||
<code class="ml-2 text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">gpt-4</code>
|
||||
</div>
|
||||
<ArrowRight class="h-4 w-4 text-[#999]" />
|
||||
<div class="px-3 py-2 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||
<span class="text-green-600 dark:text-green-400">发给端点</span>
|
||||
<code class="ml-2 text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">gpt-4-turbo-preview</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
<div class="px-3 py-2 rounded-lg bg-purple-500/10 border border-purple-500/20">
|
||||
<span class="text-purple-600 dark:text-purple-400">用户请求</span>
|
||||
<code class="ml-2 text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">claude-3</code>
|
||||
</div>
|
||||
<ArrowRight class="h-4 w-4 text-[#999]" />
|
||||
<div class="px-3 py-2 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||
<span class="text-green-600 dark:text-green-400">发给端点</span>
|
||||
<code class="ml-2 text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">claude-3-opus-20240229</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-4">
|
||||
在模型的端点关联配置中,可以为每个端点设置「目标模型名」来实现这种映射。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 别名功能 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
模型别名
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mb-4">
|
||||
别名让用户可以用多个名称访问同一个模型。适用于:
|
||||
</p>
|
||||
|
||||
<ul class="space-y-2 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>兼容旧的模型名(如 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">gpt-4-turbo</code> 和 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">gpt-4-turbo-preview</code> 指向同一个模型)</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>提供简短易记的名称(如用 <code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">claude</code> 代替完整版本号)</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>版本迁移时保持向后兼容</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 下一步 -->
|
||||
<section class="pt-4">
|
||||
<RouterLink
|
||||
to="/guide/user-key"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">下一步:用户与密钥</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">管理用户和 API Key</div>
|
||||
</div>
|
||||
<ArrowRight class="h-5 w-5 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</RouterLink>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
243
frontend/src/views/public/guide/Overview.vue
Normal file
243
frontend/src/views/public/guide/Overview.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { ArrowRight, Server, Layers, Key, Box, ChevronRight } from 'lucide-vue-next'
|
||||
import { coreConcepts, apiFormats, configSteps, panelClasses } from './guide-config'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
baseUrl?: string
|
||||
}>(),
|
||||
{
|
||||
baseUrl: typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com'
|
||||
}
|
||||
)
|
||||
|
||||
// 概念图标映射
|
||||
const conceptIcons = {
|
||||
blue: Server,
|
||||
green: Box,
|
||||
purple: Layers,
|
||||
orange: Key
|
||||
}
|
||||
|
||||
// 概念颜色类
|
||||
const conceptColors = {
|
||||
blue: 'border-blue-500/30 bg-blue-500/5',
|
||||
green: 'border-green-500/30 bg-green-500/5',
|
||||
purple: 'border-purple-500/30 bg-purple-500/5',
|
||||
orange: 'border-orange-500/30 bg-orange-500/5'
|
||||
}
|
||||
|
||||
const conceptIconColors = {
|
||||
blue: 'text-blue-500',
|
||||
green: 'text-green-500',
|
||||
purple: 'text-purple-500',
|
||||
orange: 'text-orange-500'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-8">
|
||||
<!-- 标题区域 -->
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-3xl font-bold text-[#262624] dark:text-[#f1ead8]">
|
||||
欢迎使用 Aether
|
||||
</h1>
|
||||
<p class="text-lg text-[#666663] dark:text-[#a3a094]">
|
||||
Aether 是一个 AI API 网关,帮助你统一管理多个 AI 服务供应商,实现负载均衡、访问控制和用量统计。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 核心概念 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
核心概念
|
||||
</h2>
|
||||
<p class="text-[#666663] dark:text-[#a3a094]">
|
||||
理解这四个核心概念,是配置 Aether 的基础:
|
||||
</p>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<div
|
||||
v-for="concept in coreConcepts"
|
||||
:key="concept.name"
|
||||
:class="[
|
||||
'p-4 rounded-xl border-2 transition-all',
|
||||
conceptColors[concept.color as keyof typeof conceptColors]
|
||||
]"
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<component
|
||||
:is="conceptIcons[concept.color as keyof typeof conceptIcons]"
|
||||
:class="[
|
||||
'h-6 w-6 flex-shrink-0 mt-0.5',
|
||||
conceptIconColors[concept.color as keyof typeof conceptIconColors]
|
||||
]"
|
||||
/>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ concept.name }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
{{ concept.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 关系图 -->
|
||||
<div :class="[panelClasses.section, 'p-6 mt-6']">
|
||||
<h3 class="text-sm font-medium text-[#666663] dark:text-[#a3a094] mb-4">
|
||||
它们之间的关系
|
||||
</h3>
|
||||
<div class="flex items-center justify-center gap-3 flex-wrap">
|
||||
<div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||
<Server class="h-4 w-4 text-blue-500" />
|
||||
<span class="text-sm font-medium text-blue-600 dark:text-blue-400">供应商</span>
|
||||
</div>
|
||||
<ChevronRight class="h-5 w-5 text-[#999] hidden sm:block" />
|
||||
<ArrowRight class="h-5 w-5 text-[#999] sm:hidden" />
|
||||
<div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||
<Box class="h-4 w-4 text-green-500" />
|
||||
<span class="text-sm font-medium text-green-600 dark:text-green-400">端点</span>
|
||||
</div>
|
||||
<ChevronRight class="h-5 w-5 text-[#999] hidden sm:block" />
|
||||
<ArrowRight class="h-5 w-5 text-[#999] sm:hidden" />
|
||||
<div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-purple-500/10 border border-purple-500/20">
|
||||
<Layers class="h-4 w-4 text-purple-500" />
|
||||
<span class="text-sm font-medium text-purple-600 dark:text-purple-400">模型</span>
|
||||
</div>
|
||||
<ChevronRight class="h-5 w-5 text-[#999] hidden sm:block" />
|
||||
<ArrowRight class="h-5 w-5 text-[#999] sm:hidden" />
|
||||
<div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-orange-500/10 border border-orange-500/20">
|
||||
<Key class="h-4 w-4 text-orange-500" />
|
||||
<span class="text-sm font-medium text-orange-600 dark:text-orange-400">API Key</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-center text-sm text-[#666663] dark:text-[#a3a094] mt-4">
|
||||
一个供应商可以有多个端点,一个模型可以关联多个端点,一个 API Key 可以访问多个模型
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 配置流程 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
配置流程
|
||||
</h2>
|
||||
<p class="text-[#666663] dark:text-[#a3a094]">
|
||||
按照以下步骤完成初始配置:
|
||||
</p>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div
|
||||
v-for="step in configSteps"
|
||||
:key="step.step"
|
||||
:class="[panelClasses.section, 'p-4 relative']"
|
||||
>
|
||||
<div class="absolute -top-3 -left-2 w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm">
|
||||
{{ step.step }}
|
||||
</div>
|
||||
<div class="pt-2">
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ step.title }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
{{ step.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 支持的 API 格式 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
支持的 API 格式
|
||||
</h2>
|
||||
<p class="text-[#666663] dark:text-[#a3a094]">
|
||||
Aether 支持多种 API 格式,可以作为不同客户端的统一入口:
|
||||
</p>
|
||||
|
||||
<div :class="[panelClasses.section, 'overflow-hidden']">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50">
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">格式</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">端点</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">认证方式</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">常用客户端</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="format in apiFormats"
|
||||
:key="format.name"
|
||||
class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] last:border-0"
|
||||
>
|
||||
<td class="px-4 py-3 font-medium text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ format.name }}
|
||||
</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-[#666663] dark:text-[#a3a094]">
|
||||
{{ format.endpoint }}
|
||||
</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-[#666663] dark:text-[#a3a094]">
|
||||
{{ format.auth }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-[#666663] dark:text-[#a3a094]">
|
||||
{{ format.clients.join(', ') }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 快速导航 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
开始配置
|
||||
</h2>
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<RouterLink
|
||||
to="/guide/provider"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<Server class="h-5 w-5 text-[#cc785c]" />
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">供应商管理</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">添加和配置 API 供应商</div>
|
||||
</div>
|
||||
<ArrowRight class="h-5 w-5 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink
|
||||
to="/guide/model"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<Layers class="h-5 w-5 text-[#cc785c]" />
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">模型管理</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">配置模型和负载均衡</div>
|
||||
</div>
|
||||
<ArrowRight class="h-5 w-5 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink
|
||||
to="/guide/user-key"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<Key class="h-5 w-5 text-[#cc785c]" />
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">用户与密钥</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">管理用户和 API Key</div>
|
||||
</div>
|
||||
<ArrowRight class="h-5 w-5 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
343
frontend/src/views/public/guide/ProviderGuide.vue
Normal file
343
frontend/src/views/public/guide/ProviderGuide.vue
Normal file
@@ -0,0 +1,343 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { ArrowRight, Server, Plus, Settings, Check, AlertTriangle, Info } from 'lucide-vue-next'
|
||||
import { apiFormats, panelClasses } from './guide-config'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
baseUrl?: string
|
||||
}>(),
|
||||
{
|
||||
baseUrl: typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com'
|
||||
}
|
||||
)
|
||||
|
||||
// 端点配置字段说明
|
||||
const endpointFields = [
|
||||
{ name: '名称', description: '端点的显示名称,用于区分不同的端点', required: true },
|
||||
{ name: 'URL', description: 'API 的基础 URL,如 https://api.openai.com', required: true },
|
||||
{ name: 'API Key', description: '调用该 API 需要的密钥', required: true },
|
||||
{ name: 'API 格式', description: '该端点支持的 API 格式(OpenAI/Claude/Gemini 等)', required: true },
|
||||
{ name: '优先级', description: '数字越大优先级越高,用于负载均衡', required: false },
|
||||
{ name: '权重', description: '加权负载均衡时使用的权重值', required: false },
|
||||
{ name: '代理', description: '如需通过代理访问,填写代理地址', required: false },
|
||||
{ name: '超时', description: '请求超时时间(秒)', required: false }
|
||||
]
|
||||
|
||||
// 常见供应商配置示例
|
||||
const providerExamples = [
|
||||
{
|
||||
name: 'OpenAI',
|
||||
url: 'https://api.openai.com',
|
||||
format: 'OpenAI',
|
||||
note: '官方 API,需要国际信用卡或通过代理访问'
|
||||
},
|
||||
{
|
||||
name: 'Anthropic',
|
||||
url: 'https://api.anthropic.com',
|
||||
format: 'Claude',
|
||||
note: '官方 Claude API'
|
||||
},
|
||||
{
|
||||
name: 'Google AI',
|
||||
url: 'https://generativelanguage.googleapis.com',
|
||||
format: 'Gemini',
|
||||
note: '官方 Gemini API'
|
||||
},
|
||||
{
|
||||
name: 'Azure OpenAI',
|
||||
url: 'https://{resource}.openai.azure.com',
|
||||
format: 'OpenAI',
|
||||
note: '需要替换 {resource} 为你的资源名'
|
||||
},
|
||||
{
|
||||
name: 'OpenRouter',
|
||||
url: 'https://openrouter.ai/api',
|
||||
format: 'OpenAI',
|
||||
note: '聚合多家供应商的 API 代理'
|
||||
},
|
||||
{
|
||||
name: '自托管 / 其他',
|
||||
url: 'https://your-api.com',
|
||||
format: 'OpenAI',
|
||||
note: '大多数 OpenAI 兼容服务选择 OpenAI 格式'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-8">
|
||||
<!-- 标题 -->
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-3xl font-bold text-[#262624] dark:text-[#f1ead8]">
|
||||
供应商管理
|
||||
</h1>
|
||||
<p class="text-lg text-[#666663] dark:text-[#a3a094]">
|
||||
供应商和端点是 Aether 的基础配置,决定了系统可以调用哪些 AI 服务。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 概念说明 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
供应商 vs 端点
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<div class="p-2 rounded-lg bg-blue-500/10">
|
||||
<Server class="h-5 w-5 text-blue-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">供应商 (Provider)</h3>
|
||||
</div>
|
||||
<ul class="space-y-2 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>逻辑分组,用于组织管理多个端点</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>一个供应商可以有多个端点</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>例如:OpenAI 供应商下可以有官方端点和多个代理端点</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<div class="p-2 rounded-lg bg-green-500/10">
|
||||
<Settings class="h-5 w-5 text-green-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">端点 (Endpoint)</h3>
|
||||
</div>
|
||||
<ul class="space-y-2 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>实际的 API 配置单元</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>包含 URL、密钥、API 格式等信息</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>系统实际调用的是端点,而非供应商</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 添加供应商步骤 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
添加供应商
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5 space-y-4']">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">进入供应商管理页面</h3>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
登录管理后台,在左侧菜单点击「供应商管理」
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">创建供应商</h3>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
点击「添加供应商」按钮,填写供应商名称(如 OpenAI、Anthropic)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-[#cc785c] flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">添加端点</h3>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
在供应商下点击「添加端点」,填写 API URL、密钥等配置
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 端点配置字段 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
端点配置字段
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'overflow-hidden']">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50">
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">字段</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">说明</th>
|
||||
<th class="px-4 py-3 text-center font-medium text-[#666663] dark:text-[#a3a094]">必填</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="field in endpointFields"
|
||||
:key="field.name"
|
||||
class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] last:border-0"
|
||||
>
|
||||
<td class="px-4 py-3 font-medium text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ field.name }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-[#666663] dark:text-[#a3a094]">
|
||||
{{ field.description }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span v-if="field.required" :class="panelClasses.badgeGreen">必填</span>
|
||||
<span v-else class="text-[#999]">可选</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- API 格式选择 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
API 格式选择
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4']">
|
||||
<div class="flex items-start gap-3">
|
||||
<Info class="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">如何选择正确的 API 格式?</p>
|
||||
<p class="mt-1">
|
||||
根据目标 API 服务的实际格式选择。大多数第三方 API 代理(如 OpenRouter)都兼容 OpenAI 格式。
|
||||
如果不确定,可以先尝试 OpenAI 格式。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'overflow-hidden']">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50">
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">格式</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">端点路径</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">认证方式</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="format in apiFormats"
|
||||
:key="format.name"
|
||||
class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] last:border-0"
|
||||
>
|
||||
<td class="px-4 py-3 font-medium text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ format.name }}
|
||||
</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-[#666663] dark:text-[#a3a094]">
|
||||
{{ format.endpoint }}
|
||||
</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-[#666663] dark:text-[#a3a094]">
|
||||
{{ format.auth }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 常见供应商示例 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
常见供应商配置
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
v-for="example in providerExamples"
|
||||
:key="example.name"
|
||||
:class="[panelClasses.section, 'p-4']"
|
||||
>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ example.name }}
|
||||
</h3>
|
||||
<div class="mt-2 space-y-1 text-sm">
|
||||
<div>
|
||||
<span class="text-[#666663] dark:text-[#a3a094]">URL: </span>
|
||||
<code class="text-xs bg-[#f5f5f0] dark:bg-[#1f1d1a] px-1.5 py-0.5 rounded">{{ example.url }}</code>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[#666663] dark:text-[#a3a094]">格式: </span>
|
||||
<span :class="panelClasses.badgeBlue">{{ example.format }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-[#999]">{{ example.note }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 注意事项 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
注意事项
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4 space-y-3']">
|
||||
<div class="flex items-start gap-3">
|
||||
<AlertTriangle class="h-5 w-5 text-yellow-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">API Key 安全</p>
|
||||
<p class="text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
端点的 API Key 会被加密存储,但仍建议使用子账号或限定范围的 Key,而非主账号 Key。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<AlertTriangle class="h-5 w-5 text-yellow-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">测试端点</p>
|
||||
<p class="text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
添加端点后,可以在健康监控页面测试连通性。如果显示不健康,请检查 URL 和 Key 是否正确。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 下一步 -->
|
||||
<section class="pt-4">
|
||||
<RouterLink
|
||||
to="/guide/model"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">下一步:模型管理</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">配置模型映射和负载均衡</div>
|
||||
</div>
|
||||
<ArrowRight class="h-5 w-5 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</RouterLink>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
365
frontend/src/views/public/guide/UserKeyGuide.vue
Normal file
365
frontend/src/views/public/guide/UserKeyGuide.vue
Normal file
@@ -0,0 +1,365 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { ArrowRight, Users, Key, Shield, Check, Info, AlertTriangle, Clock } from 'lucide-vue-next'
|
||||
import { panelClasses } from './guide-config'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
baseUrl?: string
|
||||
}>(),
|
||||
{
|
||||
baseUrl: typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com'
|
||||
}
|
||||
)
|
||||
|
||||
// 用户字段说明
|
||||
const userFields = [
|
||||
{ name: '用户名/邮箱', description: '用户的登录凭证', required: true },
|
||||
{ name: '角色', description: '普通用户或管理员', required: true },
|
||||
{ name: '状态', description: '启用/禁用用户', required: false },
|
||||
{ name: '默认配额', description: '该用户创建的 Key 默认继承的配额设置', required: false }
|
||||
]
|
||||
|
||||
// API Key 字段说明
|
||||
const keyFields = [
|
||||
{ name: '名称', description: 'Key 的描述性名称,方便识别', required: true },
|
||||
{ name: '所属用户', description: 'Key 归属的用户', required: true },
|
||||
{ name: '有效期', description: '过期时间,不设置则永久有效', required: false },
|
||||
{ name: '允许的模型', description: '限制该 Key 可访问的模型列表,不设置则可访问所有模型', required: false },
|
||||
{ name: '请求配额', description: '每日/每月的请求次数限制', required: false },
|
||||
{ name: 'Token 配额', description: '每日/每月的 Token 用量限制', required: false },
|
||||
{ name: 'IP 白名单', description: '限制只有特定 IP 可以使用该 Key', required: false },
|
||||
{ name: '状态', description: '启用/禁用该 Key', required: false }
|
||||
]
|
||||
|
||||
// 权限对比
|
||||
const roleComparison = [
|
||||
{ feature: '查看仪表盘', user: true, admin: true },
|
||||
{ feature: '使用 API', user: true, admin: true },
|
||||
{ feature: '管理自己的 API Key', user: true, admin: true },
|
||||
{ feature: '查看用量统计', user: true, admin: true },
|
||||
{ feature: '管理供应商/端点', user: false, admin: true },
|
||||
{ feature: '管理模型', user: false, admin: true },
|
||||
{ feature: '管理其他用户', user: false, admin: true },
|
||||
{ feature: '管理其他用户的 Key', user: false, admin: true },
|
||||
{ feature: '系统设置', user: false, admin: true }
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-8">
|
||||
<!-- 标题 -->
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-3xl font-bold text-[#262624] dark:text-[#f1ead8]">
|
||||
用户与密钥
|
||||
</h1>
|
||||
<p class="text-lg text-[#666663] dark:text-[#a3a094]">
|
||||
用户和 API Key 是 Aether 的访问控制核心。通过用户管理分配角色权限,通过 Key 管理控制 API 访问。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 用户管理 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
用户管理
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<div class="p-2 rounded-lg bg-blue-500/10">
|
||||
<Users class="h-5 w-5 text-blue-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">普通用户</h3>
|
||||
</div>
|
||||
<ul class="space-y-2 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>可以使用 API 调用模型</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>管理自己的 API Key</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>查看自己的用量统计</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<div class="p-2 rounded-lg bg-orange-500/10">
|
||||
<Shield class="h-5 w-5 text-orange-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">管理员</h3>
|
||||
</div>
|
||||
<ul class="space-y-2 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>管理供应商、端点、模型</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>管理所有用户和 Key</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>系统设置和监控</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 权限对比表 -->
|
||||
<div :class="[panelClasses.section, 'overflow-hidden']">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50">
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">功能</th>
|
||||
<th class="px-4 py-3 text-center font-medium text-[#666663] dark:text-[#a3a094]">普通用户</th>
|
||||
<th class="px-4 py-3 text-center font-medium text-[#666663] dark:text-[#a3a094]">管理员</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="item in roleComparison"
|
||||
:key="item.feature"
|
||||
class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] last:border-0"
|
||||
>
|
||||
<td class="px-4 py-3 text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ item.feature }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<Check v-if="item.user" class="h-5 w-5 text-green-500 mx-auto" />
|
||||
<span v-else class="text-[#999]">—</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<Check v-if="item.admin" class="h-5 w-5 text-green-500 mx-auto" />
|
||||
<span v-else class="text-[#999]">—</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- API Key 管理 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
API Key 管理
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2 rounded-lg bg-orange-500/10">
|
||||
<Key class="h-5 w-5 text-orange-500" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">什么是 API Key?</h3>
|
||||
</div>
|
||||
|
||||
<ul class="space-y-3 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>API Key 是用户调用 Aether API 的凭证</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>一个用户可以有多个 Key,用于不同场景(如开发、生产、测试)</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>可以为 Key 设置细粒度的权限和配额限制</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span>Key 的用量会记录到所属用户</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Key 配置字段 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
Key 配置选项
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'overflow-hidden']">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50">
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">选项</th>
|
||||
<th class="px-4 py-3 text-left font-medium text-[#666663] dark:text-[#a3a094]">说明</th>
|
||||
<th class="px-4 py-3 text-center font-medium text-[#666663] dark:text-[#a3a094]">必填</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="field in keyFields"
|
||||
:key="field.name"
|
||||
class="border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] last:border-0"
|
||||
>
|
||||
<td class="px-4 py-3 font-medium text-[#262624] dark:text-[#f1ead8]">
|
||||
{{ field.name }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-[#666663] dark:text-[#a3a094]">
|
||||
{{ field.description }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<span v-if="field.required" :class="panelClasses.badgeGreen">必填</span>
|
||||
<span v-else class="text-[#999]">可选</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 配额设置 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
配额设置
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5 space-y-4']">
|
||||
<div class="flex items-center gap-3">
|
||||
<Clock class="h-5 w-5 text-[#cc785c]" />
|
||||
<h3 class="font-semibold text-[#262624] dark:text-[#f1ead8]">配额类型</h3>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div class="p-4 rounded-lg bg-[#f5f5f0]/50 dark:bg-[#1f1d1a]/50">
|
||||
<h4 class="font-medium text-[#262624] dark:text-[#f1ead8]">请求次数配额</h4>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
限制每日/每月的 API 调用次数,超过后请求会被拒绝
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4 rounded-lg bg-[#f5f5f0]/50 dark:bg-[#1f1d1a]/50">
|
||||
<h4 class="font-medium text-[#262624] dark:text-[#f1ead8]">Token 用量配额</h4>
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
限制每日/每月的 Token 消耗量,适合控制成本
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<Info class="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">配额继承</p>
|
||||
<p class="mt-1">
|
||||
可以在用户级别设置默认配额,新创建的 Key 会自动继承该配额。
|
||||
也可以在创建 Key 时覆盖默认配额。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 模型访问控制 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
模型访问控制
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-5']">
|
||||
<p class="text-sm text-[#666663] dark:text-[#a3a094] mb-4">
|
||||
通过「允许的模型」字段,可以限制 Key 只能访问特定模型:
|
||||
</p>
|
||||
|
||||
<ul class="space-y-2 text-sm text-[#666663] dark:text-[#a3a094]">
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span><strong>不设置</strong>:Key 可以访问所有启用的模型</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<Check class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<span><strong>设置模型列表</strong>:Key 只能访问列表中的模型</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="mt-4 p-4 rounded-lg bg-yellow-500/10 border border-yellow-500/20">
|
||||
<div class="flex items-start gap-2">
|
||||
<AlertTriangle class="h-5 w-5 text-yellow-600 dark:text-yellow-400 flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm">
|
||||
<p class="font-medium text-yellow-700 dark:text-yellow-300">使用场景</p>
|
||||
<p class="text-yellow-600 dark:text-yellow-400 mt-1">
|
||||
比如限制免费用户只能使用 gpt-3.5,付费用户可以使用 gpt-4。
|
||||
或者为不同项目创建只能访问特定模型的 Key。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 安全建议 -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="text-xl font-semibold text-[#262624] dark:text-[#f1ead8]">
|
||||
安全建议
|
||||
</h2>
|
||||
|
||||
<div :class="[panelClasses.section, 'p-4 space-y-3']">
|
||||
<div class="flex items-start gap-3">
|
||||
<Shield class="h-5 w-5 text-[#cc785c] flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">设置 Key 有效期</p>
|
||||
<p class="text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
为临时使用的 Key 设置过期时间,避免遗忘造成安全风险。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<Shield class="h-5 w-5 text-[#cc785c] flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">按场景分配 Key</p>
|
||||
<p class="text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
为开发、测试、生产环境分别创建 Key,便于追踪和管理。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<Shield class="h-5 w-5 text-[#cc785c] flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">启用 IP 白名单</p>
|
||||
<p class="text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
对于生产环境的 Key,配置 IP 白名单可以防止 Key 泄露后被滥用。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<Shield class="h-5 w-5 text-[#cc785c] flex-shrink-0 mt-0.5" />
|
||||
<div class="text-sm">
|
||||
<p class="font-medium text-[#262624] dark:text-[#f1ead8]">定期审计</p>
|
||||
<p class="text-[#666663] dark:text-[#a3a094] mt-1">
|
||||
定期检查用量统计和审计日志,发现异常及时禁用相关 Key。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 下一步 -->
|
||||
<section class="pt-4">
|
||||
<RouterLink
|
||||
to="/guide/advanced"
|
||||
:class="[panelClasses.section, panelClasses.cardHover, 'p-4 flex items-center gap-3 group']"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-[#262624] dark:text-[#f1ead8]">下一步:高级功能</div>
|
||||
<div class="text-sm text-[#666663] dark:text-[#a3a094]">格式转换、请求头规则等高级配置</div>
|
||||
</div>
|
||||
<ArrowRight class="h-5 w-5 text-[#999] group-hover:text-[#cc785c] transition-colors" />
|
||||
</RouterLink>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
264
frontend/src/views/public/guide/guide-config.ts
Normal file
264
frontend/src/views/public/guide/guide-config.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
import type { Component } from 'vue'
|
||||
import {
|
||||
BookOpen,
|
||||
Server,
|
||||
Layers,
|
||||
Users,
|
||||
Settings,
|
||||
HelpCircle
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
// 导航配置
|
||||
export interface GuideNavItem {
|
||||
id: string
|
||||
name: string
|
||||
path: string
|
||||
icon: Component
|
||||
description?: string
|
||||
}
|
||||
|
||||
export const guideNavItems: GuideNavItem[] = [
|
||||
{
|
||||
id: 'overview',
|
||||
name: '概览',
|
||||
path: '/guide',
|
||||
icon: BookOpen,
|
||||
description: '系统架构与核心概念'
|
||||
},
|
||||
{
|
||||
id: 'provider',
|
||||
name: '供应商管理',
|
||||
path: '/guide/provider',
|
||||
icon: Server,
|
||||
description: '添加供应商与端点配置'
|
||||
},
|
||||
{
|
||||
id: 'model',
|
||||
name: '模型管理',
|
||||
path: '/guide/model',
|
||||
icon: Layers,
|
||||
description: '模型映射与负载均衡'
|
||||
},
|
||||
{
|
||||
id: 'user-key',
|
||||
name: '用户与密钥',
|
||||
path: '/guide/user-key',
|
||||
icon: Users,
|
||||
description: 'API Key 与权限管理'
|
||||
},
|
||||
{
|
||||
id: 'advanced',
|
||||
name: '高级功能',
|
||||
path: '/guide/advanced',
|
||||
icon: Settings,
|
||||
description: '格式转换、请求头规则等'
|
||||
},
|
||||
{
|
||||
id: 'faq',
|
||||
name: '常见问题',
|
||||
path: '/guide/faq',
|
||||
icon: HelpCircle,
|
||||
description: '使用中的常见问题'
|
||||
}
|
||||
]
|
||||
|
||||
// 样式类常量
|
||||
export const panelClasses = {
|
||||
card: 'bg-white/70 dark:bg-[#262624]/80 backdrop-blur-sm rounded-2xl border border-[#e5e4df] dark:border-[rgba(227,224,211,0.16)]',
|
||||
cardHover: 'hover:border-[#cc785c]/30 dark:hover:border-[#d4a27f]/30 transition-colors',
|
||||
section: 'bg-white/50 dark:bg-[#262624]/60 backdrop-blur-sm rounded-xl border border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)]',
|
||||
commandPanel: 'rounded-xl border border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] bg-white/50 dark:bg-[#1f1d1a]/50',
|
||||
configPanel: 'rounded-xl border border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)] overflow-hidden',
|
||||
panelHeader: 'px-4 py-2 border-b border-[#e5e4df] dark:border-[rgba(227,224,211,0.08)] bg-[#fafaf7]/50 dark:bg-[#1f1d1a]/50',
|
||||
codeBody: 'p-0',
|
||||
badge: 'inline-flex items-center gap-1.5 rounded-full bg-[#cc785c]/10 dark:bg-[#cc785c]/20 border border-[#cc785c]/20 dark:border-[#cc785c]/40 px-3 py-1.5 text-xs font-medium text-[#cc785c] dark:text-[#d4a27f]',
|
||||
badgeBlue: 'inline-flex items-center gap-1.5 rounded-full bg-blue-500/10 dark:bg-blue-500/20 border border-blue-500/20 dark:border-blue-500/40 px-2 py-0.5 text-xs font-medium text-blue-600 dark:text-blue-400',
|
||||
badgeGreen: 'inline-flex items-center gap-1.5 rounded-full bg-green-500/10 dark:bg-green-500/20 border border-green-500/20 dark:border-green-500/40 px-2 py-0.5 text-xs font-medium text-green-600 dark:text-green-400',
|
||||
badgeYellow: 'inline-flex items-center gap-1.5 rounded-full bg-yellow-500/10 dark:bg-yellow-500/20 border border-yellow-500/20 dark:border-yellow-500/40 px-2 py-0.5 text-xs font-medium text-yellow-600 dark:text-yellow-400',
|
||||
iconButtonSmall: [
|
||||
'flex items-center justify-center rounded-lg border h-7 w-7',
|
||||
'border-[#e5e4df] dark:border-[rgba(227,224,211,0.12)]',
|
||||
'bg-transparent',
|
||||
'text-[#666663] dark:text-[#f1ead8]',
|
||||
'transition hover:bg-[#f0f0eb] dark:hover:bg-[#3a3731]'
|
||||
].join(' ')
|
||||
} as const
|
||||
|
||||
// 核心概念数据
|
||||
export const coreConcepts = [
|
||||
{
|
||||
name: '供应商 (Provider)',
|
||||
description: '代表一个 AI 服务提供商,如 OpenAI、Anthropic、Google 等',
|
||||
color: 'blue'
|
||||
},
|
||||
{
|
||||
name: '端点 (Endpoint)',
|
||||
description: '供应商下的具体 API 端点,包含 URL、密钥、API 格式等配置',
|
||||
color: 'green'
|
||||
},
|
||||
{
|
||||
name: '模型 (Model)',
|
||||
description: '可供用户使用的模型,可关联多个端点实现负载均衡',
|
||||
color: 'purple'
|
||||
},
|
||||
{
|
||||
name: 'API Key',
|
||||
description: '用户访问系统的凭证,可设置权限、配额、允许的模型等',
|
||||
color: 'orange'
|
||||
}
|
||||
]
|
||||
|
||||
// API 格式说明
|
||||
export const apiFormats = [
|
||||
{
|
||||
name: 'OpenAI',
|
||||
endpoint: '/v1/chat/completions',
|
||||
auth: 'Authorization: Bearer xxx',
|
||||
clients: ['OpenAI SDK', 'Cursor', 'LangChain', '大部分开源工具']
|
||||
},
|
||||
{
|
||||
name: 'OpenAI CLI',
|
||||
endpoint: '/v1/responses',
|
||||
auth: 'Authorization: Bearer xxx',
|
||||
clients: ['Codex CLI']
|
||||
},
|
||||
{
|
||||
name: 'Claude',
|
||||
endpoint: '/v1/messages',
|
||||
auth: 'x-api-key: xxx',
|
||||
clients: ['Anthropic SDK']
|
||||
},
|
||||
{
|
||||
name: 'Claude CLI',
|
||||
endpoint: '/v1/messages',
|
||||
auth: 'Authorization: Bearer xxx',
|
||||
clients: ['Claude Code']
|
||||
},
|
||||
{
|
||||
name: 'Gemini',
|
||||
endpoint: '/v1beta/models/{model}:generateContent',
|
||||
auth: 'x-goog-api-key: xxx',
|
||||
clients: ['Gemini SDK', 'Gemini CLI']
|
||||
}
|
||||
]
|
||||
|
||||
// 负载均衡模式
|
||||
export const loadBalanceModes = [
|
||||
{
|
||||
mode: 'priority',
|
||||
name: '优先级',
|
||||
description: '按端点优先级顺序调用,高优先级的先用,失败后降级到低优先级'
|
||||
},
|
||||
{
|
||||
mode: 'random',
|
||||
name: '随机',
|
||||
description: '随机选择一个可用端点,适合多个同质端点'
|
||||
},
|
||||
{
|
||||
mode: 'round_robin',
|
||||
name: '轮询',
|
||||
description: '依次轮流使用各个端点,分摊负载'
|
||||
},
|
||||
{
|
||||
mode: 'weighted',
|
||||
name: '加权',
|
||||
description: '按权重比例分配请求,权重高的端点处理更多请求'
|
||||
},
|
||||
{
|
||||
mode: 'latency',
|
||||
name: '最低延迟',
|
||||
description: '优先使用历史延迟最低的端点'
|
||||
}
|
||||
]
|
||||
|
||||
// FAQ 数据
|
||||
export const faqItems = [
|
||||
{
|
||||
id: 'concept-provider-endpoint',
|
||||
category: '概念理解',
|
||||
question: '供应商和端点有什么区别?',
|
||||
answer: '供应商是逻辑分组,用于组织管理多个端点。一个供应商可以有多个端点(比如不同区域、不同账号的 API)。端点才是实际调用 API 的配置单元,包含 URL、密钥等信息。'
|
||||
},
|
||||
{
|
||||
id: 'concept-model-mapping',
|
||||
category: '概念理解',
|
||||
question: '模型映射是什么意思?',
|
||||
answer: '模型映射让你可以用自定义的模型名(如 gpt-4)来访问实际的模型(如某端点的 gpt-4-turbo)。一个模型可以映射到多个端点,实现负载均衡和故障转移。'
|
||||
},
|
||||
{
|
||||
id: 'config-format',
|
||||
category: '配置问题',
|
||||
question: '端点的 API 格式怎么选择?',
|
||||
answer: '根据实际服务商的 API 格式选择。比如 OpenAI 官方选 OpenAI,Anthropic 官方选 Claude。如果用的是 OpenAI 兼容的第三方服务,通常选 OpenAI 格式。'
|
||||
},
|
||||
{
|
||||
id: 'config-priority',
|
||||
category: '配置问题',
|
||||
question: '端点优先级有什么用?',
|
||||
answer: '当模型关联多个端点且使用优先级负载均衡模式时,系统会先调用高优先级端点。如果失败(超时、错误等),会自动降级到低优先级端点。适合主备切换场景。'
|
||||
},
|
||||
{
|
||||
id: 'config-quota',
|
||||
category: '配置问题',
|
||||
question: '如何限制用户的使用量?',
|
||||
answer: '在 API Key 配置中设置配额:可以限制每日/每月的请求次数或 Token 用量。也可以在用户层面设置默认配额,新建的 Key 会继承用户配额。'
|
||||
},
|
||||
{
|
||||
id: 'advanced-conversion',
|
||||
category: '高级功能',
|
||||
question: '什么是格式转换?',
|
||||
answer: '格式转换允许用 OpenAI SDK 调用 Claude 模型,或用 Anthropic SDK 调用 OpenAI 模型。系统会自动转换请求和响应格式。需要在系统设置开启,并在端点配置中启用。'
|
||||
},
|
||||
{
|
||||
id: 'advanced-header-rules',
|
||||
category: '高级功能',
|
||||
question: '请求头规则有什么用?',
|
||||
answer: '可以在转发请求时添加、修改或删除 HTTP 头。常用于:添加认证信息、设置特定的 API 版本、添加跟踪标记等。'
|
||||
},
|
||||
{
|
||||
id: 'error-401',
|
||||
category: '常见错误',
|
||||
question: '返回 401 Unauthorized 错误?',
|
||||
answer: '检查:1) API Key 是否正确;2) Key 是否已过期或被禁用;3) 请求头格式是否正确(OpenAI 用 Bearer,Claude 用 x-api-key)。'
|
||||
},
|
||||
{
|
||||
id: 'error-404',
|
||||
category: '常见错误',
|
||||
question: '返回 404 Not Found 错误?',
|
||||
answer: '检查:1) 模型名称是否正确;2) 该模型是否已在系统中配置;3) API Key 是否有权限访问该模型;4) 端点 URL 是否正确。'
|
||||
},
|
||||
{
|
||||
id: 'error-502',
|
||||
category: '常见错误',
|
||||
question: '返回 502/503 错误?',
|
||||
answer: '表示上游服务不可用。检查:1) 端点健康状态;2) 供应商 API 是否正常;3) 网络连接是否正常。可以在健康监控页面查看端点状态。'
|
||||
}
|
||||
]
|
||||
|
||||
// 配置流程步骤
|
||||
export const configSteps = [
|
||||
{
|
||||
step: 1,
|
||||
title: '添加供应商',
|
||||
description: '创建一个供应商来组织你的 API 端点',
|
||||
path: '/admin/providers'
|
||||
},
|
||||
{
|
||||
step: 2,
|
||||
title: '配置端点',
|
||||
description: '在供应商下添加 API 端点,填写 URL、密钥等',
|
||||
path: '/admin/providers'
|
||||
},
|
||||
{
|
||||
step: 3,
|
||||
title: '创建模型',
|
||||
description: '创建用户可用的模型,关联到端点',
|
||||
path: '/admin/models'
|
||||
},
|
||||
{
|
||||
step: 4,
|
||||
title: '发放密钥',
|
||||
description: '为用户创建 API Key,设置权限和配额',
|
||||
path: '/admin/keys'
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user