feat: 大规模性能优化 - Redis Pipeline 批量操作、索引系统、连接池优化

This commit is contained in:
SunSeekerX
2025-12-31 02:08:47 +08:00
parent a345812cd7
commit 584fa8c9c1
68 changed files with 6541 additions and 4536 deletions

View File

@@ -0,0 +1,495 @@
<template>
<div class="tutorial-content">
<!-- 第一步安装 Node.js -->
<NodeInstallTutorial :platform="platform" :step-number="1" tool-name="Claude Code" />
<!-- 第二步安装 Claude Code -->
<div class="mb-4 sm:mb-10 sm:mb-6">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<span
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-green-500 text-xs font-bold text-white sm:mr-3 sm:h-8 sm:w-8 sm:text-sm"
>2</span
>
安装 Claude Code
</h4>
<div
class="mb-4 rounded-xl border border-green-100 bg-gradient-to-r from-green-50 to-emerald-50 p-4 dark:border-green-500/40 dark:from-green-950/30 dark:to-emerald-950/30 sm:mb-6 sm:p-6"
>
<h5
class="mb-2 flex items-center text-base font-semibold text-gray-800 dark:text-gray-200 sm:mb-3 sm:text-lg"
>
<i class="fas fa-download mr-2 text-green-600" />
安装 Claude Code
</h5>
<p class="mb-3 text-sm text-gray-700 dark:text-gray-300 sm:mb-4 sm:text-base">
{{ platform === 'windows' ? '打开 PowerShell 或 CMD' : '打开终端' }}运行以下命令
</p>
<div
class="mb-4 overflow-x-auto rounded-lg bg-gray-900 p-3 font-mono text-xs text-green-400 sm:p-4 sm:text-sm"
>
<div class="mb-2"># 全局安装 Claude Code</div>
<div class="whitespace-nowrap text-gray-300">
{{
platform === 'windows'
? 'npm install -g @anthropic-ai/claude-code'
: 'sudo npm install -g @anthropic-ai/claude-code'
}}
</div>
</div>
<p class="text-sm text-gray-600 dark:text-gray-400">
这个命令会从 npm 官方仓库下载并安装最新版本的 Claude Code
</p>
<div
class="mt-4 rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-500/40 dark:bg-blue-950/30 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-blue-800 dark:text-blue-300 sm:text-base">
提示
</h6>
<ul class="space-y-1 text-xs text-blue-700 dark:text-blue-300 sm:text-sm">
<template v-if="platform === 'windows'">
<li> 建议使用 PowerShell 而不是 CMD功能更强大</li>
<li> 如果遇到权限问题以管理员身份运行 PowerShell</li>
</template>
<template v-else-if="platform === 'macos'">
<li> 如果遇到权限问题可以使用 sudo</li>
<li> 或者使用 nvm 安装的 Node.js 避免权限问题</li>
</template>
<template v-else>
<li> 使用 nvm 安装的 Node.js 可以避免 sudo</li>
<li> WSL2 用户确保在 Linux 子系统中运行</li>
</template>
</ul>
</div>
</div>
<!-- 验证安装 -->
<div
class="rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-500/40 dark:bg-green-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-green-800 dark:text-green-300">验证 Claude Code 安装</h6>
<p class="mb-3 text-sm text-green-700 dark:text-green-300">
安装完成后输入以下命令检查是否安装成功
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">claude --version</div>
</div>
<p class="mt-2 text-sm text-green-700 dark:text-green-300">
如果显示版本号恭喜你Claude Code 已经成功安装了
</p>
</div>
</div>
<!-- 第三步设置环境变量 -->
<div class="mb-6 sm:mb-10">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<span
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-purple-500 text-xs font-bold text-white sm:mr-3 sm:h-8 sm:w-8 sm:text-sm"
>3</span
>
设置环境变量
</h4>
<div
class="mb-4 rounded-xl border border-purple-100 bg-gradient-to-r from-purple-50 to-pink-50 p-4 dark:border-purple-500/40 dark:from-purple-950/30 dark:to-pink-950/30 sm:mb-6 sm:p-6"
>
<h5
class="mb-2 flex items-center text-base font-semibold text-gray-800 dark:text-gray-200 sm:mb-3 sm:text-lg"
>
<i class="fas fa-cog mr-2 text-purple-600" />
配置 Claude Code 环境变量
</h5>
<p class="mb-3 text-sm text-gray-700 dark:text-gray-300 sm:mb-4 sm:text-base">
为了让 Claude Code 连接到你的中转服务需要设置两个环境变量
</p>
<div class="space-y-4">
<!-- Windows 环境变量设置 -->
<template v-if="platform === 'windows'">
<div
class="rounded-lg border border-purple-200 bg-white p-3 dark:border-purple-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
方法一PowerShell 临时设置当前会话
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">
PowerShell 中运行以下命令
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
$env:ANTHROPIC_BASE_URL = "{{ currentBaseUrl }}"
</div>
<div class="whitespace-nowrap text-gray-300">
$env:ANTHROPIC_AUTH_TOKEN = "你的API密钥"
</div>
</div>
<p class="mt-2 text-xs text-yellow-700 dark:text-yellow-400">
💡 记得将 "你的API密钥" 替换为在上方 "API Keys" 标签页中创建的实际密钥
</p>
</div>
<div
class="rounded-lg border border-purple-200 bg-white p-3 dark:border-purple-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
方法二PowerShell 永久设置用户级
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">
PowerShell 中运行以下命令设置用户级环境变量
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="mb-2"># 设置用户级环境变量永久生效</div>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL", "{{
currentBaseUrl
}}", [System.EnvironmentVariableTarget]::User)
</div>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN",
"你的API密钥", [System.EnvironmentVariableTarget]::User)
</div>
</div>
<p class="mt-2 text-xs text-blue-700 dark:text-blue-300">
💡 设置后需要重新打开 PowerShell 窗口才能生效
</p>
</div>
</template>
<!-- macOS / Linux 环境变量设置 -->
<template v-else>
<div
class="rounded-lg border border-purple-200 bg-white p-3 dark:border-purple-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
方法一临时设置当前会话
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">在终端中运行以下命令</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
export ANTHROPIC_BASE_URL="{{ currentBaseUrl }}"
</div>
<div class="whitespace-nowrap text-gray-300">
export ANTHROPIC_AUTH_TOKEN="你的API密钥"
</div>
</div>
<p class="mt-2 text-xs text-yellow-700 dark:text-yellow-400">
💡 记得将 "你的API密钥" 替换为在上方 "API Keys" 标签页中创建的实际密钥
</p>
</div>
<div
class="rounded-lg border border-purple-200 bg-white p-3 dark:border-purple-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
方法二永久设置Shell 配置文件
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">
将以下内容添加到你的 shell 配置文件中{{
platform === 'macos' ? '~/.zshrc' : '~/.bashrc'
}}
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
export ANTHROPIC_BASE_URL="{{ currentBaseUrl }}"
</div>
<div class="whitespace-nowrap text-gray-300">
export ANTHROPIC_AUTH_TOKEN="你的API密钥"
</div>
</div>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">然后执行</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
source {{ platform === 'macos' ? '~/.zshrc' : '~/.bashrc' }}
</div>
</div>
</div>
</template>
</div>
</div>
<!-- VSCode 插件配置 -->
<div
class="mt-6 rounded-lg border border-indigo-200 bg-indigo-50 p-3 dark:border-indigo-500/40 dark:bg-indigo-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-indigo-800 dark:text-indigo-300">
VSCode Claude 插件配置
</h6>
<p class="mb-3 text-sm text-indigo-700 dark:text-indigo-300">
如果使用 VSCode Claude 插件需要在配置文件中进行设置
</p>
<div class="mb-3 space-y-2">
<p class="text-sm text-indigo-700 dark:text-indigo-300">
<strong>配置文件位置</strong>
<code class="rounded bg-indigo-100 px-1 dark:bg-indigo-900">{{
platform === 'windows'
? 'C:\\Users\\你的用户名\\.claude\\config.json'
: '~/.claude/config.json'
}}</code>
</p>
<p class="text-xs text-indigo-600 dark:text-indigo-400">
💡 如果该文件不存在请手动创建
</p>
</div>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">{</div>
<div class="whitespace-nowrap text-gray-300">"primaryApiKey": "crs"</div>
<div class="whitespace-nowrap text-gray-300">}</div>
</div>
</div>
<!-- 验证环境变量设置 -->
<div
class="mt-6 rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-500/40 dark:bg-blue-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-blue-800 dark:text-blue-300">验证环境变量设置</h6>
<p class="mb-3 text-sm text-blue-700 dark:text-blue-300">
设置完环境变量后可以通过以下命令验证是否设置成功
</p>
<div class="space-y-4">
<div>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
{{ platform === 'windows' ? '在 PowerShell 中验证:' : '在终端中验证:' }}
</h6>
<div
class="space-y-1 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<template v-if="platform === 'windows'">
<div class="whitespace-nowrap text-gray-300">echo $env:ANTHROPIC_BASE_URL</div>
<div class="whitespace-nowrap text-gray-300">echo $env:ANTHROPIC_AUTH_TOKEN</div>
</template>
<template v-else>
<div class="whitespace-nowrap text-gray-300">echo $ANTHROPIC_BASE_URL</div>
<div class="whitespace-nowrap text-gray-300">echo $ANTHROPIC_AUTH_TOKEN</div>
</template>
</div>
</div>
</div>
<div class="mt-3 space-y-2">
<p class="text-sm text-blue-700 dark:text-blue-300">
<strong>预期输出示例</strong>
</p>
<div class="rounded bg-gray-100 p-2 font-mono text-sm dark:bg-gray-700">
<div>{{ currentBaseUrl }}</div>
<div>cr_xxxxxxxxxxxxxxxxxx</div>
</div>
<p class="text-xs text-blue-700 dark:text-blue-300">
💡 如果输出为空或显示变量名本身说明环境变量设置失败请重新设置
</p>
</div>
</div>
</div>
<!-- 第四步开始使用 -->
<div class="mb-6 sm:mb-8">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<span
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-orange-500 text-xs font-bold text-white sm:mr-3 sm:h-8 sm:w-8 sm:text-sm"
>4</span
>
开始使用 Claude Code
</h4>
<div
class="rounded-xl border border-orange-100 bg-gradient-to-r from-orange-50 to-yellow-50 p-4 dark:border-orange-500/40 dark:from-orange-950/30 dark:to-yellow-950/30 sm:p-6"
>
<p class="mb-3 text-sm text-gray-700 dark:text-gray-300 sm:mb-4 sm:text-base">
现在你可以开始使用 Claude Code
</p>
<div class="space-y-4">
<div>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
启动 Claude Code
</h6>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">claude</div>
</div>
</div>
<div>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
在特定项目中使用
</h6>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="mb-2"># 进入你的项目目录</div>
<div class="whitespace-nowrap text-gray-300">
cd
{{
platform === 'windows' ? 'C:\\path\\to\\your\\project' : '/path/to/your/project'
}}
</div>
<div class="mb-2 mt-2"># 启动 Claude Code</div>
<div class="whitespace-nowrap text-gray-300">claude</div>
</div>
</div>
</div>
</div>
</div>
<!-- 故障排除 -->
<div class="mb-8">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<i class="fas fa-wrench mr-2 text-red-600 sm:mr-3" />
{{ platformName }} 常见问题解决
</h4>
<div class="space-y-4">
<details
class="rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800"
>
<summary
class="cursor-pointer p-3 text-sm font-medium text-gray-800 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 sm:p-4 sm:text-base"
>
安装时提示 "permission denied" 错误
</summary>
<div class="px-3 pb-3 text-gray-600 dark:text-gray-400 sm:px-4 sm:pb-4">
<p class="mb-2">这通常是权限问题尝试以下解决方法</p>
<ul class="list-inside list-disc space-y-1 text-sm">
<template v-if="platform === 'windows'">
<li>以管理员身份运行 PowerShell</li>
<li>
或者配置 npm 使用用户目录<code
class="rounded bg-gray-200 px-1 text-xs dark:bg-gray-700 sm:text-sm"
>npm config set prefix %APPDATA%\npm</code
>
</li>
</template>
<template v-else>
<li>使用 sudo 运行安装命令</li>
<li>或者使用 nvm 安装 Node.js 避免权限问题</li>
</template>
</ul>
</div>
</details>
<details
v-if="platform === 'windows'"
class="rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800"
>
<summary
class="cursor-pointer p-3 text-sm font-medium text-gray-800 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 sm:p-4 sm:text-base"
>
PowerShell 执行策略错误
</summary>
<div class="px-3 pb-3 text-gray-600 dark:text-gray-400 sm:px-4 sm:pb-4">
<p class="mb-2">如果遇到执行策略限制运行</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
</div>
</div>
</div>
</details>
<details
class="rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800"
>
<summary
class="cursor-pointer p-3 text-sm font-medium text-gray-800 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 sm:p-4 sm:text-base"
>
环境变量设置后不生效
</summary>
<div class="px-3 pb-3 text-gray-600 dark:text-gray-400 sm:px-4 sm:pb-4">
<p class="mb-2">设置永久环境变量后需要</p>
<ul class="list-inside list-disc space-y-1 text-sm">
<template v-if="platform === 'windows'">
<li>重新启动 PowerShell CMD</li>
<li>或者注销并重新登录 Windows</li>
<li>
验证设置<code
class="rounded bg-gray-200 px-1 text-xs dark:bg-gray-700 sm:text-sm"
>echo $env:ANTHROPIC_BASE_URL</code
>
</li>
</template>
<template v-else>
<li>重新打开终端窗口</li>
<li>
或者执行
<code class="rounded bg-gray-200 px-1 text-xs dark:bg-gray-700 sm:text-sm"
>source {{ platform === 'macos' ? '~/.zshrc' : '~/.bashrc' }}</code
>
</li>
<li>
验证设置<code
class="rounded bg-gray-200 px-1 text-xs dark:bg-gray-700 sm:text-sm"
>echo $ANTHROPIC_BASE_URL</code
>
</li>
</template>
</ul>
</div>
</details>
<details
v-if="platform === 'linux'"
class="rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800"
>
<summary
class="cursor-pointer p-3 text-sm font-medium text-gray-800 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 sm:p-4 sm:text-base"
>
WSL2 中无法访问 Windows 文件
</summary>
<div class="px-3 pb-3 text-gray-600 dark:text-gray-400 sm:px-4 sm:pb-4">
<p class="mb-2">WSL2 可以通过 /mnt/ 路径访问 Windows 文件</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">cd /mnt/c/Users/你的用户名/项目目录</div>
</div>
</div>
</details>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useTutorialUrls } from '@/composables/useTutorialUrls'
import NodeInstallTutorial from './NodeInstallTutorial.vue'
const props = defineProps({
platform: {
type: String,
required: true,
validator: (value) => ['windows', 'macos', 'linux'].includes(value)
}
})
const { currentBaseUrl } = useTutorialUrls()
const platformName = computed(() => {
const names = { windows: 'Windows', macos: 'macOS', linux: 'Linux / WSL2' }
return names[props.platform]
})
</script>

