Files
sanbuphy f35cddeb8b feat(appendix): 重构工程实践章节,添加交互式演示组件
## 新增组件 (14个)
- CodeSmellDemo.vue: 代码异味识别演示
- DecisionMatrixDemo.vue: 决策矩阵工具
- DesignPatternCatalogDemo.vue: 设计模式目录
- DocStructureDemo.vue: 文档结构示例
- LicenseComparisonDemo.vue: 开源许可证对比
- OpenSourceWorkflowDemo.vue: 开源协作流程
- PatternPlaygroundDemo.vue: 设计模式演练场
- RefactoringDemo.vue: 重构实战演示
- SecurityChecklistDemo.vue: 安全检查清单
- TDDCycleDemo.vue: TDD 循环演示
- TechRadarDemo.vue: 技术雷达图
- TechWritingPracticeDemo.vue: 技术写作实践
- TestPyramidDemo.vue: 测试金字塔
- WebSecurityDemo.vue: Web 安全演示

## 文档更新 (7篇)
- code-quality-refactoring.md: 代码质量与重构
- design-patterns.md: 设计模式
- open-source-collaboration.md: 开源协作
- security-thinking.md: 安全思维
- technical-writing.md: 技术写作
- technology-selection.md: 技术选型
- testing-strategies.md: 测试策略

## 其他变更
- 将 browser-as-os.md 内容合并到 computer-networks.md
- 更新 .gitignore 和 theme/index.js
2026-02-24 13:03:21 +08:00

