前端: 仪表盘快速开始卡片 + 移动端适配 + 主页 curl 用浏览器 host
- 仪表盘底部新增「快速开始」:Base URL(浏览器 host)+ 协议徽标 + curl 示例(可复制) - 最近请求改为响应式两行(模型+状态 / token·成本·时间),窄屏不再挤压 - 主页调用演示改用 window.location.origin,去掉写死的 api.openteam.dev Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import ThemeToggle from '@/components/ui/ThemeToggle.vue'
|
import ThemeToggle from '@/components/ui/ThemeToggle.vue'
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
|
// 调用演示用浏览器当前 host(兼容端点),避免写死域名
|
||||||
|
const host = window.location.host
|
||||||
|
const baseURL = `${window.location.origin}/v1`
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -70,11 +74,11 @@ const auth = useAuthStore()
|
|||||||
<div class="flex items-center gap-1.5 border-b border-edge px-4 py-2.5">
|
<div class="flex items-center gap-1.5 border-b border-edge px-4 py-2.5">
|
||||||
<span class="size-2.5 rounded-full bg-surface2" />
|
<span class="size-2.5 rounded-full bg-surface2" />
|
||||||
<span class="size-2.5 rounded-full bg-surface2" />
|
<span class="size-2.5 rounded-full bg-surface2" />
|
||||||
<span class="ml-2 text-muted">curl api.openteam.dev</span>
|
<span class="ml-2 text-muted">curl {{ host }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-3 p-4 leading-relaxed">
|
<div class="space-y-3 p-4 leading-relaxed">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-muted"><span class="text-accent">$</span> curl https://api.openteam.dev/v1/chat/completions</p>
|
<p class="text-muted"><span class="text-accent">$</span> curl {{ baseURL }}/chat/completions</p>
|
||||||
<p class="text-muted"> -H <span class="text-accent">"Authorization: Bearer sk-..."</span> \</p>
|
<p class="text-muted"> -H <span class="text-accent">"Authorization: Bearer sk-..."</span> \</p>
|
||||||
<p class="text-muted"> -d <span class="text-ink">'{"model": "claude-sonnet-5", "messages": [{"role": "user", "content": "你好"}]}'</span></p>
|
<p class="text-muted"> -d <span class="text-ink">'{"model": "claude-sonnet-5", "messages": [{"role": "user", "content": "你好"}]}'</span></p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { onMounted, ref } from 'vue'
|
|||||||
import { http, errMsg } from '@/api/client'
|
import { http, errMsg } from '@/api/client'
|
||||||
import { useToastStore } from '@/stores/toast'
|
import { useToastStore } from '@/stores/toast'
|
||||||
import { fmtMoney, fmtNum, fmtCost, fmtTime } from '@/lib/format'
|
import { fmtMoney, fmtNum, fmtCost, fmtTime } from '@/lib/format'
|
||||||
|
import { copyText } from '@/lib/clipboard'
|
||||||
import Badge from '@/components/ui/Badge.vue'
|
import Badge from '@/components/ui/Badge.vue'
|
||||||
|
import Button from '@/components/ui/Button.vue'
|
||||||
import Skeleton from '@/components/ui/Skeleton.vue'
|
import Skeleton from '@/components/ui/Skeleton.vue'
|
||||||
import TrendChart from '@/components/ui/TrendChart.vue'
|
import TrendChart from '@/components/ui/TrendChart.vue'
|
||||||
import type { UsageLog } from '@/types'
|
import type { UsageLog } from '@/types'
|
||||||
@@ -44,6 +46,24 @@ function daysAgo(n: number): string {
|
|||||||
return d.toISOString().slice(0, 10)
|
return d.toISOString().slice(0, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 快速开始:Base URL 用浏览器当前 host(兼容端点),curl 示例
|
||||||
|
const baseURL = `${window.location.origin}/v1`
|
||||||
|
const curlCmd = `curl ${baseURL}/chat/completions \\
|
||||||
|
-H "Authorization: Bearer sk-xxxx" \\
|
||||||
|
-H "Content-Type: application/json" \\
|
||||||
|
-d '{"model":"gpt-test","stream":true,"messages":[{"role":"user","content":"hi"}]}'`
|
||||||
|
const copied = ref('')
|
||||||
|
|
||||||
|
async function copy(text: string, key: string) {
|
||||||
|
const ok = await copyText(text)
|
||||||
|
if (ok) {
|
||||||
|
copied.value = key
|
||||||
|
setTimeout(() => (copied.value = ''), 2000)
|
||||||
|
} else {
|
||||||
|
toast.err('复制失败,请手动复制')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(load)
|
onMounted(load)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -102,16 +122,18 @@ onMounted(load)
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul v-else class="divide-y divide-edge">
|
<ul v-else class="divide-y divide-edge">
|
||||||
<li v-for="l in logs" :key="l.id" class="flex items-center justify-between py-2">
|
<li v-for="l in logs" :key="l.id" class="py-2">
|
||||||
<div class="min-w-0">
|
<div class="flex items-center justify-between gap-2">
|
||||||
<p class="truncate font-mono text-xs text-ink">{{ l.model }}</p>
|
<p class="min-w-0 truncate font-mono text-xs text-ink">{{ l.model }}</p>
|
||||||
<p class="text-[11px] text-muted">{{ fmtTime(l.created_at) }}</p>
|
<Badge class="shrink-0" :variant="l.status === 'success' ? 'ok' : l.status === 'canceled' ? 'neutral' : 'err'">{{ l.status }}</Badge>
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span class="mono-num text-xs text-muted">{{ l.input_tokens }}/{{ l.output_tokens }}</span>
|
|
||||||
<span class="mono-num w-16 text-right text-xs text-muted">{{ fmtCost(l.cost) }}</span>
|
|
||||||
<Badge :variant="l.status === 'success' ? 'ok' : l.status === 'canceled' ? 'neutral' : 'err'">{{ l.status }}</Badge>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="mt-0.5 flex flex-wrap items-center gap-x-2 text-[11px] text-muted">
|
||||||
|
<span class="mono-num">{{ l.input_tokens }}/{{ l.output_tokens }}</span>
|
||||||
|
<span class="opacity-40">·</span>
|
||||||
|
<span class="mono-num">{{ fmtCost(l.cost) }}</span>
|
||||||
|
<span class="opacity-40">·</span>
|
||||||
|
<span>{{ fmtTime(l.created_at) }}</span>
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="logs.length === 0" class="py-6 text-center text-xs text-muted">
|
<li v-if="logs.length === 0" class="py-6 text-center text-xs text-muted">
|
||||||
还没有请求记录,去
|
还没有请求记录,去
|
||||||
@@ -121,5 +143,44 @@ onMounted(load)
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 快速开始 -->
|
||||||
|
<div class="card p-5">
|
||||||
|
<div class="mb-4">
|
||||||
|
<h2 class="text-sm font-semibold">快速开始</h2>
|
||||||
|
<p class="text-xs text-muted">通过兼容端点接入,OpenAI / Anthropic 协议自动转换</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<p class="mb-1.5 text-[11px] font-medium text-muted">Base URL</p>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<code class="min-w-0 flex-1 overflow-x-auto rounded-md border border-edge bg-surface px-3 py-2 font-mono text-xs whitespace-nowrap text-ink">{{ baseURL }}</code>
|
||||||
|
<Button size="sm" variant="ghost" class="shrink-0" @click="copy(baseURL, 'base')">
|
||||||
|
{{ copied === 'base' ? '已复制' : '复制' }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p class="mb-1.5 text-[11px] font-medium text-muted">兼容</p>
|
||||||
|
<div class="flex flex-wrap gap-1.5">
|
||||||
|
<Badge variant="neutral">messages</Badge>
|
||||||
|
<Badge variant="neutral">chat/completions</Badge>
|
||||||
|
<Badge variant="neutral">responses</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p class="mb-1.5 text-[11px] font-medium text-muted">curl 示例</p>
|
||||||
|
<div class="relative">
|
||||||
|
<pre class="overflow-x-auto rounded-md border border-edge bg-surface px-3 py-2.5 pr-20 font-mono text-[11px] leading-relaxed text-ink">{{ curlCmd }}</pre>
|
||||||
|
<Button size="sm" variant="ghost" class="absolute top-2 right-2" @click="copy(curlCmd, 'curl')">
|
||||||
|
{{ copied === 'curl' ? '已复制' : '复制' }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user