feat: 更新附录文档及对应交互组件
This commit is contained in:
@@ -80,10 +80,8 @@
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>
|
||||
<span
|
||||
>无论哪种 API,结构都一样:地址(找谁)+ 参数(要什么)=
|
||||
响应(得到什么)。</span
|
||||
>
|
||||
<span>无论哪种 API,结构都一样:地址(找谁)+ 参数(要什么)=
|
||||
响应(得到什么)。</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
<div class="doc-title">Headers</div>
|
||||
<pre class="doc-pre">
|
||||
Authorization: Bearer sk-xxx
|
||||
Content-Type: application/json</pre
|
||||
>
|
||||
Content-Type: application/json</pre>
|
||||
</div>
|
||||
|
||||
<div class="doc-section">
|
||||
@@ -69,10 +68,8 @@ response = client.chat.completions.create(
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>
|
||||
<span
|
||||
>看文档找三样:地址(Base
|
||||
URL)、鉴权(Authorization)、参数(Parameters)。</span
|
||||
>
|
||||
<span>看文档找三样:地址(Base
|
||||
URL)、鉴权(Authorization)、参数(Parameters)。</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -49,10 +49,8 @@
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>
|
||||
<span
|
||||
>HTTP 方法就是动词——GET 是"问",POST 是"做",PUT/PATCH 是"改",DELETE
|
||||
是"删"。</span
|
||||
>
|
||||
<span>HTTP 方法就是动词——GET 是"问",POST 是"做",PUT/PATCH 是"改",DELETE
|
||||
是"删"。</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
type="text"
|
||||
placeholder="/users"
|
||||
class="input"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<div class="input-row">
|
||||
<label>方法</label>
|
||||
@@ -41,42 +41,24 @@
|
||||
type="password"
|
||||
placeholder="sk-..."
|
||||
class="input"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="send-btn"
|
||||
:disabled="loading"
|
||||
@click="sendRequest"
|
||||
>
|
||||
<button class="send-btn" :disabled="loading" @click="sendRequest">
|
||||
{{ loading ? '发送中...' : '🚀 发送' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="right-panel">
|
||||
<div
|
||||
v-if="!response"
|
||||
class="empty"
|
||||
>
|
||||
点击发送查看结果
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="response"
|
||||
>
|
||||
<div
|
||||
class="status-bar"
|
||||
:class="getStatusClass(response.status)"
|
||||
>
|
||||
<div v-if="!response" class="empty">点击发送查看结果</div>
|
||||
<div v-else class="response">
|
||||
<div class="status-bar" :class="getStatusClass(response.status)">
|
||||
<span class="code">{{ response.status }}</span>
|
||||
<span class="text">{{ response.statusText }}</span>
|
||||
</div>
|
||||
<div class="body">
|
||||
<pre>{{ JSON.stringify(response.data, null, 2) }}</pre>
|
||||
</div>
|
||||
<div
|
||||
v-if="response.explanation"
|
||||
class="explanation"
|
||||
>
|
||||
<div v-if="response.explanation" class="explanation">
|
||||
💡 {{ response.explanation }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -85,18 +67,10 @@
|
||||
|
||||
<div class="quick-actions">
|
||||
<span class="label">快速尝试:</span>
|
||||
<button @click="tryEndpoint('/users')">
|
||||
✅ GET /users
|
||||
</button>
|
||||
<button @click="tryError401">
|
||||
❌ 401
|
||||
</button>
|
||||
<button @click="tryError404">
|
||||
❌ 404
|
||||
</button>
|
||||
<button @click="tryError429">
|
||||
❌ 429
|
||||
</button>
|
||||
<button @click="tryEndpoint('/users')">✅ GET /users</button>
|
||||
<button @click="tryError401">❌ 401</button>
|
||||
<button @click="tryError404">❌ 404</button>
|
||||
<button @click="tryError429">❌ 429</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -205,9 +179,18 @@ function tryError429() {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.icon { font-size: 18px; }
|
||||
.title { font-weight: 600; font-size: 0.9rem; }
|
||||
.subtitle { font-size: 0.75rem; color: var(--vp-c-text-3); margin-left: auto; }
|
||||
.icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.demo-layout {
|
||||
display: flex;
|
||||
@@ -230,8 +213,14 @@ function tryError429() {
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.demo-layout { flex-direction: column; }
|
||||
.left-panel { width: 100%; border-right: none; border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.demo-layout {
|
||||
flex-direction: column;
|
||||
}
|
||||
.left-panel {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
}
|
||||
|
||||
.input-row {
|
||||
@@ -314,15 +303,31 @@ function tryError429() {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.status-bar.success { background: #dcfce7; }
|
||||
.status-bar.success .code { color: #166534; }
|
||||
.status-bar.client-error { background: #fee2e2; }
|
||||
.status-bar.client-error .code { color: #991b1b; }
|
||||
.status-bar.server-error { background: #fef3c7; }
|
||||
.status-bar.server-error .code { color: #92400e; }
|
||||
.status-bar.success {
|
||||
background: #dcfce7;
|
||||
}
|
||||
.status-bar.success .code {
|
||||
color: #166534;
|
||||
}
|
||||
.status-bar.client-error {
|
||||
background: #fee2e2;
|
||||
}
|
||||
.status-bar.client-error .code {
|
||||
color: #991b1b;
|
||||
}
|
||||
.status-bar.server-error {
|
||||
background: #fef3c7;
|
||||
}
|
||||
.status-bar.server-error .code {
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.code { font-weight: bold; }
|
||||
.text { color: var(--vp-c-text-2); }
|
||||
.code {
|
||||
font-weight: bold;
|
||||
}
|
||||
.text {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.body {
|
||||
flex: 1;
|
||||
|
||||
@@ -89,10 +89,8 @@
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>
|
||||
<span
|
||||
>点击按钮 → 发送请求 → 服务器处理 → 返回数据。这就是 API
|
||||
调用的完整流程。</span
|
||||
>
|
||||
<span>点击按钮 → 发送请求 → 服务器处理 → 返回数据。这就是 API
|
||||
调用的完整流程。</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div class="api-types-demo">
|
||||
<div class="switch-bar">
|
||||
<button
|
||||
v-for="type in types"
|
||||
:key="type.id"
|
||||
:class="{ active: active === type.id }"
|
||||
@click="active = type.id"
|
||||
>
|
||||
{{ type.icon }} {{ type.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="display-area">
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="label">调用对象</span>
|
||||
<span class="value">{{ currentType.target }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">通信方式</span>
|
||||
<span class="value">{{ currentType.comm }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">延迟</span>
|
||||
<span class="value">{{ currentType.latency }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">典型场景</span>
|
||||
<span class="value">{{ currentType.scenarios }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-preview">
|
||||
<div class="code-header">{{ currentType.name }} 示例</div>
|
||||
<pre><code>{{ currentType.example }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const active = ref('function')
|
||||
|
||||
const types = [
|
||||
{
|
||||
id: 'function',
|
||||
icon: '📦',
|
||||
name: '函数 API',
|
||||
target: '本地代码库',
|
||||
comm: '函数调用',
|
||||
latency: '纳秒级',
|
||||
scenarios: '数据处理、文件操作',
|
||||
example: `len("hello") # 返回 5
|
||||
max([1, 5, 3]) # 返回 5
|
||||
open("file.txt").read() # 读取文件`
|
||||
},
|
||||
{
|
||||
id: 'system',
|
||||
icon: '⚙️',
|
||||
name: '操作系统 API',
|
||||
target: '操作系统内核',
|
||||
comm: '系统调用',
|
||||
latency: '微秒级',
|
||||
scenarios: '文件操作、进程管理',
|
||||
example: `with open("file.txt", "r") as f:
|
||||
content = f.read()
|
||||
|
||||
subprocess.run(["ls", "-l"])`
|
||||
},
|
||||
{
|
||||
id: 'web',
|
||||
icon: '🌐',
|
||||
name: 'Web API',
|
||||
target: '远程服务器',
|
||||
comm: 'HTTP 请求',
|
||||
latency: '毫秒级',
|
||||
scenarios: 'AI 调用、数据获取',
|
||||
example: `requests.post(
|
||||
"https://api.deepseek.com/v1/chat/completions",
|
||||
json={"model": "deepseek-chat", "messages": [...]}
|
||||
)`
|
||||
}
|
||||
]
|
||||
|
||||
const currentType = computed(() => {
|
||||
return types.find((t) => t.id === active.value) || types[0]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.api-types-demo {
|
||||
margin: 20px 0;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.switch-bar {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.switch-bar button {
|
||||
flex: 1;
|
||||
padding: 10px 16px;
|
||||
background: var(--vp-c-bg);
|
||||
border: none;
|
||||
border-right: 1px solid var(--vp-c-divider);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.switch-bar button:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.switch-bar button:hover {
|
||||
background: var(--vp-c-bg-mute);
|
||||
}
|
||||
|
||||
.switch-bar button.active {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.display-area {
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.info-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 10px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 10px;
|
||||
color: var(--vp-c-text-3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.code-preview {
|
||||
background: #0a0a0a;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.code-header {
|
||||
padding: 8px 12px;
|
||||
background: #18181b;
|
||||
color: #71717a;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid #27272a;
|
||||
}
|
||||
|
||||
.code-preview pre {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
color: #e4e4e7;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.code-preview code {
|
||||
font-family: 'Menlo', 'Monaco', monospace;
|
||||
}
|
||||
</style>
|
||||
@@ -53,10 +53,8 @@ result = <span class="func">greet</span>(<span class="str">"张三"</span>)</cod
|
||||
|
||||
<div class="info-box">
|
||||
<strong>核心思想:</strong>
|
||||
<span
|
||||
>你不需要知道函数内部怎么实现,只需要知道怎么调用它。这就是 API
|
||||
的本质。</span
|
||||
>
|
||||
<span>你不需要知道函数内部怎么实现,只需要知道怎么调用它。这就是 API
|
||||
的本质。</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<div class="http-methods-demo">
|
||||
<div class="methods-grid">
|
||||
<div
|
||||
v-for="method in methods"
|
||||
:key="method.id"
|
||||
:class="['method-card', method.id, { active: active === method.id }]"
|
||||
@click="active = method.id"
|
||||
>
|
||||
<span class="method-badge">{{ method.id }}</span>
|
||||
<div class="method-info">
|
||||
<div class="method-name">{{ method.name }}</div>
|
||||
<div class="method-use">{{ method.use }}</div>
|
||||
</div>
|
||||
<div class="method-flags">
|
||||
<span :class="['flag', method.idempotent ? 'yes' : 'no']">
|
||||
{{ method.idempotent ? '幂等' : '不幂等' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method-detail">
|
||||
<div class="detail-header">
|
||||
<span :class="['detail-badge', currentMethod.id]">{{
|
||||
currentMethod.id
|
||||
}}</span>
|
||||
<span class="detail-title">{{ currentMethod.name }} - {{ currentMethod.use }}</span>
|
||||
</div>
|
||||
<div class="detail-desc">{{ currentMethod.desc }}</div>
|
||||
<div class="detail-analogy">
|
||||
<span class="analogy-label">餐厅类比:</span>
|
||||
<span class="analogy-text">{{ currentMethod.analogy }}</span>
|
||||
</div>
|
||||
<div class="detail-code">
|
||||
<pre><code>{{ currentMethod.example }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const active = ref('get')
|
||||
|
||||
const methods = [
|
||||
{
|
||||
id: 'get',
|
||||
name: '获取',
|
||||
use: '查询数据',
|
||||
idempotent: true,
|
||||
desc: '从服务器获取资源,不会修改任何数据',
|
||||
analogy: '"服务员,菜单给我看看"',
|
||||
example: `GET /api/users # 获取用户列表
|
||||
GET /api/users/123 # 获取单个用户
|
||||
GET /api/products?cat=phone # 查询手机商品`
|
||||
},
|
||||
{
|
||||
id: 'post',
|
||||
name: '创建',
|
||||
use: '新增数据',
|
||||
idempotent: false,
|
||||
desc: '向服务器提交数据,创建新资源',
|
||||
analogy: '"给我来份宫保鸡丁"',
|
||||
example: `POST /api/users
|
||||
Body: {"name": "张三", "email": "zhang@example.com"}
|
||||
|
||||
POST /api/orders
|
||||
Body: {"items": [{"id": 1, "qty": 2}]}`
|
||||
},
|
||||
{
|
||||
id: 'put',
|
||||
name: '全量更新',
|
||||
use: '替换资源',
|
||||
idempotent: true,
|
||||
desc: '用新数据完整替换旧资源',
|
||||
analogy: '"把宫保鸡丁改成糖醋里脊"',
|
||||
example: `PUT /api/users/123
|
||||
Body: {"name": "李四", "email": "li@example.com", "age": 25}
|
||||
# 注意:必须提供所有字段`
|
||||
},
|
||||
{
|
||||
id: 'patch',
|
||||
name: '部分更新',
|
||||
use: '修改字段',
|
||||
idempotent: false,
|
||||
desc: '只修改资源的部分字段',
|
||||
analogy: '"宫保鸡丁不要放花生"',
|
||||
example: `PATCH /api/users/123
|
||||
Body: {"name": "王五"}
|
||||
# 只修改 name,其他字段保持不变`
|
||||
},
|
||||
{
|
||||
id: 'delete',
|
||||
name: '删除',
|
||||
use: '删除资源',
|
||||
idempotent: true,
|
||||
desc: '从服务器删除资源',
|
||||
analogy: '"算了,那道菜不要了"',
|
||||
example: `DELETE /api/users/123 # 删除指定用户
|
||||
DELETE /api/orders/456 # 取消订单`
|
||||
}
|
||||
]
|
||||
|
||||
const currentMethod = computed(() => {
|
||||
return methods.find((m) => m.id === active.value) || methods[0]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.http-methods-demo {
|
||||
margin: 20px 0;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.methods-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 0;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.methods-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.methods-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.method-card {
|
||||
padding: 12px 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border-right: 1px solid var(--vp-c-divider);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.method-card:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.method-card:hover {
|
||||
background: var(--vp-c-bg-mute);
|
||||
}
|
||||
|
||||
.method-card.active {
|
||||
background: color-mix(in srgb, var(--vp-c-brand) 10%, transparent);
|
||||
}
|
||||
|
||||
.method-badge {
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
min-width: 36px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.method-card.get .method-badge {
|
||||
background: #22c55e;
|
||||
color: white;
|
||||
}
|
||||
.method-card.post .method-badge {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
.method-card.put .method-badge {
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
}
|
||||
.method-card.patch .method-badge {
|
||||
background: #8b5cf6;
|
||||
color: white;
|
||||
}
|
||||
.method-card.delete .method-badge {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.method-info {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.method-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.method-use {
|
||||
font-size: 10px;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
|
||||
.method-flags {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.flag {
|
||||
font-size: 9px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.flag.yes {
|
||||
background: #22c55e22;
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
.flag.no {
|
||||
background: #ef444422;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.method-detail {
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-badge {
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.detail-badge.get {
|
||||
background: #22c55e;
|
||||
color: white;
|
||||
}
|
||||
.detail-badge.post {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
.detail-badge.put {
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
}
|
||||
.detail-badge.patch {
|
||||
background: #8b5cf6;
|
||||
color: white;
|
||||
}
|
||||
.detail-badge.delete {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.detail-desc {
|
||||
font-size: 13px;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.detail-analogy {
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.analogy-label {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.detail-code {
|
||||
background: #0a0a0a;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail-code pre {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
color: #e4e4e7;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.detail-code code {
|
||||
font-family: 'Menlo', 'Monaco', monospace;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="status-categories">
|
||||
<div class="cards-grid">
|
||||
<div
|
||||
v-for="cat in categories"
|
||||
:key="cat.code"
|
||||
:class="['status-card', cat.id]"
|
||||
>
|
||||
<div class="card-header">
|
||||
<span class="card-code">{{ cat.code }}xx</span>
|
||||
<span class="card-name">{{ cat.name }}</span>
|
||||
</div>
|
||||
<div class="card-desc">{{ cat.desc }}</div>
|
||||
<div class="card-examples">
|
||||
<span v-for="ex in cat.examples" :key="ex" class="tag">{{ ex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="memory-tip">
|
||||
<span class="tip-icon">💡</span>
|
||||
<span class="tip-text">
|
||||
<strong>记忆技巧:</strong>
|
||||
<span class="tip-2">2️⃣ 成功</span> •
|
||||
<span class="tip-3">3️⃣ 重定向</span> •
|
||||
<span class="tip-4">4️⃣ 客户端错</span> •
|
||||
<span class="tip-5">5️⃣ 服务器错</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const categories = [
|
||||
{
|
||||
id: 'success',
|
||||
code: '2',
|
||||
name: '成功',
|
||||
desc: '请求被成功接收、理解并处理',
|
||||
examples: ['200 OK', '201 Created', '204 No Content']
|
||||
},
|
||||
{
|
||||
id: 'redirect',
|
||||
code: '3',
|
||||
name: '重定向',
|
||||
desc: '需要进一步操作才能完成请求',
|
||||
examples: ['301 永久移动', '304 未修改', '307 临时重定向']
|
||||
},
|
||||
{
|
||||
id: 'client-error',
|
||||
code: '4',
|
||||
name: '客户端错误',
|
||||
desc: '请求包含错误或无法完成',
|
||||
examples: ['400 参数错误', '401 未认证', '403 无权限', '404 不存在']
|
||||
},
|
||||
{
|
||||
id: 'server-error',
|
||||
code: '5',
|
||||
name: '服务器错误',
|
||||
desc: '服务器无法处理有效请求',
|
||||
examples: ['500 内部错误', '502 网关错误', '503 服务不可用']
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.status-categories {
|
||||
margin: 20px 0;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cards-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.cards-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.status-card {
|
||||
padding: 14px 12px;
|
||||
background: var(--vp-c-bg);
|
||||
border-right: 1px solid var(--vp-c-divider);
|
||||
transition:
|
||||
transform 0.2s,
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.status-card:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.status-card.success {
|
||||
border-top: 3px solid #22c55e;
|
||||
}
|
||||
|
||||
.status-card.redirect {
|
||||
border-top: 3px solid #f59e0b;
|
||||
}
|
||||
|
||||
.status-card.client-error {
|
||||
border-top: 3px solid #ef4444;
|
||||
}
|
||||
|
||||
.status-card.server-error {
|
||||
border-top: 3px solid #8b5cf6;
|
||||
}
|
||||
|
||||
.status-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.card-code {
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
min-width: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.success .card-code {
|
||||
background: #22c55e22;
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
.redirect .card-code {
|
||||
background: #f59e0b22;
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.client-error .card-code {
|
||||
background: #ef444422;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.server-error .card-code {
|
||||
background: #8b5cf622;
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 11px;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.card-examples {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 2px 6px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 3px;
|
||||
font-size: 9px;
|
||||
font-family: 'Menlo', 'Monaco', monospace;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
|
||||
.memory-tip {
|
||||
padding: 12px 16px;
|
||||
background: var(--vp-c-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
font-size: 12px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.tip-text strong {
|
||||
color: var(--vp-c-text-1);
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.tip-2 {
|
||||
color: #22c55e;
|
||||
}
|
||||
.tip-3 {
|
||||
color: #f59e0b;
|
||||
}
|
||||
.tip-4 {
|
||||
color: #ef4444;
|
||||
}
|
||||
.tip-5 {
|
||||
color: #8b5cf6;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user