138 lines
5.9 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="tech-radar-demo">
<div class="demo-label">技术雷达 点击技术点查看详情</div>
<div class="radar-container">
<div class="radar-rings">
<div v-for="ring in rings" :key="ring.name" class="ring" :class="ring.cls">
<span class="ring-label">{{ ring.name }}</span>
</div>
</div>
<div
v-for="tech in techs"
:key="tech.name"
class="tech-dot"
:class="[tech.category, { active: selected === tech.name }]"
:style="tech.pos"
@click="selected = selected === tech.name ? '' : tech.name"
>
<span class="dot-label">{{ tech.name }}</span>
</div>
</div>
<div class="legend">
<span v-for="c in cats" :key="c.cls" class="legend-item">
<span class="legend-dot" :class="c.cls"></span>{{ c.name }}
</span>
</div>
<Transition name="fade">
<div v-if="selectedTech" class="info-card">
<h4>{{ selectedTech.name }}</h4>
<div class="info-ring">环位{{ selectedTech.ring }}</div>
<p>{{ selectedTech.desc }}</p>
</div>
</Transition>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const selected = ref('')
const selectedTech = computed(() => techs.find(t => t.name === selected.value))
const rings = [
{ name: '采纳', cls: 'adopt' },
{ name: '试验', cls: 'trial' },
{ name: '评估', cls: 'assess' },
{ name: '暂缓', cls: 'hold' }
]
const cats = [
{ name: '语言', cls: 'lang' },
{ name: '框架', cls: 'framework' },
{ name: '工具', cls: 'tool' },
{ name: '平台', cls: 'platform' }
]
const techs = [
{ name: 'TypeScript', category: 'lang', ring: '采纳', pos: { top: '42%', left: '30%' }, desc: '类型安全的 JavaScript 超集,已成为前端项目标配。' },
{ name: 'React', category: 'framework', ring: '采纳', pos: { top: '35%', left: '55%' }, desc: '生态最丰富的前端框架,适合大型团队和复杂应用。' },
{ name: 'Vue', category: 'framework', ring: '采纳', pos: { top: '50%', left: '45%' }, desc: '渐进式框架,学习曲线平缓,中文社区活跃。' },
{ name: 'Go', category: 'lang', ring: '采纳', pos: { top: '55%', left: '32%' }, desc: '高并发后端首选,编译快、部署简单。' },
{ name: 'Rust', category: 'lang', ring: '试验', pos: { top: '30%', left: '22%' }, desc: '内存安全无 GC,适合系统编程和高性能场景,学习曲线陡峭。' },
{ name: 'Svelte', category: 'framework', ring: '试验', pos: { top: '25%', left: '60%' }, desc: '编译时框架,无虚拟 DOM,包体积极小。' },
{ name: 'Bun', category: 'tool', ring: '评估', pos: { top: '18%', left: '42%' }, desc: '新一代 JS 运行时,速度极快但生态尚在完善。' },
{ name: 'Deno', category: 'platform', ring: '评估', pos: { top: '15%', left: '55%' }, desc: '安全优先的 JS/TS 运行时,内置工具链。' },
{ name: 'jQuery', category: 'framework', ring: '暂缓', pos: { top: '8%', left: '38%' }, desc: '历史功臣,但现代框架已全面替代,新项目不建议使用。' }
]
</script>
<style scoped>
.tech-radar-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background: var(--vp-c-bg-soft);
padding: 1rem 1.2rem;
margin: 1rem 0;
}
.demo-label { font-size: 0.78rem; font-weight: bold; color: var(--vp-c-text-2); margin-bottom: 1rem; text-align: center; }
.radar-container { position: relative; width: 100%; padding-top: 70%; margin-bottom: 1rem; }
.radar-rings { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.ring {
border-radius: 50%;
position: absolute;
display: flex;
align-items: flex-start;
justify-content: center;
padding-top: 4px;
}
.ring .ring-label { font-size: 0.68rem; font-weight: 600; opacity: 0.6; }
.ring.adopt { width: 90%; height: 90%; background: #d1fae520; border: 1px dashed #6ee7b7; }
.ring.trial { width: 66%; height: 66%; background: #dbeafe20; border: 1px dashed #93c5fd; }
.ring.assess { width: 42%; height: 42%; background: #fef3c720; border: 1px dashed #fcd34d; }
.ring.hold { width: 20%; height: 20%; background: #fee2e220; border: 1px dashed #fca5a5; }
.tech-dot {
position: absolute;
padding: 3px 8px;
border-radius: 12px;
font-size: 0.72rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
white-space: nowrap;
}
.tech-dot:hover { transform: scale(1.1); }
.tech-dot.active { box-shadow: 0 0 0 2px var(--vp-c-brand); transform: scale(1.15); }
.tech-dot.lang { background: #dbeafe; color: #1e40af; }
.tech-dot.framework { background: #d1fae5; color: #065f46; }
.tech-dot.tool { background: #fef3c7; color: #92400e; }
.tech-dot.platform { background: #fae8ff; color: #86198f; }
:root.dark .tech-dot.lang { background: #172554; color: #93c5fd; }
:root.dark .tech-dot.framework { background: #022c22; color: #6ee7b7; }
:root.dark .tech-dot.tool { background: #451a03; color: #fcd34d; }
:root.dark .tech-dot.platform { background: #4a044e; color: #f0abfc; }
.legend { display: flex; justify-content: center; gap: 1rem; font-size: 0.75rem; color: var(--vp-c-text-3); margin-bottom: 0.8rem; flex-wrap: wrap; }
.legend-item { display: flex; align-items: center; gap: 4px; }
.legend-dot { width: 10px; height: 10px; border-radius: 50%; }
.legend-dot.lang { background: #3b82f6; }
.legend-dot.framework { background: #10b981; }
.legend-dot.tool { background: #f59e0b; }
.legend-dot.platform { background: #d946ef; }
.info-card { border: 1px solid var(--vp-c-divider); border-radius: 8px; padding: 1rem; background: var(--vp-c-bg); }
.info-card h4 { margin: 0 0 4px; font-size: 1rem; }
.info-ring { font-size: 0.75rem; color: var(--vp-c-text-3); margin-bottom: 6px; }
.info-card p { font-size: 0.85rem; color: var(--vp-c-text-2); margin: 0; }
.fade-enter-active, .fade-leave-active { transition: opacity 0.25s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>