Files
sanbuphy 0eba9e87e9 fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier)
- Relaxed strict Vue/JS rules for demo code compatibility
- Fix syntax errors in ApiPlayground and VoiceCloningDemo
- Fix duplicate else-if condition in ApiPlayground
- Fix Promise executor async pattern in AutoregressiveAudioDemo
- Add TypeScript file support to ESLint config

Warnings reduced from 295 to 251 problems.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 17:38:10 +08:00

339 lines
8.0 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="acid-demo">
<div class="demo-header">
<span class="icon">🔒</span>
<span class="title">事务 ACID 特性演示</span>
<span class="subtitle">理解事务如何保证数据安全</span>
</div>
<div class="intro-text">
想象<span class="highlight">银行转账</span>A 转给 B 100 这个操作包含两步 A 100 B 100如果只扣了钱但没到账就是灾难事务保证这两步<span class="highlight">要么全成功要么全失败</span>
</div>
<div class="acid-cards">
<div
v-for="item in acidItems"
:key="item.key"
class="acid-card"
:class="{ active: activeItem === item.key }"
@click="activeItem = activeItem === item.key ? null : item.key"
>
<div class="card-icon">
{{ item.icon }}
</div>
<div class="card-letter">
{{ item.letter }}
</div>
<div class="card-name">
{{ item.name }}
</div>
<div class="card-meaning">
{{ item.meaning }}
</div>
</div>
</div>
<Transition name="fade">
<div
v-if="activeItem"
class="detail-panel"
>
<div class="detail-header">
<span class="detail-icon">{{ currentItem?.icon }}</span>
<span class="detail-title">{{ currentItem?.name }} ({{ currentItem?.letter }})</span>
</div>
<div class="detail-content">
<div class="explanation">
<strong>含义</strong>{{ currentItem?.explanation }}
</div>
<div class="example">
<div class="example-label">
🌰 银行转账例子
</div>
<div class="example-text">
{{ currentItem?.example }}
</div>
</div>
</div>
</div>
</Transition>
<div
v-if="!activeItem"
class="hint-text"
>
👆 点击上方任意特性查看详细解释
</div>
<div class="scenario-box">
<div class="scenario-title">
🎯 12306 抢票场景
</div>
<div class="scenario-content">
<p><strong>场景</strong>用户 A B 同时看到还剩 1 张票同时点击购买</p>
<p><strong>没有事务</strong>A 扣库存B 也扣库存同一张票卖给了两个人</p>
<p><strong>有事务隔离性</strong>A 的操作加锁B 必须等待A 买完后库存变为 0B 看到的是"已售罄"</p>
</div>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>核心思想</strong>ACID 四个特性共同保证了数据在高并发环境下的<span class="highlight">不丢不乱不冲突</span>这就是为什么所有涉及资金订单的系统都必须使用数据库事务
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeItem = ref(null)
const acidItems = ref([
{
key: 'atomicity',
letter: 'A',
icon: '⚛️',
name: '原子性',
meaning: 'Atomicity',
explanation: '事务中的操作要么全部成功,要么全部失败,不会出现"做了一半"的情况。',
example: '转账时,扣款和入账必须同时成功。如果扣款成功但入账失败,系统会自动回滚,把钱退回去。'
},
{
key: 'consistency',
letter: 'C',
icon: '⚖️',
name: '一致性',
meaning: 'Consistency',
explanation: '事务执行前后,数据都必须处于合法状态,满足所有约束条件。',
example: '转账前后,A 和 B 的余额总和必须不变。票卖完了,库存必须是 0,不能是负数。'
},
{
key: 'isolation',
letter: 'I',
icon: '🔒',
name: '隔离性',
meaning: 'Isolation',
explanation: '多个事务同时执行时,互不干扰,每个事务都感觉不到其他事务的存在。',
example: 'A 在买票时,B 看到的结果应该是"已售罄"或"还剩 1 张",不会看到 A 买了一半的中间状态(比如库存变成了 0.5)。'
},
{
key: 'durability',
letter: 'D',
icon: '💾',
name: '持久性',
meaning: 'Durability',
explanation: '事务一旦提交,结果就会永久保存,即使断电、宕机也不会丢失。',
example: '订单成功后,即使服务器立刻断电,已售出的票记录也不会消失。重启服务器后,数据依然在。'
}
])
const currentItem = computed(() => {
return acidItems.value.find(item => item.key === activeItem.value)
})
</script>
<style scoped>
.acid-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg-soft);
padding: 0.75rem;
margin: 0.5rem 0;
}
.demo-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.demo-header .icon { font-size: 1.25rem; }
.demo-header .title { font-weight: bold; font-size: 1rem; }
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.85rem; margin-left: 0.5rem; }
.intro-text {
font-size: 0.9rem;
color: var(--vp-c-text-2);
line-height: 1.6;
margin-bottom: 1rem;
padding: 0.75rem;
background: var(--vp-c-bg);
border-radius: 6px;
}
.intro-text .highlight {
color: var(--vp-c-brand-1);
font-weight: 500;
}
.acid-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.75rem;
margin-bottom: 1rem;
}
@media (max-width: 768px) {
.acid-cards {
grid-template-columns: repeat(2, 1fr);
}
}
.acid-card {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 0.75rem;
text-align: center;
cursor: pointer;
transition: all 0.2s;
}
.acid-card:hover {
background: var(--vp-c-bg-soft);
}
.acid-card.active {
background: var(--vp-c-brand-soft);
border-color: var(--vp-c-brand);
}
.card-icon {
font-size: 1.5rem;
margin-bottom: 0.25rem;
}
.card-letter {
font-size: 1.25rem;
font-weight: bold;
color: var(--vp-c-brand-1);
margin-bottom: 0.25rem;
}
.card-name {
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 0.25rem;
}
.card-meaning {
font-size: 0.7rem;
color: var(--vp-c-text-3);
font-family: monospace;
}
.detail-panel {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 0.75rem;
border: 1px solid var(--vp-c-divider);
}
.detail-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.detail-icon {
font-size: 1.5rem;
}
.detail-title {
font-weight: 600;
font-size: 1rem;
color: var(--vp-c-text-1);
}
.detail-content {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.explanation {
font-size: 0.85rem;
color: var(--vp-c-text-2);
line-height: 1.5;
}
.example {
background: var(--vp-c-bg-soft);
padding: 0.75rem;
border-radius: 6px;
border-left: 3px solid var(--vp-c-brand);
}
.example-label {
font-size: 0.8rem;
font-weight: 500;
color: var(--vp-c-text-2);
margin-bottom: 0.5rem;
}
.example-text {
font-size: 0.85rem;
color: var(--vp-c-text-1);
line-height: 1.5;
}
.hint-text {
text-align: center;
font-size: 0.85rem;
color: var(--vp-c-text-3);
margin-bottom: 0.75rem;
}
.scenario-box {
background: rgba(34, 197, 94, 0.05);
border: 1px solid rgba(34, 197, 94, 0.2);
border-radius: 6px;
padding: 0.75rem;
margin-bottom: 0.75rem;
}
.scenario-title {
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--vp-c-text-1);
}
.scenario-content p {
font-size: 0.8rem;
color: var(--vp-c-text-2);
margin: 0.25rem 0;
line-height: 1.5;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
line-height: 1.5;
}
.info-box .icon { margin-right: 0.25rem; }
.info-box .highlight {
color: var(--vp-c-brand-1);
font-weight: 500;
}
</style>