feat: update docs and components, fix DLQ demo bug
This commit is contained in:
@@ -4,18 +4,55 @@
|
||||
<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="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"/>
|
||||
<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>
|
||||
|
||||
@@ -38,23 +75,91 @@ 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 = [] }
|
||||
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); }
|
||||
.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>
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
</div>
|
||||
<div class="input-line">
|
||||
<span class="prompt">$</span>
|
||||
<input v-model="cmd" @keyup.enter="execute" placeholder="git status" class="cmd-input" />
|
||||
<input
|
||||
v-model="cmd"
|
||||
@keyup.enter="execute"
|
||||
placeholder="git status"
|
||||
class="cmd-input"
|
||||
/>
|
||||
<button @click="execute" class="run-btn">运行</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,7 +27,9 @@
|
||||
<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>
|
||||
<button @click="runCmd('git commit -m \'msg\'')" class="cmd-btn">
|
||||
提交
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,13 +48,19 @@ 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' })
|
||||
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' })
|
||||
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')) {
|
||||
@@ -55,7 +68,7 @@ const execute = () => {
|
||||
} else {
|
||||
output.value.push({ type: 'error', text: 'Unknown command' })
|
||||
}
|
||||
|
||||
|
||||
cmd.value = ''
|
||||
}
|
||||
|
||||
@@ -88,11 +101,22 @@ const runCmd = (c) => {
|
||||
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; }
|
||||
.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;
|
||||
@@ -100,7 +124,9 @@ const runCmd = (c) => {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.prompt { color: #10b981; }
|
||||
.prompt {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.cmd-input {
|
||||
flex: 1;
|
||||
@@ -111,7 +137,9 @@ const runCmd = (c) => {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.cmd-input:focus { outline: none; }
|
||||
.cmd-input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.run-btn {
|
||||
padding: 0.25rem 0.75rem;
|
||||
@@ -139,7 +167,10 @@ const runCmd = (c) => {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.cmd-btn:hover { background: var(--vp-c-brand); color: var(--vp-c-bg); }
|
||||
.cmd-btn:hover {
|
||||
background: var(--vp-c-brand);
|
||||
color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.info-box {
|
||||
padding: 1rem;
|
||||
@@ -149,5 +180,9 @@ const runCmd = (c) => {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.info-box p { margin: 0; color: var(--vp-c-text-1); line-height: 1.6; }
|
||||
.info-box p {
|
||||
margin: 0;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,19 +2,35 @@
|
||||
<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 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 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('incoming')" class="action-btn">
|
||||
保留传入
|
||||
</button>
|
||||
<button @click="resolve('manual')" class="action-btn">手动合并</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,21 +44,74 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const resolved = ref(false)
|
||||
const resolve = (choice) => { resolved.value = true }
|
||||
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); }
|
||||
.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>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<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>
|
||||
<span class="hash">{{ c.substring(0, 6) }}</span>
|
||||
</div>
|
||||
<div v-if="local.length === 0" class="empty">无</div>
|
||||
</div>
|
||||
@@ -20,7 +20,7 @@
|
||||
<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>
|
||||
<span class="hash">{{ c.substring(0, 6) }}</span>
|
||||
</div>
|
||||
<div v-if="remote.length === 0" class="empty">无</div>
|
||||
</div>
|
||||
@@ -29,8 +29,16 @@
|
||||
|
||||
<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="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>
|
||||
@@ -47,35 +55,125 @@ 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 }
|
||||
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); }
|
||||
.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); }
|
||||
.repos {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.sync {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
<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>
|
||||
<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>
|
||||
@@ -19,9 +19,19 @@
|
||||
</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="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>
|
||||
@@ -36,25 +46,112 @@
|
||||
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 = [] }
|
||||
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); }
|
||||
.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>
|
||||
|
||||
@@ -3,16 +3,28 @@
|
||||
<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>
|
||||
<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 + '%'}">
|
||||
<div class="bar full" :style="{ height: fullSize + '%' }">
|
||||
<span class="label">完整备份: {{ fullSize }}MB</span>
|
||||
</div>
|
||||
<div class="bar git" :style="{height: gitSize + '%'}">
|
||||
<div class="bar git" :style="{ height: gitSize + '%' }">
|
||||
<span class="label">Git 存储: {{ gitSize }}MB</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,7 +57,9 @@ const versionCount = ref(5)
|
||||
const fullSize = ref(500)
|
||||
const gitSize = ref(50)
|
||||
|
||||
const savedPercent = computed(() => Math.round((1 - gitSize.value / fullSize.value) * 100))
|
||||
const savedPercent = computed(() =>
|
||||
Math.round((1 - gitSize.value / fullSize.value) * 100)
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -95,8 +109,12 @@ const savedPercent = computed(() => Math.round((1 - gitSize.value / fullSize.val
|
||||
transition: height 0.5s ease;
|
||||
}
|
||||
|
||||
.bar.full { background: linear-gradient(135deg, #ef4444, #dc2626); }
|
||||
.bar.git { background: linear-gradient(135deg, #10b981, #059669); }
|
||||
.bar.full {
|
||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||
}
|
||||
.bar.git {
|
||||
background: linear-gradient(135deg, #10b981, #059669);
|
||||
}
|
||||
|
||||
.stats {
|
||||
display: flex;
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
</div>
|
||||
<div class="desk-surface">
|
||||
<transition-group name="file-pop">
|
||||
<div
|
||||
v-for="file in workingFiles"
|
||||
:key="file.id"
|
||||
<div
|
||||
v-for="file in workingFiles"
|
||||
:key="file.id"
|
||||
class="file-card"
|
||||
@click="addToStaging(file)"
|
||||
>
|
||||
@@ -25,7 +25,9 @@
|
||||
</transition-group>
|
||||
<div v-if="workingFiles.length === 0" class="empty-state">
|
||||
桌上很干净 ✨
|
||||
<button class="create-btn" @click="createNewFile">新建文件 📝</button>
|
||||
<button class="create-btn" @click="createNewFile">
|
||||
新建文件 📝
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,9 +51,9 @@
|
||||
<div class="box-container">
|
||||
<div class="box-body">
|
||||
<transition-group name="file-drop">
|
||||
<div
|
||||
v-for="file in stagedFiles"
|
||||
:key="file.id"
|
||||
<div
|
||||
v-for="file in stagedFiles"
|
||||
:key="file.id"
|
||||
class="file-card mini"
|
||||
@click="unstageFile(file)"
|
||||
>
|
||||
@@ -68,8 +70,8 @@
|
||||
<div class="box-flap right"></div>
|
||||
</div>
|
||||
<div class="staging-actions">
|
||||
<button
|
||||
class="commit-btn"
|
||||
<button
|
||||
class="commit-btn"
|
||||
:disabled="stagedFiles.length === 0"
|
||||
@click="commitFiles"
|
||||
>
|
||||
@@ -96,9 +98,9 @@
|
||||
</div>
|
||||
<div class="cabinet-body">
|
||||
<transition-group name="drawer-slide">
|
||||
<div
|
||||
v-for="commit in commits.slice().reverse()"
|
||||
:key="commit.hash"
|
||||
<div
|
||||
v-for="commit in commits.slice().reverse()"
|
||||
:key="commit.hash"
|
||||
class="drawer-item"
|
||||
>
|
||||
<div class="drawer-handle"></div>
|
||||
@@ -107,7 +109,9 @@
|
||||
<span class="commit-msg">{{ commit.message }}</span>
|
||||
</div>
|
||||
<div class="commit-files">
|
||||
<span v-for="f in commit.files" :key="f" class="tiny-file">📄</span>
|
||||
<span v-for="f in commit.files" :key="f" class="tiny-file"
|
||||
>📄</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
@@ -151,7 +155,7 @@ const createNewFile = () => {
|
||||
}
|
||||
|
||||
const addToStaging = (file) => {
|
||||
const index = workingFiles.value.findIndex(f => f.id === file.id)
|
||||
const index = workingFiles.value.findIndex((f) => f.id === file.id)
|
||||
if (index !== -1) {
|
||||
workingFiles.value.splice(index, 1)
|
||||
stagedFiles.value.push(file)
|
||||
@@ -159,7 +163,7 @@ const addToStaging = (file) => {
|
||||
}
|
||||
|
||||
const unstageFile = (file) => {
|
||||
const index = stagedFiles.value.findIndex(f => f.id === file.id)
|
||||
const index = stagedFiles.value.findIndex((f) => f.id === file.id)
|
||||
if (index !== -1) {
|
||||
stagedFiles.value.splice(index, 1)
|
||||
workingFiles.value.push(file)
|
||||
@@ -168,17 +172,23 @@ const unstageFile = (file) => {
|
||||
|
||||
const commitFiles = () => {
|
||||
if (stagedFiles.value.length === 0) return
|
||||
|
||||
|
||||
const files = [...stagedFiles.value]
|
||||
stagedFiles.value = []
|
||||
|
||||
const msgs = ['Fix bug', 'Add feature', 'Update docs', 'Refactor code', 'Initial commit']
|
||||
|
||||
const msgs = [
|
||||
'Fix bug',
|
||||
'Add feature',
|
||||
'Update docs',
|
||||
'Refactor code',
|
||||
'Initial commit'
|
||||
]
|
||||
const randomMsg = msgs[Math.floor(Math.random() * msgs.length)]
|
||||
|
||||
|
||||
commits.value.push({
|
||||
hash: Math.random().toString(16).substr(2, 6),
|
||||
message: randomMsg,
|
||||
files: files.map(f => f.name)
|
||||
files: files.map((f) => f.name)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -223,10 +233,21 @@ const commitFiles = () => {
|
||||
border-bottom: 2px dashed var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.zone-icon { font-size: 1.5rem; }
|
||||
.zone-info { display: flex; flex-direction: column; }
|
||||
.zone-title { font-weight: bold; font-size: 0.9rem; }
|
||||
.zone-desc { font-size: 0.7rem; color: var(--vp-c-text-2); }
|
||||
.zone-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.zone-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.zone-title {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.zone-desc {
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
@@ -240,7 +261,10 @@ const commitFiles = () => {
|
||||
}
|
||||
|
||||
/* 1. Working Desk */
|
||||
.zone.working { border-color: #f59e0b; background: #fffbeb; }
|
||||
.zone.working {
|
||||
border-color: #f59e0b;
|
||||
background: #fffbeb;
|
||||
}
|
||||
.desk-surface {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -265,22 +289,33 @@ const commitFiles = () => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.2s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-card:hover {
|
||||
transform: translateY(-4px) rotate(2deg);
|
||||
box-shadow: 0 8px 12px rgba(0,0,0,0.1);
|
||||
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.1);
|
||||
border-color: #f59e0b;
|
||||
}
|
||||
|
||||
.file-icon { font-size: 2rem; margin-bottom: 4px; }
|
||||
.file-name { font-size: 0.7rem; text-align: center; word-break: break-all; line-height: 1.2; }
|
||||
.file-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.file-name {
|
||||
font-size: 0.7rem;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.action-hint {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(245, 158, 11, 0.9);
|
||||
color: white;
|
||||
display: flex;
|
||||
@@ -292,7 +327,9 @@ const commitFiles = () => {
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.file-card:hover .action-hint { opacity: 1; }
|
||||
.file-card:hover .action-hint {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
background: #f59e0b;
|
||||
@@ -306,7 +343,10 @@ const commitFiles = () => {
|
||||
}
|
||||
|
||||
/* 2. Staging Box */
|
||||
.zone.staging { border-color: #3b82f6; background: #eff6ff; }
|
||||
.zone.staging {
|
||||
border-color: #3b82f6;
|
||||
background: #eff6ff;
|
||||
}
|
||||
.box-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
@@ -341,10 +381,19 @@ const commitFiles = () => {
|
||||
padding: 4px 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
.file-card.mini .file-icon { font-size: 1rem; margin: 0; }
|
||||
.file-card.mini .file-name { font-size: 0.8rem; }
|
||||
.file-card.mini:hover { border-color: #ef4444; }
|
||||
.file-card.mini .action-hint { background: rgba(239, 68, 68, 0.9); }
|
||||
.file-card.mini .file-icon {
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
.file-card.mini .file-name {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.file-card.mini:hover {
|
||||
border-color: #ef4444;
|
||||
}
|
||||
.file-card.mini .action-hint {
|
||||
background: rgba(239, 68, 68, 0.9);
|
||||
}
|
||||
|
||||
.box-flap {
|
||||
position: absolute;
|
||||
@@ -356,10 +405,23 @@ const commitFiles = () => {
|
||||
border-bottom: none;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.box-flap.left { left: 0; border-radius: 4px 0 0 0; transform-origin: bottom left; transform: rotate(10deg); }
|
||||
.box-flap.right { right: 0; border-radius: 0 4px 0 0; transform-origin: bottom right; transform: rotate(-10deg); }
|
||||
.box-flap.left {
|
||||
left: 0;
|
||||
border-radius: 4px 0 0 0;
|
||||
transform-origin: bottom left;
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
.box-flap.right {
|
||||
right: 0;
|
||||
border-radius: 0 4px 0 0;
|
||||
transform-origin: bottom right;
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
|
||||
.staging-actions { margin-top: 12px; text-align: center; }
|
||||
.staging-actions {
|
||||
margin-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.commit-btn {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
@@ -371,11 +433,21 @@ const commitFiles = () => {
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 4px 6px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
.commit-btn:disabled { background: #93c5fd; cursor: not-allowed; box-shadow: none; }
|
||||
.commit-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 6px 8px rgba(59, 130, 246, 0.4); }
|
||||
.commit-btn:disabled {
|
||||
background: #93c5fd;
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
.commit-btn:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 8px rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
|
||||
/* 3. Repo Cabinet */
|
||||
.zone.repo { border-color: #10b981; background: #ecfdf5; }
|
||||
.zone.repo {
|
||||
border-color: #10b981;
|
||||
background: #ecfdf5;
|
||||
}
|
||||
.cabinet-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -403,11 +475,27 @@ const commitFiles = () => {
|
||||
border: 1px solid #10b981;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.commit-info { flex: 1; display: flex; flex-direction: column; }
|
||||
.commit-hash { font-size: 0.6rem; color: #10b981; font-family: monospace; }
|
||||
.commit-msg { font-size: 0.8rem; font-weight: bold; }
|
||||
.commit-files { display: flex; gap: 2px; }
|
||||
.tiny-file { font-size: 0.6rem; }
|
||||
.commit-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.commit-hash {
|
||||
font-size: 0.6rem;
|
||||
color: #10b981;
|
||||
font-family: monospace;
|
||||
}
|
||||
.commit-msg {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
.commit-files {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
.tiny-file {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
/* Arrows */
|
||||
.flow-arrow {
|
||||
@@ -418,25 +506,70 @@ const commitFiles = () => {
|
||||
width: 40px;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
.arrow-line { width: 100%; height: 2px; background: currentColor; }
|
||||
.arrow-label { font-size: 0.7rem; margin: 4px 0; font-weight: bold; white-space: nowrap; }
|
||||
.arrow-head { font-size: 0.8rem; }
|
||||
.arrow-line {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
}
|
||||
.arrow-label {
|
||||
font-size: 0.7rem;
|
||||
margin: 4px 0;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.arrow-head {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Transitions */
|
||||
.file-pop-enter-active, .file-pop-leave-active { transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
|
||||
.file-pop-enter-from { opacity: 0; transform: scale(0.5); }
|
||||
.file-pop-leave-to { opacity: 0; transform: scale(0); }
|
||||
.file-pop-enter-active,
|
||||
.file-pop-leave-active {
|
||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.file-pop-enter-from {
|
||||
opacity: 0;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
.file-pop-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
.file-drop-enter-active, .file-drop-leave-active { transition: all 0.3s ease; }
|
||||
.file-drop-enter-from { opacity: 0; transform: translateY(-20px); }
|
||||
.file-drop-leave-to { opacity: 0; transform: translateX(20px); }
|
||||
.file-drop-enter-active,
|
||||
.file-drop-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.file-drop-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
.file-drop-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.drawer-slide-enter-active { transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
|
||||
.drawer-slide-enter-from { opacity: 0; transform: translateX(50px); }
|
||||
.drawer-slide-enter-active {
|
||||
transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.drawer-slide-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(50px);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.scene { flex-direction: column; min-width: auto; }
|
||||
.flow-arrow { transform: rotate(90deg); margin: 10px 0; width: 100%; align-items: center; }
|
||||
.arrow-line { width: 2px; height: 20px; }
|
||||
.scene {
|
||||
flex-direction: column;
|
||||
min-width: auto;
|
||||
}
|
||||
.flow-arrow {
|
||||
transform: rotate(90deg);
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
.arrow-line {
|
||||
width: 2px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,15 +15,21 @@
|
||||
<button @click="makeCommit" :disabled="!inited" class="action-btn">
|
||||
✅ 提交
|
||||
</button>
|
||||
<button @click="createBranch" :disabled="!inited || hasBranch" class="action-btn">
|
||||
<button
|
||||
@click="createBranch"
|
||||
:disabled="!inited || hasBranch"
|
||||
class="action-btn"
|
||||
>
|
||||
🌿 创建分支
|
||||
</button>
|
||||
<button @click="mergeBranch" :disabled="!hasBranch || merging" class="action-btn">
|
||||
<button
|
||||
@click="mergeBranch"
|
||||
:disabled="!hasBranch || merging"
|
||||
class="action-btn"
|
||||
>
|
||||
🔀 合并分支
|
||||
</button>
|
||||
<button @click="reset" class="action-btn secondary">
|
||||
🔄 重置
|
||||
</button>
|
||||
<button @click="reset" class="action-btn secondary">🔄 重置</button>
|
||||
</div>
|
||||
|
||||
<!-- 提交历史可视化 -->
|
||||
@@ -31,18 +37,62 @@
|
||||
<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
|
||||
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" />
|
||||
<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" />
|
||||
<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" />
|
||||
<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>
|
||||
@@ -65,7 +115,9 @@
|
||||
|
||||
<!-- 说明 -->
|
||||
<div class="info-box">
|
||||
<p><strong>💡 工作流程:</strong> 初始化 → 提交 → 创建分支 → 开发 → 合并</p>
|
||||
<p>
|
||||
<strong>💡 工作流程:</strong> 初始化 → 提交 → 创建分支 → 开发 → 合并
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user