feat(docs): add interactive demo components for technical appendices

Add placeholder Vue components for visualizing technical concepts across multiple domains including frontend routing, browser rendering, cache design, queue design, database principles, API design, cloud services, and backend evolution. These components provide interactive educational content for the documentation.

Update documentation structure to include new appendix sections and enhance existing content with visual components. Remove unused 'codex' dependency from package.json.
This commit is contained in:
sanbuphy
2026-02-06 03:34:50 +08:00
parent e8bba6f7c0
commit 7c70c37072
171 changed files with 69830 additions and 6689 deletions
@@ -0,0 +1,470 @@
<template>
<div class="role-policy-demo">
<div class="demo-header">
<h4>角色与策略关系可视化</h4>
<p class="demo-desc">拖动查看角色如何关联多个策略</p>
</div>
<div class="visualization-container">
<!-- Central Role -->
<div class="central-role">
<div class="role-core" @click="toggleRoleDetails"
:class="{ expanded: showRoleDetails }">
<div class="role-icon">🎭</div>
<div class="role-info">
<span class="role-name">{{ roleName }}</span>
<span class="role-type">{{ roleType }}</span>
</div>
<div class="expand-icon">{{ showRoleDetails ? '▼' : '▶' }}</div>
</div>
<!-- Trust Policy -->
<div class="trust-policy" v-if="showRoleDetails">
<div class="policy-header">
<span class="policy-icon">🔐</span>
<span class="policy-title">信任策略 (Trust Policy)</span>
</div>
<div class="policy-content">
<div class="policy-item" v-for="(trust, i) in trustPolicy" :key="i">
<span class="principal">{{ trust.principal }}</span>
<span class="action">可执行: {{ trust.action }}</span>
<span class="condition" v-if="trust.condition">条件: {{ trust.condition }}</span>
</div>
</div>
</div>
</div>
<!-- Connection Lines (SVG) -->
<svg class="connection-lines" v-if="mounted">
<line
v-for="(line, index) in connectionLines"
:key="index"
:x1="line.x1"
:y1="line.y1"
:x2="line.x2"
:y2="line.y2"
:class="['connection-line', line.type, { active: hoveredPolicy === line.policyIndex }]"
@mouseenter="hoveredPolicy = line.policyIndex"
@mouseleave="hoveredPolicy = null"
/>
</svg>
<!-- Attached Policies -->
<div class="attached-policies">
<div
v-for="(policy, index) in attachedPolicies"
:key="index"
class="policy-card"
:class="{ active: hoveredPolicy === index, selected: selectedPolicy === index }"
:style="getPolicyPosition(index)"
@mouseenter="hoveredPolicy = index"
@mouseleave="hoveredPolicy = null"
@click="selectPolicy(index)"
>
<div class="policy-header">
<span class="policy-icon">{{ policy.icon }}</span>
<span class="policy-name">{{ policy.name }}</span>
</div>
<div class="policy-permissions" v-if="selectedPolicy === index">
<div class="permission-item" v-for="(perm, i) in policy.permissions" :key="i">
<span class="perm-effect" :class="perm.effect">{{ perm.effect }}</span>
<span class="perm-action">{{ perm.action }}</span>
<span class="perm-resource">{{ perm.resource }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
// Role Data
const roleName = ref('CrossAccountS3AccessRole')
const roleType = ref('跨账号访问角色')
const showRoleDetails = ref(false)
const trustPolicy = ref([
{ principal: '账号 A (123456789012)', action: 'sts:AssumeRole', condition: 'ExternalId 匹配' },
{ principal: '特定 IAM 用户', action: 'sts:AssumeRole', condition: 'IP 白名单' }
])
// Policies Data
const attachedPolicies = ref([
{
name: 'S3ReadWritePolicy',
icon: '📦',
permissions: [
{ effect: 'Allow', action: 's3:GetObject', resource: 'arn:aws:s3:::bucket/*' },
{ effect: 'Allow', action: 's3:PutObject', resource: 'arn:aws:s3:::bucket/*' },
{ effect: 'Allow', action: 's3:ListBucket', resource: 'arn:aws:s3:::bucket' }
]
},
{
name: 'CloudWatchLogsPolicy',
icon: '📊',
permissions: [
{ effect: 'Allow', action: 'logs:CreateLogGroup', resource: '*' },
{ effect: 'Allow', action: 'logs:CreateLogStream', resource: '*' },
{ effect: 'Allow', action: 'logs:PutLogEvents', resource: '*' }
]
},
{
name: 'DenySensitiveData',
icon: '🚫',
permissions: [
{ effect: 'Deny', action: 's3:GetObject', resource: 'arn:aws:s3:::bucket/sensitive/*' },
{ effect: 'Deny', action: 's3:DeleteObject', resource: 'arn:aws:s3:::bucket/*' }
]
}
])
// State
const hoveredPolicy = ref(null)
const selectedPolicy = ref(0)
const mounted = ref(false)
const connectionLines = ref([])
// Methods
function toggleRoleDetails() {
showRoleDetails.value = !showRoleDetails.value
}
function selectPolicy(index) {
selectedPolicy.value = index
}
function selectFeature(platform, index) {
// For compatibility with other demos
}
function getPolicyPosition(index) {
const positions = [
{ top: '0%', right: '0%' },
{ top: '35%', right: '5%' },
{ top: '70%', right: '0%' }
]
return positions[index] || positions[0]
}
function calculateConnections() {
// Simplified connection calculation
connectionLines.value = attachedPolicies.value.map((_, index) => ({
x1: 50,
y1: 50,
x2: 80 + (index * 5),
y2: 20 + (index * 30),
type: index === 2 ? 'deny' : 'allow',
policyIndex: index
}))
}
// Lifecycle
onMounted(() => {
nextTick(() => {
mounted.value = true
calculateConnections()
})
})
onUnmounted(() => {
mounted.value = false
})
</script>
<style scoped>
.role-policy-demo {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
padding: 24px;
color: white;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
min-height: 600px;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
font-size: 1.4rem;
}
.demo-desc {
margin: 0;
opacity: 0.9;
font-size: 0.9rem;
}
.visualization-container {
position: relative;
min-height: 500px;
}
/* Central Role */
.central-role {
position: absolute;
left: 5%;
top: 50%;
transform: translateY(-50%);
width: 280px;
z-index: 10;
}
.role-core {
background: rgba(255, 255, 255, 0.95);
border-radius: 16px;
padding: 20px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
.role-core:hover {
transform: scale(1.02);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
}
.role-core.expanded {
border-radius: 16px 16px 0 0;
}
.role-icon {
font-size: 2.5rem;
text-align: center;
margin-bottom: 8px;
}
.role-info {
text-align: center;
}
.role-name {
display: block;
color: #333;
font-weight: 700;
font-size: 1rem;
margin-bottom: 4px;
}
.role-type {
display: block;
color: #666;
font-size: 0.8rem;
}
.expand-icon {
text-align: center;
margin-top: 8px;
color: #999;
font-size: 0.8rem;
}
/* Trust Policy */
.trust-policy {
background: rgba(255, 255, 255, 0.95);
border-radius: 0 0 16px 16px;
padding: 16px 20px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.policy-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
}
.policy-icon {
font-size: 1.2rem;
}
.policy-title {
font-weight: 700;
color: #333;
font-size: 0.85rem;
}
.policy-content {
display: flex;
flex-direction: column;
gap: 8px;
}
.policy-item {
background: rgba(102, 126, 234, 0.1);
border-radius: 6px;
padding: 8px;
font-size: 0.75rem;
display: flex;
flex-direction: column;
gap: 2px;
}
.principal {
font-weight: 600;
color: #667eea;
}
.action {
color: #4caf50;
}
.condition {
color: #ff9800;
}
/* Connection Lines SVG */
.connection-lines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.connection-line {
stroke: rgba(255, 255, 255, 0.3);
stroke-width: 2;
fill: none;
pointer-events: stroke;
cursor: pointer;
transition: all 0.3s ease;
}
.connection-line.allow {
stroke: #4caf50;
}
.connection-line.deny {
stroke: #f44336;
stroke-dasharray: 5, 5;
}
.connection-line:hover,
.connection-line.active {
stroke-width: 4;
opacity: 1;
}
/* Attached Policies */
.attached-policies {
position: absolute;
right: 5%;
top: 50%;
transform: translateY(-50%);
width: 240px;
}
.policy-card {
background: rgba(255, 255, 255, 0.95);
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
position: relative;
}
.policy-card:hover,
.policy-card.active {
transform: translateX(-8px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
}
.policy-card.selected {
border: 2px solid #667eea;
}
.policy-card .policy-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
}
.policy-card .policy-icon {
font-size: 1.4rem;
}
.policy-card .policy-name {
font-weight: 700;
color: #333;
font-size: 0.9rem;
}
.policy-permissions {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.permission-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px;
margin-bottom: 4px;
background: rgba(0, 0, 0, 0.03);
border-radius: 4px;
font-size: 0.7rem;
}
.perm-effect {
padding: 2px 6px;
border-radius: 3px;
font-weight: 600;
font-size: 0.65rem;
text-transform: uppercase;
}
.perm-effect.Allow {
background: #4caf50;
color: white;
}
.perm-effect.Deny {
background: #f44336;
color: white;
}
.perm-action {
font-family: monospace;
color: #667eea;
}
.perm-resource {
color: #999;
font-size: 0.6rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 1024px) {
.visualization-container {
display: flex;
flex-direction: column;
gap: 20px;
}
.central-role,
.attached-policies {
position: static;
transform: none;
width: 100%;
}
.connection-lines {
display: none;
}
}
</style>