View File

@@ -0,0 +1,354 @@
<template>
<div class="tutorial-section">
<!-- 第一步安装 Node.js -->
<NodeInstallTutorial :platform="platform" :step-number="1" tool-name="Codex" />
<!-- 第二步配置 Codex -->
<div class="mb-4 sm:mb-10 sm:mb-6">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<span
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-indigo-500 text-xs font-bold text-white sm:mr-3 sm:h-8 sm:w-8 sm:text-sm"
>2</span
>
配置 Codex
</h4>
<p class="mb-3 text-sm text-gray-700 dark:text-gray-300 sm:mb-4 sm:text-base">
配置 Codex 以连接到中转服务
</p>
<div class="space-y-4">
<!-- config.toml 配置 -->
<div
class="rounded-lg border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-500/40 dark:bg-yellow-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-yellow-800 dark:text-yellow-300">
1. 配置文件 config.toml
</h6>
<p class="mb-3 text-sm text-yellow-700 dark:text-yellow-300">
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">{{ configPath }}</code>
文件开头添加以下配置
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div
v-for="line in configTomlLines"
:key="line"
class="whitespace-nowrap text-gray-300"
:class="{ 'mt-2': line === '' }"
>
{{ line || '&nbsp;' }}
</div>
</div>
<p class="mt-3 text-sm text-yellow-600 dark:text-yellow-400">一键写入命令</p>
<div
class="mt-2 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">{{ configTomlWriteCmd }}</div>
</div>
</div>
<!-- auth.json 配置 -->
<div
class="rounded-lg border border-orange-200 bg-orange-50 p-3 dark:border-orange-500/40 dark:bg-orange-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-orange-800 dark:text-orange-300">
2. 认证文件 auth.json
</h6>
<p class="mb-3 text-sm text-orange-700 dark:text-orange-300">
<code class="rounded bg-orange-100 px-1 dark:bg-orange-900">{{ authPath }}</code>
文件中配置
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">{</div>
<div class="whitespace-nowrap text-gray-300">&nbsp;&nbsp;"OPENAI_API_KEY": null</div>
<div class="whitespace-nowrap text-gray-300">}</div>
</div>
<div
class="mt-3 rounded border border-red-200 bg-red-50 p-2 dark:border-red-500/40 dark:bg-red-950/30"
>
<p class="text-sm font-semibold text-red-700 dark:text-red-300">
必须将 OPENAI_API_KEY 设置为 null否则 Codex 会优先使用它而忽略环境变量
</p>
</div>
<p class="mt-3 text-sm text-orange-600 dark:text-orange-400">一键写入命令</p>
<div
class="mt-2 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">{{ authJsonWriteCmd }}</div>
</div>
</div>
<!-- 环境变量配置 -->
<div
class="rounded-lg border border-purple-200 bg-purple-50 p-3 dark:border-purple-500/40 dark:bg-purple-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-purple-800 dark:text-purple-300">
3. 设置环境变量 CRS_OAI_KEY
</h6>
<p class="mb-3 text-sm text-purple-700 dark:text-purple-300">
设置环境变量 CRS_OAI_KEY 为您的 API 密钥格式如 cr_xxxxxxxxxx
</p>
<!-- Windows -->
<template v-if="platform === 'windows'">
<p class="mb-1 text-sm text-purple-600 dark:text-purple-400">
系统级环境变量推荐
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("CRS_OAI_KEY", "cr_xxxxxxxxxx",
[System.EnvironmentVariableTarget]::Machine)
</div>
</div>
<p class="mb-1 text-sm text-purple-600 line-through opacity-60 dark:text-purple-400">
用户级环境变量
<span class="text-xs text-red-500">不推荐</span>
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 opacity-60 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300 line-through">
[System.Environment]::SetEnvironmentVariable("CRS_OAI_KEY", "cr_xxxxxxxxxx",
[System.EnvironmentVariableTarget]::User)
</div>
</div>
<p class="text-sm text-purple-600 dark:text-purple-400">
💡 设置后需要重新打开终端窗口才能生效
</p>
</template>
<!-- macOS / Linux -->
<template v-else>
<p class="mb-1 text-sm text-purple-600 dark:text-purple-400">
检查当前 shell<code class="rounded bg-purple-100 px-1 dark:bg-purple-900"
>echo $SHELL</code
>
</p>
<!-- 检查旧配置 -->
<details
class="my-3 rounded-lg border border-blue-200 bg-blue-50 dark:border-blue-500/40 dark:bg-blue-950/30"
>
<summary
class="cursor-pointer p-2 text-sm font-medium text-blue-800 dark:text-blue-300"
>
检查是否已有旧配置
</summary>
<div class="px-3 pb-3">
<p class="mb-2 text-sm text-blue-700 dark:text-blue-300">
如果之前配置过建议先检查并清理旧配置
</p>
<div
class="mb-2 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="text-gray-500"># zsh</div>
<div class="whitespace-nowrap text-gray-300">grep 'CRS_OAI_KEY' ~/.zshrc</div>
<div class="mt-1 text-gray-500"># bash</div>
<div class="whitespace-nowrap text-gray-300">grep 'CRS_OAI_KEY' ~/.bashrc</div>
</div>
<p class="text-sm text-blue-600 dark:text-blue-400">
如果有输出说明已配置过可以手动编辑文件修改或删除旧配置
</p>
</div>
</details>
<p class="mb-1 mt-2 text-sm text-purple-600 dark:text-purple-400">
{{ platform === 'macos' ? 'zsh (macOS 默认)' : 'bash (Linux 默认)' }}
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
echo 'export CRS_OAI_KEY="cr_xxxxxxxxxx"' >>
{{
platform === 'macos'
? '~/.zshrc && source ~/.zshrc'
: '~/.bashrc && source ~/.bashrc'
}}
</div>
</div>
<p class="mb-1 text-sm text-purple-600 dark:text-purple-400">
{{ platform === 'macos' ? 'bash' : 'zsh' }}
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
echo 'export CRS_OAI_KEY="cr_xxxxxxxxxx"' >>
{{
platform === 'macos'
? '~/.bashrc && source ~/.bashrc'
: '~/.zshrc && source ~/.zshrc'
}}
</div>
</div>
<p class="text-sm text-purple-600 dark:text-purple-400">
💡 设置后需要重新打开终端窗口或执行 source 命令才能生效
</p>
</template>
</div>
<!-- 验证环境变量 -->
<div
class="rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-500/40 dark:bg-green-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-green-800 dark:text-green-300">4. 验证环境变量</h6>
<p class="mb-2 text-sm text-green-700 dark:text-green-300">
重新打开终端后验证环境变量是否设置成功
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div v-if="platform === 'windows'" class="whitespace-nowrap text-gray-300">
Get-ChildItem Env:CRS_OAI_KEY
</div>
<div v-else class="whitespace-nowrap text-gray-300">
echo "CRS_OAI_KEY: $CRS_OAI_KEY"
</div>
</div>
</div>
<!-- 删除环境变量 -->
<details
class="rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800"
>
<summary class="cursor-pointer p-3 text-sm font-medium text-gray-800 dark:text-gray-300">
如何删除环境变量
</summary>
<div class="px-3 pb-3">
<template v-if="platform === 'windows'">
<p class="mb-1 text-sm text-gray-600 dark:text-gray-400">删除用户级环境变量</p>
<div
class="mb-2 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("CRS_OAI_KEY", $null,
[System.EnvironmentVariableTarget]::User)
</div>
</div>
<p class="mb-1 text-sm text-gray-600 dark:text-gray-400">删除系统级环境变量</p>
<div
class="mb-2 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("CRS_OAI_KEY", $null,
[System.EnvironmentVariableTarget]::Machine)
</div>
</div>
</template>
<template v-else>
<p class="mb-1 text-sm text-gray-600 dark:text-gray-400"> zsh 配置中删除</p>
<div
class="mb-2 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="text-gray-500"># 删除包含 CRS_OAI_KEY 的行</div>
<div class="whitespace-nowrap text-gray-300">
sed -i '' '/CRS_OAI_KEY/d' ~/.zshrc && source ~/.zshrc
</div>
</div>
<p class="mb-1 text-sm text-gray-600 dark:text-gray-400"> bash 配置中删除</p>
<div
class="mb-2 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="text-gray-500"># 删除包含 CRS_OAI_KEY 的行</div>
<div class="whitespace-nowrap text-gray-300">
sed -i '' '/CRS_OAI_KEY/d' ~/.bashrc && source ~/.bashrc
</div>
</div>
</template>
<p class="mb-1 text-sm text-gray-600 dark:text-gray-400">验证是否删除成功</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div v-if="platform === 'windows'" class="whitespace-nowrap text-gray-300">
Get-ChildItem Env:CRS_OAI_KEY
</div>
<div v-else class="whitespace-nowrap text-gray-300">
echo "CRS_OAI_KEY: $CRS_OAI_KEY"
</div>
</div>
</div>
</details>
<!-- 提示 -->
<div
class="rounded-lg border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-500/40 dark:bg-yellow-950/30 sm:p-4"
>
<p class="text-sm text-yellow-700 dark:text-yellow-300">
💡 请将示例中的
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">cr_xxxxxxxxxx</code>
替换为您的实际 API 密钥
</p>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useTutorialUrls } from '@/composables/useTutorialUrls'
import NodeInstallTutorial from './NodeInstallTutorial.vue'
const props = defineProps({
platform: {
type: String,
required: true,
validator: (value) => ['windows', 'macos', 'linux'].includes(value)
}
})
const { openaiBaseUrl } = useTutorialUrls()
const configPath = computed(() =>
props.platform === 'windows' ? '%USERPROFILE%\\.codex\\config.toml' : '~/.codex/config.toml'
)
const authPath = computed(() =>
props.platform === 'windows' ? '%USERPROFILE%\\.codex\\auth.json' : '~/.codex/auth.json'
)
const configTomlLines = computed(() => [
'model_provider = "crs"',
'model = "gpt-5-codex"',
'model_reasoning_effort = "high"',
'disable_response_storage = true',
'preferred_auth_method = "apikey"',
'',
'[model_providers.crs]',
'name = "crs"',
`base_url = "${openaiBaseUrl.value}"`,
'wire_api = "responses"',
'requires_openai_auth = true',
'env_key = "CRS_OAI_KEY"'
])
const configTomlContent = computed(() => configTomlLines.value.join('\n'))
const configTomlWriteCmd = computed(() => {
if (props.platform === 'windows') {
const escaped = configTomlContent.value.replace(/"/g, '`"').replace(/\n/g, '`n')
return `New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\\.codex" | Out-Null; "${escaped}" | Set-Content -Path "$env:USERPROFILE\\.codex\\config.toml" -Force`
}
const escaped = configTomlContent.value.replace(/\n/g, '\\n')
return `mkdir -p ~/.codex && printf '${escaped}\\n' > ~/.codex/config.toml`
})
const authJsonWriteCmd = computed(() => {
if (props.platform === 'windows') {
return `New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\\.codex" | Out-Null; '{"OPENAI_API_KEY": null}' | Set-Content -Path "$env:USERPROFILE\\.codex\\auth.json" -Force`
}
return `mkdir -p ~/.codex && echo '{"OPENAI_API_KEY": null}' > ~/.codex/auth.json`
})
</script>

View File

@@ -0,0 +1,98 @@
<template>
<div class="tutorial-section">
<!-- 第一步安装 Node.js -->
<NodeInstallTutorial :platform="platform" :step-number="1" tool-name="Droid CLI" />
<!-- 第二步配置 Droid CLI -->
<div class="mb-4 sm:mb-10 sm:mb-6">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<span
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-blue-500 text-xs font-bold text-white sm:mr-3 sm:h-8 sm:w-8 sm:text-sm"
>2</span
>
配置 Droid CLI
</h4>
<p class="mb-3 text-sm text-gray-700 dark:text-gray-300 sm:mb-4 sm:text-base">
Droid CLI 使用
<code class="rounded bg-gray-100 px-1 dark:bg-gray-800">~/.factory/config.json</code>
保存自定义模型
<template v-if="platform === 'windows'">
Windows 中可直接编辑
<code class="rounded bg-gray-100 px-1 dark:bg-gray-800"
>C:\Users\你的用户名\.factory\config.json</code
>
</template>
<template v-else>
在终端中可使用
<code class="rounded bg-gray-100 px-1 dark:bg-gray-800">vim ~/.factory/config.json</code>
编辑
</template>
</p>
<div
class="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-500/40 dark:bg-blue-950/30 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-blue-800 dark:text-blue-200 sm:text-base">
配置文件示例
</h6>
<p class="mb-3 text-sm text-blue-700 dark:text-blue-200">
将以下内容追加到配置文件中并替换示例中的域名和 API 密钥
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div
v-for="(line, index) in droidCliConfigLines"
:key="line + index"
class="whitespace-pre text-gray-300"
>
{{ line }}
</div>
</div>
<p class="mt-3 text-xs text-blue-700 dark:text-blue-200 sm:text-sm">
💡 Droid CLI 中选择自定义模型即可使用新的 Droid 账号池确保服务地址可被本地访问
</p>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useTutorialUrls } from '@/composables/useTutorialUrls'
import NodeInstallTutorial from './NodeInstallTutorial.vue'
defineProps({
platform: {
type: String,
required: true,
validator: (value) => ['windows', 'macos', 'linux'].includes(value)
}
})
const { droidClaudeBaseUrl, droidOpenaiBaseUrl } = useTutorialUrls()
const droidCliConfigLines = computed(() => [
'{',
' "custom_models": [',
' {',
' "model_display_name": "Sonnet 4.5 [crs]",',
' "model": "claude-sonnet-4-5-20250929",',
` "base_url": "${droidClaudeBaseUrl.value}",`,
' "api_key": "你的API密钥",',
' "provider": "anthropic",',
' "max_tokens": 8192',
' },',
' {',
' "model_display_name": "GPT5-Codex [crs]",',
' "model": "gpt-5-codex",',
` "base_url": "${droidOpenaiBaseUrl.value}",`,
' "api_key": "你的API密钥",',
' "provider": "openai",',
' "max_tokens": 16384',
' }',
' ]',
'}'
])
</script>

