mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat: api-stats页面增加周限总限查询
This commit is contained in:
20
README.md
20
README.md
@@ -17,10 +17,17 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⭐ 如果觉得有用,点个Star支持一下吧!
|
## 💎 Claude 拼车 - Claude Code 合租服务推荐
|
||||||
|
|
||||||
> 开源不易,你的Star是我持续更新的动力 🚀
|
<div align="center">
|
||||||
> 欢迎加入 [Telegram 公告频道](https://t.me/claude_relay_service) 获取最新动态
|
|
||||||
|
| 平台 | 类型 | 介绍 |
|
||||||
|
|:---:|:---:|:---|
|
||||||
|
| **[PinCC.ai](https://pincc.ai/)** | 🏆 **官方运营** | 项目官方直营的Claude拼车服务<br>提供200刀 Claude Code Max 套餐共享服务 |
|
||||||
|
| **[CToK.ai](https://ctok.ai/)** | 🤝 合作伙伴 | 社区认可的Claude拼车服务,稳定可靠 |
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -42,11 +49,6 @@
|
|||||||
|
|
||||||
如果有以上困惑,那这个项目可能适合你。
|
如果有以上困惑,那这个项目可能适合你。
|
||||||
|
|
||||||
> 💡 **Claude Code 拼车服务**
|
|
||||||
> 目前有两个稳定的 Claude Code Max 20X 200刀 拼车渠道:
|
|
||||||
> 1. **PinCC** - 项目官方运营的拼车服务:[https://pincc.ai/](https://pincc.ai/)
|
|
||||||
> 2. **CToK** - 社区认可的合作伙伴服务:[https://ctok.ai/](https://ctok.ai/)
|
|
||||||
|
|
||||||
### 适合的场景
|
### 适合的场景
|
||||||
|
|
||||||
✅ **找朋友拼车**: 三五好友一起分摊Claude Code Max订阅
|
✅ **找朋友拼车**: 三五好友一起分摊Claude Code Max订阅
|
||||||
@@ -76,8 +78,6 @@
|
|||||||
|
|
||||||
## 🚀 核心功能
|
## 🚀 核心功能
|
||||||
|
|
||||||
> 📸 **[查看演示站点](https://demo.pincc.ai/admin-next/login)**
|
|
||||||
|
|
||||||
### 基础功能
|
### 基础功能
|
||||||
|
|
||||||
- ✅ **多账户管理**: 可以添加多个Claude账户自动轮换
|
- ✅ **多账户管理**: 可以添加多个Claude账户自动轮换
|
||||||
|
|||||||
@@ -376,12 +376,14 @@ router.post('/api/user-stats', async (req, res) => {
|
|||||||
rateLimitCost: parseFloat(fullKeyData.rateLimitCost) || 0, // 新增:费用限制
|
rateLimitCost: parseFloat(fullKeyData.rateLimitCost) || 0, // 新增:费用限制
|
||||||
dailyCostLimit: fullKeyData.dailyCostLimit || 0,
|
dailyCostLimit: fullKeyData.dailyCostLimit || 0,
|
||||||
totalCostLimit: fullKeyData.totalCostLimit || 0,
|
totalCostLimit: fullKeyData.totalCostLimit || 0,
|
||||||
|
weeklyOpusCostLimit: parseFloat(fullKeyData.weeklyOpusCostLimit) || 0, // Opus 周费用限制
|
||||||
// 当前使用量
|
// 当前使用量
|
||||||
currentWindowRequests,
|
currentWindowRequests,
|
||||||
currentWindowTokens,
|
currentWindowTokens,
|
||||||
currentWindowCost, // 新增:当前窗口费用
|
currentWindowCost, // 新增:当前窗口费用
|
||||||
currentDailyCost,
|
currentDailyCost,
|
||||||
currentTotalCost: totalCost,
|
currentTotalCost: totalCost,
|
||||||
|
weeklyOpusCost: (await redis.getWeeklyOpusCost(keyId)) || 0, // 当前 Opus 周费用
|
||||||
// 时间窗口信息
|
// 时间窗口信息
|
||||||
windowStartTime,
|
windowStartTime,
|
||||||
windowEndTime,
|
windowEndTime,
|
||||||
|
|||||||
@@ -135,6 +135,59 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 总费用限制 -->
|
||||||
|
<div>
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<span class="text-sm font-medium text-gray-600 dark:text-gray-400 md:text-base"
|
||||||
|
>总费用限制</span
|
||||||
|
>
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400 md:text-sm">
|
||||||
|
<span v-if="statsData.limits.totalCostLimit > 0">
|
||||||
|
${{ statsData.limits.currentTotalCost.toFixed(4) }} / ${{
|
||||||
|
statsData.limits.totalCostLimit.toFixed(2)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span v-else class="flex items-center gap-1">
|
||||||
|
${{ statsData.limits.currentTotalCost.toFixed(4) }} / <i class="fas fa-infinity" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="statsData.limits.totalCostLimit > 0"
|
||||||
|
class="h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="h-2 rounded-full transition-all duration-300"
|
||||||
|
:class="getTotalCostProgressColor()"
|
||||||
|
:style="{ width: getTotalCostProgress() + '%' }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else class="h-2 w-full rounded-full bg-gray-200">
|
||||||
|
<div class="h-2 rounded-full bg-blue-500" style="width: 0%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Opus 模型周费用限制 -->
|
||||||
|
<div v-if="statsData.limits.weeklyOpusCostLimit > 0">
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<span class="text-sm font-medium text-gray-600 dark:text-gray-400 md:text-base"
|
||||||
|
>Opus 模型周费用限制</span
|
||||||
|
>
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400 md:text-sm">
|
||||||
|
${{ statsData.limits.weeklyOpusCost.toFixed(4) }} / ${{
|
||||||
|
statsData.limits.weeklyOpusCostLimit.toFixed(2)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700">
|
||||||
|
<div
|
||||||
|
class="h-2 rounded-full transition-all duration-300"
|
||||||
|
:class="getOpusWeeklyCostProgressColor()"
|
||||||
|
:style="{ width: getOpusWeeklyCostProgress() + '%' }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 时间窗口限制 -->
|
<!-- 时间窗口限制 -->
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
@@ -334,6 +387,43 @@ const getDailyCostProgressColor = () => {
|
|||||||
return 'bg-green-500'
|
return 'bg-green-500'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取总费用进度
|
||||||
|
const getTotalCostProgress = () => {
|
||||||
|
if (!statsData.value.limits.totalCostLimit || statsData.value.limits.totalCostLimit === 0)
|
||||||
|
return 0
|
||||||
|
const percentage =
|
||||||
|
(statsData.value.limits.currentTotalCost / statsData.value.limits.totalCostLimit) * 100
|
||||||
|
return Math.min(percentage, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取总费用进度条颜色
|
||||||
|
const getTotalCostProgressColor = () => {
|
||||||
|
const progress = getTotalCostProgress()
|
||||||
|
if (progress >= 100) return 'bg-red-500'
|
||||||
|
if (progress >= 80) return 'bg-yellow-500'
|
||||||
|
return 'bg-blue-500'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Opus周费用进度
|
||||||
|
const getOpusWeeklyCostProgress = () => {
|
||||||
|
if (
|
||||||
|
!statsData.value.limits.weeklyOpusCostLimit ||
|
||||||
|
statsData.value.limits.weeklyOpusCostLimit === 0
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
const percentage =
|
||||||
|
(statsData.value.limits.weeklyOpusCost / statsData.value.limits.weeklyOpusCostLimit) * 100
|
||||||
|
return Math.min(percentage, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Opus周费用进度条颜色
|
||||||
|
const getOpusWeeklyCostProgressColor = () => {
|
||||||
|
const progress = getOpusWeeklyCostProgress()
|
||||||
|
if (progress >= 100) return 'bg-red-500'
|
||||||
|
if (progress >= 80) return 'bg-yellow-500'
|
||||||
|
return 'bg-indigo-500' // 使用紫色表示Opus模型
|
||||||
|
}
|
||||||
|
|
||||||
// 格式化数字
|
// 格式化数字
|
||||||
const formatNumber = (num) => {
|
const formatNumber = (num) => {
|
||||||
if (typeof num !== 'number') {
|
if (typeof num !== 'number') {
|
||||||
|
|||||||
@@ -260,6 +260,12 @@ export const useApiStatsStore = defineStore('apistats', () => {
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
statsData.value = result.data
|
statsData.value = result.data
|
||||||
|
|
||||||
|
// 调试:打印返回的限制数据
|
||||||
|
console.log('API Stats - Full response:', result.data)
|
||||||
|
console.log('API Stats - limits data:', result.data.limits)
|
||||||
|
console.log('API Stats - weeklyOpusCostLimit:', result.data.limits?.weeklyOpusCostLimit)
|
||||||
|
console.log('API Stats - weeklyOpusCost:', result.data.limits?.weeklyOpusCost)
|
||||||
|
|
||||||
// 同时加载今日和本月的统计数据
|
// 同时加载今日和本月的统计数据
|
||||||
await loadAllPeriodStats()
|
await loadAllPeriodStats()
|
||||||
|
|
||||||
|
|||||||
@@ -430,16 +430,14 @@
|
|||||||
<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"
|
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">model_provider = "crs"</div>
|
<div
|
||||||
<div class="whitespace-nowrap text-gray-300">model = "gpt-5-codex"</div>
|
v-for="line in codexConfigContent.configToml"
|
||||||
<div class="whitespace-nowrap text-gray-300">model_reasoning_effort = "high"</div>
|
:key="line"
|
||||||
<div class="whitespace-nowrap text-gray-300">disable_response_storage = true</div>
|
class="whitespace-nowrap text-gray-300"
|
||||||
<div class="whitespace-nowrap text-gray-300">preferred_auth_method = "apikey"</div>
|
:class="{ 'mt-2': line === '' }"
|
||||||
<div class="mt-2"></div>
|
>
|
||||||
<div class="whitespace-nowrap text-gray-300">[model_providers.crs]</div>
|
{{ line }}
|
||||||
<div class="whitespace-nowrap text-gray-300">name = "crs"</div>
|
</div>
|
||||||
<div class="whitespace-nowrap text-gray-300">base_url = "{{ openaiBaseUrl }}"</div>
|
|
||||||
<div class="whitespace-nowrap text-gray-300">wire_api = "responses"</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-sm text-yellow-700">
|
<p class="mt-3 text-sm text-yellow-700">
|
||||||
在
|
在
|
||||||
@@ -449,13 +447,59 @@
|
|||||||
<div
|
<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"
|
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">{</div>
|
<div
|
||||||
<div class="whitespace-nowrap text-gray-300">"OPENAI_API_KEY": "你的API密钥"</div>
|
v-for="line in codexConfigContent.authJson"
|
||||||
<div class="whitespace-nowrap text-gray-300">}</div>
|
:key="line"
|
||||||
|
class="whitespace-nowrap text-gray-300"
|
||||||
|
>
|
||||||
|
{{ line }}
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 text-xs text-yellow-700">
|
</div>
|
||||||
💡 使用与 Claude Code 相同的 API 密钥即可,格式如 cr_xxxxxxxxxx。
|
<div class="mt-3 space-y-3 text-xs text-yellow-700 dark:text-yellow-300">
|
||||||
|
<!-- 描述文字 -->
|
||||||
|
<p>{{ codexConfigContent.authInstructions.description }}</p>
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<h6 class="text-sm font-medium text-yellow-800 dark:text-yellow-200">
|
||||||
|
{{ codexConfigContent.authInstructions.title }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<!-- 当前平台对应的环境变量设置 -->
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="font-medium">
|
||||||
|
{{ codexConfigContent.authInstructions.platform.title }}:
|
||||||
</p>
|
</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">
|
||||||
|
{{ codexConfigContent.authInstructions.platform.command }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Shell 配置文件(仅对于 macOS/Linux 显示) -->
|
||||||
|
<div v-if="codexConfigContent.authInstructions.persistent" class="space-y-2">
|
||||||
|
<p class="font-medium">
|
||||||
|
{{ codexConfigContent.authInstructions.persistent.title }}:
|
||||||
|
</p>
|
||||||
|
<p class="text-xs">
|
||||||
|
{{ codexConfigContent.authInstructions.persistent.description }}
|
||||||
|
</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="command in codexConfigContent.authInstructions.persistent.commands"
|
||||||
|
:key="command"
|
||||||
|
class="whitespace-nowrap text-gray-300"
|
||||||
|
:class="{ 'mt-2': command === '' }"
|
||||||
|
>
|
||||||
|
{{ command }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -917,16 +961,14 @@
|
|||||||
<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"
|
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">model_provider = "crs"</div>
|
<div
|
||||||
<div class="whitespace-nowrap text-gray-300">model = "gpt-5-codex"</div>
|
v-for="line in codexConfigContent.configToml"
|
||||||
<div class="whitespace-nowrap text-gray-300">model_reasoning_effort = "high"</div>
|
:key="line"
|
||||||
<div class="whitespace-nowrap text-gray-300">disable_response_storage = true</div>
|
class="whitespace-nowrap text-gray-300"
|
||||||
<div class="whitespace-nowrap text-gray-300">preferred_auth_method = "apikey"</div>
|
:class="{ 'mt-2': line === '' }"
|
||||||
<div class="mt-2"></div>
|
>
|
||||||
<div class="whitespace-nowrap text-gray-300">[model_providers.crs]</div>
|
{{ line }}
|
||||||
<div class="whitespace-nowrap text-gray-300">name = "crs"</div>
|
</div>
|
||||||
<div class="whitespace-nowrap text-gray-300">base_url = "{{ openaiBaseUrl }}"</div>
|
|
||||||
<div class="whitespace-nowrap text-gray-300">wire_api = "responses"</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-sm text-yellow-700">
|
<p class="mt-3 text-sm text-yellow-700">
|
||||||
在
|
在
|
||||||
@@ -936,13 +978,59 @@
|
|||||||
<div
|
<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"
|
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">{</div>
|
<div
|
||||||
<div class="whitespace-nowrap text-gray-300">"OPENAI_API_KEY": "你的API密钥"</div>
|
v-for="line in codexConfigContent.authJson"
|
||||||
<div class="whitespace-nowrap text-gray-300">}</div>
|
:key="line"
|
||||||
|
class="whitespace-nowrap text-gray-300"
|
||||||
|
>
|
||||||
|
{{ line }}
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 text-xs text-yellow-700">
|
</div>
|
||||||
💡 使用与 Claude Code 相同的 API 密钥即可,格式如 cr_xxxxxxxxxx。
|
<div class="mt-3 space-y-3 text-xs text-yellow-700 dark:text-yellow-300">
|
||||||
|
<!-- 描述文字 -->
|
||||||
|
<p>{{ codexConfigContent.authInstructions.description }}</p>
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<h6 class="text-sm font-medium text-yellow-800 dark:text-yellow-200">
|
||||||
|
{{ codexConfigContent.authInstructions.title }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<!-- 当前平台对应的环境变量设置 -->
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="font-medium">
|
||||||
|
{{ codexConfigContent.authInstructions.platform.title }}:
|
||||||
</p>
|
</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">
|
||||||
|
{{ codexConfigContent.authInstructions.platform.command }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Shell 配置文件(仅对于 macOS/Linux 显示) -->
|
||||||
|
<div v-if="codexConfigContent.authInstructions.persistent" class="space-y-2">
|
||||||
|
<p class="font-medium">
|
||||||
|
{{ codexConfigContent.authInstructions.persistent.title }}:
|
||||||
|
</p>
|
||||||
|
<p class="text-xs">
|
||||||
|
{{ codexConfigContent.authInstructions.persistent.description }}
|
||||||
|
</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="command in codexConfigContent.authInstructions.persistent.commands"
|
||||||
|
:key="command"
|
||||||
|
class="whitespace-nowrap text-gray-300"
|
||||||
|
:class="{ 'mt-2': command === '' }"
|
||||||
|
>
|
||||||
|
{{ command }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1395,16 +1483,14 @@
|
|||||||
<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"
|
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">model_provider = "crs"</div>
|
<div
|
||||||
<div class="whitespace-nowrap text-gray-300">model = "gpt-5-codex"</div>
|
v-for="line in codexConfigContent.configToml"
|
||||||
<div class="whitespace-nowrap text-gray-300">model_reasoning_effort = "high"</div>
|
:key="line"
|
||||||
<div class="whitespace-nowrap text-gray-300">disable_response_storage = true</div>
|
class="whitespace-nowrap text-gray-300"
|
||||||
<div class="whitespace-nowrap text-gray-300">preferred_auth_method = "apikey"</div>
|
:class="{ 'mt-2': line === '' }"
|
||||||
<div class="mt-2"></div>
|
>
|
||||||
<div class="whitespace-nowrap text-gray-300">[model_providers.crs]</div>
|
{{ line }}
|
||||||
<div class="whitespace-nowrap text-gray-300">name = "crs"</div>
|
</div>
|
||||||
<div class="whitespace-nowrap text-gray-300">base_url = "{{ openaiBaseUrl }}"</div>
|
|
||||||
<div class="whitespace-nowrap text-gray-300">wire_api = "responses"</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-sm text-yellow-700">
|
<p class="mt-3 text-sm text-yellow-700">
|
||||||
在
|
在
|
||||||
@@ -1414,13 +1500,59 @@
|
|||||||
<div
|
<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"
|
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">{</div>
|
<div
|
||||||
<div class="whitespace-nowrap text-gray-300">"OPENAI_API_KEY": "你的API密钥"</div>
|
v-for="line in codexConfigContent.authJson"
|
||||||
<div class="whitespace-nowrap text-gray-300">}</div>
|
:key="line"
|
||||||
|
class="whitespace-nowrap text-gray-300"
|
||||||
|
>
|
||||||
|
{{ line }}
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 text-xs text-yellow-700">
|
</div>
|
||||||
💡 使用与 Claude Code 相同的 API 密钥即可,格式如 cr_xxxxxxxxxx。
|
<div class="mt-3 space-y-3 text-xs text-yellow-700 dark:text-yellow-300">
|
||||||
|
<!-- 描述文字 -->
|
||||||
|
<p>{{ codexConfigContent.authInstructions.description }}</p>
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<h6 class="text-sm font-medium text-yellow-800 dark:text-yellow-200">
|
||||||
|
{{ codexConfigContent.authInstructions.title }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<!-- 当前平台对应的环境变量设置 -->
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="font-medium">
|
||||||
|
{{ codexConfigContent.authInstructions.platform.title }}:
|
||||||
</p>
|
</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">
|
||||||
|
{{ codexConfigContent.authInstructions.platform.command }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Shell 配置文件(仅对于 macOS/Linux 显示) -->
|
||||||
|
<div v-if="codexConfigContent.authInstructions.persistent" class="space-y-2">
|
||||||
|
<p class="font-medium">
|
||||||
|
{{ codexConfigContent.authInstructions.persistent.title }}:
|
||||||
|
</p>
|
||||||
|
<p class="text-xs">
|
||||||
|
{{ codexConfigContent.authInstructions.persistent.description }}
|
||||||
|
</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="command in codexConfigContent.authInstructions.persistent.commands"
|
||||||
|
:key="command"
|
||||||
|
class="whitespace-nowrap text-gray-300"
|
||||||
|
:class="{ 'mt-2': command === '' }"
|
||||||
|
>
|
||||||
|
{{ command }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1627,7 +1759,6 @@ const getBaseUrlPrefix = () => {
|
|||||||
origin = currentUrl.substring(0, pathStart)
|
origin = currentUrl.substring(0, pathStart)
|
||||||
} else {
|
} else {
|
||||||
// 最后的降级方案,使用相对路径
|
// 最后的降级方案,使用相对路径
|
||||||
console.warn('无法获取完整的 origin,将使用相对路径')
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1649,6 +1780,92 @@ const geminiBaseUrl = computed(() => {
|
|||||||
const openaiBaseUrl = computed(() => {
|
const openaiBaseUrl = computed(() => {
|
||||||
return getBaseUrlPrefix() + '/openai'
|
return getBaseUrlPrefix() + '/openai'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Codex 配置内容
|
||||||
|
const codexConfigContent = computed(() => {
|
||||||
|
// 根据当前激活的教程系统获取对应的环境变量设置说明
|
||||||
|
const getCurrentPlatformAuthInstructions = () => {
|
||||||
|
const baseInstructions = {
|
||||||
|
title: '环境变量设置方法',
|
||||||
|
description:
|
||||||
|
'💡 将 OPENAI_API_KEY 设置为 null,然后设置环境变量 CRS_OAI_KEY 为您的 API 密钥(格式如 cr_xxxxxxxxxx)。'
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (activeTutorialSystem.value) {
|
||||||
|
case 'windows':
|
||||||
|
return {
|
||||||
|
...baseInstructions,
|
||||||
|
platform: {
|
||||||
|
title: 'Windows',
|
||||||
|
command: 'set CRS_OAI_KEY=cr_xxxxxxxxxx'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'macos':
|
||||||
|
return {
|
||||||
|
...baseInstructions,
|
||||||
|
platform: {
|
||||||
|
title: 'macOS',
|
||||||
|
command: 'export CRS_OAI_KEY=cr_xxxxxxxxxx'
|
||||||
|
},
|
||||||
|
persistent: {
|
||||||
|
title: 'Shell 配置文件(持久保存)',
|
||||||
|
description: '添加到您的 shell 配置文件中:',
|
||||||
|
commands: [
|
||||||
|
'# 对于 zsh (默认)',
|
||||||
|
'echo "export CRS_OAI_KEY=cr_xxxxxxxxxx" >> ~/.zshrc',
|
||||||
|
'source ~/.zshrc',
|
||||||
|
'',
|
||||||
|
'# 对于 bash',
|
||||||
|
'echo "export CRS_OAI_KEY=cr_xxxxxxxxxx" >> ~/.bash_profile',
|
||||||
|
'source ~/.bash_profile'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'linux':
|
||||||
|
return {
|
||||||
|
...baseInstructions,
|
||||||
|
platform: {
|
||||||
|
title: 'Linux',
|
||||||
|
command: 'export CRS_OAI_KEY=cr_xxxxxxxxxx'
|
||||||
|
},
|
||||||
|
persistent: {
|
||||||
|
title: 'Shell 配置文件(持久保存)',
|
||||||
|
description: '添加到您的 shell 配置文件中:',
|
||||||
|
commands: [
|
||||||
|
'# 对于 bash (默认)',
|
||||||
|
'echo "export CRS_OAI_KEY=cr_xxxxxxxxxx" >> ~/.bashrc',
|
||||||
|
'source ~/.bashrc',
|
||||||
|
'',
|
||||||
|
'# 对于 zsh',
|
||||||
|
'echo "export CRS_OAI_KEY=cr_xxxxxxxxxx" >> ~/.zshrc',
|
||||||
|
'source ~/.zshrc'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return baseInstructions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
configToml: [
|
||||||
|
'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"'
|
||||||
|
],
|
||||||
|
authJson: ['{', ' "OPENAI_API_KEY": null', '}'],
|
||||||
|
authInstructions: getCurrentPlatformAuthInstructions()
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -388,7 +388,6 @@ const handleLogout = async () => {
|
|||||||
showToast('Logged out successfully', 'success')
|
showToast('Logged out successfully', 'success')
|
||||||
router.push('/user-login')
|
router.push('/user-login')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Logout error:', error)
|
|
||||||
showToast('Logout failed', 'error')
|
showToast('Logout failed', 'error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user