docs(api-intro): rewrite API introduction with interactive examples and clearer explanations

- Restructure content with more engaging metaphors and practical examples
- Add simplified interactive components to demonstrate key concepts
- Improve readability with better organization and visual aids
- Update terminology to be more beginner-friendly
- Include real-world API usage scenarios
This commit is contained in:
sanbuphy
2026-01-20 08:51:04 +08:00
parent 6806f05deb
commit 389c9126a1
9 changed files with 2008 additions and 2820 deletions
@@ -1,396 +1,194 @@
<!--
ApiDocumentDemo.vue
参考 ide-intro 虚拟界面 + 点击探索风格
目标让新手学会看 API 文档的 3 个重点入口在哪 / 要填什么 / 会得到什么
ApiDocumentDemo.vue - 简化版
目标用简单的示例展示如何阅读 API 文档
-->
<template>
<div class="wrap">
<div class="head">
<div class="title">怎么读 API 文档像找按钮一样找</div>
<div class="sub">
任务请在下面的假文档依次点出<b>入口</b><b>要填什么</b><b>会得到什么</b>
<div class="demo">
<div class="title">📖 怎么读 API 文档</div>
<p class="subtitle">找到这 3 个信息就够了</p>
<div class="doc-example">
<div class="doc-header">API 文档示例</div>
<div class="doc-body">
<div class="section">
<div class="section-title">📍 1 入口在哪</div>
<div class="section-content">
<code>GET /api/users/{id}</code>
<p class="hint">这就是你要调用的"按钮"</p>
</div>
</div>
<div class="section">
<div class="section-title">📝 2 要填什么</div>
<div class="section-content">
<div class="param">
<span class="param-name">id</span>
<span class="param-desc">用户编号必填</span>
</div>
<p class="hint">你需要提供这个参数</p>
</div>
</div>
<div class="section">
<div class="section-title"> 3 会得到什么</div>
<div class="section-content">
<pre><code>{
"id": "123",
"name": "张三",
"email": "zhang@example.com"
}</code></pre>
<p class="hint">成功时返回的数据格式</p>
</div>
</div>
</div>
</div>
<div class="game">
<div class="doc">
<div class="docBar">
<span class="dot red" />
<span class="dot yellow" />
<span class="dot green" />
<span class="docTitle">API 文档示例</span>
</div>
<div class="docBody">
<button
class="block"
:class="{ hit: hits.entry }"
@click="hit('entry')"
>
<div class="blockK">入口</div>
<div class="blockV">GET /v1/users/{id}</div>
<div class="blockHint">你要按哪个按钮</div>
</button>
<button
class="block"
:class="{ hit: hits.input }"
@click="hit('input')"
>
<div class="blockK">要填什么</div>
<div class="blockV">id用户编号</div>
<div class="blockHint">你要告诉它什么</div>
</button>
<button
class="block"
:class="{ hit: hits.output }"
@click="hit('output')"
>
<div class="blockK">会得到什么</div>
<div class="blockV">{ id, name }</div>
<div class="blockHint">成功时给你的结果</div>
</button>
<button
class="block gray"
:class="{ hit: hits.fail }"
@click="hit('fail')"
>
<div class="blockK">失败会怎样常见</div>
<div class="blockV">没钥匙 / 找不到 / 太频繁</div>
<div class="blockHint">你要能看懂失败原因</div>
</button>
</div>
</div>
<div class="side">
<div class="task">
<div class="taskTitle">你要找的 3 个重点</div>
<div class="taskList">
<div :class="['taskItem', hits.entry && 'done']">
入口在哪入口
</div>
<div :class="['taskItem', hits.input && 'done']">
要填什么要填什么
</div>
<div :class="['taskItem', hits.output && 'done']">
会得到什么会得到什么
</div>
</div>
<div class="muted">你只要先会这三件事就能开始用 API </div>
</div>
<div class="explain" v-if="last">
<div class="explainTitle">你刚刚点的是{{ labelOf(last) }}</div>
<div class="explainText">{{ explainOf(last) }}</div>
</div>
<div class="actions">
<button class="btn" @click="autoWin">一键帮我找对</button>
<button class="ghost" @click="reset">重置</button>
</div>
<div class="win" v-if="won">
<div class="winTitle">完成</div>
<div class="winText">
你已经会读 80% API 文档了入口 / 要填 / 会得到
</div>
</div>
</div>
<div class="tips">
<p><strong>💡 小贴士</strong></p>
<ul>
<li>先确认这个 API 是不是你需要的</li>
<li>再看要填什么参数必填 vs 可选</li>
<li>最后看返回什么失败会怎样</li>
</ul>
</div>
</div>
</template>
<script setup>
import { computed, reactive, ref } from 'vue'
const hits = reactive({
entry: false,
input: false,
output: false,
fail: false
})
const last = ref('')
const won = computed(() => hits.entry && hits.input && hits.output)
function hit(key) {
hits[key] = true
last.value = key
}
function reset() {
hits.entry = false
hits.input = false
hits.output = false
hits.fail = false
last.value = ''
}
function autoWin() {
hits.entry = true
hits.input = true
hits.output = true
last.value = 'output'
}
function labelOf(key) {
if (key === 'entry') return '入口'
if (key === 'input') return '要填什么'
if (key === 'output') return '会得到什么'
if (key === 'fail') return '失败会怎样'
return key
}
function explainOf(key) {
if (key === 'entry') {
return '入口就是“按钮名字”。你要按哪个按钮,先找到它。'
}
if (key === 'input') {
return '要填什么 = 你需要提供的信息。比如 id、页码、搜索词。'
}
if (key === 'output') {
return '会得到什么 = 成功时返回的数据。你要关心字段有什么、有没有可能为空。'
}
if (key === 'fail') {
return '失败会怎样 = 你要能看懂失败原因,好给用户提示/重试。'
}
return ''
}
// 无需脚本逻辑
</script>
<style scoped>
.wrap {
.demo {
border: 1px solid var(--vp-c-divider);
border-radius: 14px;
border-radius: 12px;
padding: 20px;
background: var(--vp-c-bg-soft);
padding: 16px;
}
.head {
display: flex;
flex-direction: column;
gap: 6px;
margin: 16px 0;
}
.title {
font-weight: 900;
font-size: 16px;
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
color: var(--vp-c-text-1);
}
.sub {
font-size: 13px;
.subtitle {
color: var(--vp-c-text-2);
line-height: 1.6;
margin-bottom: 16px;
}
.game {
margin-top: 12px;
display: grid;
grid-template-columns: 1.2fr 0.8fr;
gap: 12px;
}
.doc {
border: 1px solid var(--vp-c-divider);
border-radius: 14px;
.doc-example {
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 12px;
overflow: hidden;
margin-bottom: 16px;
}
.docBar {
display: flex;
gap: 6px;
align-items: center;
padding: 10px 12px;
border-bottom: 1px solid var(--vp-c-divider);
.doc-header {
background: var(--vp-c-bg-soft);
}
.dot {
width: 10px;
height: 10px;
border-radius: 999px;
}
.dot.red {
background: #ef4444;
}
.dot.yellow {
background: #f59e0b;
}
.dot.green {
background: #22c55e;
}
.docTitle {
margin-left: 6px;
font-size: 12px;
font-weight: 900;
color: var(--vp-c-text-2);
}
.docBody {
padding: 12px;
display: grid;
gap: 10px;
}
.block {
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
background: var(--vp-c-bg-soft);
padding: 10px 12px;
text-align: left;
cursor: pointer;
color: var(--vp-c-text-1);
}
.block:hover {
border-color: var(--vp-c-brand-1);
}
.block.gray {
background: var(--vp-c-bg);
}
.block.hit {
border-color: #22c55e;
box-shadow: 0 0 0 3px color-mix(in srgb, #22c55e 18%, transparent);
}
.blockK {
font-size: 12px;
color: var(--vp-c-text-2);
font-weight: 900;
}
.blockV {
margin-top: 6px;
padding: 12px 16px;
font-weight: bold;
font-size: 14px;
font-weight: 900;
border-bottom: 1px solid var(--vp-c-divider);
}
.blockHint {
margin-top: 6px;
font-size: 12px;
color: var(--vp-c-text-2);
.doc-body {
padding: 16px;
}
.side {
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
background: var(--vp-c-bg);
.section {
margin-bottom: 16px;
padding: 12px;
display: grid;
gap: 12px;
}
.taskTitle {
font-weight: 900;
font-size: 13px;
color: var(--vp-c-text-1);
}
.taskList {
margin-top: 10px;
display: grid;
gap: 8px;
}
.taskItem {
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
background: var(--vp-c-bg-soft);
padding: 8px 10px;
font-size: 12px;
border-radius: 8px;
}
.section:last-child {
margin-bottom: 0;
}
.section-title {
font-weight: bold;
font-size: 14px;
margin-bottom: 8px;
color: var(--vp-c-text-1);
font-weight: 800;
}
.taskItem.done {
border-color: #22c55e;
background: color-mix(in srgb, #22c55e 12%, var(--vp-c-bg));
.section-content {
margin-left: 0;
}
.muted {
margin-top: 10px;
font-size: 12px;
color: var(--vp-c-text-2);
line-height: 1.6;
}
.explain {
border: 1px dashed var(--vp-c-divider);
border-radius: 12px;
background: var(--vp-c-bg-soft);
padding: 10px 12px;
}
.explainTitle {
font-weight: 900;
code {
background: #1e293b;
color: #e2e8f0;
padding: 4px 8px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 13px;
color: var(--vp-c-text-1);
}
.explainText {
.hint {
margin-top: 8px;
font-size: 12px;
color: var(--vp-c-text-2);
line-height: 1.6;
font-style: italic;
}
.actions {
.param {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
.btn {
border: 1px solid var(--vp-c-brand-1);
background: var(--vp-c-brand-1);
color: #fff;
border-radius: 10px;
padding: 8px 12px;
font-weight: 900;
cursor: pointer;
}
.ghost {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
border-radius: 10px;
padding: 8px 12px;
font-weight: 900;
cursor: pointer;
}
.win {
border: 1px solid #22c55e;
border-radius: 12px;
background: color-mix(in srgb, #22c55e 12%, var(--vp-c-bg));
padding: 10px 12px;
}
.winTitle {
font-weight: 900;
color: #166534;
font-size: 13px;
}
.winText {
margin-top: 8px;
.param-name {
background: #dbeafe;
color: #1e40af;
padding: 4px 8px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 12px;
color: #166534;
line-height: 1.6;
font-weight: bold;
}
@media (max-width: 720px) {
.game {
grid-template-columns: 1fr;
}
.param-desc {
font-size: 13px;
color: var(--vp-c-text-1);
}
pre {
background: #1e293b;
border-radius: 6px;
padding: 12px;
overflow-x: auto;
margin: 8px 0;
}
pre code {
background: transparent;
padding: 0;
color: #e2e8f0;
font-size: 12px;
line-height: 1.5;
}
.tips {
background: var(--vp-c-bg);
padding: 16px;
border-radius: 8px;
font-size: 14px;
line-height: 1.6;
color: var(--vp-c-text-2);
}
.tips ul {
margin: 8px 0 0 20px;
}
.tips li {
margin: 4px 0;
}
</style>