View File

@@ -0,0 +1,183 @@
<template>
<div class="tutorial-section">
<!-- 第一步安装 Node.js -->
<NodeInstallTutorial :platform="platform" :step-number="1" tool-name="Gemini CLI" />
<!-- 第二步配置环境变量 -->
<div class="mb-4 sm:mb-10 sm:mb-6">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<span
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-green-500 text-xs font-bold text-white sm:mr-3 sm:h-8 sm:w-8 sm:text-sm"
>2</span
>
配置 Gemini CLI 环境变量
</h4>
<p class="mb-3 text-sm text-gray-700 dark:text-gray-300 sm:mb-4 sm:text-base">
设置以下环境变量以连接到中转服务
</p>
<div class="space-y-4">
<!-- Windows -->
<template v-if="platform === 'windows'">
<div
class="rounded-lg border border-green-200 bg-white p-3 dark:border-green-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
PowerShell 设置方法
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">
PowerShell 中运行以下命令
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
$env:GOOGLE_GEMINI_BASE_URL = "{{ geminiBaseUrl }}"
</div>
<div class="whitespace-nowrap text-gray-300">$env:GEMINI_API_KEY = "你的API密钥"</div>
<div class="whitespace-nowrap text-gray-300">
$env:GEMINI_MODEL = "gemini-2.5-pro"
</div>
</div>
<p class="mt-2 text-xs text-yellow-700 dark:text-yellow-400">
💡 使用与 Claude Code 相同的 API 密钥即可
</p>
</div>
<div
class="rounded-lg border border-green-200 bg-white p-3 dark:border-green-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
PowerShell 永久设置用户级
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">
PowerShell 中运行以下命令
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="mb-2"># 设置用户级环境变量永久生效</div>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("GOOGLE_GEMINI_BASE_URL", "{{
geminiBaseUrl
}}", [System.EnvironmentVariableTarget]::User)
</div>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("GEMINI_API_KEY", "你的API密钥",
[System.EnvironmentVariableTarget]::User)
</div>
<div class="whitespace-nowrap text-gray-300">
[System.Environment]::SetEnvironmentVariable("GEMINI_MODEL", "gemini-2.5-pro",
[System.EnvironmentVariableTarget]::User)
</div>
</div>
<p class="mt-2 text-xs text-blue-700 dark:text-blue-300">
💡 设置后需要重新打开 PowerShell 窗口才能生效
</p>
</div>
</template>
<!-- macOS / Linux -->
<template v-else>
<div
class="rounded-lg border border-green-200 bg-white p-3 dark:border-green-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
临时设置当前会话
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">在终端中运行以下命令</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
export GOOGLE_GEMINI_BASE_URL="{{ geminiBaseUrl }}"
</div>
<div class="whitespace-nowrap text-gray-300">export GEMINI_API_KEY="你的API密钥"</div>
<div class="whitespace-nowrap text-gray-300">
export GEMINI_MODEL="gemini-2.5-pro"
</div>
</div>
<p class="mt-2 text-xs text-yellow-700 dark:text-yellow-400">
💡 使用与 Claude Code 相同的 API 密钥即可
</p>
</div>
<div
class="rounded-lg border border-green-200 bg-white p-3 dark:border-green-700 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
永久设置Shell 配置文件
</h6>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">
将以下内容添加到你的 shell 配置文件中{{
platform === 'macos' ? '~/.zshrc' : '~/.bashrc'
}}
</p>
<div
class="mb-3 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
export GOOGLE_GEMINI_BASE_URL="{{ geminiBaseUrl }}"
</div>
<div class="whitespace-nowrap text-gray-300">export GEMINI_API_KEY="你的API密钥"</div>
<div class="whitespace-nowrap text-gray-300">
export GEMINI_MODEL="gemini-2.5-pro"
</div>
</div>
<p class="mb-3 text-sm text-gray-600 dark:text-gray-400">然后执行</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">
source {{ platform === 'macos' ? '~/.zshrc' : '~/.bashrc' }}
</div>
</div>
</div>
</template>
<!-- 验证 -->
<div
class="rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-500/40 dark:bg-green-950/30 sm:p-4"
>
<h6 class="mb-2 font-medium text-green-800 dark:text-green-300">
验证 Gemini CLI 环境变量
</h6>
<p class="mb-3 text-sm text-green-700 dark:text-green-300">
{{ platform === 'windows' ? '在 PowerShell 中验证:' : '在终端中验证:' }}
</p>
<div
class="space-y-1 overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<template v-if="platform === 'windows'">
<div class="whitespace-nowrap text-gray-300">echo $env:GOOGLE_GEMINI_BASE_URL</div>
<div class="whitespace-nowrap text-gray-300">echo $env:GEMINI_API_KEY</div>
<div class="whitespace-nowrap text-gray-300">echo $env:GEMINI_MODEL</div>
</template>
<template v-else>
<div class="whitespace-nowrap text-gray-300">echo $GOOGLE_GEMINI_BASE_URL</div>
<div class="whitespace-nowrap text-gray-300">echo $GEMINI_API_KEY</div>
<div class="whitespace-nowrap text-gray-300">echo $GEMINI_MODEL</div>
</template>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useTutorialUrls } from '@/composables/useTutorialUrls'
import NodeInstallTutorial from './NodeInstallTutorial.vue'
defineProps({
platform: {
type: String,
required: true,
validator: (value) => ['windows', 'macos', 'linux'].includes(value)
}
})
const { geminiBaseUrl } = useTutorialUrls()
</script>

