2026-02-06 03:34:50 +08:00
|
|
|
|
<template>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
<div class="sc-root">
|
|
|
|
|
|
<div class="sc-terminal">
|
|
|
|
|
|
<div class="term-bar">
|
|
|
|
|
|
<span class="dot r" /><span class="dot y" /><span class="dot g" />
|
|
|
|
|
|
<span class="term-title">HTTP 状态码演示</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div ref="termEl" class="term-body">
|
|
|
|
|
|
<div v-for="(l, i) in lines" :key="i" class="t-line">
|
|
|
|
|
|
<span v-if="l.kind === 'cmd'" class="t-ps">> </span>
|
|
|
|
|
|
<span :class="'t-' + l.kind">{{ l.text }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="t-line">
|
|
|
|
|
|
<span class="t-ps">> </span>
|
|
|
|
|
|
<span class="t-typing">{{ typing }}<span class="t-cur">▋</span></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="sc-btns">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-for="op in ops"
|
|
|
|
|
|
:key="op.id"
|
|
|
|
|
|
:disabled="running || !op.ok()"
|
2026-02-23 01:50:43 +08:00
|
|
|
|
:class="[
|
|
|
|
|
|
'sc-btn',
|
|
|
|
|
|
{ 'sc-btn--on': active === op.id, 'sc-btn--dim': !op.ok() }
|
|
|
|
|
|
]"
|
2026-02-22 23:20:27 +08:00
|
|
|
|
@click="run(op)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<code>{{ op.cmd }}</code>
|
|
|
|
|
|
</button>
|
2026-02-23 01:50:43 +08:00
|
|
|
|
<button class="sc-btn sc-btn--reset" :disabled="running" @click="reset">
|
|
|
|
|
|
重置
|
|
|
|
|
|
</button>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
<div class="sc-codes">
|
|
|
|
|
|
<div class="code-section">
|
|
|
|
|
|
<div class="section-header success">
|
|
|
|
|
|
<span class="section-icon">✅</span>
|
|
|
|
|
|
<span class="section-title">2xx 成功</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="section-body">
|
2026-02-23 01:50:43 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-for="c in successCodes"
|
|
|
|
|
|
:key="c.code"
|
|
|
|
|
|
class="code-item"
|
|
|
|
|
|
:class="{ active: activeCode === c.code }"
|
|
|
|
|
|
>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
<span class="code-num">{{ c.code }}</span>
|
|
|
|
|
|
<span class="code-name">{{ c.name }}</span>
|
|
|
|
|
|
<span class="code-desc">{{ c.desc }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
<div class="code-section">
|
|
|
|
|
|
<div class="section-header client">
|
|
|
|
|
|
<span class="section-icon">⚠️</span>
|
|
|
|
|
|
<span class="section-title">4xx 客户端错误</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="section-body">
|
2026-02-23 01:50:43 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-for="c in clientCodes"
|
|
|
|
|
|
:key="c.code"
|
|
|
|
|
|
class="code-item"
|
|
|
|
|
|
:class="{ active: activeCode === c.code }"
|
|
|
|
|
|
>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
<span class="code-num">{{ c.code }}</span>
|
|
|
|
|
|
<span class="code-name">{{ c.name }}</span>
|
|
|
|
|
|
<span class="code-desc">{{ c.desc }}</span>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
</div>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
<div class="code-section">
|
|
|
|
|
|
<div class="section-header server">
|
|
|
|
|
|
<span class="section-icon">🔴</span>
|
|
|
|
|
|
<span class="section-title">5xx 服务端错误</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="section-body">
|
2026-02-23 01:50:43 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-for="c in serverCodes"
|
|
|
|
|
|
:key="c.code"
|
|
|
|
|
|
class="code-item"
|
|
|
|
|
|
:class="{ active: activeCode === c.code }"
|
|
|
|
|
|
>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
<span class="code-num">{{ c.code }}</span>
|
|
|
|
|
|
<span class="code-name">{{ c.name }}</span>
|
|
|
|
|
|
<span class="code-desc">{{ c.desc }}</span>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
|
|
|
|
|
|
<div v-if="hint" class="sc-hint">💡 {{ hint }}</div>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
import { ref, nextTick } from 'vue'
|
|
|
|
|
|
|
|
|
|
|
|
const termEl = ref(null)
|
|
|
|
|
|
const lines = ref([{ kind: 'dim', text: '// 点击按钮查看不同状态码的含义' }])
|
|
|
|
|
|
const typing = ref('')
|
|
|
|
|
|
const running = ref(false)
|
|
|
|
|
|
const active = ref(null)
|
|
|
|
|
|
const activeCode = ref(null)
|
|
|
|
|
|
const hint = ref('点击命令按钮,了解常见的 HTTP 状态码。')
|
|
|
|
|
|
|
|
|
|
|
|
const successCodes = ref([
|
|
|
|
|
|
{ code: 200, name: 'OK', desc: '请求成功' },
|
|
|
|
|
|
{ code: 201, name: 'Created', desc: '创建成功' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ code: 204, name: 'No Content', desc: '成功但无返回内容' }
|
2026-02-22 23:20:27 +08:00
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
const clientCodes = ref([
|
|
|
|
|
|
{ code: 400, name: 'Bad Request', desc: '请求格式错误' },
|
|
|
|
|
|
{ code: 401, name: 'Unauthorized', desc: '未认证' },
|
|
|
|
|
|
{ code: 403, name: 'Forbidden', desc: '无权限' },
|
|
|
|
|
|
{ code: 404, name: 'Not Found', desc: '资源不存在' },
|
|
|
|
|
|
{ code: 422, name: 'Unprocessable', desc: '语义错误' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ code: 429, name: 'Too Many', desc: '请求过多' }
|
2026-02-22 23:20:27 +08:00
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
const serverCodes = ref([
|
|
|
|
|
|
{ code: 500, name: 'Server Error', desc: '服务器内部错误' },
|
|
|
|
|
|
{ code: 502, name: 'Bad Gateway', desc: '网关错误' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ code: 503, name: 'Unavailable', desc: '服务不可用' }
|
2026-02-22 23:20:27 +08:00
|
|
|
|
])
|
|
|
|
|
|
|
2026-02-23 01:50:43 +08:00
|
|
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
2026-02-22 23:20:27 +08:00
|
|
|
|
|
|
|
|
|
|
const ops = [
|
2026-02-06 03:34:50 +08:00
|
|
|
|
{
|
2026-02-22 23:20:27 +08:00
|
|
|
|
id: '200',
|
|
|
|
|
|
cmd: '200 OK',
|
|
|
|
|
|
ok: () => true,
|
|
|
|
|
|
output: [
|
|
|
|
|
|
{ kind: 'dim', text: '// 最常用的成功状态码' },
|
|
|
|
|
|
{ kind: 'grn', text: 'HTTP/1.1 200 OK' },
|
|
|
|
|
|
{ kind: 'dim', text: 'Content-Type: application/json' },
|
|
|
|
|
|
{ kind: 'dim', text: '' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ kind: 'grn', text: '{ "code": 0, "data": { ... } }' }
|
2026-02-06 03:34:50 +08:00
|
|
|
|
],
|
2026-02-22 23:20:27 +08:00
|
|
|
|
hint: '200 表示请求成功处理。GET 查询、PUT/PATCH 更新成功时常用。',
|
2026-02-23 01:50:43 +08:00
|
|
|
|
do: () => {
|
|
|
|
|
|
activeCode.value = 200
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-02-22 23:20:27 +08:00
|
|
|
|
id: '201',
|
|
|
|
|
|
cmd: '201 Created',
|
|
|
|
|
|
ok: () => true,
|
|
|
|
|
|
output: [
|
|
|
|
|
|
{ kind: 'dim', text: '// 创建资源成功' },
|
|
|
|
|
|
{ kind: 'grn', text: 'HTTP/1.1 201 Created' },
|
|
|
|
|
|
{ kind: 'dim', text: 'Location: /api/users/123' },
|
|
|
|
|
|
{ kind: 'dim', text: '' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ kind: 'grn', text: '{ "code": 0, "data": { "id": 123 } }' }
|
2026-02-06 03:34:50 +08:00
|
|
|
|
],
|
2026-02-22 23:20:27 +08:00
|
|
|
|
hint: '201 表示资源创建成功。响应头 Location 指向新资源的地址。',
|
2026-02-23 01:50:43 +08:00
|
|
|
|
do: () => {
|
|
|
|
|
|
activeCode.value = 201
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-02-22 23:20:27 +08:00
|
|
|
|
id: '400',
|
|
|
|
|
|
cmd: '400 Bad Request',
|
|
|
|
|
|
ok: () => true,
|
|
|
|
|
|
output: [
|
|
|
|
|
|
{ kind: 'dim', text: '// 客户端请求有问题' },
|
|
|
|
|
|
{ kind: 'red', text: 'HTTP/1.1 400 Bad Request' },
|
|
|
|
|
|
{ kind: 'dim', text: '' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ kind: 'red', text: '{ "code": 10001, "message": "参数格式错误" }' }
|
2026-02-06 03:34:50 +08:00
|
|
|
|
],
|
2026-02-22 23:20:27 +08:00
|
|
|
|
hint: '400 表示请求语法错误。比如 JSON 格式不对、缺少必填参数。',
|
2026-02-23 01:50:43 +08:00
|
|
|
|
do: () => {
|
|
|
|
|
|
activeCode.value = 400
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-02-22 23:20:27 +08:00
|
|
|
|
id: '401',
|
|
|
|
|
|
cmd: '401 Unauthorized',
|
|
|
|
|
|
ok: () => true,
|
|
|
|
|
|
output: [
|
|
|
|
|
|
{ kind: 'dim', text: '// 需要登录认证' },
|
|
|
|
|
|
{ kind: 'red', text: 'HTTP/1.1 401 Unauthorized' },
|
|
|
|
|
|
{ kind: 'dim', text: 'WWW-Authenticate: Bearer' },
|
|
|
|
|
|
{ kind: 'dim', text: '' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ kind: 'red', text: '{ "code": 10018, "message": "请先登录" }' }
|
2026-02-06 03:34:50 +08:00
|
|
|
|
],
|
2026-02-22 23:20:27 +08:00
|
|
|
|
hint: '401 表示未认证。Token 过期、未登录时返回,客户端应引导用户登录。',
|
2026-02-23 01:50:43 +08:00
|
|
|
|
do: () => {
|
|
|
|
|
|
activeCode.value = 401
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-02-22 23:20:27 +08:00
|
|
|
|
id: '403',
|
|
|
|
|
|
cmd: '403 Forbidden',
|
|
|
|
|
|
ok: () => true,
|
|
|
|
|
|
output: [
|
|
|
|
|
|
{ kind: 'dim', text: '// 已登录但无权限' },
|
|
|
|
|
|
{ kind: 'red', text: 'HTTP/1.1 403 Forbidden' },
|
|
|
|
|
|
{ kind: 'dim', text: '' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ kind: 'red', text: '{ "code": 10021, "message": "需要管理员权限" }' }
|
2026-02-06 03:34:50 +08:00
|
|
|
|
],
|
2026-02-22 23:20:27 +08:00
|
|
|
|
hint: '403 表示已认证但无权限。普通用户访问管理员接口时返回。',
|
2026-02-23 01:50:43 +08:00
|
|
|
|
do: () => {
|
|
|
|
|
|
activeCode.value = 403
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-02-22 23:20:27 +08:00
|
|
|
|
id: '404',
|
|
|
|
|
|
cmd: '404 Not Found',
|
|
|
|
|
|
ok: () => true,
|
|
|
|
|
|
output: [
|
|
|
|
|
|
{ kind: 'dim', text: '// 资源不存在' },
|
|
|
|
|
|
{ kind: 'red', text: 'HTTP/1.1 404 Not Found' },
|
|
|
|
|
|
{ kind: 'dim', text: '' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{ kind: 'red', text: '{ "code": 10002, "message": "用户不存在" }' }
|
2026-02-06 03:34:50 +08:00
|
|
|
|
],
|
2026-02-22 23:20:27 +08:00
|
|
|
|
hint: '404 表示请求的资源不存在。URL 错误或资源已被删除。',
|
2026-02-23 01:50:43 +08:00
|
|
|
|
do: () => {
|
|
|
|
|
|
activeCode.value = 404
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2026-02-22 23:20:27 +08:00
|
|
|
|
id: '500',
|
|
|
|
|
|
cmd: '500 Server Error',
|
|
|
|
|
|
ok: () => true,
|
|
|
|
|
|
output: [
|
|
|
|
|
|
{ kind: 'dim', text: '// 服务器内部错误' },
|
|
|
|
|
|
{ kind: 'red', text: 'HTTP/1.1 500 Internal Server Error' },
|
|
|
|
|
|
{ kind: 'dim', text: '' },
|
2026-02-23 01:50:43 +08:00
|
|
|
|
{
|
|
|
|
|
|
kind: 'red',
|
|
|
|
|
|
text: '{ "code": 10000, "message": "服务器错误,请联系管理员" }'
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
],
|
2026-02-22 23:20:27 +08:00
|
|
|
|
hint: '500 表示服务器内部错误。代码 bug、数据库连接失败等,不要暴露堆栈信息!',
|
2026-02-23 01:50:43 +08:00
|
|
|
|
do: () => {
|
|
|
|
|
|
activeCode.value = 500
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
async function run(op) {
|
|
|
|
|
|
if (running.value) return
|
|
|
|
|
|
running.value = true
|
|
|
|
|
|
active.value = op.id
|
|
|
|
|
|
activeCode.value = null
|
|
|
|
|
|
hint.value = ''
|
|
|
|
|
|
typing.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
for (const ch of op.cmd) {
|
|
|
|
|
|
typing.value += ch
|
|
|
|
|
|
await sleep(18)
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
await sleep(80)
|
|
|
|
|
|
lines.value.push({ kind: 'cmd', text: op.cmd })
|
|
|
|
|
|
typing.value = ''
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
scroll()
|
|
|
|
|
|
await sleep(150)
|
|
|
|
|
|
|
|
|
|
|
|
for (const l of op.output) {
|
|
|
|
|
|
lines.value.push(l)
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
scroll()
|
|
|
|
|
|
await sleep(50)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
op.do()
|
|
|
|
|
|
await sleep(120)
|
|
|
|
|
|
hint.value = op.hint
|
|
|
|
|
|
running.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function scroll() {
|
|
|
|
|
|
if (termEl.value) termEl.value.scrollTop = termEl.value.scrollHeight
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
function reset() {
|
|
|
|
|
|
lines.value = [{ kind: 'dim', text: '// 点击按钮查看不同状态码的含义' }]
|
|
|
|
|
|
active.value = null
|
|
|
|
|
|
activeCode.value = null
|
|
|
|
|
|
hint.value = '点击命令按钮,了解常见的 HTTP 状态码。'
|
|
|
|
|
|
typing.value = ''
|
|
|
|
|
|
running.value = false
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.sc-root {
|
2026-02-06 03:34:50 +08:00
|
|
|
|
border: 1px solid var(--vp-c-divider);
|
2026-02-22 23:20:27 +08:00
|
|
|
|
border-radius: 10px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
overflow: hidden;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
background: var(--vp-c-bg-soft);
|
|
|
|
|
|
margin: 1rem 0;
|
|
|
|
|
|
font-size: 0.85rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.sc-terminal {
|
|
|
|
|
|
background: #141420;
|
|
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.term-bar {
|
2026-02-06 03:34:50 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
gap: 5px;
|
|
|
|
|
|
padding: 7px 12px;
|
|
|
|
|
|
background: #1e1e2e;
|
|
|
|
|
|
}
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.dot {
|
|
|
|
|
|
width: 11px;
|
|
|
|
|
|
height: 11px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dot.r {
|
|
|
|
|
|
background: #ff5f57;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dot.y {
|
|
|
|
|
|
background: #febc2e;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dot.g {
|
|
|
|
|
|
background: #28c840;
|
|
|
|
|
|
}
|
|
|
|
|
|
.term-title {
|
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
font-family: monospace;
|
|
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
|
|
|
|
|
|
.term-body {
|
|
|
|
|
|
min-height: 90px;
|
|
|
|
|
|
max-height: 140px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
|
padding: 0.7rem 1rem;
|
|
|
|
|
|
font-family: 'Menlo', 'Monaco', monospace;
|
|
|
|
|
|
font-size: 0.76rem;
|
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
color: #cdd6f4;
|
|
|
|
|
|
}
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.t-line {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
min-width: min-content;
|
|
|
|
|
|
}
|
|
|
|
|
|
.t-ps {
|
|
|
|
|
|
color: #89b4fa;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.t-cmd {
|
|
|
|
|
|
color: #cdd6f4;
|
|
|
|
|
|
}
|
|
|
|
|
|
.t-dim {
|
|
|
|
|
|
color: #585b70;
|
|
|
|
|
|
}
|
|
|
|
|
|
.t-grn {
|
|
|
|
|
|
color: #a6e3a1;
|
|
|
|
|
|
}
|
|
|
|
|
|
.t-red {
|
|
|
|
|
|
color: #f38ba8;
|
|
|
|
|
|
}
|
|
|
|
|
|
.t-typing {
|
|
|
|
|
|
color: #cdd6f4;
|
|
|
|
|
|
}
|
|
|
|
|
|
.t-cur {
|
|
|
|
|
|
animation: blink 1s step-end infinite;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes blink {
|
|
|
|
|
|
0%,
|
|
|
|
|
|
100% {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
50% {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
|
|
|
|
|
|
.sc-btns {
|
2026-02-06 03:34:50 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
gap: 6px;
|
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
|
background: #0d0d1a;
|
|
|
|
|
|
border-top: 1px solid #2a2a3e;
|
|
|
|
|
|
}
|
|
|
|
|
|
.sc-btn {
|
|
|
|
|
|
background: #1e1e2e;
|
|
|
|
|
|
border: 1px solid #313244;
|
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
|
padding: 4px 9px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
cursor: pointer;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
transition: border-color 0.2s;
|
|
|
|
|
|
}
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.sc-btn code {
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
color: #7f849c;
|
|
|
|
|
|
font-family: monospace;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.sc-btn:hover:not(:disabled) {
|
|
|
|
|
|
border-color: var(--vp-c-brand);
|
|
|
|
|
|
}
|
|
|
|
|
|
.sc-btn--on {
|
|
|
|
|
|
border-color: var(--vp-c-brand) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.sc-btn--on code {
|
|
|
|
|
|
color: var(--vp-c-brand);
|
|
|
|
|
|
}
|
|
|
|
|
|
.sc-btn--dim {
|
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.sc-btn--reset {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border-color: #313244;
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
}
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.sc-btn--reset code {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.sc-btn--reset::after {
|
|
|
|
|
|
content: '重置';
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
color: #585b70;
|
|
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
|
|
|
|
|
|
.sc-codes {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
|
gap: 0;
|
|
|
|
|
|
border-top: 1px solid var(--vp-c-divider);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-section {
|
|
|
|
|
|
border-right: 1px solid var(--vp-c-divider);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-section:last-child {
|
|
|
|
|
|
border-right: none;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.section-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
padding: 8px 10px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: 700;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
font-size: 0.8rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.section-header.success {
|
|
|
|
|
|
background: color-mix(in srgb, #22c55e 8%, var(--vp-c-bg-alt));
|
|
|
|
|
|
color: #22c55e;
|
|
|
|
|
|
}
|
|
|
|
|
|
.section-header.client {
|
|
|
|
|
|
background: color-mix(in srgb, #f59e0b 8%, var(--vp-c-bg-alt));
|
|
|
|
|
|
color: #d97706;
|
|
|
|
|
|
}
|
|
|
|
|
|
.section-header.server {
|
|
|
|
|
|
background: color-mix(in srgb, #ef4444 8%, var(--vp-c-bg-alt));
|
|
|
|
|
|
color: #ef4444;
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.section-icon {
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.section-body {
|
|
|
|
|
|
padding: 8px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
gap: 4px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-item {
|
2026-02-06 03:34:50 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
gap: 6px;
|
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
background: var(--vp-c-bg);
|
|
|
|
|
|
border: 1px solid transparent;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.code-item.active {
|
|
|
|
|
|
border-color: var(--vp-c-brand);
|
|
|
|
|
|
background: color-mix(in srgb, var(--vp-c-brand) 8%, var(--vp-c-bg));
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-num {
|
|
|
|
|
|
font-family: monospace;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: 700;
|
2026-02-22 23:20:27 +08:00
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
min-width: 28px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
2026-02-23 01:50:43 +08:00
|
|
|
|
.code-item.active .code-num {
|
|
|
|
|
|
color: var(--vp-c-brand);
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-name {
|
|
|
|
|
|
font-size: 0.72rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
color: var(--vp-c-text-1);
|
2026-02-22 23:20:27 +08:00
|
|
|
|
font-weight: 600;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-desc {
|
|
|
|
|
|
font-size: 0.68rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
color: var(--vp-c-text-3);
|
2026-02-22 23:20:27 +08:00
|
|
|
|
margin-left: auto;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.sc-hint {
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
background: var(--vp-c-bg-alt);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
border-top: 1px solid var(--vp-c-divider);
|
2026-02-22 23:20:27 +08:00
|
|
|
|
font-size: 0.82rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
color: var(--vp-c-text-2);
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-22 23:20:27 +08:00
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
|
.sc-codes {
|
|
|
|
|
|
grid-template-columns: 1fr;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-section {
|
|
|
|
|
|
border-right: none;
|
|
|
|
|
|
border-bottom: 1px solid var(--vp-c-divider);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
2026-02-22 23:20:27 +08:00
|
|
|
|
.code-section:last-child {
|
|
|
|
|
|
border-bottom: none;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|