Files
sanbuphy 1062e2e16f refactor: 重构 api-intro、api-design、transistor-to-cpu 组件为紧凑布局
- 重构 api-intro 7 个 Vue 组件为更紧凑的左右布局
- 重构 api-design 相关组件
- 重构 transistor-to-cpu 相关组件
- 统一使用 demo-root -> demo-header -> demo-layout -> info-box 结构
- 扩写文章内容为 MIT 讲义风格
2026-02-23 01:50:43 +08:00

318 lines
6.8 KiB
Vue
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="graph-structure-demo">
<div class="demo-header">
<span class="title">图结构复杂关系的表示</span>
<span class="subtitle">节点和边的网络</span>
</div>
<div class="graph-types">
<div class="type-selector">
<button
:class="['type-btn', { active: graphType === 'undirected' }]"
@click="graphType = 'undirected'"
>
无向图
</button>
<button
:class="['type-btn', { active: graphType === 'directed' }]"
@click="graphType = 'directed'"
>
有向图
</button>
<button
:class="['type-btn', { active: graphType === 'weighted' }]"
@click="graphType = 'weighted'"
>
带权图
</button>
</div>
</div>
<div class="graph-visualization">
<svg viewBox="0 0 400 300" class="graph-svg">
<!-- 连接线 -->
<line
v-for="edge in edges"
:key="edge.id"
:x1="nodes[edge.from].x"
:y1="nodes[edge.from].y"
:x2="nodes[edge.to].x"
:y2="nodes[edge.to].y"
:stroke="edge.weight ? '#3b82f6' : 'var(--vp-c-divider)'"
:stroke-width="edge.weight ? '3' : '2'"
:marker-end="graphType === 'directed' ? 'url(#arrow)' : ''"
/>
<!-- 箭头定义 -->
<defs v-if="graphType === 'directed'">
<marker
id="arrow"
viewBox="0 0 10 10"
refX="20"
refY="5"
markerWidth="6"
markerHeight="6"
orient="auto"
>
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--vp-c-divider)" />
</marker>
</defs>
<!-- 节点 -->
<g
v-for="(node, index) in nodes"
:key="index"
class="graph-node"
@click="selectedNode = index"
>
<circle
:cx="node.x"
:cy="node.y"
r="20"
:fill="
selectedNode === index
? 'var(--vp-c-brand)'
: 'var(--vp-c-brand-soft)'
"
stroke="var(--vp-c-brand)"
stroke-width="2"
/>
<text
:x="node.x"
:y="node.y"
text-anchor="middle"
dominant-baseline="middle"
fill="white"
font-size="12"
font-weight="600"
>
{{ node.label }}
</text>
</g>
</svg>
</div>
<div class="graph-info">
<div class="info-title">图的特点</div>
<div class="info-grid">
<div class="info-item">
<div class="item-label">节点 (V)</div>
<div class="item-value">{{ nodes.length }}</div>
</div>
<div class="info-item">
<div class="item-label"> (E)</div>
<div class="item-value">{{ edges.length }}</div>
</div>
<div class="info-item">
<div class="item-label"></div>
<div class="item-value">{{ averageDegree }}</div>
</div>
</div>
</div>
<div class="applications">
<div class="app-title">应用场景</div>
<div class="app-list">
<div class="app-item">
<span class="app-icon">🗺</span>
<span class="app-text">地图导航最短路径</span>
</div>
<div class="app-item">
<span class="app-icon">👥</span>
<span class="app-text">社交网络好友关系</span>
</div>
<div class="app-item">
<span class="app-icon">🌐</span>
<span class="app-text">网页链接PageRank</span>
</div>
<div class="app-item">
<span class="app-icon">🔗</span>
<span class="app-text">依赖关系包管理</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const graphType = ref('undirected')
const selectedNode = ref(null)
const nodes = [
{ label: 'A', x: 200, y: 50 },
{ label: 'B', x: 100, y: 130 },
{ label: 'C', x: 300, y: 130 },
{ label: 'D', x: 100, y: 250 },
{ label: 'E', x: 300, y: 250 }
]
const edges = ref([
{ id: 1, from: 0, to: 1 },
{ id: 2, from: 0, to: 2 },
{ id: 3, from: 1, to: 2 },
{ id: 4, from: 1, to: 3 },
{ id: 5, from: 2, to: 4 },
{ id: 6, from: 3, to: 4 }
])
const averageDegree = computed(() => {
return ((edges.value.length * 2) / nodes.length).toFixed(1)
})
</script>
<style scoped>
.graph-structure-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.demo-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
.demo-header .title {
font-weight: 700;
font-size: 1.1rem;
}
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.9rem;
}
.graph-types {
margin-bottom: 2rem;
}
.type-selector {
display: flex;
gap: 1rem;
justify-content: center;
}
.type-btn {
padding: 0.6rem 1.25rem;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 6px;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s;
}
.type-btn.active {
background: var(--vp-c-brand);
border-color: var(--vp-c-brand);
color: white;
}
.graph-visualization {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
}
.graph-svg {
width: 100%;
height: auto;
}
.graph-node {
cursor: pointer;
}
.graph-node circle {
transition: all 0.3s;
}
.graph-node:hover circle {
r: 25;
}
.graph-info {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.info-title {
font-weight: 600;
font-size: 1rem;
margin-bottom: 1rem;
color: var(--vp-c-brand);
}
.info-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.info-item {
text-align: center;
padding: 1rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.item-label {
font-size: 0.8rem;
color: var(--vp-c-text-2);
margin-bottom: 0.5rem;
}
.item-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--vp-c-brand);
}
.applications {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1.5rem;
}
.app-title {
font-weight: 600;
font-size: 1rem;
margin-bottom: 1rem;
color: var(--vp-c-brand);
}
.app-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 0.75rem;
}
.app-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.app-icon {
font-size: 1.3rem;
}
.app-text {
font-size: 0.85rem;
}
</style>