View File

@@ -0,0 +1,234 @@
<template>
<div class="mb-4 sm:mb-10 sm:mb-6">
<h4
class="mb-3 flex items-center text-lg font-semibold text-gray-800 dark:text-gray-300 sm:mb-4 sm:text-xl"
>
<span
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-blue-500 text-xs font-bold text-white sm:mr-3 sm:h-8 sm:w-8 sm:text-sm"
>{{ stepNumber }}</span
>
安装 Node.js 环境
</h4>
<p class="mb-4 text-sm text-gray-600 dark:text-gray-400 sm:mb-4 sm:mb-6 sm:text-base">
{{ toolName }} 需要 Node.js 环境才能运行
</p>
<!-- Windows -->
<div v-if="platform === 'windows'" class="node-install-section">
<div
class="mb-4 rounded-xl border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 dark:border-blue-500/40 dark:from-blue-950/30 dark:to-indigo-950/30 sm:mb-6 sm:p-6"
>
<h5
class="mb-2 flex items-center text-base font-semibold text-gray-800 dark:text-gray-200 sm:mb-3 sm:text-lg"
>
<i class="fab fa-windows mr-2 text-blue-600" />
Windows 安装方法
</h5>
<div class="mb-3 sm:mb-4">
<p class="mb-2 text-sm text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-base">
方法一官网下载推荐
</p>
<ol
class="ml-2 list-inside list-decimal space-y-1 text-xs text-gray-600 dark:text-gray-400 sm:ml-4 sm:space-y-2 sm:text-sm"
>
<li>
打开浏览器访问
<code
class="rounded bg-gray-100 px-1 py-1 text-xs dark:bg-gray-800 dark:text-yellow-400 sm:px-2 sm:text-sm"
>https://nodejs.org/</code
>
</li>
<li>点击 "LTS" 版本进行下载推荐长期支持版本</li>
<li>
下载完成后双击
<code
class="rounded bg-gray-100 px-1 py-1 text-xs dark:bg-gray-800 dark:text-yellow-400 sm:px-2 sm:text-sm"
>.msi</code
>
文件
</li>
<li>按照安装向导完成安装保持默认设置即可</li>
</ol>
</div>
<div class="mb-3 sm:mb-4">
<p class="mb-2 text-sm text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-base">
方法二使用包管理器
</p>
<p class="mb-2 text-xs text-gray-600 dark:text-gray-400 sm:text-sm">
如果你安装了 Chocolatey Scoop可以使用命令行安装
</p>
<div
class="overflow-x-auto rounded-lg bg-gray-900 p-3 font-mono text-xs text-green-400 dark:border dark:border-slate-700 dark:bg-slate-900 sm:p-4 sm:text-sm"
>
<div class="mb-2"># 使用 Chocolatey</div>
<div class="whitespace-nowrap text-gray-300">choco install nodejs</div>
<div class="mb-2 mt-3"># 或使用 Scoop</div>
<div class="whitespace-nowrap text-gray-300">scoop install nodejs</div>
</div>
</div>
<div
class="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-500/40 dark:bg-blue-950/30 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-blue-800 dark:text-blue-300 sm:text-base">
Windows 注意事项
</h6>
<ul class="space-y-1 text-xs text-blue-700 dark:text-blue-300 sm:text-sm">
<li> 建议使用 PowerShell 而不是 CMD</li>
<li> 如果遇到权限问题尝试以管理员身份运行</li>
<li> 某些杀毒软件可能会误报需要添加白名单</li>
</ul>
</div>
</div>
<VerifyInstall terminal="PowerShell 或 CMD" />
</div>
<!-- macOS -->
<div v-else-if="platform === 'macos'" class="node-install-section">
<div
class="mb-4 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4 dark:border-gray-700 dark:from-gray-800 dark:to-slate-800 sm:mb-6 sm:p-6"
>
<h5
class="mb-2 flex items-center text-base font-semibold text-gray-800 dark:text-gray-200 sm:mb-3 sm:text-lg"
>
<i class="fab fa-apple mr-2 text-gray-700 dark:text-gray-400" />
macOS 安装方法
</h5>
<div class="mb-4">
<p class="mb-3 text-gray-700 dark:text-gray-300">方法一使用 Homebrew推荐</p>
<p class="mb-2 text-xs text-gray-600 dark:text-gray-400 sm:text-sm">
如果你已经安装了 Homebrew使用它安装 Node.js 会更方便
</p>
<div
class="overflow-x-auto rounded-lg bg-gray-900 p-3 font-mono text-xs text-green-400 dark:border dark:border-slate-700 dark:bg-slate-900 sm:p-4 sm:text-sm"
>
<div class="mb-2"># 更新 Homebrew</div>
<div class="whitespace-nowrap text-gray-300">brew update</div>
<div class="mb-2 mt-3"># 安装 Node.js</div>
<div class="whitespace-nowrap text-gray-300">brew install node</div>
</div>
</div>
<div class="mb-4">
<p class="mb-3 text-gray-700 dark:text-gray-300">方法二官网下载</p>
<ol
class="ml-2 list-inside list-decimal space-y-1 text-xs text-gray-600 dark:text-gray-400 sm:ml-4 sm:space-y-2 sm:text-sm"
>
<li>
访问
<code
class="rounded bg-gray-100 px-1 py-1 text-xs dark:bg-gray-700 sm:px-2 sm:text-sm"
>https://nodejs.org/</code
>
</li>
<li>下载适合 macOS LTS 版本</li>
<li>
打开下载的
<code
class="rounded bg-gray-100 px-1 py-1 text-xs dark:bg-gray-700 sm:px-2 sm:text-sm"
>.pkg</code
>
文件
</li>
<li>按照安装程序指引完成安装</li>
</ol>
</div>
<div
class="rounded-lg border border-gray-200 bg-gray-50 p-3 dark:border-gray-600 dark:bg-gray-800 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-gray-800 dark:text-gray-300 sm:text-base">
macOS 注意事项
</h6>
<ul class="space-y-1 text-xs text-gray-700 dark:text-gray-300 sm:text-sm">
<li>
如果遇到权限问题可能需要使用
<code class="rounded bg-gray-200 px-1 text-xs dark:bg-gray-700 sm:text-sm">sudo</code>
</li>
<li> 首次运行可能需要在系统偏好设置中允许</li>
<li> 建议使用 Terminal iTerm2</li>
</ul>
</div>
</div>
<VerifyInstall terminal="Terminal" />
</div>
<!-- Linux -->
<div v-else class="node-install-section">
<div
class="mb-4 rounded-xl border border-orange-100 bg-gradient-to-r from-orange-50 to-amber-50 p-4 dark:border-orange-500/40 dark:from-orange-950/30 dark:to-amber-950/30 sm:mb-6 sm:p-6"
>
<h5
class="mb-2 flex items-center text-base font-semibold text-gray-800 dark:text-gray-200 sm:mb-3 sm:text-lg"
>
<i class="fab fa-linux mr-2 text-orange-600" />
Linux / WSL2 安装方法
</h5>
<div class="mb-4">
<p class="mb-3 text-gray-700 dark:text-gray-300">方法一使用 nvm推荐</p>
<p class="mb-2 text-xs text-gray-600 dark:text-gray-400 sm:text-sm">
nvm 可以方便地管理多个 Node.js 版本
</p>
<div
class="overflow-x-auto rounded-lg bg-gray-900 p-3 font-mono text-xs text-green-400 dark:border dark:border-slate-700 dark:bg-slate-900 sm:p-4 sm:text-sm"
>
<div class="mb-2"># 安装 nvm</div>
<div class="whitespace-nowrap text-gray-300">
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
</div>
<div class="mb-2 mt-3"># 重新加载 shell 配置</div>
<div class="whitespace-nowrap text-gray-300">source ~/.bashrc</div>
<div class="mb-2 mt-3"># 安装最新 LTS 版本</div>
<div class="whitespace-nowrap text-gray-300">nvm install --lts</div>
</div>
</div>
<div class="mb-4">
<p class="mb-3 text-gray-700 dark:text-gray-300">方法二使用包管理器</p>
<div
class="overflow-x-auto rounded-lg bg-gray-900 p-3 font-mono text-xs text-green-400 dark:border dark:border-slate-700 dark:bg-slate-900 sm:p-4 sm:text-sm"
>
<div class="mb-2"># Ubuntu/Debian</div>
<div class="whitespace-nowrap text-gray-300">
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
</div>
<div class="whitespace-nowrap text-gray-300">sudo apt-get install -y nodejs</div>
<div class="mb-2 mt-3"># Fedora</div>
<div class="whitespace-nowrap text-gray-300">sudo dnf install nodejs</div>
<div class="mb-2 mt-3"># Arch Linux</div>
<div class="whitespace-nowrap text-gray-300">sudo pacman -S nodejs npm</div>
</div>
</div>
<div
class="rounded-lg border border-orange-200 bg-orange-50 p-3 dark:border-orange-500/40 dark:bg-orange-950/30 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-orange-800 dark:text-orange-300 sm:text-base">
Linux / WSL2 注意事项
</h6>
<ul class="space-y-1 text-xs text-orange-700 dark:text-orange-300 sm:text-sm">
<li> WSL2 用户建议在 Linux 子系统中安装而不是 Windows</li>
<li> 使用 nvm 可以避免权限问题</li>
<li> 确保 shell 配置文件正确加载了 nvm</li>
</ul>
</div>
</div>
<VerifyInstall terminal="终端" />
</div>
</div>
</template>
<script setup>
import VerifyInstall from './VerifyInstall.vue'
defineProps({
platform: {
type: String,
required: true,
validator: (value) => ['windows', 'macos', 'linux'].includes(value)
},
stepNumber: {
type: [Number, String],
default: 1
},
toolName: {
type: String,
default: 'CLI 工具'
}
})
</script>

