Files
test-repo/docs/.vitepress/theme/components/appendix/api-intro/ApiDocumentDemo.vue
T
2026-01-19 23:45:08 +08:00

397 lines
8.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
ApiDocumentDemo.vue
参考 ide-intro 虚拟界面 + 点击探索风格
目标让新手学会看 API 文档的 3 个重点入口在哪 / 要填什么 / 会得到什么
-->
<template>
<div class="wrap">
<div class="head">
<div class="title">怎么读 API 文档像找按钮一样找</div>
<div class="sub">
任务请在下面的假文档依次点出<b>入口</b><b>要填什么</b><b>会得到什么</b>
</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>
</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 {
border: 1px solid var(--vp-c-divider);
border-radius: 14px;
background: var(--vp-c-bg-soft);
padding: 16px;
}
.head {
display: flex;
flex-direction: column;
gap: 6px;
}
.title {
font-weight: 900;
font-size: 16px;
color: var(--vp-c-text-1);
}
.sub {
font-size: 13px;
color: var(--vp-c-text-2);
line-height: 1.6;
}
.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;
background: var(--vp-c-bg);
overflow: hidden;
}
.docBar {
display: flex;
gap: 6px;
align-items: center;
padding: 10px 12px;
border-bottom: 1px solid var(--vp-c-divider);
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;
font-size: 14px;
font-weight: 900;
}
.blockHint {
margin-top: 6px;
font-size: 12px;
color: var(--vp-c-text-2);
}
.side {
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
background: var(--vp-c-bg);
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;
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));
}
.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;
font-size: 13px;
color: var(--vp-c-text-1);
}
.explainText {
margin-top: 8px;
font-size: 12px;
color: var(--vp-c-text-2);
line-height: 1.6;
}
.actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-end;
}
.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;
font-size: 12px;
color: #166534;
line-height: 1.6;
}
@media (max-width: 720px) {
.game {
grid-template-columns: 1fr;
}
}
</style>