Merge branch 'dev'

This commit is contained in:
sanbuphy
2026-02-18 16:05:04 +08:00
567 changed files with 44988 additions and 18499 deletions
+131 -127
View File
@@ -344,7 +344,7 @@ export default defineConfig({
text: '高级开发',
link: '/zh-cn/stage-3/intro'
},
{ text: '附录', link: '/zh-cn/appendix/intro' }
{ text: '附录', link: '/zh-cn/appendix/index' }
],
sidebar: {
'/zh-cn/stage-0/': productManagerSidebar,
@@ -445,8 +445,8 @@ export default defineConfig({
collapsed: false,
items: [
{
text: '高级一:MCP 与 ClaudeCode Skills',
link: '/zh-cn/stage-3/core-skills/3.1-mcp-claudecode-skills/'
text: '高级一:MCP 与 Claude Code Skills',
link: '/zh-cn/stage-3/core-skills/3.1-mcp-claude-code-skills/'
},
{
text: '高级二:如何让 Coding Tools 长时间工作',
@@ -570,152 +570,156 @@ export default defineConfig({
],
'/zh-cn/appendix/': [
{
text: '人工智能基础',
text: '一、计算机是怎么回事',
collapsed: false,
items: [
{
text: '提示词工程',
link: '/zh-cn/appendix/prompt-engineering'
},
{
text: '人工智能进化史',
link: '/zh-cn/appendix/ai-evolution'
},
{ text: '大语言模型', link: '/zh-cn/appendix/llm-intro' },
{ text: '多模态大模型', link: '/zh-cn/appendix/vlm-intro' },
{
text: 'AI 绘画原理',
link: '/zh-cn/appendix/image-gen-intro'
},
{ text: 'AI 音频模型', link: '/zh-cn/appendix/audio-intro' },
{
text: '上下文工程',
link: '/zh-cn/appendix/context-engineering'
},
{ text: 'Agent 智能体', link: '/zh-cn/appendix/agent-intro' },
{
text: 'AI 能力词典',
link: '/zh-cn/appendix/ai-capability-dictionary'
}
{ text: '从晶体管到 CPU', link: '/zh-cn/appendix/1-computer-fundamentals/transistor-to-cpu' },
{ text: '操作系统(进程 / 内存 / 文件系统)', link: '/zh-cn/appendix/1-computer-fundamentals/operating-systems' },
{ text: '数据的编码、存储与传输', link: '/zh-cn/appendix/1-computer-fundamentals/data-encoding-storage' },
{ text: '网络:两台电脑如何对话', link: '/zh-cn/appendix/1-computer-fundamentals/computer-networks' },
{ text: '数据结构', link: '/zh-cn/appendix/1-computer-fundamentals/data-structures' },
{ text: '算法思维入门', link: '/zh-cn/appendix/1-computer-fundamentals/algorithm-thinking' },
{ text: '编程语言图谱', link: '/zh-cn/appendix/1-computer-fundamentals/programming-languages' },
{ text: '类型系统与编译原理入门', link: '/zh-cn/appendix/1-computer-fundamentals/type-systems-compilers' }
]
},
{
text: '前端开发',
text: '二、开发环境与工具',
collapsed: false,
items: [
{
text: 'HTML/CSS/JS 基础',
link: '/zh-cn/appendix/web-basics'
},
{
text: '前端进化史',
link: '/zh-cn/appendix/frontend-evolution'
},
{
text: '前端性能优化',
link: '/zh-cn/appendix/frontend-performance'
},
{
text: 'Canvas 2D 入门',
link: '/zh-cn/appendix/canvas-intro'
},
{
text: 'URL 到浏览器显示',
link: '/zh-cn/appendix/url-to-browser'
},
{
text: '浏览器调试器',
link: '/zh-cn/appendix/browser-devtools'
},
{
text: '浏览器渲染原理',
link: '/zh-cn/appendix/browser-rendering-pipeline'
},
{
text: '前端路由原理',
link: '/zh-cn/appendix/frontend-routing'
},
{
text: '组件状态管理',
link: '/zh-cn/appendix/component-state-management'
},
{
text: '前端工程化',
link: '/zh-cn/appendix/frontend-engineering'
}
{ text: '集成开发环境 (IDE) 基础', link: '/zh-cn/appendix/2-development-tools/ide-basics' },
{ text: '命令行与 Shell 脚本', link: '/zh-cn/appendix/2-development-tools/command-line-shell' },
{ text: '编辑器与 AI 编程助手', link: '/zh-cn/appendix/2-development-tools/editors-and-ai' },
{ text: 'Git:代码的时光机', link: '/zh-cn/appendix/2-development-tools/git-version-control' },
{ text: '环境变量与 PATH', link: '/zh-cn/appendix/2-development-tools/environment-path' },
{ text: '端口与 localhost', link: '/zh-cn/appendix/2-development-tools/ports-localhost' },
{ text: 'SSH 与密钥认证', link: '/zh-cn/appendix/2-development-tools/ssh-authentication' },
{ text: '包管理器(npm / pip / cargo', link: '/zh-cn/appendix/2-development-tools/package-managers' },
{ text: '调试的艺术', link: '/zh-cn/appendix/2-development-tools/debugging-art/' },
{ text: '正则表达式', link: '/zh-cn/appendix/2-development-tools/regex' }
]
},
{
text: '后端开发',
text: '三、浏览器与前端',
collapsed: false,
items: [
{
text: '后端进化史',
link: '/zh-cn/appendix/backend-evolution'
},
{
text: '后端分层架构',
link: '/zh-cn/appendix/backend-layered-architecture'
},
{
text: '后端编程语言',
link: '/zh-cn/appendix/backend-languages'
},
{
text: '并发编程模型',
link: '/zh-cn/appendix/concurrency-models'
},
{
text: '接口设计规范',
link: '/zh-cn/appendix/api-design'
},
{ text: '数据库原理', link: '/zh-cn/appendix/database-intro' },
{ text: '系统缓存设计', link: '/zh-cn/appendix/cache-design' },
{ text: '消息队列设计', link: '/zh-cn/appendix/queue-design' },
{ text: '鉴权原理与实战', link: '/zh-cn/appendix/auth-design' },
{
text: '网关与反向代理',
link: '/zh-cn/appendix/gateway-proxy'
},
{
text: '负载均衡策略',
link: '/zh-cn/appendix/load-balancing'
},
{ text: '埋点设计', link: '/zh-cn/appendix/tracking-design' },
{ text: '线上运维', link: '/zh-cn/appendix/operations' }
{ text: 'JavaScript 语言深入', link: '/zh-cn/appendix/3-browser-and-frontend/javascript-deep-dive' },
{ text: 'TypeScript:给 JS 加上类型系统', link: '/zh-cn/appendix/3-browser-and-frontend/typescript' },
{ text: '前端框架对比(React / Vue / Svelte / Angular', link: '/zh-cn/appendix/3-browser-and-frontend/frontend-frameworks' },
{ text: '浏览器是一个操作系统', link: '/zh-cn/appendix/3-browser-and-frontend/browser-as-os' },
{ text: '浏览器渲染管道', link: '/zh-cn/appendix/3-browser-and-frontend/browser-as-os-rendering' },
{ text: 'HTML / CSS 布局体系', link: '/zh-cn/appendix/3-browser-and-frontend/html-css-layout' },
{ text: 'JavaScript 运行时', link: '/zh-cn/appendix/3-browser-and-frontend/javascript-runtime' },
{ text: '前端框架的本质', link: '/zh-cn/appendix/3-browser-and-frontend/frontend-framework-nature' },
{ text: '状态管理哲学', link: '/zh-cn/appendix/3-browser-and-frontend/state-management' },
{ text: '路由与导航', link: '/zh-cn/appendix/3-browser-and-frontend/routing-navigation' },
{ text: '图形与动画(Canvas / SVG / WebGL', link: '/zh-cn/appendix/3-browser-and-frontend/graphics-animation' },
{ text: '实时通信(WebSocket / SSE', link: '/zh-cn/appendix/3-browser-and-frontend/realtime-communication' },
{ text: '网页性能的度量与优化', link: '/zh-cn/appendix/3-browser-and-frontend/web-performance' },
{ text: '前端工程化全貌', link: '/zh-cn/appendix/3-browser-and-frontend/frontend-engineering' },
{ text: '无障碍与国际化', link: '/zh-cn/appendix/3-browser-and-frontend/a11n-i18n' }
]
},
{
text: '云计算与服务',
text: '四、服务器与后端',
collapsed: false,
items: [
{
text: '云服务基础',
link: '/zh-cn/appendix/cloud-services'
},
{
text: 'IAM 权限管理',
link: '/zh-cn/appendix/cloud-iam'
},
{
text: '对象存储与 CDN',
link: '/zh-cn/appendix/cloud-storage-cdn'
}
{ text: '后端语言对比(Node.js / Go / Java / Rust', link: '/zh-cn/appendix/4-server-and-backend/backend-languages' },
{ text: '客户端语言对比(Swift / Kotlin / Dart', link: '/zh-cn/appendix/4-server-and-backend/client-languages' },
{ text: '跨平台方案对比(React Native / Flutter / Electron / Tauri', link: '/zh-cn/appendix/4-server-and-backend/cross-platform' },
{ text: 'HTTP 协议', link: '/zh-cn/appendix/4-server-and-backend/http-protocol' },
{ text: '一个请求的完整旅程', link: '/zh-cn/appendix/4-server-and-backend/request-journey' },
{ text: 'Web 框架的本质', link: '/zh-cn/appendix/4-server-and-backend/web-frameworks' },
{ text: 'API 设计哲学(REST / GraphQL / gRPC', link: '/zh-cn/appendix/4-server-and-backend/api-design' },
{ text: 'API 入门', link: '/zh-cn/appendix/4-server-and-backend/api-intro' },
{ text: '序列化与数据格式', link: '/zh-cn/appendix/4-server-and-backend/serialization' },
{ text: '认证与授权体系', link: '/zh-cn/appendix/4-server-and-backend/auth-authorization' },
{ text: '并发、异步与多线程', link: '/zh-cn/appendix/4-server-and-backend/concurrency-async' },
{ text: '缓存的层次与策略', link: '/zh-cn/appendix/4-server-and-backend/caching' },
{ text: '消息队列与事件驱动', link: '/zh-cn/appendix/4-server-and-backend/message-queues' },
{ text: '异步任务队列与生产消费模型', link: '/zh-cn/appendix/4-server-and-backend/async-task-queues' },
{ text: '限流与背压控制', link: '/zh-cn/appendix/4-server-and-backend/rate-limiting-backpressure' },
{ text: '搜索引擎原理', link: '/zh-cn/appendix/4-server-and-backend/search-engines' },
{ text: '文件存储与对象存储', link: '/zh-cn/appendix/4-server-and-backend/file-storage' },
{ text: '后端分层架构', link: '/zh-cn/appendix/4-server-and-backend/backend-layered-architecture' }
]
},
{
text: '通用技能',
text: '五、数据',
collapsed: false,
items: [
{ text: 'API 入门', link: '/zh-cn/appendix/api-intro' },
{ text: 'IDE 原理', link: '/zh-cn/appendix/ide-intro' },
{ text: '终端入门', link: '/zh-cn/appendix/terminal-intro' },
{ text: 'Git 详细介绍', link: '/zh-cn/appendix/git-intro' },
{
text: '计算机网络',
link: '/zh-cn/appendix/computer-networks'
},
{ text: '部署与上线', link: '/zh-cn/appendix/deployment' }
{ text: 'SQL', link: '/zh-cn/appendix/5-data/sql' },
{ text: '数据库原理(索引 / 事务 / 查询优化)', link: '/zh-cn/appendix/5-data/database-fundamentals' },
{ text: '数据模型全景(文档 / 图 / 时序 / 向量)', link: '/zh-cn/appendix/5-data/data-models' },
{ text: '数据埋点与用户行为采集', link: '/zh-cn/appendix/5-data/data-tracking' },
{ text: '数据分析基础(统计 / 指标 / 漏斗)', link: '/zh-cn/appendix/5-data/data-analysis' },
{ text: 'A/B 测试与实验驱动', link: '/zh-cn/appendix/5-data/ab-testing' },
{ text: '数据可视化与仪表盘', link: '/zh-cn/appendix/5-data/data-visualization' },
{ text: '数据治理与数据质量', link: '/zh-cn/appendix/5-data/data-governance' }
]
},
{
text: '六、架构与系统设计',
collapsed: false,
items: [
{ text: '从单体到微服务的演进', link: '/zh-cn/appendix/6-architecture-and-system-design/monolith-to-microservices' },
{ text: '分布式系统的挑战', link: '/zh-cn/appendix/6-architecture-and-system-design/distributed-systems' },
{ text: '高可用与容灾', link: '/zh-cn/appendix/6-architecture-and-system-design/high-availability' },
{ text: '系统设计方法论', link: '/zh-cn/appendix/6-architecture-and-system-design/system-design-methodology' }
]
},
{
text: '七、基础设施与运维',
collapsed: false,
items: [
{ text: 'Linux 基础', link: '/zh-cn/appendix/7-infrastructure-and-operations/linux-basics' },
{ text: 'Docker 容器化', link: '/zh-cn/appendix/7-infrastructure-and-operations/docker-containers' },
{ text: 'Kubernetes 编排', link: '/zh-cn/appendix/7-infrastructure-and-operations/kubernetes' },
{ text: 'CI / CD 自动化', link: '/zh-cn/appendix/7-infrastructure-and-operations/ci-cd' },
{ text: '域名、DNS 与 HTTPS', link: '/zh-cn/appendix/7-infrastructure-and-operations/dns-https' },
{ text: '负载均衡与网关', link: '/zh-cn/appendix/7-infrastructure-and-operations/load-balancing-gateway' },
{ text: '网关与反向代理', link: '/zh-cn/appendix/7-infrastructure-and-operations/gateway-proxy' },
{ text: '云平台实战', link: '/zh-cn/appendix/7-infrastructure-and-operations/cloud-platforms' },
{ text: 'IAM 权限管理', link: '/zh-cn/appendix/7-infrastructure-and-operations/cloud-iam' },
{ text: '对象存储与 CDN', link: '/zh-cn/appendix/7-infrastructure-and-operations/cloud-storage-cdn' },
{ text: '基础设施即代码', link: '/zh-cn/appendix/7-infrastructure-and-operations/infrastructure-as-code' },
{ text: '监控、日志与告警', link: '/zh-cn/appendix/7-infrastructure-and-operations/monitoring-logging' },
{ text: '故障排查与应急响应', link: '/zh-cn/appendix/7-infrastructure-and-operations/incident-response' }
]
},
{
text: '八、人工智能',
collapsed: false,
items: [
{ text: 'AI 简史与核心概念', link: '/zh-cn/appendix/8-artificial-intelligence/ai-history' },
{ text: '神经网络与深度学习', link: '/zh-cn/appendix/8-artificial-intelligence/neural-networks' },
{ text: 'Transformer 与注意力机制', link: '/zh-cn/appendix/8-artificial-intelligence/transformer-attention' },
{ text: '大语言模型的工作原理', link: '/zh-cn/appendix/8-artificial-intelligence/llm-principles' },
{ text: '提示词工程', link: '/zh-cn/appendix/8-artificial-intelligence/prompt-engineering' },
{ text: '上下文工程', link: '/zh-cn/appendix/8-artificial-intelligence/context-engineering' },
{ text: '多模态模型(视觉 / 音频 / 视频)', link: '/zh-cn/appendix/8-artificial-intelligence/multimodal-models' },
{ text: '图像生成原理', link: '/zh-cn/appendix/8-artificial-intelligence/image-generation' },
{ text: '语音合成与识别', link: '/zh-cn/appendix/8-artificial-intelligence/speech-synthesis-recognition' },
{ text: 'Embedding 与向量检索', link: '/zh-cn/appendix/8-artificial-intelligence/embedding-vector-retrieval' },
{ text: 'RAG 架构', link: '/zh-cn/appendix/8-artificial-intelligence/rag' },
{ text: 'AI Agent 与工具调用', link: '/zh-cn/appendix/8-artificial-intelligence/ai-agents' },
{ text: 'AI 协议(MCP 等)', link: '/zh-cn/appendix/8-artificial-intelligence/ai-protocols' },
{ text: '模型微调与部署', link: '/zh-cn/appendix/8-artificial-intelligence/model-finetuning-deployment' },
{ text: 'AI 原生应用设计', link: '/zh-cn/appendix/8-artificial-intelligence/ai-native-app-design' },
{ text: 'AI 能力词典', link: '/zh-cn/appendix/8-artificial-intelligence/ai-capability-dictionary' }
]
},
{
text: '九、工程素养',
collapsed: false,
items: [
{ text: '代码质量与重构', link: '/zh-cn/appendix/9-engineering-excellence/code-quality-refactoring' },
{ text: '测试策略', link: '/zh-cn/appendix/9-engineering-excellence/testing-strategies' },
{ text: '设计模式', link: '/zh-cn/appendix/9-engineering-excellence/design-patterns' },
{ text: '安全思维与攻防基础', link: '/zh-cn/appendix/9-engineering-excellence/security-thinking' },
{ text: '技术文档写作', link: '/zh-cn/appendix/9-engineering-excellence/technical-writing' },
{ text: '开源协作', link: '/zh-cn/appendix/9-engineering-excellence/open-source-collaboration' },
{ text: '技术选型方法论', link: '/zh-cn/appendix/9-engineering-excellence/technology-selection' }
]
}
]
+86 -8
View File
@@ -86,7 +86,6 @@ onMounted(() => {
applyLineHeight(savedLineHeight)
isHydrated.value = true
// 初始化 outline 自动滚动功能
initOutlineAutoScroll()
})
@@ -95,7 +94,34 @@ onMounted(() => {
// 当页面滚动时,自动滚动 outline 让当前激活项保持在可视区域
// ============================================
function initOutlineAutoScroll() {
// 使用 MutationObserver 监听 outline 的变化
const outlineSelectors = [
'.VPDocAsideOutline',
'.VPTableOfContents',
'.vitepress-doc-sidebar',
'.sidebar-outline',
'aside'
]
const sidebarSelectors = [
'.VPSidebar',
'.VPDocSidebar',
'.vitepress-doc-sidebar'
]
let outlineContainer = null
for (const selector of outlineSelectors) {
outlineContainer = document.querySelector(selector)
if (outlineContainer) break
}
if (!outlineContainer) return
let sidebarContainer = null
for (const selector of sidebarSelectors) {
sidebarContainer = document.querySelector(selector)
if (sidebarContainer) break
}
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
@@ -107,7 +133,17 @@ function initOutlineAutoScroll() {
}
})
// 开始监听
const sidebarObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const target = mutation.target
if (target.classList.contains('is-active')) {
scrollSidebarToActiveItem(target)
}
}
}
})
const startObserving = () => {
const outlineContainer = document.querySelector('.VPDocAsideOutline')
if (outlineContainer) {
@@ -116,32 +152,48 @@ function initOutlineAutoScroll() {
subtree: true,
attributeFilter: ['class']
})
const existingActive = outlineContainer.querySelector('.active')
if (existingActive) {
scrollOutlineToActiveItem(existingActive)
}
}
if (sidebarContainer) {
sidebarObserver.observe(sidebarContainer, {
attributes: true,
subtree: true,
attributeFilter: ['class']
})
const existingSidebarActive = sidebarContainer.querySelector('.is-active')
if (existingSidebarActive) {
scrollSidebarToActiveItem(existingSidebarActive)
}
}
}
// 页面加载完成后开始监听
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startObserving)
} else {
startObserving()
}
// 同时监听路由变化(VitePress 是 SPA
const originalPushState = history.pushState
const originalReplaceState = history.replaceState
history.pushState = function (...args) {
originalPushState.apply(this, args)
setTimeout(startObserving, 100)
setTimeout(startObserving, 300)
}
history.replaceState = function (...args) {
originalReplaceState.apply(this, args)
setTimeout(startObserving, 100)
setTimeout(startObserving, 300)
}
window.addEventListener('popstate', () => {
setTimeout(startObserving, 100)
setTimeout(startObserving, 300)
})
}
@@ -172,6 +224,32 @@ function scrollOutlineToActiveItem(activeLink) {
}
}
// 滚动侧边栏让当前激活项保持在可视区域中心
function scrollSidebarToActiveItem(activeItem) {
const sidebarContainer = document.querySelector('.VPSidebar') || document.querySelector('.VPDocSidebar')
if (!sidebarContainer || !activeItem) return
const targetElement = activeItem.querySelector('.item') || activeItem.querySelector('a') || activeItem
const containerRect = sidebarContainer.getBoundingClientRect()
const targetRect = targetElement.getBoundingClientRect()
const targetTop = targetRect.top - containerRect.top + sidebarContainer.scrollTop
const targetHeight = targetRect.height
const targetCenterY = targetTop + targetHeight / 2
const isInside = targetRect.top >= containerRect.top - 20 &&
targetRect.bottom <= containerRect.bottom + 20
if (!isInside) {
const targetScrollTop = targetCenterY - containerRect.height / 2
sidebarContainer.scrollTo({
top: targetScrollTop,
behavior: 'smooth'
})
}
}
watch(fontSize, (next) => {
if (!isHydrated.value) return
const normalized = clampFontSize(next)
@@ -156,7 +156,7 @@ const current = ref(modules[0])
.icon {
width: 28px;
height: 28px;
border-radius: 8px;
border-radius: 6px;
display: grid;
place-items: center;
background: var(--vp-c-bg-soft);
@@ -261,7 +261,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
padding: 12px;
min-height: 120px;
max-height: 160px;
overflow-y: auto;
}
.msg-row {
@@ -421,7 +421,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
gap: 8px;
padding: 10px 14px;
background: #dcfce7;
border-radius: 8px;
border-radius: 6px;
margin-bottom: 16px;
font-size: 12px;
color: #166534;
@@ -433,7 +433,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
gap: 8px;
padding: 10px 14px;
background: var(--vp-c-brand-soft);
border-radius: 8px;
border-radius: 6px;
font-size: 12px;
color: var(--vp-c-text-1);
}
@@ -588,7 +588,7 @@ const truncate = (str, len) => str?.length > len ? str.slice(0, len) + '...' : s
/* 对话区 */
.chat-area {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 12px;
margin-bottom: 16px;
}
@@ -613,7 +613,7 @@ const truncate = (str, len) => str?.length > len ? str.slice(0, len) + '...' : s
.messages {
max-height: 200px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 10px;
@@ -732,7 +732,7 @@ const truncate = (str, len) => str?.length > len ? str.slice(0, len) + '...' : s
.memory-panel {
background: var(--vp-c-bg-soft);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s;
@@ -778,7 +778,7 @@ const truncate = (str, len) => str?.length > len ? str.slice(0, len) + '...' : s
padding: 10px;
min-height: 80px;
max-height: 120px;
overflow-y: auto;
}
.empty {
@@ -1060,7 +1060,7 @@ const truncate = (str, len) => str?.length > len ? str.slice(0, len) + '...' : s
gap: 12px;
padding: 12px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
}
.practice-icon {
@@ -435,7 +435,7 @@ const truncate = (str, len) => str.length > len ? str.slice(0, len) + '...' : st
.phase {
margin-bottom: 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
opacity: 0.5;
transition: all 0.3s;
@@ -525,7 +525,7 @@ const truncate = (str, len) => str.length > len ? str.slice(0, len) + '...' : st
gap: 12px;
padding: 12px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
border: 2px solid transparent;
transition: all 0.3s;
position: relative;
@@ -685,7 +685,7 @@ const truncate = (str, len) => str.length > len ? str.slice(0, len) + '...' : st
.final-output {
padding: 12px;
background: #dcfce7;
border-radius: 8px;
border-radius: 6px;
}
.output-bubble {
@@ -704,7 +704,7 @@ const truncate = (str, len) => str.length > len ? str.slice(0, len) + '...' : st
.control-btn {
padding: 10px 24px;
border-radius: 8px;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
@@ -762,7 +762,7 @@ const truncate = (str, len) => str.length > len ? str.slice(0, len) + '...' : st
.explanation-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 14px;
text-align: center;
}
@@ -788,7 +788,7 @@ const truncate = (str, len) => str.length > len ? str.slice(0, len) + '...' : st
.comparison-section {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 14px;
}
@@ -304,7 +304,7 @@ reset()
.goal-bar {
background: var(--vp-c-brand-soft);
border-left: 3px solid var(--vp-c-brand);
border-radius: 8px;
border-radius: 6px;
padding: 10px 14px;
margin-bottom: 16px;
font-size: 14px;
@@ -403,7 +403,7 @@ reset()
.log-box, .thought-box {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
}
@@ -431,7 +431,7 @@ reset()
padding: 10px 12px;
min-height: 100px;
max-height: 140px;
overflow-y: auto;
}
.empty {
@@ -534,7 +534,7 @@ reset()
gap: 8px;
padding: 10px 14px;
background: var(--vp-c-brand-soft);
border-radius: 8px;
border-radius: 6px;
font-size: 12px;
color: var(--vp-c-text-1);
}
@@ -476,7 +476,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
.thinking-section {
margin-bottom: 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
}
@@ -516,7 +516,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
.tools-section {
margin-bottom: 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
}
@@ -530,7 +530,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
gap: 10px;
padding: 10px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
margin-bottom: 8px;
border: 1px solid var(--vp-c-divider);
transition: all 0.3s;
@@ -603,7 +603,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
padding: 12px;
background: #dcfce7;
border: 1px solid #86efac;
border-radius: 8px;
border-radius: 6px;
}
.response-header {
@@ -628,7 +628,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
transition: background 0.2s;
@@ -651,7 +651,7 @@ const wait = (ms) => new Promise(r => setTimeout(r, ms))
margin-top: 16px;
padding: 12px 16px;
background: var(--vp-c-brand-soft);
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
}
@@ -163,7 +163,7 @@ const steps = [
.n {
width: 26px;
height: 26px;
border-radius: 8px;
border-radius: 6px;
display: grid;
place-items: center;
background: var(--vp-c-bg-soft);
@@ -266,7 +266,7 @@ const reset = () => { currentStep.value = 0 }
.user-input-bar {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 10px 14px;
margin-bottom: 16px;
font-size: 14px;
@@ -460,7 +460,7 @@ const reset = () => { currentStep.value = 0 }
.final-result {
background: var(--vp-c-brand-soft);
border-left: 3px solid var(--vp-c-brand);
border-radius: 8px;
border-radius: 6px;
padding: 12px 14px;
margin-bottom: 16px;
font-size: 13px;
@@ -513,7 +513,7 @@ const reset = () => { currentStep.value = 0 }
gap: 8px;
padding: 10px 14px;
background: var(--vp-c-brand-soft);
border-radius: 8px;
border-radius: 6px;
font-size: 12px;
color: var(--vp-c-text-1);
}
@@ -179,7 +179,7 @@ const reset = () => {
.icon {
width: 28px;
height: 28px;
border-radius: 8px;
border-radius: 6px;
display: grid;
place-items: center;
background: var(--vp-c-bg-soft);
@@ -140,7 +140,7 @@ const getInsight = (index) => {
margin-bottom: 24px;
padding: 16px;
background-color: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
}
.word-token {
@@ -234,7 +234,7 @@ onMounted(() => {
display: flex;
justify-content: center;
background-color: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 10px;
}
@@ -213,7 +213,7 @@ watch([featureCount, valuesPerFeature], () => {
text-align: center;
background-color: var(--vp-c-bg-alt);
padding: 16px;
border-radius: 8px;
border-radius: 6px;
}
.formula-suffix {
@@ -234,7 +234,7 @@ watch([featureCount, valuesPerFeature], () => {
width: 50px;
height: 50px;
border: 2px solid;
border-radius: 8px;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
@@ -208,7 +208,7 @@ const mode = ref('discriminative')
gap: 10px;
background-color: var(--vp-c-bg);
padding: 16px;
border-radius: 8px;
border-radius: 6px;
}
.io-box {
@@ -149,7 +149,7 @@ const handleTabClick = (tab) => {
}
.main-card {
border-radius: 8px;
border-radius: 6px;
}
.card-header {
@@ -334,7 +334,7 @@ const isConnectionFocus = (c) => {
display: flex;
justify-content: center;
background-color: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 10px;
}
@@ -274,7 +274,7 @@ const train = () => {
.result-box {
background-color: var(--vp-c-bg-alt);
padding: 12px;
border-radius: 8px;
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
text-align: center;
}
@@ -1,50 +1,496 @@
<!--
DocumentationDemo.vue - API 文档演示组件
展示 API 文档的编写规范和最佳实践
-->
<template>
<div class="demo-container">
<div class="demo-header">
<h4>{{ title }}</h4>
<p class="hint">{{ description }}</p>
<div class="demo">
<div class="header">
<span class="icon">📚</span>
<span class="title">API 文档最好的 API 文档就是代码本身</span>
</div>
<div class="demo-content">
<el-alert type="info" :closable="false">
文档演示组件占位符 - 待实现具体交互
</el-alert>
<div class="content">
<div class="tools-tabs">
<button
v-for="tool in tools"
:key="tool.id"
class="tool-btn"
:class="{ active: selectedTool === tool.id }"
@click="selectedTool = tool.id"
>
<span class="tool-icon">{{ tool.icon }}</span>
<span class="tool-name">{{ tool.name }}</span>
</button>
</div>
<div class="tool-detail" v-if="currentTool">
<div class="tool-header">
<div class="tool-title">{{ currentTool.name }}</div>
<div class="tool-tags">
<span class="tag" :class="tag.class" v-for="tag in currentTool.tags" :key="tag.text">
{{ tag.text }}
</span>
</div>
</div>
<div class="tool-description">
<p>{{ currentTool.description }}</p>
</div>
<div class="feature-section">
<h4>核心特性</h4>
<div class="feature-list">
<div v-for="(feature, idx) in currentTool.features" :key="idx" class="feature-item">
<span class="feature-icon"></span>
<span class="feature-text">{{ feature }}</span>
</div>
</div>
</div>
<div class="example-section">
<h4>文档示例OpenAPI 3.0</h4>
<div class="code-block">
<pre><code>{{ currentTool.example }}</code></pre>
</div>
</div>
<div class="tools-section">
<h4>🔧 推荐工具</h4>
<div class="tools-grid">
<div v-for="(rec, idx) in currentTool.recommendations" :key="idx" class="tool-card">
<div class="rec-name">{{ rec.name }}</div>
<div class="rec-desc">{{ rec.description }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
const title = ref('API文档演示')
const description = ref('展示RESTful API文档的编写规范和最佳实践,包括Swagger、OpenAPI等工具的使用')
const tools = [
{
id: 'openapi',
name: 'OpenAPI 规范',
icon: '📋',
tags: [
{ text: '行业标准', class: 'primary' },
{ text: '语言无关', class: 'secondary' }
],
description: 'OpenAPI Specification(原 Swagger)是描述 REST API 的标准格式,可以被工具解析生成交互式文档、客户端 SDK、服务器存根等。',
features: [
'标准化的 YAML/JSON 格式描述 API',
'支持路径、参数、响应模型、认证等完整定义',
'生态系统丰富,支持 100+ 工具',
'可以生成交互式文档(Swagger UI',
'可以从代码注释自动生成',
'支持 API 版本控制和演进'
],
example: `openapi: 3.0.0
info:
title: 用户服务 API
version: 1.0.0
description: 提供用户管理相关接口
servers:
- url: https://api.example.com/v1
paths:
/users:
get:
summary: 获取用户列表
parameters:
- name: page
in: query
schema:
type: integer
default: 1
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
code:
type: integer
data:
type: array
items:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
format: email`,
recommendations: [
{ name: 'Swagger UI', description: '最流行的交互式文档界面' },
{ name: 'Redoc', description: '美观的现代文档生成器' },
{ name: 'Stoplight', description: '可视化的 API 设计平台' }
]
},
{
id: 'swagger',
name: 'Swagger 工具链',
icon: '🛠️',
tags: [
{ text: '工具集', class: 'success' },
{ text: '自动化', class: 'info' }
],
description: 'Swagger 是一套围绕 OpenAPI 规范构建的工具,包括编辑器、UI、代码生成器等,帮助开发者快速构建和使用 API。',
features: [
'Swagger Editor:在线编写和验证 OpenAPI 文档',
'Swagger UI:自动生成交互式文档',
'Swagger Codegen:根据文档生成客户端 SDK',
'支持主流编程语言和框架',
'集成到 CI/CD 流程',
'自动保持文档与代码同步'
],
example: `# Swagger Editor 示例配置
swagger: '2.0'
info:
title: 示例 API
version: '1.0.0'
host: api.example.com
basePath: /v1
schemes:
- https
paths:
/users:
get:
tags:
- Users
summary: 获取所有用户
produces:
- application/json
responses:
200:
description: 成功
schema:
type: object
properties:
code:
type: integer
data:
type: array`,
recommendations: [
{ name: 'Swagger Editor', description: '在线编辑器,实时预览' },
{ name: 'Swagger Codegen', description: '生成 40+ 种语言的客户端' },
{ name: 'Postman', description: '导入 OpenAPI 进行测试' }
]
},
{
id: 'best-practices',
name: '文档最佳实践',
icon: '⭐',
tags: [
{ text: '经验', class: 'warning' },
{ text: '规范', class: 'secondary' }
],
description: '好的 API 文档应该像用户手册一样清晰,让开发者不问问题就能完成集成。',
features: [
'每个接口都有完整的请求示例',
'提供多种语言的代码示例(curl、JavaScript、Python',
'错误码文档化,附带解决方案',
'提供沙箱环境或测试工具',
'包含认证流程和获取 Token 的方法',
'实时更新,与代码保持一致',
'版本变更日志和迁移指南'
],
example: `# 完整的接口文档示例
## 获取用户信息
**请求示例**
\`\`\`bash
curl -X GET \\
https://api.example.com/v1/users/123 \\
-H "Authorization: Bearer YOUR_TOKEN"
\`\`\`
**成功响应**
\`\`\`json
{
"code": 0,
"message": "success",
"data": {
"id": 123,
"name": "张三",
"email": "zhangsan@example.com"
}
}
\`\`\`
**错误响应**
| 错误码 | 说明 | 解决方案 |
|--------|------|----------|
| 10010 | 用户不存在 | 检查 user_id 是否正确 |
| 10018 | Token 已过期 | 重新调用登录接口 |
**在线测试**
[🚀 API Explorer 中测试](https://api.example.com/docs)`,
recommendations: [
{ name: 'API Blueprint', description: ' Markdown 风格的 API 文档' },
{ name: 'Docusaurus', description: ' Facebook 开源的文档平台' },
{ name: 'GitBook', description: '美观的文档托管平台' }
]
}
]
const selectedTool = ref('openapi')
const currentTool = computed(() =>
tools.find(t => t.id === selectedTool.value)
)
</script>
<style scoped>
.demo-container {
.demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 20px;
border-radius: 12px;
background: var(--vp-c-bg-soft);
margin: 24px 0;
overflow: hidden;
}
.demo-header {
.header {
padding: 16px 20px;
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: white;
display: flex;
align-items: center;
gap: 12px;
}
.icon {
font-size: 24px;
}
.title {
font-weight: 600;
font-size: 16px;
}
.content {
padding: 24px;
}
.tools-tabs {
display: flex;
gap: 12px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.demo-header h4 {
margin: 0 0 8px 0;
.tool-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border: 2px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
transition: all 0.2s ease;
}
.tool-btn:hover {
border-color: rgba(var(--vp-c-brand-rgb), 0.5);
transform: translateY(-2px);
}
.tool-btn.active {
border-color: var(--vp-c-brand);
background: var(--vp-c-brand);
color: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.tool-icon {
font-size: 18px;
}
.tool-name {
font-weight: 600;
font-size: 14px;
}
.tool-detail {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
}
.tool-header {
padding: 16px;
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-divider);
display: flex;
justify-content: space-between;
align-items: center;
}
.tool-title {
font-weight: 700;
font-size: 16px;
color: var(--vp-c-text-1);
}
.hint {
margin: 0;
font-size: 14px;
color: var(--vp-c-text-2);
.tool-tags {
display: flex;
gap: 8px;
}
.demo-content {
.tag {
padding: 4px 10px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
}
.tag.primary {
background: #dbeafe;
color: #1d4ed8;
}
.tag.secondary {
background: #e0e7ff;
color: #4338ca;
}
.tag.success {
background: #dcfce7;
color: #16a34a;
}
.tag.info {
background: #ccfbf1;
color: #0f766e;
}
.tag.warning {
background: #fef3c7;
color: #d97706;
}
.tool-description {
padding: 16px;
font-size: 14px;
color: var(--vp-c-text-2);
line-height: 1.7;
border-bottom: 1px solid var(--vp-c-divider);
}
.feature-section, .example-section, .tools-section {
padding: 16px;
}
.feature-section h4, .example-section h4, .tools-section h4 {
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 12px 0;
}
.feature-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.feature-item {
display: flex;
flex-direction: column;
gap: 16px;
align-items: flex-start;
gap: 8px;
padding: 8px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.feature-icon {
color: #22c55e;
font-weight: 700;
font-size: 16px;
flex-shrink: 0;
}
.feature-text {
font-size: 13px;
color: var(--vp-c-text-2);
line-height: 1.5;
}
.code-block {
background: var(--vp-c-bg-alt);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
}
.code-block pre {
margin: 0;
padding: 16px;
font-size: 12px;
line-height: 1.5;
overflow-x: auto;
}
.code-block code {
font-family: monospace;
color: var(--vp-c-text-1);
}
.tools-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.tool-card {
padding: 12px;
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
text-align: center;
}
.rec-name {
font-weight: 600;
font-size: 13px;
color: var(--vp-c-text-1);
margin-bottom: 4px;
}
.rec-desc {
font-size: 12px;
color: var(--vp-c-text-2);
line-height: 1.4;
}
@media (max-width: 768px) {
.tools-tabs {
flex-direction: column;
}
.feature-list {
grid-template-columns: 1fr;
}
.tools-grid {
grid-template-columns: 1fr;
}
}
</style>
@@ -1,97 +1,379 @@
<!--
ErrorHandlingDemo.vue - 错误处理演示组件
展示错误处理的正确和错误示例对比
-->
<template>
<div class="demo-container">
<div class="demo-header">
<h4>错误处理演示</h4>
<p class="hint">展示RESTful API中的错误处理机制</p>
<div class="demo">
<div class="header">
<span class="icon">🚨</span>
<span class="title">错误处理优雅地"拒绝"</span>
</div>
<div class="demo-content">
<div class="error-types">
<div class="error-item">
<span class="code">400</span>
<span class="name">Bad Request</span>
<span class="desc">请求参数错误</span>
<div class="content">
<div class="comparison-tabs">
<button
class="tab-btn bad"
:class="{ active: selectedTab === 'bad' }"
@click="selectedTab = 'bad'"
>
错误示范
</button>
<button
class="tab-btn good"
:class="{ active: selectedTab === 'good' }"
@click="selectedTab = 'good'"
>
正确示范
</button>
</div>
<!-- 错误示范 -->
<div v-if="selectedTab === 'bad'" class="comparison bad">
<div class="response-preview">
<div class="status-line bad">
<span>HTTP/1.1</span>
<span class="code">200 OK</span>
</div>
<div class="response-body">
<pre><code>{
"error": "出错了"
}</code></pre>
</div>
</div>
<div class="error-item">
<span class="code">401</span>
<span class="name">Unauthorized</span>
<span class="desc">未授权访问</span>
<div class="problems">
<h4>问题分析</h4>
<ul>
<li>
<span class="icon"></span>
HTTP 状态码说"成功"但业务说"出错" - 前后端状态不一致
</li>
<li>
<span class="icon"></span>
错误信息太笼统无法定位问题
</li>
<li>
<span class="icon"></span>
没有错误代码难以程序化判断
</li>
<li>
<span class="icon"></span>
浏览器和 CDN 会缓存这个"成功的"响应
</li>
</ul>
</div>
<div class="error-item">
<span class="code">404</span>
<span class="name">Not Found</span>
<span class="desc">资源不存在</span>
</div>
<!-- 正确示范 -->
<div v-if="selectedTab === 'good'" class="comparison good">
<div class="response-preview">
<div class="status-line">
<span>HTTP/1.1</span>
<span class="code">422 Unprocessable Entity</span>
</div>
<div class="response-body">
<pre><code>{{ JSON.stringify(goodResponse, null, 2) }}</code></pre>
</div>
</div>
<div class="error-item">
<span class="code">500</span>
<span class="name">Server Error</span>
<span class="desc">服务器内部错误</span>
<div class="highlights">
<h4>正确做法</h4>
<ul>
<li>
<span class="icon"></span>
<strong>正确的 HTTP 状态码</strong>: 422 表示语义错误
</li>
<li>
<span class="icon"></span>
<strong>业务错误码</strong>: `code: 20003` 可用于程序判断
</li>
<li>
<span class="icon"></span>
<strong>详细错误信息</strong>: `errors` 数组包含具体字段和原因
</li>
<li>
<span class="icon"></span>
<strong>可追踪性</strong>: `request_id` 用于日志查询
</li>
<li>
<span class="icon"></span>
<strong>帮助链接</strong>: `help_url` 引导用户查看文档
</li>
</ul>
</div>
<div class="error-codes">
<h4>错误码体系</h4>
<div class="code-list">
<div v-for="item in errorCodeItems" :key="item.code" class="code-item">
<span class="code-badge">{{ item.code }}</span>
<span class="code-desc">{{ item.desc }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const selectedTab = ref('bad')
const goodResponse = {
code: 20003,
message: '密码强度不足',
errors: [
{
field: 'password',
code: 'VALIDATION_ERROR',
message: '密码必须包含至少 1 个大写字母、1 个小写字母、1 个数字,且长度至少 8 位'
}
],
request_id: 'req-550e8400-e29b-41d4-a716-44665544000',
timestamp: '2024-01-15T09:30:00.000Z',
help_url: 'https://docs.example.com/errors/20003'
}
const errorCodeItems = [
{ code: '1XXYY', desc: '通用错误(第1位固定为1' },
{ code: '10001', desc: '参数错误' },
{ code: '10010', desc: '用户不存在' },
{ code: '10018', desc: 'Token 已过期' },
{ code: '10021', desc: '权限不足' },
{ code: '20003', desc: '密码强度不足' },
{ code: '20014', desc: '余额不足' }
]
</script>
<style scoped>
.demo-container {
.demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 20px;
border-radius: 12px;
background: var(--vp-c-bg-soft);
margin: 24px 0;
overflow: hidden;
}
.demo-header {
margin-bottom: 20px;
}
.demo-header h4 {
margin: 0 0 8px 0;
color: var(--vp-c-text-1);
}
.hint {
margin: 0;
font-size: 14px;
color: var(--vp-c-text-2);
}
.demo-content {
.header {
padding: 16px 20px;
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
color: white;
display: flex;
flex-direction: column;
gap: 16px;
}
.error-types {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.error-item {
.icon {
font-size: 24px;
}
.title {
font-weight: 600;
font-size: 16px;
}
.content {
padding: 24px;
}
.comparison-tabs {
display: flex;
gap: 12px;
margin-bottom: 20px;
}
.tab-btn {
flex: 1;
padding: 12px;
border: 2px solid;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.tab-btn.bad {
border-color: #ef4444;
background: var(--vp-c-bg);
color: #ef4444;
}
.tab-btn.bad:hover {
background: #fef2f2;
}
.tab-btn.bad.active {
background: #ef4444;
color: white;
}
.tab-btn.good {
border-color: #22c55e;
background: var(--vp-c-bg);
color: #22c55e;
}
.tab-btn.good:hover {
background: #f0fdf4;
}
.tab-btn.good.active {
background: #22c55e;
color: white;
}
.comparison {
background: var(--vp-c-bg);
border-radius: 6px;
overflow: hidden;
}
.response-preview {
margin-bottom: 20px;
}
.status-line {
display: flex;
align-items: center;
gap: 16px;
gap: 12px;
padding: 12px 16px;
background: var(--vp-c-bg);
border-radius: 8px;
border-left: 4px solid #f56c6c;
}
.error-item .code {
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-divider);
font-family: monospace;
font-weight: 600;
color: #f56c6c;
font-size: 16px;
min-width: 50px;
font-size: 13px;
}
.error-item .name {
font-weight: 500;
.status-line.bad .code {
color: #ef4444;
font-weight: 700;
}
.status-line:not(.bad) .code {
color: #d97706;
font-weight: 700;
}
.response-body {
padding: 16px;
}
.response-body pre {
margin: 0;
background: var(--vp-c-bg-alt);
padding: 16px;
border-radius: 6px;
font-size: 12px;
line-height: 1.5;
overflow-x: auto;
}
.response-body code {
font-family: monospace;
color: var(--vp-c-text-1);
min-width: 120px;
}
.error-item .desc {
color: var(--vp-c-text-2);
.problems, .highlights {
padding: 16px;
}
.problems h4, .highlights h4 {
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 12px 0;
}
.problems ul, .highlights ul {
margin: 0;
padding-left: 0;
list-style: none;
}
.problems li, .highlights li {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 10px;
margin-bottom: 8px;
border-radius: 6px;
line-height: 1.6;
font-size: 13px;
}
.problems li {
background: #fef2f2;
color: #991b1b;
}
.highlights li {
background: #f0fdf4;
color: #166534;
}
.problems li .icon, .highlights li .icon {
font-size: 16px;
flex-shrink: 0;
}
.problems li strong, .highlights li strong {
font-weight: 600;
}
.error-codes {
padding: 16px;
border-top: 1px solid var(--vp-c-divider);
}
.error-codes h4 {
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 12px 0;
}
.code-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.code-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
border-left: 3px solid var(--vp-c-brand);
}
.code-badge {
font-family: monospace;
font-size: 12px;
font-weight: 700;
padding: 4px 8px;
background: var(--vp-c-brand);
color: white;
border-radius: 4px;
min-width: 70px;
text-align: center;
}
.code-desc {
font-size: 13px;
color: var(--vp-c-text-2);
}
@media (max-width: 640px) {
.comparison-tabs {
flex-direction: column;
}
.status-line {
flex-wrap: wrap;
}
}
</style>
@@ -268,7 +268,7 @@ const currentMethod = computed(() =>
.method-detail {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -306,7 +306,7 @@ const currentMethod = computed(() =>
align-items: center;
padding: 12px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
opacity: 0.6;
transition: all 0.2s ease;
@@ -361,7 +361,7 @@ const currentMethod = computed(() =>
.example-item {
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 12px;
margin-bottom: 12px;
}
@@ -48,7 +48,7 @@
<style scoped>
.demo-container {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
background: var(--vp-c-bg-soft);
}
@@ -82,7 +82,7 @@
.section {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
border-left: 4px solid var(--vp-c-brand);
}
@@ -39,7 +39,7 @@
<style scoped>
.demo-container {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
background: var(--vp-c-bg-soft);
}
@@ -77,7 +77,7 @@
gap: 12px;
padding: 12px;
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
}
.icon {
@@ -1,50 +1,397 @@
<!--
ResponseStructureDemo.vue - HTTP 响应结构演示组件
展示标准化 API 响应结构和分页响应
-->
<template>
<div class="demo-container">
<div class="demo-header">
<h4>{{ title }}</h4>
<p class="hint">{{ description }}</p>
<div class="demo">
<div class="header">
<span class="icon">📦</span>
<span class="title">HTTP 响应结构标准化的数据契约</span>
</div>
<div class="demo-content">
<el-alert type="info" :closable="false">
响应结构演示组件占位符 - 待实现具体交互
</el-alert>
<div class="content">
<div class="response-tabs">
<button
v-for="tab in tabs"
:key="tab.id"
class="tab-btn"
:class="{ active: selectedTab === tab.id }"
@click="selectedTab = tab.id"
>
{{ tab.name }}
</button>
</div>
<div class="response-detail">
<div class="response-header">
<div class="status-line">
<span class="http-version">HTTP/1.1</span>
<span class="status-code" :class="getStatusClass(currentResponse.status)">{{ currentResponse.status }}</span>
<span class="status-text">{{ currentResponse.statusText }}</span>
</div>
</div>
<div class="response-headers">
<div class="header-item">
<span class="header-key">Content-Type:</span>
<span class="header-value">application/json</span>
</div>
<div class="header-item">
<span class="header-key">X-Request-ID:</span>
<span class="header-value">req-550e8400-e29b-41d4</span>
</div>
<div class="header-item">
<span class="header-key">X-Response-Time:</span>
<span class="header-value">45ms</span>
</div>
</div>
<div class="response-body">
<pre><code>{{ JSON.stringify(currentResponse.body, null, 2) }}</code></pre>
</div>
<div class="field-descriptions">
<h4>字段说明</h4>
<div class="field-list">
<div v-for="field in currentResponse.fields" :key="field.name" class="field-item">
<div class="field-name">
<code>{{ field.name }}</code>
<span class="field-type">{{ field.type }}</span>
</div>
<div class="field-desc">{{ field.description }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
const title = ref('HTTP响应结构演示')
const description = ref('展示HTTP响应的结构,包括状态行、响应头和响应')
const tabs = [
{ id: 'success', name: '成功响应' },
{ id: 'pagination', name: '分页响应' },
{ id: 'error', name: '错误响应' }
]
const responses = {
success: {
status: 200,
statusText: 'OK',
body: {
code: 0,
message: 'success',
data: {
id: 123,
name: '张三',
email: 'zhangsan@example.com',
phone: '13800138000',
created_at: '2024-01-15T10:30:00.000Z'
},
request_id: 'req-550e8400-e29b-41d4-a716-446655440000',
timestamp: '2024-01-15T10:30:00.000Z'
},
fields: [
{ name: 'code', type: 'integer', description: '业务状态码,0 表示成功' },
{ name: 'message', type: 'string', description: '状态描述,成功时为 "success"' },
{ name: 'data', type: 'object', description: '业务数据,成功时返回具体数据' },
{ name: 'request_id', type: 'string', description: '请求唯一标识,用于问题追踪' },
{ name: 'timestamp', type: 'string', description: '响应时间戳,ISO 8601 格式' }
]
},
pagination: {
status: 200,
statusText: 'OK',
body: {
code: 0,
message: 'success',
data: {
list: [
{ id: 1, name: '商品A', price: 100 },
{ id: 2, name: '商品B', price: 200 }
],
pagination: {
page: 1,
page_size: 20,
total: 156,
total_pages: 8,
has_next: true,
has_prev: false
}
},
request_id: 'req-550e8400-e29b-41d4-a716-446655440000',
timestamp: '2024-01-15T10:30:00.000Z'
},
fields: [
{ name: 'list', type: 'array', description: '数据列表' },
{ name: 'pagination', type: 'object', description: '分页信息对象' },
{ name: 'page', type: 'integer', description: '当前页码' },
{ name: 'page_size', type: 'integer', description: '每页数量' },
{ name: 'total', type: 'integer', description: '总记录数' },
{ name: 'total_pages', type: 'integer', description: '总页数' },
{ name: 'has_next', type: 'boolean', description: '是否有下一页' },
{ name: 'has_prev', type: 'boolean', description: '是否有上一页' }
]
},
error: {
status: 422,
statusText: 'Unprocessable Entity',
body: {
code: 20003,
message: '密码强度不足',
errors: [
{
field: 'password',
code: 'VALIDATION_ERROR',
message: '密码必须包含至少 1 个大写字母、1 个小写字母、1 个数字,且长度至少 8 位'
}
],
request_id: 'req-550e8400-e29b-41d4-a716-446655440000',
timestamp: '2024-01-15T10:30:00.000Z',
help_url: 'https://docs.example.com/errors/20003'
},
fields: [
{ name: 'code', type: 'integer', description: '错误码,非 0 表示失败' },
{ name: 'message', type: 'string', description: '错误描述,供用户阅读' },
{ name: 'errors', type: 'array', description: '详细错误信息数组(可选)' },
{ name: 'help_url', type: 'string', description: '错误文档链接(可选)' }
]
}
}
const selectedTab = ref('success')
const currentResponse = computed(() => responses[selectedTab.value])
function getStatusClass(status) {
const prefix = Math.floor(status / 100)
switch (prefix) {
case 2: return 'success'
case 3: return 'redirect'
case 4: return 'client-error'
case 5: return 'server-error'
default: return ''
}
}
</script>
<style scoped>
.demo-container {
.demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 20px;
border-radius: 12px;
background: var(--vp-c-bg-soft);
margin: 24px 0;
overflow: hidden;
}
.demo-header {
.header {
padding: 16px 20px;
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
color: white;
display: flex;
align-items: center;
gap: 12px;
}
.icon {
font-size: 24px;
}
.title {
font-weight: 600;
font-size: 16px;
}
.content {
padding: 24px;
}
.response-tabs {
display: flex;
gap: 8px;
margin-bottom: 20px;
}
.demo-header h4 {
margin: 0 0 8px 0;
color: var(--vp-c-text-1);
.tab-btn {
padding: 8px 16px;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
}
.hint {
margin: 0;
font-size: 14px;
.tab-btn:hover {
transform: translateY(-1px);
}
.tab-btn.active {
background: var(--vp-c-brand);
color: white;
border-color: var(--vp-c-brand);
transform: scale(1.05);
}
.response-detail {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
}
.response-header {
padding: 16px;
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-divider);
}
.status-line {
display: flex;
align-items: center;
gap: 12px;
}
.http-version {
font-family: monospace;
font-size: 13px;
color: var(--vp-c-text-2);
}
.demo-content {
.status-code {
padding: 4px 8px;
border-radius: 4px;
font-weight: 700;
font-size: 13px;
font-family: monospace;
}
.status-code.success {
background: #dcfce7;
color: #16a34a;
}
.status-code.client-error {
background: #fef3c7;
color: #d97706;
}
.status-text {
font-size: 13px;
color: var(--vp-c-text-2);
}
.response-headers {
padding: 12px 16px;
border-bottom: 1px solid var(--vp-c-divider);
}
.header-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 0;
font-size: 12px;
}
.header-key {
font-family: monospace;
font-weight: 600;
color: var(--vp-c-text-2);
min-width: 120px;
}
.header-value {
font-family: monospace;
color: var(--vp-c-text-1);
}
.response-body {
padding: 16px;
border-bottom: 1px solid var(--vp-c-divider);
}
.response-body pre {
margin: 0;
background: var(--vp-c-bg-alt);
padding: 16px;
border-radius: 6px;
font-size: 12px;
line-height: 1.5;
overflow-x: auto;
}
.response-body code {
font-family: monospace;
color: var(--vp-c-text-1);
}
.field-descriptions {
padding: 16px;
}
.field-descriptions h4 {
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 12px 0;
}
.field-list {
display: flex;
flex-direction: column;
gap: 16px;
gap: 12px;
}
.field-item {
padding: 12px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
border-left: 3px solid var(--vp-c-brand);
}
.field-name {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.field-name code {
font-family: monospace;
font-size: 13px;
color: var(--vp-c-brand);
font-weight: 600;
}
.field-type {
font-size: 11px;
padding: 2px 6px;
background: rgba(var(--vp-c-brand-rgb), 0.1);
color: var(--vp-c-brand);
border-radius: 4px;
font-family: monospace;
}
.field-desc {
font-size: 13px;
color: var(--vp-c-text-2);
line-height: 1.6;
}
@media (max-width: 640px) {
.response-tabs {
flex-direction: column;
}
.status-line {
flex-wrap: wrap;
}
.header-item {
flex-direction: column;
align-items: flex-start;
}
}
</style>
@@ -194,9 +194,9 @@ const activePrinciple = computed(() =>
.restful-design-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -225,8 +225,8 @@ const activePrinciple = computed(() =>
.principle-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
cursor: pointer;
transition: all 0.2s;
text-align: center;
@@ -260,7 +260,7 @@ const activePrinciple = computed(() =>
.detail-panel {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 1.25rem;
}
@@ -417,7 +417,7 @@ function toggleExpand(number) {
align-items: center;
padding: 12px 20px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
transition: all 0.2s ease;
@@ -465,7 +465,7 @@ function toggleExpand(number) {
.status-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
cursor: pointer;
transition: all 0.2s ease;
@@ -1,50 +1,413 @@
<!--
VersioningStrategyDemo.vue - API 版本控制策略演示
展示 4 种版本控制策略的对比
-->
<template>
<div class="demo-container">
<div class="demo-header">
<h4>{{ title }}</h4>
<p class="hint">{{ description }}</p>
<div class="demo">
<div class="header">
<span class="icon">🔢</span>
<span class="title">API 版本控制向后兼容的艺术</span>
</div>
<div class="demo-content">
<el-alert type="info" :closable="false">
版本策略演示组件占位符 - 待实现具体交互
</el-alert>
<div class="content">
<div class="strategies">
<div
v-for="strategy in strategies"
:key="strategy.id"
class="strategy-card"
:class="{ active: selectedStrategy === strategy.id }"
@click="selectedStrategy = strategy.id"
>
<div class="strategy-header">
<div class="strategy-name">{{ strategy.name }}</div>
<div class="strategy-stars">
<span v-for="n in strategy.stars" :key="n" class="star"></span>
</div>
</div>
<div class="strategy-example">{{ strategy.example }}</div>
</div>
</div>
<div class="strategy-detail" v-if="currentStrategy">
<div class="detail-header">
<div class="detail-title">{{ currentStrategy.name }}</div>
<div class="detail-recommendation" :class="currentStrategy.level">
{{ currentStrategy.level === 'high' ? '强烈推荐' : currentStrategy.level === 'medium' ? '可以使用' : '不推荐' }}
</div>
</div>
<div class="detail-sections">
<div class="detail-section">
<h4> 优点</h4>
<ul>
<li v-for="(pro, idx) in currentStrategy.pros" :key="idx">{{ pro }}</li>
</ul>
</div>
<div class="detail-section">
<h4> 缺点</h4>
<ul>
<li v-for="(con, idx) in currentStrategy.cons" :key="idx">{{ con }}</li>
</ul>
</div>
</div>
<div class="detail-section example">
<h4>💻 实现示例</h4>
<div class="code-box">
<div class="code-header">Request</div>
<pre><code>{{ currentStrategy.codeExample.request }}</code></pre>
<div class="code-header">Response Headers</div>
<pre><code>{{ currentStrategy.codeExample.response }}</code></pre>
</div>
</div>
<div class="detail-section tips">
<h4>💡 最佳实践</h4>
<ul>
<li v-for="(tip, idx) in currentStrategy.tips" :key="idx">{{ tip }}</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
const title = ref('版本策略演示')
const description = ref('展示API版本控制的策略,包括URL版本、Header版本、内容协商等方式')
const strategies = [
{
id: 'url-path',
name: 'URL Path 版本',
example: '/v1/users',
stars: 4,
level: 'high',
pros: [
'最直观,一目了然看到版本号',
'易于缓存和控制权限',
'文档清晰,社区主流做法',
'支持不同版本的并行部署'
],
cons: [
'URL 会变化,不符合 REST 资源唯一性',
'需要配置路由规则'
],
codeExample: {
request: `GET /v1/users HTTP/1.1
Host: api.example.com`,
response: `HTTP/1.1 200 OK
Content-Type: application/json
X-API-Version: v1`
},
tips: [
'版本号放在路径开头:`/v1/users`',
'使用语义化版本号(Semantic Versioning',
'废弃版本返回 Sunset 头部',
'客户端升级提示可通过响应头提示'
]
},
{
id: 'header',
name: 'Header 版本',
example: 'API-Version: v1',
stars: 2,
level: 'medium',
pros: [
'URL 保持简洁不变',
'版本控制不影响路由'
],
cons: [
'不直观,需要在工具里配置 Header',
'缓存策略复杂',
'文档不够清晰',
'调试不便'
],
codeExample: {
request: `GET /users HTTP/1.1
Host: api.example.com
API-Version: v1`,
response: `HTTP/1.1 200 OK
Content-Type: application/json
X-API-Version: v1`
},
tips: [
'使用自定义 Header`API-Version` 或 `Accept`',
'需在 API Gateway 中统一处理',
'适合内部系统或对 API 规范要求高的场景'
]
},
{
id: 'content-negotiation',
name: '内容协商',
example: 'Accept: application/vnd.api.v1+json',
stars: 2,
level: 'medium',
pros: [
'符合 HTTP 标准',
'URL 完全不变'
],
cons: [
'复杂,理解成本高',
'开发者容易用错',
'缓存和代理支持不佳'
],
codeExample: {
request: `GET /users HTTP/1.1
Host: api.example.com
Accept: application/vnd.api.v1+json`,
response: `HTTP/1.1 200 OK
Content-Type: application/vnd.api.v1+json`
},
tips: [
'使用 Vendor MIME 类型:`application/vnd.{company}.{resource}.v{version}+json`',
'需要 API Gateway 或框架支持内容协商',
'GitHub API 使用此策略'
]
},
{
id: 'query-param',
name: 'Query 参数',
example: '/users?version=v1',
stars: 1,
level: 'low',
pros: [
'实现简单'
],
cons: [
'不专业,容易忽视',
'缓存麻烦(不同参数视为不同资源)',
'URL 混乱'
],
codeExample: {
request: `GET /users?version=v1 HTTP/1.1
Host: api.example.com`,
response: `HTTP/1.1 200 OK
Content-Type: application/json`
},
tips: [
'仅用于快速原型或内部工具',
'生产环境不推荐使用'
]
}
]
const selectedStrategy = ref('url-path')
const currentStrategy = computed(() =>
strategies.find(s => s.id === selectedStrategy.value)
)
</script>
<style scoped>
.demo-container {
.demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 20px;
border-radius: 12px;
background: var(--vp-c-bg-soft);
margin: 24px 0;
overflow: hidden;
}
.demo-header {
margin-bottom: 20px;
.header {
padding: 16px 20px;
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
color: white;
display: flex;
align-items: center;
gap: 12px;
}
.demo-header h4 {
margin: 0 0 8px 0;
.icon {
font-size: 24px;
}
.title {
font-weight: 600;
font-size: 16px;
}
.content {
padding: 24px;
}
.strategies {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.strategy-card {
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 6px;
padding: 16px;
cursor: pointer;
transition: all 0.2s ease;
}
.strategy-card:hover {
border-color: rgba(var(--vp-c-brand-rgb), 0.5);
transform: translateY(-2px);
}
.strategy-card.active {
border-color: var(--vp-c-brand);
box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb), 0.15);
}
.strategy-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.strategy-name {
font-weight: 600;
font-size: 14px;
color: var(--vp-c-text-1);
}
.hint {
margin: 0;
font-size: 14px;
color: var(--vp-c-text-2);
.strategy-stars {
display: flex;
gap: 2px;
}
.demo-content {
.star {
font-size: 12px;
}
.strategy-example {
font-family: monospace;
font-size: 12px;
color: var(--vp-c-text-2);
background: var(--vp-c-bg-soft);
padding: 6px 10px;
border-radius: 4px;
margin-top: 8px;
}
.strategy-detail {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
}
.detail-header {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 16px;
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-divider);
}
.detail-title {
font-weight: 700;
font-size: 16px;
color: var(--vp-c-text-1);
}
.detail-recommendation {
padding: 4px 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
}
.detail-recommendation.high {
background: #dcfce7;
color: #16a34a;
}
.detail-recommendation.medium {
background: #fef3c7;
color: #d97706;
}
.detail-recommendation.low {
background: #fee2e2;
color: #dc2626;
}
.detail-sections {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
padding: 16px;
}
.detail-section {
padding: 16px;
}
.detail-section h4 {
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 12px 0;
}
.detail-section ul {
margin: 0;
padding-left: 20px;
}
.detail-section li {
font-size: 13px;
color: var(--vp-c-text-2);
line-height: 1.6;
margin: 6px 0;
}
.detail-section.example {
grid-column: 1 / -1;
padding: 16px;
background: var(--vp-c-bg-soft);
}
.code-box {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
}
.code-header {
padding: 8px 12px;
background: var(--vp-c-bg-soft);
font-size: 12px;
font-weight: 600;
color: var(--vp-c-text-2);
border-bottom: 1px solid var(--vp-c-divider);
}
.code-box pre {
margin: 0;
padding: 12px;
font-size: 12px;
line-height: 1.5;
overflow-x: auto;
}
.code-box code {
font-family: monospace;
color: var(--vp-c-text-1);
}
.detail-section.tips {
background: #eff6ff;
border-left: 3px solid #3b82f6;
}
@media (max-width: 768px) {
.strategies {
grid-template-columns: 1fr;
}
.detail-sections {
grid-template-columns: 1fr;
}
}
</style>
@@ -250,7 +250,7 @@ function sendRequest() {
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 8px;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
@@ -303,7 +303,7 @@ function sendRequest() {
font-family: monospace;
font-size: 12px;
overflow-x: auto;
overflow-y: auto;
max-height: 200px;
margin: 0;
}
@@ -150,7 +150,7 @@ const isHuman = ref(false)
.api-doc {
background: #1e293b;
border-radius: 8px;
border-radius: 6px;
padding: 20px;
color: #e2e8f0;
font-family: monospace;
@@ -237,7 +237,7 @@
.method-examples {
background: var(--vp-c-bg-soft);
padding: 12px;
border-radius: 8px;
border-radius: 6px;
margin-bottom: 12px;
}
@@ -249,7 +249,7 @@
.method-tip {
padding: 10px 12px;
border-radius: 8px;
border-radius: 6px;
font-size: 12px;
line-height: 1.5;
}
@@ -339,7 +339,7 @@ tr:last-child td {
.tips {
background: var(--vp-c-bg);
padding: 16px;
border-radius: 8px;
border-radius: 6px;
font-size: 14px;
line-height: 1.8;
color: var(--vp-c-text-2);
@@ -345,7 +345,7 @@ function sendRequest() {
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 8px;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
margin-top: 8px;
@@ -415,7 +415,7 @@ function sendRequest() {
padding: 12px;
margin-bottom: 12px;
overflow-x: auto;
overflow-y: auto;
max-height: 180px;
}
@@ -143,7 +143,7 @@ function callApi() {
.response-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
animation: slideUp 0.3s ease-out;
}
@@ -349,7 +349,7 @@ async function runDemo() {
background: #1e293b;
color: #e2e8f0;
padding: 12px;
border-radius: 8px;
border-radius: 6px;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 12px;
line-height: 1.6;
@@ -371,7 +371,7 @@ async function runDemo() {
.summary {
background: var(--vp-c-bg-soft);
padding: 16px;
border-radius: 8px;
border-radius: 6px;
font-size: 14px;
line-height: 1.8;
}
@@ -443,7 +443,7 @@ async function runDemo() {
.result-body {
background: #f0fdf4;
border: 2px solid #86efac;
border-radius: 8px;
border-radius: 6px;
padding: 16px;
font-size: 14px;
line-height: 1.6;
@@ -163,7 +163,7 @@ function send() {
padding: 10px 20px;
font-size: 14px;
font-weight: bold;
border-radius: 8px;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
}
@@ -183,7 +183,7 @@ function send() {
.result-box {
padding: 12px 16px;
border-radius: 8px;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
}
@@ -378,7 +378,7 @@ onMounted(() => {
.flow-section {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -418,7 +418,7 @@ onMounted(() => {
padding: 16px;
background: var(--vp-c-bg-soft);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
@@ -458,14 +458,14 @@ onMounted(() => {
padding: 12px;
background: var(--vp-c-bg-soft);
border: 1px dashed var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
cursor: pointer;
color: var(--vp-c-text-2);
}
.audio-preview {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 12px;
}
@@ -479,7 +479,7 @@ onMounted(() => {
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 8px;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
display: flex;
@@ -512,7 +512,7 @@ onMounted(() => {
.result-box {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
border: 1px solid var(--vp-c-divider);
}
@@ -540,7 +540,7 @@ textarea {
width: 100%;
padding: 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
font-size: 14px;
resize: vertical;
@@ -654,7 +654,7 @@ textarea {
.comparison-section {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -673,7 +673,7 @@ textarea {
.comp-card {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
text-align: center;
}
@@ -705,7 +705,7 @@ textarea {
.pipeline-comparison {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -724,7 +724,7 @@ textarea {
.pipeline {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
}
@@ -761,7 +761,7 @@ textarea {
gap: 12px;
padding: 16px;
background: var(--vp-c-bg-mute);
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
}
@@ -462,7 +462,7 @@ onUnmounted(() => {
width: 100%;
padding: 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
font-size: 14px;
resize: vertical;
background: var(--vp-c-bg);
@@ -504,7 +504,7 @@ onUnmounted(() => {
.action-btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-mute);
cursor: pointer;
font-size: 14px;
@@ -557,7 +557,7 @@ onUnmounted(() => {
width: 100%;
padding: 16px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
}
.result-label {
@@ -592,7 +592,7 @@ onUnmounted(() => {
gap: 12px;
padding: 16px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
opacity: 0.5;
transition: all 0.2s;
}
@@ -692,7 +692,7 @@ onUnmounted(() => {
.embedding-viz {
padding: 16px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
}
.viz-title {
@@ -720,7 +720,7 @@ onUnmounted(() => {
.waveform-container {
padding: 16px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
}
.waveform-container canvas {
@@ -777,7 +777,7 @@ onUnmounted(() => {
gap: 8px;
padding: 12px;
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
}
@@ -334,7 +334,7 @@ onMounted(() => {
<style scoped>
.audio-tokenization-demo {
margin: 1rem 0;
margin: 0.5rem 0;
}
.header-title {
@@ -352,7 +352,7 @@ onMounted(() => {
.codec-flow {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -385,7 +385,7 @@ onMounted(() => {
.step-visual {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 12px;
margin-bottom: 8px;
min-height: 80px;
@@ -484,7 +484,7 @@ onMounted(() => {
.bitrate-comparison {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -502,7 +502,7 @@ onMounted(() => {
.bitrate-card {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
text-align: center;
cursor: pointer;
@@ -549,7 +549,7 @@ onMounted(() => {
.token-visualization {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -561,7 +561,7 @@ onMounted(() => {
.token-display {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
overflow-x: auto;
}
@@ -619,7 +619,7 @@ onMounted(() => {
.applications {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -637,7 +637,7 @@ onMounted(() => {
.app-card {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
text-align: center;
}
@@ -56,7 +56,7 @@
<style scoped>
.waveform-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
background: var(--vp-c-bg-soft);
margin: 20px 0;
@@ -319,7 +319,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
<style scoped>
.emotion-control-demo {
margin: 1rem 0;
margin: 0.5rem 0;
}
.header-title {
@@ -337,7 +337,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
.emotion-selector {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -354,7 +354,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
.emotion-card {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
text-align: center;
cursor: pointer;
@@ -389,7 +389,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
.emotion-embedding {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -404,7 +404,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
height: auto;
max-height: 200px;
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
}
.embedding-legend {
@@ -430,7 +430,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
.parameter-controls {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -447,7 +447,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
.control-item {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
}
@@ -468,7 +468,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
.preview-section {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -488,7 +488,7 @@ watch(selectedEmotion, drawEmotionEmbedding)
.tech-explanation {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
}
@@ -342,7 +342,7 @@ watch([selectedType, fftSize, melBins], updateVisualization)
margin-bottom: 24px;
padding: 16px;
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
}
.audio-types {
@@ -405,7 +405,7 @@ watch([selectedType, fftSize, melBins], updateVisualization)
.visualization {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -416,7 +416,7 @@ watch([selectedType, fftSize, melBins], updateVisualization)
.viz-section.highlight {
border: 2px solid #67c23a;
border-radius: 8px;
border-radius: 6px;
padding: 12px;
}
@@ -486,7 +486,7 @@ watch([selectedType, fftSize, melBins], updateVisualization)
.explanation {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -518,7 +518,7 @@ watch([selectedType, fftSize, melBins], updateVisualization)
height: 80px;
padding: 10px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
}
.freq-bars .bar {
@@ -545,7 +545,7 @@ watch([selectedType, fftSize, melBins], updateVisualization)
gap: 12px;
padding: 16px;
background: var(--vp-c-bg-mute);
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
}
@@ -301,7 +301,7 @@ watch([selectedArch, activeStage], drawVisualization)
.arch-btn {
padding: 12px 20px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
display: flex;
@@ -344,7 +344,7 @@ watch([selectedArch, activeStage], drawVisualization)
flex-wrap: wrap;
padding: 20px;
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
margin-bottom: 20px;
}
@@ -358,7 +358,7 @@ watch([selectedArch, activeStage], drawVisualization)
.stage-content {
background: var(--vp-c-bg-soft);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 12px 16px;
text-align: center;
transition: all 0.2s;
@@ -406,7 +406,7 @@ watch([selectedArch, activeStage], drawVisualization)
.stage-detail {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -433,7 +433,7 @@ watch([selectedArch, activeStage], drawVisualization)
.detail-canvas {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
margin-bottom: 16px;
}
@@ -459,7 +459,7 @@ watch([selectedArch, activeStage], drawVisualization)
.comparison-table {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -475,7 +475,7 @@ watch([selectedArch, activeStage], drawVisualization)
flex-direction: column;
gap: 1px;
background: var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
}
@@ -510,7 +510,7 @@ watch([selectedArch, activeStage], drawVisualization)
.models-section {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -529,7 +529,7 @@ watch([selectedArch, activeStage], drawVisualization)
.model-card {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
text-align: center;
border: 2px solid transparent;
@@ -567,7 +567,7 @@ watch([selectedArch, activeStage], drawVisualization)
gap: 12px;
padding: 16px;
background: var(--vp-c-bg-mute);
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
}
@@ -369,7 +369,7 @@ onMounted(() => {
.demo-area {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -412,7 +412,7 @@ onMounted(() => {
.audio-card {
background: var(--vp-c-bg-soft);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
text-align: center;
cursor: pointer;
@@ -472,7 +472,7 @@ onMounted(() => {
width: 100%;
padding: 12px;
border: 2px dashed var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
cursor: pointer;
color: var(--vp-c-text-2);
@@ -499,7 +499,7 @@ onMounted(() => {
gap: 8px;
padding: 12px 16px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
opacity: 0.5;
transition: all 0.3s;
}
@@ -525,7 +525,7 @@ onMounted(() => {
.feature-viz {
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 16px;
text-align: center;
}
@@ -545,7 +545,7 @@ onMounted(() => {
width: 100%;
padding: 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
font-size: 14px;
resize: vertical;
@@ -558,7 +558,7 @@ onMounted(() => {
background: linear-gradient(120deg, #409eff, #67c23a);
color: white;
border: none;
border-radius: 8px;
border-radius: 6px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
@@ -595,7 +595,7 @@ onMounted(() => {
margin-top: 16px;
padding: 16px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
border: 2px solid #67c23a;
}
@@ -653,7 +653,7 @@ onMounted(() => {
.tips-section {
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
@@ -675,7 +675,7 @@ onMounted(() => {
gap: 12px;
padding: 16px;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
}
.tip-icon {
@@ -699,7 +699,7 @@ onMounted(() => {
gap: 12px;
padding: 16px;
background: var(--vp-c-bg-mute);
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
}
@@ -131,9 +131,9 @@ const active = computed(
.auth-basics-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -197,8 +197,8 @@ const active = computed(
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -233,7 +233,7 @@ const active = computed(
.box {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -127,9 +127,9 @@ const active = computed(
.auth-evolution-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -151,13 +151,13 @@ const active = computed(
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.75rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.stage {
text-align: left;
padding: 0.75rem;
border-radius: 8px;
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
cursor: pointer;
@@ -193,8 +193,8 @@ const active = computed(
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -219,7 +219,7 @@ const active = computed(
.box {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -377,9 +377,9 @@ const resetDemo = () => {
.auth-interactive-login {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -421,7 +421,7 @@ const resetDemo = () => {
.mode-btn {
padding: 0.6rem 1.2rem;
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg);
cursor: pointer;
font-size: 0.9rem;
@@ -616,8 +616,8 @@ const resetDemo = () => {
.flow-stage {
background: var(--vp-c-bg-soft);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
animation: slideIn 0.4s ease;
}
@@ -827,7 +827,7 @@ const resetDemo = () => {
.reset-btn {
padding: 0.75rem 2rem;
border: none;
border-radius: 8px;
border-radius: 6px;
background: #64748b;
color: white;
font-weight: 600;
@@ -149,9 +149,9 @@ const decisionLog = computed(() => {
.authn-authz-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -179,8 +179,8 @@ const decisionLog = computed(() => {
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -229,7 +229,7 @@ const decisionLog = computed(() => {
.result {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 0.75rem;
}
@@ -153,9 +153,9 @@ const reset = () => {
.csrf-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -218,8 +218,8 @@ const reset = () => {
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -237,7 +237,7 @@ const reset = () => {
margin-top: 0.75rem;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -272,7 +272,7 @@ const reset = () => {
margin-top: 0.75rem;
border: 1px solid rgba(var(--vp-c-brand-rgb), 0.18);
background: rgba(var(--vp-c-brand-rgb), 0.06);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -174,9 +174,9 @@ const copy = async (text) => {
.jwt-workflow-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -239,8 +239,8 @@ const copy = async (text) => {
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -277,7 +277,7 @@ const copy = async (text) => {
min-width: 220px;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
opacity: 0.6;
}
@@ -308,7 +308,7 @@ const copy = async (text) => {
margin-top: 0.75rem;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -339,7 +339,7 @@ const copy = async (text) => {
margin-top: 0.75rem;
border: 1px solid rgba(var(--vp-c-brand-rgb), 0.18);
background: rgba(var(--vp-c-brand-rgb), 0.06);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -200,9 +200,9 @@ const copy = async (text) => {
.oauth2-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -265,8 +265,8 @@ const copy = async (text) => {
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -300,7 +300,7 @@ const copy = async (text) => {
margin-top: 0.75rem;
border: 1px solid rgba(var(--vp-c-brand-rgb), 0.18);
background: rgba(var(--vp-c-brand-rgb), 0.06);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -227,9 +227,9 @@ onMounted(() => {
.password-hashing-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -258,8 +258,8 @@ onMounted(() => {
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -345,7 +345,7 @@ onMounted(() => {
.mono-box {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
overflow-x: auto;
}
@@ -366,7 +366,7 @@ onMounted(() => {
margin-top: 0.75rem;
border: 1px solid rgba(var(--vp-c-brand-rgb), 0.18);
background: rgba(var(--vp-c-brand-rgb), 0.06);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -193,9 +193,9 @@ Set-Cookie: session_id=; Max-Age=0`
.session-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -258,8 +258,8 @@ Set-Cookie: session_id=; Max-Age=0`
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -271,7 +271,7 @@ Set-Cookie: session_id=; Max-Age=0`
.box {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 0.75rem;
}
@@ -339,7 +339,7 @@ Set-Cookie: session_id=; Max-Age=0`
margin-top: 0.75rem;
border: 1px solid rgba(var(--vp-c-brand-rgb), 0.18);
background: rgba(var(--vp-c-brand-rgb), 0.06);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
}
@@ -194,9 +194,9 @@ const recommendation = computed(() => {
.session-vs-jwt-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.header {
@@ -224,8 +224,8 @@ const recommendation = computed(() => {
.card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.card-title {
@@ -294,7 +294,7 @@ const recommendation = computed(() => {
.box {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
border-radius: 8px;
border-radius: 6px;
padding: 0.75rem;
margin-top: 0.75rem;
}
@@ -1,8 +1,9 @@
<template>
<div class="architecture-comparison-demo">
<div class="demo-header">
<h4>🏗 架构演进对比</h4>
<p>四个时代的核心架构特征对比</p>
<span class="icon">🏗</span>
<span class="title">架构演进对比</span>
<span class="subtitle">四个时代的核心架构特征</span>
</div>
<div class="comparison-grid">
@@ -50,6 +51,11 @@
</div>
</div>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想:</strong>架构演进是为了解决上一个时代的痛点,但也带来了新的复杂度
</div>
</div>
</template>
@@ -106,40 +112,49 @@ const currentEra = computed(() => {
<style scoped>
.architecture-comparison-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.4rem;
margin-bottom: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.demo-header h4 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
.demo-header .icon {
font-size: 1rem;
}
.demo-header .title {
font-weight: bold;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.demo-header p {
margin: 0;
font-size: 0.9rem;
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.75rem;
margin-left: 0.4rem;
}
.comparison-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.75rem;
margin-bottom: 1.5rem;
gap: 0.4rem;
margin-bottom: 0.75rem;
}
.era-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 4px;
padding: 0.5rem;
text-align: center;
cursor: pointer;
transition: all 0.2s;
@@ -147,96 +162,95 @@ const currentEra = computed(() => {
.era-card:hover {
border-color: var(--vp-c-brand);
transform: translateY(-2px);
transform: translateY(-1px);
}
.era-card.active {
border-color: var(--vp-c-brand);
background: rgba(102, 126, 234, 0.1);
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
background: var(--vp-c-brand-soft);
}
.era-icon {
font-size: 1.5rem;
margin-bottom: 0.5rem;
font-size: 1rem;
margin-bottom: 0.25rem;
}
.era-name {
font-weight: 600;
font-size: 0.9rem;
font-size: 0.75rem;
color: var(--vp-c-text-1);
margin-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
.era-year {
font-size: 0.75rem;
font-size: 0.6rem;
color: var(--vp-c-text-3);
margin-bottom: 0.5rem;
margin-bottom: 0.25rem;
}
.era-tag {
display: inline-block;
padding: 0.15rem 0.5rem;
padding: 0.1rem 0.3rem;
background: var(--vp-c-bg-soft);
border-radius: 10px;
font-size: 0.7rem;
border-radius: 6px;
font-size: 0.55rem;
color: var(--vp-c-text-2);
}
.detail-panel {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1.5rem;
border-radius: 4px;
padding: 0.5rem;
}
.detail-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
gap: 0.4rem;
margin-bottom: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.detail-icon {
font-size: 1.5rem;
font-size: 1rem;
}
.detail-header h5 {
margin: 0;
font-size: 1.1rem;
font-size: 0.85rem;
color: var(--vp-c-text-1);
}
.detail-content {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
gap: 0.4rem;
}
.feature-section {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 1rem;
border-radius: 4px;
padding: 0.4rem;
}
.feature-section h6 {
margin: 0 0 0.75rem 0;
font-size: 0.85rem;
margin: 0 0 0.3rem 0;
font-size: 0.7rem;
color: var(--vp-c-brand);
}
.feature-section ul {
margin: 0;
padding-left: 1.25rem;
font-size: 0.85rem;
padding-left: 0.75rem;
font-size: 0.65rem;
color: var(--vp-c-text-2);
}
.feature-section li {
margin-bottom: 0.4rem;
line-height: 1.5;
margin-bottom: 0.15rem;
line-height: 1.3;
}
.feature-section li:last-child {
@@ -246,31 +260,50 @@ const currentEra = computed(() => {
.tech-stack {
grid-column: 1 / -1;
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 1rem;
border-radius: 4px;
padding: 0.4rem;
}
.tech-stack h6 {
margin: 0 0 0.75rem 0;
font-size: 0.85rem;
margin: 0 0 0.3rem 0;
font-size: 0.7rem;
color: var(--vp-c-brand);
}
.tech-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
gap: 0.25rem;
}
.tech-tag {
padding: 0.25rem 0.75rem;
padding: 0.15rem 0.4rem;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
font-size: 0.8rem;
border-radius: 3px;
font-size: 0.6rem;
color: var(--vp-c-text-2);
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-top: 0.5rem;
display: flex;
gap: 0.2rem;
}
.info-box .icon {
flex-shrink: 0;
}
.info-box strong {
color: var(--vp-c-text-1);
}
@media (max-width: 768px) {
.comparison-grid {
grid-template-columns: repeat(2, 1fr);
@@ -433,8 +433,8 @@ const stages = [
.server-box {
background: #cbd5e1;
border: 2px solid #94a3b8;
padding: 1rem;
border-radius: 8px;
padding: 0.75rem;
border-radius: 6px;
text-align: center;
}
.file-system {
@@ -464,7 +464,7 @@ const stages = [
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
margin: 1rem 0;
margin: 0.5rem 0;
}
.module {
background: #bfdbfe;
@@ -367,7 +367,7 @@ const currentStageData = computed(() => stageData[currentStage.value])
gap: 10px;
padding: 10px 12px;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
border-radius: 6px;
font-size: 13px;
}
@@ -1,8 +1,9 @@
<template>
<div class="container-docker-demo">
<div class="demo-header">
<h4>🐳 Docker 容器化演示</h4>
<p>理解容器如何让应用"一次打包,到处运行"</p>
<span class="icon">🐳</span>
<span class="title">Docker 容器化演示</span>
<span class="subtitle">理解容器如何让应用"一次打包,到处运行"</span>
</div>
<div class="docker-visualization">
@@ -46,6 +47,11 @@
<div class="benefit-desc">{{ benefit.desc }}</div>
</div>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想</strong>容器化让应用"一次构建,到处运行"解决了环境一致性和快速部署的问题
</div>
</div>
</template>
@@ -67,41 +73,50 @@ const benefits = [
<style scoped>
.container-docker-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.4rem;
margin-bottom: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.demo-header h4 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
.demo-header .icon {
font-size: 1rem;
}
.demo-header .title {
font-weight: bold;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.demo-header p {
margin: 0;
font-size: 0.9rem;
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.75rem;
margin-left: 0.4rem;
}
.docker-visualization {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
gap: 0.5rem;
margin-bottom: 0.75rem;
align-items: stretch;
}
.layer {
flex: 1;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
padding: 0.5rem;
cursor: pointer;
transition: all 0.3s;
}
@@ -112,9 +127,9 @@ const benefits = [
}
.layer h5 {
margin: 0 0 1rem 0;
margin: 0 0 0.5rem 0;
text-align: center;
font-size: 0.95rem;
font-size: 0.75rem;
color: var(--vp-c-text-1);
}
@@ -122,14 +137,14 @@ const benefits = [
.docker-stack {
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: 0.25rem;
}
.layer-item {
padding: 0.6rem;
border-radius: 4px;
padding: 0.3rem;
border-radius: 3px;
text-align: center;
font-size: 0.8rem;
font-size: 0.65rem;
}
.layer-item.app {
@@ -164,47 +179,47 @@ const benefits = [
.containers {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
gap: 0.25rem;
}
.container-box {
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 6px;
padding: 0.5rem;
border-radius: 4px;
padding: 0.25rem;
text-align: center;
}
.container-app {
font-weight: 600;
font-size: 0.8rem;
font-size: 0.65rem;
color: var(--vp-c-brand);
margin-bottom: 0.2rem;
margin-bottom: 0.1rem;
}
.container-deps {
font-size: 0.7rem;
font-size: 0.55rem;
color: var(--vp-c-text-2);
}
.docker-engine {
padding: 0.6rem;
padding: 0.3rem;
background: rgba(16, 185, 129, 0.1);
border: 1px solid rgba(16, 185, 129, 0.3);
border-radius: 4px;
border-radius: 3px;
text-align: center;
font-size: 0.8rem;
font-size: 0.65rem;
font-weight: 600;
color: #059669;
}
.host-os,
.hardware {
padding: 0.6rem;
padding: 0.3rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
border-radius: 3px;
text-align: center;
font-size: 0.8rem;
font-size: 0.65rem;
color: var(--vp-c-text-2);
}
@@ -213,45 +228,45 @@ const benefits = [
align-items: center;
font-weight: 700;
color: var(--vp-c-text-3);
font-size: 0.9rem;
font-size: 0.75rem;
}
.benefits-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
grid-template-columns: repeat(4, 1fr);
gap: 0.4rem;
}
.benefit-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 4px;
padding: 0.5rem;
text-align: center;
transition: all 0.2s;
}
.benefit-card:hover {
border-color: var(--vp-c-brand);
transform: translateY(-2px);
transform: translateY(-1px);
}
.benefit-icon {
font-size: 2rem;
margin-bottom: 0.5rem;
font-size: 1.25rem;
margin-bottom: 0.25rem;
}
.benefit-title {
font-weight: 600;
font-size: 0.95rem;
font-size: 0.75rem;
color: var(--vp-c-text-1);
margin-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
.benefit-desc {
font-size: 0.8rem;
font-size: 0.65rem;
color: var(--vp-c-text-2);
line-height: 1.4;
line-height: 1.3;
}
@media (max-width: 768px) {
@@ -261,11 +276,30 @@ const benefits = [
.vs-divider {
justify-content: center;
padding: 0.5rem 0;
padding: 0.25rem 0;
}
.benefits-grid {
grid-template-columns: 1fr;
grid-template-columns: repeat(2, 1fr);
}
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-top: 0.5rem;
display: flex;
gap: 0.2rem;
}
.info-box .icon {
flex-shrink: 0;
}
.info-box strong {
color: var(--vp-c-text-1);
}
</style>
@@ -96,10 +96,10 @@ const steps = [
<style scoped>
.deployment-flow-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -185,7 +185,7 @@ const steps = [
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
}
.step-detail h5 {
@@ -1,8 +1,9 @@
<template>
<div class="evolution-intro-demo">
<div class="intro-header">
<h3>后端架构进化之旅</h3>
<p>用一个餐厅的成长历程理解后端架构的 30 年变迁</p>
<div class="demo-header">
<span class="icon">🏗</span>
<span class="title">后端架构进化之旅</span>
<span class="subtitle">用餐厅比喻理解 30 年架构演进</span>
</div>
<div class="timeline-cards">
@@ -20,7 +21,7 @@
</div>
</div>
<div class="stage-detail">
<div class="stage-detail" v-if="currentStage !== null">
<Transition name="fade" mode="out-in">
<div :key="currentStage" class="detail-panel">
<div class="detail-header">
@@ -46,6 +47,11 @@
</div>
</Transition>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想</strong>架构演进是为了解决上一个时代的痛点但也带来了新的复杂度没有最好的架构只有最适合的架构
</div>
</div>
</template>
@@ -116,148 +122,177 @@ const stages = [
<style scoped>
.evolution-intro-demo {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
padding: 32px;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 0.75rem;
margin: 0.5rem 0;
}
.intro-header {
text-align: center;
margin-bottom: 32px;
.demo-header {
display: flex;
align-items: center;
gap: 0.4rem;
margin-bottom: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.intro-header h3 {
font-size: 24px;
font-weight: 700;
margin: 0 0 8px 0;
.demo-header .icon {
font-size: 1rem;
}
.intro-header p {
font-size: 14px;
opacity: 0.9;
margin: 0;
.demo-header .title {
font-weight: bold;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.75rem;
margin-left: 0.4rem;
}
.timeline-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 24px;
gap: 0.4rem;
margin-bottom: 0.5rem;
}
.stage-card {
background: rgba(255, 255, 255, 0.1);
border: 2px solid transparent;
border-radius: 12px;
padding: 16px 12px;
background: var(--vp-c-bg);
border: 1px solid transparent;
border-radius: 4px;
padding: 0.75rem 0.5rem;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
transition: all 0.3s;
}
.stage-card:hover {
background: rgba(255, 255, 255, 0.15);
transform: translateY(-2px);
border-color: var(--vp-c-brand);
transform: translateY(-1px);
}
.stage-card.active {
background: rgba(255, 255, 255, 0.25);
border-color: rgba(255, 255, 255, 0.5);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
border-color: var(--vp-c-brand);
background: var(--vp-c-brand-soft);
}
.stage-era {
font-size: 11px;
opacity: 0.7;
margin-bottom: 4px;
font-size: 0.6rem;
color: var(--vp-c-text-3);
margin-bottom: 0.1rem;
}
.stage-icon {
font-size: 32px;
margin-bottom: 8px;
font-size: 1rem;
margin-bottom: 0.2rem;
}
.stage-name {
font-size: 14px;
font-size: 0.7rem;
font-weight: 600;
margin-bottom: 2px;
color: var(--vp-c-text-1);
margin-bottom: 0.1rem;
}
.stage-arch {
font-size: 11px;
opacity: 0.7;
font-size: 0.55rem;
color: var(--vp-c-text-3);
}
.stage-detail {
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
padding: 24px;
background: var(--vp-c-bg);
border-radius: 4px;
padding: 0.5rem;
}
.detail-panel {
animation: fadeIn 0.4s ease;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
from { opacity: 0; transform: translateY(5px); }
to { opacity: 1; transform: translateY(0); }
}
.detail-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
gap: 0.4rem;
margin-bottom: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.detail-icon {
font-size: 32px;
font-size: 1rem;
}
.detail-header h4 {
font-size: 20px;
font-size: 0.85rem;
font-weight: 600;
margin: 0;
color: var(--vp-c-text-1);
}
.detail-content {
display: grid;
gap: 16px;
grid-template-columns: repeat(3, 1fr);
gap: 0.4rem;
}
.detail-section h5 {
font-size: 14px;
font-size: 0.7rem;
font-weight: 600;
margin: 0 0 8px 0;
color: #ffd700;
margin: 0 0 0.3rem 0;
color: var(--vp-c-brand);
}
.detail-section p {
font-size: 13px;
line-height: 1.6;
margin: 0;
opacity: 0.9;
font-size: 0.65rem;
line-height: 1.4;
margin: 0 0 0.3rem 0;
color: var(--vp-c-text-2);
}
.detail-section ul {
margin: 0;
padding-left: 18px;
padding-left: 0.75rem;
}
.detail-section li {
font-size: 13px;
line-height: 1.6;
margin-bottom: 4px;
opacity: 0.9;
font-size: 0.6rem;
line-height: 1.4;
margin-bottom: 0.25rem;
color: var(--vp-c-text-2);
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
margin-top: 1rem;
display: flex;
gap: 0.25rem;
}
.info-box .icon {
flex-shrink: 0;
}
.info-box strong {
color: var(--vp-c-text-1);
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.4s ease;
transition: all 0.3s ease;
}
.fade-enter-from {
@@ -275,8 +310,8 @@ const stages = [
grid-template-columns: repeat(2, 1fr);
}
.stage-detail {
padding: 16px;
.detail-content {
grid-template-columns: 1fr;
}
}
</style>
@@ -240,10 +240,10 @@ const resetCluster = () => {
<style scoped>
.container-docker-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.demo-header {
@@ -273,8 +273,8 @@ const resetCluster = () => {
flex: 1;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
cursor: pointer;
transition: all 0.3s;
}
@@ -398,8 +398,8 @@ const resetCluster = () => {
.benefit-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
text-align: center;
transition: all 0.2s;
}
@@ -150,7 +150,7 @@ const insightIcon = computed(() => {
.architecture {
background: var(--vp-c-bg);
border-radius: 10px;
padding: 1rem;
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
@@ -248,7 +248,7 @@ const insightIcon = computed(() => {
margin-top: 1.5rem;
padding: 0.75rem 1rem;
background: var(--vp-c-bg);
border-radius: 8px;
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
}
@@ -134,10 +134,10 @@ const resetFlow = () => {
<style scoped>
.microservices-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.demo-header {
@@ -166,8 +166,8 @@ const resetFlow = () => {
.service-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
cursor: pointer;
transition: all 0.2s;
}
@@ -249,8 +249,8 @@ const resetFlow = () => {
.communication-flow {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.communication-flow h5 {
@@ -1,8 +1,9 @@
<template>
<div class="monolith-demo">
<div class="demo-header">
<h4>🏢 单体架构演示</h4>
<p>观察单体应用如何处理请求以及模块间的依赖关系</p>
<span class="icon">🏢</span>
<span class="title">单体架构演示</span>
<span class="subtitle">观察单体应用如何处理请求</span>
</div>
<div class="monolith-diagram">
@@ -47,13 +48,9 @@
<button class="control-btn" @click="reset">重置</button>
</div>
<div class="demo-explanation">
<h5>💡 单体架构的特点</h5>
<ul>
<li><strong>共享进程空间</strong>所有模块在同一个进程中运行内存共享</li>
<li><strong>数据库耦合</strong>所有模块共享同一个数据库Schema变更影响全局</li>
<li><strong>级联故障</strong>一个模块崩溃可能导致整个进程挂掉雪崩效应</li>
</ul>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想</strong>所有模块在同一个进程中运行内存共享但一个模块崩溃可能导致整个进程挂掉雪崩效应
</div>
</div>
</template>
@@ -134,41 +131,50 @@ const reset = () => {
<style scoped>
.monolith-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.4rem;
margin-bottom: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.demo-header h4 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
.demo-header .icon {
font-size: 1rem;
}
.demo-header .title {
font-weight: bold;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.demo-header p {
margin: 0;
font-size: 0.9rem;
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.75rem;
margin-left: 0.4rem;
}
.monolith-diagram {
display: flex;
gap: 1.5rem;
gap: 0.75rem;
align-items: flex-start;
margin-bottom: 1.5rem;
margin-bottom: 0.75rem;
}
.monolith-box {
flex: 1;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-brand);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.5rem;
transition: all 0.3s;
}
@@ -181,23 +187,24 @@ const reset = () => {
text-align: center;
font-weight: 600;
color: var(--vp-c-brand);
margin-bottom: 1rem;
padding-bottom: 0.5rem;
margin-bottom: 0.5rem;
padding-bottom: 0.25rem;
border-bottom: 1px solid var(--vp-c-divider);
font-size: 0.75rem;
}
.modules-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.75rem;
margin-bottom: 1rem;
gap: 0.4rem;
margin-bottom: 0.5rem;
}
.module-box {
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 0.75rem;
border-radius: 4px;
padding: 0.4rem;
text-align: center;
cursor: pointer;
transition: all 0.2s;
@@ -218,21 +225,21 @@ const reset = () => {
}
.module-icon {
font-size: 1.5rem;
margin-bottom: 0.25rem;
font-size: 1rem;
margin-bottom: 0.1rem;
}
.module-name {
font-size: 0.8rem;
font-size: 0.65rem;
font-weight: 600;
color: var(--vp-c-text-1);
margin-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
.module-status {
font-size: 0.7rem;
padding: 0.1rem 0.4rem;
border-radius: 10px;
font-size: 0.55rem;
padding: 0.05rem 0.25rem;
border-radius: 6px;
display: inline-block;
}
@@ -255,38 +262,38 @@ const reset = () => {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.75rem;
gap: 0.3rem;
padding: 0.4rem;
background: var(--vp-c-bg-soft);
border: 1px dashed var(--vp-c-divider);
border-radius: 6px;
border-radius: 4px;
}
.db-icon {
font-size: 1.25rem;
font-size: 1rem;
}
.db-label {
font-size: 0.8rem;
font-size: 0.65rem;
color: var(--vp-c-text-2);
}
.request-flow {
width: 150px;
width: 100px;
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: 0.25rem;
}
.flow-request {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
gap: 0.25rem;
padding: 0.3rem;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
font-size: 0.75rem;
border-radius: 4px;
font-size: 0.6rem;
}
.flow-request.active {
@@ -309,19 +316,19 @@ const reset = () => {
.controls {
display: flex;
gap: 0.75rem;
gap: 0.4rem;
justify-content: center;
margin-bottom: 1.5rem;
margin-bottom: 0.75rem;
flex-wrap: wrap;
}
.control-btn {
padding: 0.5rem 1rem;
border-radius: 6px;
padding: 0.3rem 0.6rem;
border-radius: 4px;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
font-size: 0.85rem;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s;
}
@@ -339,34 +346,6 @@ const reset = () => {
background: rgba(239, 68, 68, 0.1);
}
.demo-explanation {
padding-top: 1.5rem;
border-top: 1px solid var(--vp-c-divider);
}
.demo-explanation h5 {
font-size: 1rem;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 0.75rem 0;
}
.demo-explanation ul {
margin: 0 0 1rem 0;
padding-left: 1.25rem;
}
.demo-explanation li {
font-size: 0.9rem;
color: var(--vp-c-text-2);
line-height: 1.6;
margin-bottom: 0.4rem;
}
.demo-explanation li strong {
color: var(--vp-c-text-1);
}
@media (max-width: 768px) {
.monolith-diagram {
flex-direction: column;
@@ -378,4 +357,23 @@ const reset = () => {
flex-wrap: wrap;
}
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-top: 0.5rem;
display: flex;
gap: 0.2rem;
}
.info-box .icon {
flex-shrink: 0;
}
.info-box strong {
color: var(--vp-c-text-1);
}
</style>
@@ -159,7 +159,7 @@ const deployRelease = () => {
.module-btn {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg);
padding: 0.5rem;
font-size: 0.9rem;
@@ -186,7 +186,7 @@ const deployRelease = () => {
.risk-meter {
background: var(--vp-c-bg);
border-radius: 10px;
padding: 1rem;
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
@@ -217,7 +217,7 @@ const deployRelease = () => {
margin-top: 1rem;
width: 100%;
border: none;
border-radius: 8px;
border-radius: 6px;
padding: 0.6rem;
background: var(--vp-c-brand);
color: #fff;
@@ -229,7 +229,7 @@ const deployRelease = () => {
margin-top: 0.75rem;
font-size: 0.9rem;
padding: 0.5rem 0.75rem;
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg);
border: 1px dashed var(--vp-c-divider);
}
@@ -79,10 +79,10 @@ const reset = () => {
<style scoped>
.monolith-microservice-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.controls {
@@ -129,8 +129,8 @@ const reset = () => {
.architecture-block {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
display: flex;
flex-direction: column;
align-items: center;
@@ -146,8 +146,8 @@ const reset = () => {
.server-container {
border: 2px solid #3b82f6;
background: #eff6ff;
padding: 1rem;
border-radius: 8px;
padding: 0.75rem;
border-radius: 6px;
width: 100%;
text-align: center;
transition: all 0.3s;
@@ -1,8 +1,9 @@
<template>
<div class="physical-server-demo">
<div class="demo-header">
<h4>🖥 物理服务器时代演示</h4>
<p>点击"发送请求"观察早期 CGI 服务器的处理瓶颈</p>
<span class="icon">🖥</span>
<span class="title">物理服务器时代演示</span>
<span class="subtitle">观察早期 CGI 服务器的处理瓶颈</span>
</div>
<div class="demo-stage">
@@ -85,27 +86,9 @@
</div>
</div>
<div class="demo-explanation">
<h5>💡 早期的痛点在哪里</h5>
<ul>
<li>
<strong>进程启动开销</strong>每个请求都要启动新的 CGI
进程就像每来一个客人都要重新搭一个厨房
</li>
<li>
<strong>资源无法复用</strong>数据库连接每次都要重新建立CPU
频繁在进程间切换
</li>
<li>
<strong>扩展困难</strong>只能买更强的单机垂直扩展无法通过增加机器分担压力
</li>
</ul>
<p class="demo-conclusion">
这就是<strong>物理服务器 + CGI</strong>时代的核心问题<span
class="highlight"
>进程级隔离带来了稳定性但也带来了巨大的性能开销</span
>
</p>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想</strong>进程级隔离带来了稳定性但也带来了巨大的性能开销
</div>
</div>
</template>
@@ -190,77 +173,86 @@ const sendRequest = async () => {
<style scoped>
.physical-server-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.4rem;
margin-bottom: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.demo-header h4 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
.demo-header .icon {
font-size: 1rem;
}
.demo-header .title {
font-weight: bold;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.demo-header p {
margin: 0;
font-size: 0.9rem;
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.75rem;
margin-left: 0.4rem;
}
.demo-stage {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 1rem;
margin-bottom: 1.5rem;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.client-zone,
.server-zone {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.5rem;
}
.zone-title {
font-size: 0.85rem;
font-size: 0.7rem;
font-weight: 600;
color: var(--vp-c-brand);
margin-bottom: 0.75rem;
margin-bottom: 0.4rem;
text-align: center;
}
.request-queue {
min-height: 60px;
min-height: 40px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 0.5rem;
margin-bottom: 0.75rem;
border-radius: 4px;
padding: 0.3rem;
margin-bottom: 0.4rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
gap: 0.15rem;
}
.request-card {
background: var(--vp-c-brand);
color: white;
border-radius: 4px;
padding: 0.4rem 0.5rem;
border-radius: 3px;
padding: 0.25rem 0.3rem;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.75rem;
gap: 0.3rem;
font-size: 0.6rem;
}
.req-method {
background: rgba(255, 255, 255, 0.2);
padding: 0.1rem 0.3rem;
border-radius: 3px;
padding: 0.05rem 0.2rem;
border-radius: 2px;
font-weight: 600;
}
@@ -269,9 +261,9 @@ const sendRequest = async () => {
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 6px;
padding: 0.6rem;
font-size: 0.85rem;
border-radius: 4px;
padding: 0.4rem;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s;
}
@@ -290,12 +282,12 @@ const sendRequest = async () => {
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 60px;
min-width: 40px;
}
.network-line {
width: 3px;
height: 120px;
width: 2px;
height: 80px;
background: var(--vp-c-divider);
border-radius: 2px;
position: relative;
@@ -309,29 +301,29 @@ const sendRequest = async () => {
}
.latency-display {
margin-top: 0.5rem;
font-size: 0.75rem;
margin-top: 0.3rem;
font-size: 0.6rem;
color: var(--vp-c-brand);
font-weight: 600;
}
.server-status {
margin-bottom: 0.75rem;
margin-bottom: 0.4rem;
}
.status-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
gap: 0.3rem;
padding: 0.3rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
margin-bottom: 0.5rem;
border-radius: 4px;
margin-bottom: 0.3rem;
}
.status-dot {
width: 8px;
height: 8px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--vp-c-success);
}
@@ -347,59 +339,59 @@ const sendRequest = async () => {
}
.status-text {
font-size: 0.8rem;
font-size: 0.65rem;
color: var(--vp-c-text-2);
}
.cpu-usage {
display: flex;
align-items: center;
gap: 0.5rem;
gap: 0.3rem;
}
.cpu-bar {
flex: 1;
height: 6px;
height: 4px;
background: var(--vp-c-bg-soft);
border-radius: 3px;
border-radius: 2px;
overflow: hidden;
}
.cpu-fill {
height: 100%;
background: var(--vp-c-danger);
border-radius: 3px;
border-radius: 2px;
transition: width 0.1s ease;
}
.cpu-text {
font-size: 0.7rem;
font-size: 0.55rem;
color: var(--vp-c-text-2);
min-width: 60px;
min-width: 50px;
text-align: right;
}
.process-queue {
display: flex;
flex-direction: column;
gap: 0.4rem;
gap: 0.25rem;
}
.process-item {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 0.5rem;
border-radius: 4px;
padding: 0.3rem;
}
.proc-name {
display: block;
font-size: 0.7rem;
font-size: 0.55rem;
color: var(--vp-c-text-2);
margin-bottom: 0.3rem;
margin-bottom: 0.2rem;
}
.proc-progress {
height: 4px;
height: 3px;
background: var(--vp-c-bg);
border-radius: 2px;
overflow: hidden;
@@ -412,64 +404,39 @@ const sendRequest = async () => {
transition: width 0.1s linear;
}
.demo-explanation {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 1px solid var(--vp-c-divider);
}
.demo-explanation h5 {
font-size: 1rem;
font-weight: 600;
color: var(--vp-c-text-1);
margin: 0 0 0.75rem 0;
}
.demo-explanation ul {
margin: 0 0 1rem 0;
padding-left: 1.25rem;
}
.demo-explanation li {
font-size: 0.9rem;
color: var(--vp-c-text-2);
line-height: 1.6;
margin-bottom: 0.4rem;
}
.demo-explanation li strong {
color: var(--vp-c-text-1);
}
.demo-conclusion {
font-size: 0.9rem;
color: var(--vp-c-text-2);
line-height: 1.6;
margin: 0;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.highlight {
color: var(--vp-c-brand);
font-weight: 600;
}
@media (max-width: 768px) {
.demo-stage {
grid-template-columns: 1fr;
gap: 1rem;
gap: 0.5rem;
}
.connection-zone {
flex-direction: row;
height: 60px;
height: 40px;
}
.network-line {
width: 100%;
height: 3px;
height: 2px;
}
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-top: 0.5rem;
display: flex;
gap: 0.2rem;
}
.info-box .icon {
flex-shrink: 0;
}
.info-box strong {
color: var(--vp-c-text-1);
}
</style>
@@ -62,10 +62,10 @@ const comparisonData = [
<style scoped>
.scaling-strategy-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -142,7 +142,7 @@ const scaleHint = computed(() => {
.card {
background: var(--vp-c-bg);
border-radius: 12px;
padding: 1rem;
padding: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
@@ -161,10 +161,10 @@ onUnmounted(() => {
<style scoped>
.serverless-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1.5rem;
margin: 1rem 0;
margin: 0.5rem 0;
}
.demo-header {
@@ -199,8 +199,8 @@ onUnmounted(() => {
.function-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
cursor: pointer;
transition: all 0.2s;
text-align: center;
@@ -276,8 +276,8 @@ onUnmounted(() => {
.auto-scaling-panel {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
}
.scaling-title {
@@ -335,8 +335,8 @@ onUnmounted(() => {
.traffic-simulator {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 1.5rem;
}
@@ -1,8 +1,9 @@
<template>
<div class="tech-stack-timeline-demo">
<div class="demo-header">
<h4>📚 技术栈演进时间线</h4>
<p>每个时代的主流技术栈</p>
<span class="icon">📚</span>
<span class="title">技术栈演进时间线</span>
<span class="subtitle">每个时代的主流技术栈</span>
</div>
<div class="timeline">
@@ -102,137 +103,127 @@ const eras = [
<style scoped>
.tech-stack-timeline-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
margin-bottom: 1rem;
margin-bottom: 0.5rem;
}
.demo-header h4 {
margin: 0 0 0.25rem 0;
font-size: 1rem;
margin: 0 0 0.15rem 0;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.demo-header p {
margin: 0;
font-size: 0.85rem;
font-size: 0.75rem;
color: var(--vp-c-text-2);
}
.timeline {
display: flex;
flex-direction: column;
gap: 0.5rem;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.4rem;
}
.era-section {
display: flex;
gap: 0.75rem;
flex-direction: column;
gap: 0.25rem;
cursor: pointer;
transition: all 0.2s;
background: var(--vp-c-bg);
border-radius: 4px;
padding: 0.4rem;
}
.era-section:hover,
.era-section.active {
background: var(--vp-c-bg);
border-radius: 6px;
background: var(--vp-c-brand-soft);
}
.era-marker {
display: flex;
flex-direction: column;
align-items: center;
width: 24px;
display: none;
}
.era-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--vp-c-divider);
border: 2px solid var(--vp-c-bg);
transition: all 0.2s;
display: none;
}
.era-section.active .era-dot {
background: var(--vp-c-brand);
border-color: var(--vp-c-brand);
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
display: none;
}
.era-line {
flex: 1;
width: 2px;
background: var(--vp-c-divider);
margin: 4px 0;
display: none;
}
.era-content {
flex: 1;
padding: 0.5rem 0.5rem 0.5rem 0;
}
.era-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
gap: 0.25rem;
margin-bottom: 0.3rem;
flex-wrap: wrap;
}
.era-icon {
font-size: 1.25rem;
font-size: 1rem;
}
.era-name {
font-weight: 600;
font-size: 0.95rem;
font-size: 0.7rem;
color: var(--vp-c-text-1);
}
.era-period {
font-size: 0.75rem;
font-size: 0.6rem;
color: var(--vp-c-text-3);
background: var(--vp-c-bg-soft);
padding: 0.1rem 0.4rem;
border-radius: 4px;
padding: 0.05rem 0.25rem;
border-radius: 3px;
}
.tech-categories {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.category {
background: var(--vp-c-bg-soft);
border-radius: 4px;
padding: 0.5rem;
border-radius: 3px;
padding: 0.25rem;
}
.category-name {
font-size: 0.75rem;
font-size: 0.6rem;
font-weight: 600;
color: var(--vp-c-text-2);
margin-bottom: 0.25rem;
margin-bottom: 0.1rem;
}
.tech-tags {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
gap: 0.15rem;
}
.tech-tag {
font-size: 0.7rem;
padding: 0.1rem 0.35rem;
font-size: 0.55rem;
padding: 0.05rem 0.2rem;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 3px;
border-radius: 2px;
color: var(--vp-c-text-2);
}
@@ -243,8 +234,27 @@ const eras = [
}
@media (max-width: 768px) {
.tech-categories {
grid-template-columns: 1fr;
.timeline {
grid-template-columns: repeat(2, 1fr);
}
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-top: 0.5rem;
display: flex;
gap: 0.2rem;
}
.info-box .icon {
flex-shrink: 0;
}
.info-box strong {
color: var(--vp-c-text-1);
}
</style>
@@ -230,12 +230,12 @@ const getCurrentLang = () => {
<style scoped>
.backend-languages-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -286,10 +286,10 @@ const getCurrentLang = () => {
}
.language-card {
padding: 1rem;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border: 2px solid transparent;
border-radius: 8px;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
@@ -338,8 +338,8 @@ const getCurrentLang = () => {
.lang-detail {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
margin-top: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
@@ -149,12 +149,12 @@ const getModelInfo = () => {
<style scoped>
.concurrency-model-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -93,12 +93,12 @@ const maxTime = computed(() => {
<style scoped>
.developer-efficiency-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -174,12 +174,12 @@ const getBarClass = (score) => {
<style scoped>
.language-comparison-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -89,12 +89,12 @@ const ecosystems = [
<style scoped>
.language-ecosystem-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -165,12 +165,12 @@ const reset = () => {
<style scoped>
.language-selector-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -282,8 +282,8 @@ const reset = () => {
.recommendation-panel {
background: var(--vp-c-bg);
padding: 1rem;
border-radius: 8px;
padding: 0.75rem;
border-radius: 6px;
margin-bottom: 1rem;
border: 2px solid var(--vp-c-brand);
}
@@ -64,12 +64,12 @@ const models = [
<style scoped>
.memory-management-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -118,7 +118,7 @@ const models = [
.model-card {
background: var(--vp-c-bg);
padding: 1rem;
padding: 0.75rem;
border-radius: 6px;
text-align: center;
border: 1px solid var(--vp-c-divider);
@@ -157,12 +157,12 @@ runBenchmark()
<style scoped>
.performance-benchmark-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -122,12 +122,12 @@ const getLineCount = (lang) => {
<style scoped>
.syntax-comparison-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
max-height: 600px;
overflow-y: auto;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -256,7 +256,7 @@ const getLineCount = (lang) => {
.code-content {
margin: 0;
padding: 1rem;
padding: 0.75rem;
background: #1e1e1e;
color: #d4d4d4;
font-family: 'Monaco', 'Menlo', monospace;
@@ -276,7 +276,7 @@ const tabs = [
gap: 8px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
margin-bottom: 20px;
}
@@ -284,7 +284,7 @@ const tabs = [
width: 100%;
max-width: 400px;
padding: 16px;
border-radius: 8px;
border-radius: 6px;
text-align: center;
border-left: 4px solid;
}
@@ -329,7 +329,7 @@ const tabs = [
.arch-characteristics {
padding: 16px;
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
}
.arch-characteristics h5 {
@@ -357,7 +357,7 @@ const tabs = [
gap: 16px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
margin-bottom: 20px;
}
@@ -369,7 +369,7 @@ const tabs = [
.clean-layer {
padding: 12px 16px;
border-radius: 8px;
border-radius: 6px;
border-left: 4px solid;
}
@@ -407,7 +407,7 @@ const tabs = [
.dependency-rule {
background: white;
border-radius: 8px;
border-radius: 6px;
padding: 16px;
text-align: center;
border: 2px dashed #1890ff;
@@ -485,7 +485,7 @@ const tabs = [
.rec-card {
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
padding: 16px;
position: relative;
}
@@ -250,7 +250,7 @@ const toggleDetails = (section) => {
margin-top: 12px;
padding: 16px;
background: #f0f7ff;
border-radius: 8px;
border-radius: 6px;
border-left: 4px solid #409eff;
}
@@ -297,7 +297,7 @@ const toggleDetails = (section) => {
text-align: center;
padding: 16px 12px;
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
transition: all 0.3s ease;
}
@@ -180,7 +180,7 @@
.layer-box {
padding: 16px 20px;
background: #f5f7fa;
border-radius: 8px;
border-radius: 6px;
font-weight: 500;
color: #303133;
text-align: center;
@@ -202,7 +202,7 @@ const viewMode = ref('conversion')
.flow-step {
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
border-left: 4px solid #409eff;
}
@@ -286,7 +286,7 @@ const viewMode = ref('conversion')
.table-header {
background: #f5f7fa;
border-radius: 8px;
border-radius: 6px;
font-weight: 600;
color: #303133;
font-size: 13px;
@@ -334,7 +334,7 @@ const layerInfo = computed(() => {
margin-top: 16px;
background: #f6ffed;
border: 1px solid #b7eb8f;
border-radius: 8px;
border-radius: 6px;
color: #389e0d;
font-size: 13px;
}
@@ -596,7 +596,7 @@ const principles = [
margin-bottom: 20px;
padding: 16px;
background: white;
border-radius: 8px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
@@ -670,7 +670,7 @@ const principles = [
.flow-step {
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
@@ -813,7 +813,7 @@ const principles = [
padding: 10px 20px;
border: 2px solid #dcdfe6;
background: white;
border-radius: 8px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
@@ -880,7 +880,7 @@ const principles = [
.code-block {
background: #2d2d2d;
border-radius: 8px;
border-radius: 6px;
padding: 16px;
margin: 0;
overflow-x: auto;
@@ -897,7 +897,7 @@ const principles = [
margin-top: 20px;
padding: 16px;
background: white;
border-radius: 8px;
border-radius: 6px;
}
.problems-list h5 {
@@ -952,7 +952,7 @@ const principles = [
.table-header {
background: #f5f7fa;
border-radius: 8px;
border-radius: 6px;
font-weight: 600;
color: #303133;
font-size: 13px;
@@ -1029,7 +1029,7 @@ const principles = [
.table-row {
padding: 16px;
border: 1px solid #ebeef5;
border-radius: 8px;
border-radius: 6px;
margin-bottom: 12px;
}
}
@@ -422,7 +422,7 @@ const principles = [
margin-bottom: 20px;
padding: 16px;
background: white;
border-radius: 8px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
@@ -496,7 +496,7 @@ const principles = [
.flow-step {
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
@@ -644,7 +644,7 @@ const principles = [
.principle-card {
padding: 14px;
background: #f8f9fa;
border-radius: 8px;
border-radius: 6px;
border-left: 3px solid #e6a23c;
}
@@ -1523,7 +1523,7 @@ onUnmounted(() => {
}
.console-output {
flex: 1;
overflow-y: auto;
font-family: Consolas, 'Lucida Console', monospace;
font-size: 11px;
}
@@ -400,7 +400,7 @@ const selectNode = (node) => {
/* Virtual Page Preview */
.virtual-page-container {
border: 1px solid #d0d7de;
border-radius: 8px;
border-radius: 6px;
overflow: hidden;
background: #fff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
@@ -471,7 +471,7 @@ const selectNode = (node) => {
/* DevTools Simulator (Enhanced) */
.browser-devtools-demo {
border: 1px solid #d0d7de;
border-radius: 8px;
border-radius: 6px;
background-color: #ffffff;
color: #202124;
font-family: 'Segoe UI', '.SFNSDisplay', 'Roboto', sans-serif;
@@ -152,7 +152,7 @@ const runShortcut = (cmd) => {
.console-body {
height: 250px;
overflow-y: auto;
background-color: #f5f7fa;
border: 1px solid #e4e7ed;
border-radius: 4px;
@@ -154,7 +154,7 @@ const updateStyle = (prop, value) => {
.dom-content, .style-content {
padding: 10px;
overflow-y: auto;
flex: 1;
}
@@ -311,7 +311,7 @@ const flatNext = () => {
.code-area {
flex: 2;
background: #f5f7fa;
overflow-y: auto;
font-family: monospace;
border-right: 1px solid #dcdfe6;
}
@@ -125,10 +125,10 @@ function getResultLayerStyle(layer) {
<style scoped>
.composite-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -160,7 +160,7 @@ function getResultLayerStyle(layer) {
.demo-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
}
.layers-stage {
@@ -127,10 +127,10 @@
<style scoped>
.dom-render-tree-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -162,7 +162,7 @@
.demo-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
}
.trees-container {
@@ -122,10 +122,10 @@ function resetDemo() {
<style scoped>
.layout-reflow-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -142,7 +142,7 @@ function resetDemo() {
.demo-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
}
.control-panel {
@@ -199,7 +199,7 @@ function resetDemo() {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 1rem;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
min-height: 150px;
@@ -154,10 +154,10 @@ function sleep(ms) {
<style scoped>
.macro-micro-task-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -189,7 +189,7 @@ function sleep(ms) {
.demo-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
}
.event-loop-flow {
@@ -216,7 +216,7 @@ function sleep(ms) {
.execution-box {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
min-height: 80px;
display: flex;
align-items: center;
@@ -126,10 +126,10 @@ function clearHighlight() {
<style scoped>
.paint-layer-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -146,7 +146,7 @@ function clearHighlight() {
.demo-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
}
.layer-visualization {
@@ -158,7 +158,7 @@ function clearHighlight() {
height: 200px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
overflow: hidden;
}
@@ -105,10 +105,10 @@
<style scoped>
.rendering-performance-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -140,7 +140,7 @@
.demo-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 1rem;
padding: 0.75rem;
}
.performance-comparison {
@@ -108,10 +108,10 @@ const currentStage = computed(() => {
<style scoped>
.rendering-pipeline-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -158,7 +158,7 @@ const currentStage = computed(() => {
position: relative;
cursor: pointer;
padding: 0.5rem;
border-radius: 8px;
border-radius: 6px;
transition: all 0.2s ease;
}
@@ -173,7 +173,7 @@ const currentStage = computed(() => {
.stage-icon {
width: 40px;
height: 40px;
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-brand);
display: flex;
align-items: center;
@@ -217,8 +217,8 @@ const currentStage = computed(() => {
.stage-detail {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
margin-top: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
@@ -121,10 +121,10 @@ const simulateRequest = () => {
<style scoped>
.cache-architecture-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -166,7 +166,7 @@ const simulateRequest = () => {
flex-direction: column;
align-items: center;
gap: 0.5rem;
padding: 1rem;
padding: 0.75rem;
border-radius: 12px;
transition: all 0.3s;
}
@@ -316,7 +316,7 @@ const simulateRequest = () => {
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 8px;
border-radius: 6px;
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
@@ -142,10 +142,10 @@ const layers = [
<style scoped>
.cache-architecture-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
@@ -185,7 +185,7 @@ const layers = [
.layer {
width: 100%;
max-width: 400px;
border-radius: 8px;
border-radius: 6px;
transition: all 0.3s;
}
@@ -141,10 +141,10 @@ const simulateFlow = () => {
<style scoped>
.cache-hierarchy-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
padding: 0.75rem;
margin: 0.5rem 0;
max-width: 600px;
}
@@ -187,8 +187,8 @@ const simulateFlow = () => {
max-width: 400px;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
cursor: pointer;
transition: all 0.3s;
}
@@ -262,8 +262,8 @@ const simulateFlow = () => {
.data-flow {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 1rem;
border: 1px solid var(--vp-c-divider);
}
@@ -288,7 +288,7 @@ const simulateFlow = () => {
gap: 1rem;
padding: 0.75rem 1rem;
background: var(--vp-c-bg-soft);
border-radius: 8px;
border-radius: 6px;
border: 2px solid var(--vp-c-divider);
transition: all 0.3s;
width: 100%;
@@ -335,7 +335,7 @@ const simulateFlow = () => {
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 8px;
border-radius: 6px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
@@ -349,8 +349,8 @@ const simulateFlow = () => {
.comparison-table {
background: var(--vp-c-bg);
border-radius: 8px;
padding: 1rem;
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 1rem;
border: 1px solid var(--vp-c-divider);
}

Some files were not shown because too many files have changed in this diff Show More