View File

@@ -0,0 +1,30 @@
<template>
<div
class="rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-500/40 dark:bg-green-950/30 sm:p-4"
>
<h6 class="mb-2 text-sm font-medium text-green-800 dark:text-green-300 sm:text-base">
验证安装是否成功
</h6>
<p class="mb-2 text-xs text-green-700 dark:text-green-300 sm:mb-3 sm:text-sm">
安装完成后打开 {{ terminal }}输入以下命令
</p>
<div
class="overflow-x-auto rounded bg-gray-900 p-2 font-mono text-xs text-green-400 sm:p-3 sm:text-sm"
>
<div class="whitespace-nowrap text-gray-300">node --version</div>
<div class="whitespace-nowrap text-gray-300">npm --version</div>
</div>
<p class="mt-2 text-xs text-green-700 dark:text-green-300 sm:text-sm">
如果显示版本号说明安装成功了
</p>
</div>
</template>
<script setup>
defineProps({
terminal: {
type: String,
default: '终端'
}
})
</script>

View File

@@ -85,7 +85,7 @@
<div
:class="[
'h-2 w-2 rounded-full',
apiKey.isDeleted === 'true' || apiKey.deletedAt
apiKey.isDeleted === true || apiKey.deletedAt
? 'bg-gray-400'
: apiKey.isActive
? 'bg-green-400'
@@ -97,7 +97,7 @@
<div class="flex items-center">
<p class="text-sm font-medium text-gray-900">{{ apiKey.name }}</p>
<span
v-if="apiKey.isDeleted === 'true' || apiKey.deletedAt"
v-if="apiKey.isDeleted === true || apiKey.deletedAt"
class="ml-2 inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-800"
>
Deleted

