feat: comprehensive documentation and demo updates
- Update READMEs and docs across multiple languages - Enhance interactive demos for Agent, LLM, VLM, Audio, Image Gen, Terminal, and Web Basics - Add new appendix sections for Database and IDE intros - Update VitePress config, theme, and utility scripts - Clean up unused assets and components
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="branch-demo">
|
||||
<div class="panel">
|
||||
<div class="controls">
|
||||
<button @click="init" :disabled="inited" class="btn">初始化</button>
|
||||
<button @click="commit" :disabled="!inited" class="btn">提交</button>
|
||||
<button @click="branch" :disabled="!inited || hasBranch" class="btn">创建分支</button>
|
||||
<button @click="merge" :disabled="!hasBranch" class="btn">合并</button>
|
||||
<button @click="reset" class="btn secondary">重置</button>
|
||||
</div>
|
||||
|
||||
<div class="graph">
|
||||
<svg viewBox="0 0 400 120">
|
||||
<line x1="50" y1="40" x2="350" y2="40" stroke="#3b82f6" stroke-width="3"/>
|
||||
<line v-if="hasBranch" x1="150" y1="40" x2="150" y2="80" stroke="#10b981" stroke-width="3"/>
|
||||
<line v-if="hasBranch" x1="150" y1="80" x2="300" y2="80" stroke="#10b981" stroke-width="3"/>
|
||||
<circle v-for="(c,i) in main" :cx="60+i*50" cy="40" r="8" fill="#3b82f6"/>
|
||||
<circle v-for="(c,i) in feat" :cx="180+i*50" cy="80" r="8" fill="#10b981"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="status">
|
||||
<span>提交: {{ main.length }}</span>
|
||||
<span>分支: {{ hasBranch ? 2 : 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>💡 分支策略:</strong> 并行开发,互不干扰,最后合并</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const inited = ref(false)
|
||||
const hasBranch = ref(false)
|
||||
const main = ref([])
|
||||
const feat = ref([])
|
||||
|
||||
const init = () => { inited.value = true; main.value = [1] }
|
||||
const commit = () => { if(inited.value) main.value.push(1) }
|
||||
const branch = () => { if(inited.value) { hasBranch.value = true; feat.value = [1] } }
|
||||
const merge = () => { if(hasBranch.value) { main.value.push(1); hasBranch.value = false; feat.value = [] } }
|
||||
const reset = () => { inited.value = false; hasBranch.value = false; main.value = []; feat.value = [] }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.branch-demo { border: 1px solid var(--vp-c-divider); border-radius: 8px; background-color: var(--vp-c-bg-soft); padding: 1.5rem; margin: 1rem 0; }
|
||||
.controls { display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap; }
|
||||
.btn { padding: 0.5rem 1rem; border: 1px solid var(--vp-c-brand); background: var(--vp-c-bg); color: var(--vp-c-brand); border-radius: 6px; cursor: pointer; }
|
||||
.btn:hover:not(:disabled) { background: var(--vp-c-brand); color: var(--vp-c-bg); }
|
||||
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.btn.secondary { border-color: var(--vp-c-divider); }
|
||||
.graph { background: var(--vp-c-bg); border-radius: 8px; padding: 1rem; border: 1px solid var(--vp-c-divider); margin: 1rem 0; }
|
||||
.graph svg { width: 100%; height: auto; }
|
||||
.status { display: flex; gap: 2rem; }
|
||||
.info-box { padding: 1rem; background: var(--vp-c-bg); border-left: 4px solid var(--vp-c-brand); border-radius: 4px; margin-top: 1rem; }
|
||||
.info-box p { margin: 0; color: var(--vp-c-text-1); }
|
||||
</style>
|
||||
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="command-demo">
|
||||
<div class="panel">
|
||||
<div class="terminal">
|
||||
<div class="output">
|
||||
<div v-for="(line, i) in output" :key="i" :class="line.type">
|
||||
<span v-if="line.type === 'command'" class="prompt">$</span>
|
||||
<span v-html="line.text"></span>
|
||||
</div>
|
||||
<div v-if="output.length === 0" class="welcome">
|
||||
输入命令开始学习 Git
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-line">
|
||||
<span class="prompt">$</span>
|
||||
<input v-model="cmd" @keyup.enter="execute" placeholder="git status" class="cmd-input" />
|
||||
<button @click="execute" class="run-btn">运行</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quick-cmds">
|
||||
<button @click="runCmd('git init')" class="cmd-btn">初始化</button>
|
||||
<button @click="runCmd('git status')" class="cmd-btn">状态</button>
|
||||
<button @click="runCmd('git add .')" class="cmd-btn">添加</button>
|
||||
<button @click="runCmd('git commit -m \'msg\'')" class="cmd-btn">提交</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>💡 常用命令:</strong> init → status → add → commit</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const cmd = ref('')
|
||||
const output = ref([])
|
||||
|
||||
const execute = () => {
|
||||
const c = cmd.value.trim()
|
||||
if (!c) return
|
||||
|
||||
output.value.push({ type: 'command', text: c })
|
||||
|
||||
if (c === 'git init') {
|
||||
output.value.push({ type: 'success', text: 'Initialized empty Git repository' })
|
||||
} else if (c === 'git status') {
|
||||
output.value.push({ type: 'info', text: 'On branch main\nnothing to commit' })
|
||||
} else if (c === 'git add .') {
|
||||
output.value.push({ type: 'success', text: 'Files added to staging area' })
|
||||
} else if (c.startsWith('git commit')) {
|
||||
output.value.push({ type: 'success', text: '1 file committed' })
|
||||
} else {
|
||||
output.value.push({ type: 'error', text: 'Unknown command' })
|
||||
}
|
||||
|
||||
cmd.value = ''
|
||||
}
|
||||
|
||||
const runCmd = (c) => {
|
||||
cmd.value = c
|
||||
execute()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.command-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
padding: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.terminal {
|
||||
background: #1f2937;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.output {
|
||||
min-height: 150px;
|
||||
margin-bottom: 1rem;
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
.output .command { color: #10b981; }
|
||||
.output .success { color: #10b981; }
|
||||
.output .error { color: #ef4444; }
|
||||
.output .info { color: #60a5fa; }
|
||||
.output .welcome { color: #9ca3af; font-style: italic; }
|
||||
|
||||
.input-line {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.prompt { color: #10b981; }
|
||||
|
||||
.cmd-input {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #d1d5db;
|
||||
font-family: monospace;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.cmd-input:focus { outline: none; }
|
||||
|
||||
.run-btn {
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: #10b981;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.quick-cmds {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cmd-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--vp-c-brand);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.cmd-btn:hover { background: var(--vp-c-brand); color: var(--vp-c-bg); }
|
||||
|
||||
.info-box {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 4px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.info-box p { margin: 0; color: var(--vp-c-text-1); line-height: 1.6; }
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="conflict-demo">
|
||||
<div class="panel">
|
||||
<div class="editor">
|
||||
<div class="line normal"><span class="ln">1</span>function greet() {</div>
|
||||
<div class="line normal"><span class="ln">2</span> console.log('Hi');</div>
|
||||
<div class="line conflict"><span class="ln">3</span><<<<<<< HEAD</div>
|
||||
<div class="line current"><span class="ln">4</span> console.log('Welcome') // 当前版本</div>
|
||||
<div class="line conflict"><span class="ln">5</span>=======</div>
|
||||
<div class="line incoming"><span class="ln">6</span> console.log('Greetings') // 传入版本</div>
|
||||
<div class="line conflict"><span class="ln">7</span>>>>>>>>> feature</div>
|
||||
<div class="line normal"><span class="ln">8</span> console.log('Bye');</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button @click="resolve('current')" class="action-btn">保留当前</button>
|
||||
<button @click="resolve('incoming')" class="action-btn">保留传入</button>
|
||||
<button @click="resolve('manual')" class="action-btn">手动合并</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>💡 解决冲突:</strong> 选择保留哪个版本,或手动编辑合并</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const resolved = ref(false)
|
||||
const resolve = (choice) => { resolved.value = true }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.conflict-demo { border: 1px solid var(--vp-c-divider); border-radius: 8px; background-color: var(--vp-c-bg-soft); padding: 1.5rem; margin: 1rem 0; }
|
||||
.editor { background: #1f2937; border-radius: 8px; padding: 1rem; font-family: monospace; margin-bottom: 1rem; }
|
||||
.line { display: flex; gap: 0.5rem; line-height: 1.6; }
|
||||
.ln { color: #6b7280; min-width: 2rem; }
|
||||
.line.normal { color: #d1d5db; }
|
||||
.line.conflict { color: #f59e0b; }
|
||||
.line.current { color: #60a5fa; background: rgba(96,165,250,0.1); }
|
||||
.line.incoming { color: #a78bfa; background: rgba(167,139,250,0.1); }
|
||||
.actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.action-btn { padding: 0.625rem 1.25rem; border: 1px solid var(--vp-c-brand); background: var(--vp-c-bg); color: var(--vp-c-brand); border-radius: 6px; cursor: pointer; }
|
||||
.action-btn:hover { background: var(--vp-c-brand); color: var(--vp-c-bg); }
|
||||
.info-box { padding: 1rem; background: var(--vp-c-bg); border-left: 4px solid var(--vp-c-brand); border-radius: 4px; }
|
||||
.info-box p { margin: 0; color: var(--vp-c-text-1); }
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="remote-demo">
|
||||
<div class="panel">
|
||||
<div class="repos">
|
||||
<div class="repo">
|
||||
<div class="header">💻 本地</div>
|
||||
<div class="commits">
|
||||
<div v-for="c in local" :key="c" class="commit-dot">
|
||||
<span class="dot local"></span>
|
||||
<span class="hash">{{ c.substring(0,6) }}</span>
|
||||
</div>
|
||||
<div v-if="local.length === 0" class="empty">无</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sync">⇄</div>
|
||||
|
||||
<div class="repo">
|
||||
<div class="header">☁️ 远程</div>
|
||||
<div class="commits">
|
||||
<div v-for="c in remote" :key="c" class="commit-dot">
|
||||
<span class="dot remote"></span>
|
||||
<span class="hash">{{ c.substring(0,6) }}</span>
|
||||
</div>
|
||||
<div v-if="remote.length === 0" class="empty">无</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button @click="localCommit" class="btn">本地提交</button>
|
||||
<button @click="push" :disabled="local.length <= remote.length" class="btn">推送 Push</button>
|
||||
<button @click="pull" :disabled="!hasRemote" class="btn">拉取 Pull</button>
|
||||
<button @click="reset" class="btn secondary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>💡 远程协作:</strong> Push 上传,Pull 下载,保持同步</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
const local = ref([])
|
||||
const remote = ref([])
|
||||
const hasRemote = ref(false)
|
||||
|
||||
const localCommit = () => { local.value.push(Math.random().toString(16).substr(2,7)) }
|
||||
const push = () => { remote.value.push(...local.value.slice(remote.value.length)); hasRemote.value = false }
|
||||
const pull = () => { if(hasRemote.value) local.value.push(Math.random().toString(16).substr(2,7)); hasRemote.value = false }
|
||||
const reset = () => { local.value = []; remote.value = []; hasRemote.value = false }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.remote-demo { border: 1px solid var(--vp-c-divider); border-radius: 8px; background-color: var(--vp-c-bg-soft); padding: 1.5rem; margin: 1rem 0; }
|
||||
.repos { display: grid; grid-template-columns: 1fr auto 1fr; gap: 1rem; align-items: stretch; margin-bottom: 1rem; }
|
||||
.repo { border: 1px solid var(--vp-c-divider); border-radius: 8px; padding: 1rem; background: var(--vp-c-bg); }
|
||||
.header { font-weight: 600; margin-bottom: 0.5rem; }
|
||||
.commits { min-height: 80px; }
|
||||
.commit-dot { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
|
||||
.dot { width: 10px; height: 10px; border-radius: 50%; }
|
||||
.dot.local { background: #3b82f6; }
|
||||
.dot.remote { background: #10b981; }
|
||||
.hash { font-family: monospace; font-size: 0.875rem; color: var(--vp-c-text-2); }
|
||||
.sync { font-size: 2rem; text-align: center; }
|
||||
.empty { color: var(--vp-c-text-3); text-align: center; padding: 1rem; }
|
||||
.controls { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.btn { padding: 0.5rem 1rem; border: 1px solid var(--vp-c-brand); background: var(--vp-c-bg); color: var(--vp-c-brand); border-radius: 6px; cursor: pointer; }
|
||||
.btn:hover:not(:disabled) { background: var(--vp-c-brand); color: var(--vp-c-bg); }
|
||||
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.btn.secondary { border-color: var(--vp-c-divider); }
|
||||
.info-box { padding: 1rem; background: var(--vp-c-bg); border-left: 4px solid var(--vp-c-brand); border-radius: 4px; margin-top: 1rem; }
|
||||
.info-box p { margin: 0; color: var(--vp-c-text-1); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.repos { grid-template-columns: 1fr; }
|
||||
.sync { transform: rotate(90deg); }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="stash-demo">
|
||||
<div class="panel">
|
||||
<div class="areas">
|
||||
<div class="area">
|
||||
<div class="header">💻 工作区 ({{ work.length }})</div>
|
||||
<div v-for="f in work" :key="f" class="file">📄 {{ f }}</div>
|
||||
<div v-if="work.length === 0" class="empty">空</div>
|
||||
</div>
|
||||
|
||||
<div class="area">
|
||||
<div class="header">📚 Stash 栈 ({{ stash.length }})</div>
|
||||
<div v-for="(s,i) in stash" :key="i" class="stash-item">
|
||||
<span class="num">{{ i+1 }}</span>
|
||||
<span class="msg">{{ s }}</span>
|
||||
</div>
|
||||
<div v-if="stash.length === 0" class="empty">空</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button @click="doWork" :disabled="work.length > 0" class="btn">修改</button>
|
||||
<button @click="save" :disabled="work.length === 0 || stash.length >= 3" class="btn">保存</button>
|
||||
<button @click="pop" :disabled="stash.length === 0" class="btn">恢复</button>
|
||||
<button @click="reset" class="btn secondary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>💡 Stash 用途:</strong> 临时保存工作现场,切换任务</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const work = ref([])
|
||||
const stash = ref([])
|
||||
const doWork = () => { work.value = ['file.js', 'style.css'] }
|
||||
const save = () => { stash.value.push('WIP'); work.value = [] }
|
||||
const pop = () => { if(stash.value.length) { stash.value.pop(); work.value = ['file.js'] } }
|
||||
const reset = () => { work.value = []; stash.value = [] }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.stash-demo { border: 1px solid var(--vp-c-divider); border-radius: 8px; background-color: var(--vp-c-bg-soft); padding: 1.5rem; margin: 1rem 0; }
|
||||
.areas { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem; }
|
||||
.area { border: 1px solid var(--vp-c-divider); border-radius: 8px; padding: 1rem; background: var(--vp-c-bg); }
|
||||
.header { font-weight: 600; margin-bottom: 0.5rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.file, .stash-item { padding: 0.5rem; background: var(--vp-c-bg-soft); margin-bottom: 0.25rem; border-radius: 4px; font-size: 0.875rem; display: flex; gap: 0.5rem; align-items: center; }
|
||||
.stash-item .num { width: 20px; height: 20px; background: var(--vp-c-brand); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.75rem; }
|
||||
.empty { color: var(--vp-c-text-3); text-align: center; font-style: italic; padding: 1rem; }
|
||||
.controls { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.btn { padding: 0.5rem 1rem; border: 1px solid var(--vp-c-brand); background: var(--vp-c-bg); color: var(--vp-c-brand); border-radius: 6px; cursor: pointer; }
|
||||
.btn:hover:not(:disabled) { background: var(--vp-c-brand); color: var(--vp-c-bg); }
|
||||
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.btn.secondary { border-color: var(--vp-c-divider); }
|
||||
.info-box { padding: 1rem; background: var(--vp-c-bg); border-left: 4px solid var(--vp-c-brand); border-radius: 4px; margin-top: 1rem; }
|
||||
.info-box p { margin: 0; color: var(--vp-c-text-1); }
|
||||
</style>
|
||||
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="storage-demo">
|
||||
<div class="panel">
|
||||
<div class="comparison">
|
||||
<div class="mode-selector">
|
||||
<button @click="mode = 'full'" :class="{active: mode === 'full'}" class="mode-btn">完整备份</button>
|
||||
<button @click="mode = 'git'" :class="{active: mode === 'git'}" class="mode-btn">Git 增量</button>
|
||||
</div>
|
||||
|
||||
<div class="visualization">
|
||||
<div class="bar-container">
|
||||
<div class="bar full" :style="{height: fullSize + '%'}">
|
||||
<span class="label">完整备份: {{ fullSize }}MB</span>
|
||||
</div>
|
||||
<div class="bar git" :style="{height: gitSize + '%'}">
|
||||
<span class="label">Git 存储: {{ gitSize }}MB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<span class="value">{{ savedPercent }}%</span>
|
||||
<span class="label">节省空间</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="value">{{ versionCount }}</span>
|
||||
<span class="label">版本数</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>💡 Git 增量存储:</strong> 只保存变更部分,大幅节省空间</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const mode = ref('git')
|
||||
const versionCount = ref(5)
|
||||
const fullSize = ref(500)
|
||||
const gitSize = ref(50)
|
||||
|
||||
const savedPercent = computed(() => Math.round((1 - gitSize.value / fullSize.value) * 100))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.storage-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
padding: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.mode-selector {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-1);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mode-btn.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.bar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.bar {
|
||||
height: 60px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: height 0.5s ease;
|
||||
}
|
||||
|
||||
.bar.full { background: linear-gradient(135deg, #ef4444, #dc2626); }
|
||||
.bar.git { background: linear-gradient(135deg, #10b981, #059669); }
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-item .value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.stat-item .label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.info-box {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 4px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="three-areas-demo">
|
||||
<div class="panel">
|
||||
<div class="areas-container">
|
||||
<div class="area working">
|
||||
<div class="area-header">
|
||||
<span class="area-icon">💻</span>
|
||||
<span class="area-name">工作区</span>
|
||||
<span class="area-count">{{ workingFiles.length }}</span>
|
||||
</div>
|
||||
<div class="file-list">
|
||||
<div v-for="file in workingFiles" :key="file.name" class="file-item">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<button @click="addToStaging(file)" class="mini-btn">+</button>
|
||||
</div>
|
||||
<div v-if="workingFiles.length === 0" class="empty">无文件</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="area staging">
|
||||
<div class="area-header">
|
||||
<span class="area-icon">📋</span>
|
||||
<span class="area-name">暂存区</span>
|
||||
<span class="area-count">{{ stagedFiles.length }}</span>
|
||||
</div>
|
||||
<div class="file-list">
|
||||
<div v-for="file in stagedFiles" :key="file.name" class="file-item">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<button @click="commitFile(file)" class="mini-btn">✓</button>
|
||||
</div>
|
||||
<div v-if="stagedFiles.length === 0" class="empty">无文件</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="area repo">
|
||||
<div class="area-header">
|
||||
<span class="area-icon">📦</span>
|
||||
<span class="area-name">仓库</span>
|
||||
<span class="area-count">{{ commits.length }}</span>
|
||||
</div>
|
||||
<div class="commit-list">
|
||||
<div v-for="commit in commits.slice(-3).reverse()" :key="commit.hash" class="commit-item">
|
||||
<span class="commit-hash">{{ commit.hash.substring(0, 6) }}</span>
|
||||
<span class="commit-msg">{{ commit.message }}</span>
|
||||
</div>
|
||||
<div v-if="commits.length === 0" class="empty">无提交</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>💡 三区工作流:</strong> 工作区修改 → 添加到暂存区 → 提交到仓库</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const workingFiles = ref([
|
||||
{ name: 'index.vue' },
|
||||
{ name: 'style.css' }
|
||||
])
|
||||
|
||||
const stagedFiles = ref([])
|
||||
const commits = ref([])
|
||||
|
||||
const addToStaging = (file) => {
|
||||
const index = workingFiles.value.findIndex(f => f.name === file.name)
|
||||
if (index !== -1) {
|
||||
workingFiles.value.splice(index, 1)
|
||||
stagedFiles.value.push(file)
|
||||
}
|
||||
}
|
||||
|
||||
const commitFile = (file) => {
|
||||
const index = stagedFiles.value.findIndex(f => f.name === file.name)
|
||||
if (index !== -1) {
|
||||
stagedFiles.value.splice(index, 1)
|
||||
commits.value.push({
|
||||
hash: Math.random().toString(16).substr(2, 7),
|
||||
message: file.name
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.three-areas-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
padding: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.areas-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.area {
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.area.working { border-color: #f59e0b; }
|
||||
.area.staging { border-color: #3b82f6; }
|
||||
.area.repo { border-color: #10b981; }
|
||||
|
||||
.area-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.area-icon { font-size: 1.25rem; }
|
||||
.area-name { flex: 1; font-weight: 600; }
|
||||
.area-count {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.125rem 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.file-list, .commit-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.file-item, .commit-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.file-icon { font-size: 1rem; }
|
||||
.file-name { flex: 1; font-size: 0.875rem; }
|
||||
|
||||
.mini-btn {
|
||||
padding: 0.125rem 0.5rem;
|
||||
border: 1px solid var(--vp-c-brand);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-brand);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.mini-btn:hover { background: var(--vp-c-brand); color: var(--vp-c-bg); }
|
||||
|
||||
.commit-hash {
|
||||
font-family: monospace;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.commit-msg {
|
||||
flex: 1;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: var(--vp-c-text-3);
|
||||
font-style: italic;
|
||||
font-size: 0.875rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.areas-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,222 @@
|
||||
<!--
|
||||
GitWorkflowDemo.vue
|
||||
Git 工作流演示 - 简洁版
|
||||
|
||||
用途:展示 Git 的基本工作流程
|
||||
交互:初始化、提交、创建分支、合并
|
||||
-->
|
||||
<template>
|
||||
<div class="git-workflow-demo">
|
||||
<!-- 控制面板 -->
|
||||
<div class="control-panel">
|
||||
<button @click="initRepo" :disabled="inited" class="action-btn">
|
||||
🎯 初始化仓库
|
||||
</button>
|
||||
<button @click="makeCommit" :disabled="!inited" class="action-btn">
|
||||
✅ 提交
|
||||
</button>
|
||||
<button @click="createBranch" :disabled="!inited || hasBranch" class="action-btn">
|
||||
🌿 创建分支
|
||||
</button>
|
||||
<button @click="mergeBranch" :disabled="!hasBranch || merging" class="action-btn">
|
||||
🔀 合并分支
|
||||
</button>
|
||||
<button @click="reset" class="action-btn secondary">
|
||||
🔄 重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 提交历史可视化 -->
|
||||
<div class="visualization">
|
||||
<div class="graph-container">
|
||||
<svg viewBox="0 0 400 150" class="git-graph">
|
||||
<!-- 主分支线 -->
|
||||
<line x1="50" y1="60" x2="350" y2="60" stroke="#3b82f6" stroke-width="3" />
|
||||
|
||||
<!-- 分支线 -->
|
||||
<line v-if="hasBranch" x1="150" y1="60" x2="150" y2="100" stroke="#10b981" stroke-width="3" />
|
||||
<line v-if="hasBranch" x1="150" y1="100" x2="300" y2="100" stroke="#10b981" stroke-width="3" />
|
||||
|
||||
<!-- 合并线 -->
|
||||
<path v-if="merging" d="M 300 100 Q 320 80, 320 60" fill="none" stroke="#f59e0b" stroke-width="2" stroke-dasharray="5,5" />
|
||||
|
||||
<!-- 提交节点 -->
|
||||
<circle v-for="(commit, i) in mainCommits" :key="'main-'+i" :cx="80 + i * 60" cy="60" r="10" fill="#3b82f6" />
|
||||
<circle v-for="(commit, i) in branchCommits" :key="'branch-'+i" :cx="200 + i * 60" cy="100" r="10" fill="#10b981" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 状态信息 -->
|
||||
<div class="status-panel">
|
||||
<div class="status-item">
|
||||
<span class="label">提交数:</span>
|
||||
<span class="value">{{ mainCommits.length }}</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="label">分支:</span>
|
||||
<span class="value">{{ hasBranch ? '2' : '1' }}</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="label">状态:</span>
|
||||
<span class="value">{{ status }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 说明 -->
|
||||
<div class="info-box">
|
||||
<p><strong>💡 工作流程:</strong> 初始化 → 提交 → 创建分支 → 开发 → 合并</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const inited = ref(false)
|
||||
const hasBranch = ref(false)
|
||||
const merging = ref(false)
|
||||
const mainCommits = ref([])
|
||||
const branchCommits = ref([])
|
||||
|
||||
const status = computed(() => {
|
||||
if (merging) return '合并中...'
|
||||
if (hasBranch) return '分支已创建'
|
||||
if (inited) return '已初始化'
|
||||
return '未初始化'
|
||||
})
|
||||
|
||||
const initRepo = () => {
|
||||
inited.value = true
|
||||
mainCommits.value = [{ hash: 'abc123' }]
|
||||
}
|
||||
|
||||
const makeCommit = () => {
|
||||
if (inited.value) {
|
||||
mainCommits.value.push({ hash: Math.random().toString(16).substr(2, 6) })
|
||||
}
|
||||
}
|
||||
|
||||
const createBranch = () => {
|
||||
if (inited.value && !hasBranch.value) {
|
||||
hasBranch.value = true
|
||||
branchCommits.value = [{ hash: 'def456' }]
|
||||
}
|
||||
}
|
||||
|
||||
const mergeBranch = () => {
|
||||
if (hasBranch.value) {
|
||||
merging.value = true
|
||||
setTimeout(() => {
|
||||
mainCommits.value.push({ hash: Math.random().toString(16).substr(2, 6) })
|
||||
hasBranch.value = false
|
||||
branchCommits.value = []
|
||||
merging.value = false
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
inited.value = false
|
||||
hasBranch.value = false
|
||||
merging.value = false
|
||||
mainCommits.value = []
|
||||
branchCommits.value = []
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.git-workflow-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
padding: 1.5rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 0.625rem 1.25rem;
|
||||
border: 2px solid var(--vp-c-brand);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-brand);
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.action-btn:hover:not(:disabled) {
|
||||
background: var(--vp-c-brand);
|
||||
color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.action-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
border-color: var(--vp-c-divider);
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.action-btn.secondary {
|
||||
border-color: var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.visualization {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.graph-container {
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.git-graph {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.status-panel {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
margin: 1.5rem 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.status-item .label {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.status-item .value {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.info-box {
|
||||
padding: 1rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-left: 4px solid var(--vp-c-brand);
|
||||
border-radius: 4px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user