View File

@@ -0,0 +1,52 @@
import { computed } from 'vue'
export function useTutorialUrls() {
const getBaseUrlPrefix = () => {
const customPrefix = import.meta.env.VITE_API_BASE_PREFIX
if (customPrefix) {
return customPrefix.replace(/\/$/, '')
}
let origin = ''
if (window.location.origin) {
origin = window.location.origin
} else {
const protocol = window.location.protocol
const hostname = window.location.hostname
const port = window.location.port
origin = protocol + '//' + hostname
if (
port &&
((protocol === 'http:' && port !== '80') || (protocol === 'https:' && port !== '443'))
) {
origin += ':' + port
}
}
if (!origin) {
const currentUrl = window.location.href
const pathStart = currentUrl.indexOf('/', 8)
if (pathStart !== -1) {
origin = currentUrl.substring(0, pathStart)
} else {
return ''
}
}
return origin
}
const currentBaseUrl = computed(() => getBaseUrlPrefix() + '/api')
const geminiBaseUrl = computed(() => getBaseUrlPrefix() + '/gemini')
const openaiBaseUrl = computed(() => getBaseUrlPrefix() + '/openai')
const droidClaudeBaseUrl = computed(() => getBaseUrlPrefix() + '/droid/claude')
const droidOpenaiBaseUrl = computed(() => getBaseUrlPrefix() + '/droid/openai')
return {
currentBaseUrl,
geminiBaseUrl,
openaiBaseUrl,
droidClaudeBaseUrl,
droidOpenaiBaseUrl
}
}

View File

@@ -66,18 +66,14 @@ export const useAuthStore = defineStore('auth', () => {
async function verifyToken() {
try {
// 获取当前用户信息
// /web/auth/user 已做完整 token 验证session 存在性、完整性)
// 成功返回即表示 token 有效,无需再调用 dashboard
const userResult = await apiClient.get('/web/auth/user')
if (userResult.success && userResult.user) {
username.value = userResult.user.username
}
// 使用 dashboard 端点来验证 token
// 如果 token 无效,会抛出错误
const result = await apiClient.get('/admin/dashboard')
if (!result.success) {
if (!userResult.success || !userResult.user) {
logout()
return
}
username.value = userResult.user.username
} catch (error) {
// token 无效,需要重新登录
logout()

View File

@@ -2122,7 +2122,6 @@ import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { showToast } from '@/utils/toast'
import { apiClient } from '@/config/api'
import { useClientsStore } from '@/stores/clients'
import { useAuthStore } from '@/stores/auth'
import * as XLSX from 'xlsx-js-style'
import CreateApiKeyModal from '@/components/apikeys/CreateApiKeyModal.vue'
@@ -2139,7 +2138,6 @@ import ActionDropdown from '@/components/common/ActionDropdown.vue'
// 响应式数据
const router = useRouter()
const clientsStore = useClientsStore()
const authStore = useAuthStore()
const apiKeys = ref([])
@@ -4803,7 +4801,8 @@ onMounted(async () => {
fetchCostSortStatus()
// 先加载 API Keys优先显示列表
await Promise.all([clientsStore.loadSupportedClients(), loadApiKeys(), loadUsedModels()])
// supported-clients 由 Create/Edit 模态框按需加载,无需预加载
await Promise.all([loadApiKeys(), loadUsedModels()])
// 初始化全选状态
updateSelectAllState()

File diff suppressed because it is too large Load Diff