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

327 lines
7.1 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="zustand-jotai-demo">
<div class="demo-header">
<span class="icon">🐻</span>
<span class="title">Zustand & Jotai</span>
<span class="subtitle">React 轻量级状态管理</span>
</div>
<div class="intro-text">
想象你在<span class="highlight">便利店</span>工作Zustand 就像整个仓库统一管理Jotai 就像把商品拆成一个个小格子Atom每个格子独立管理按需取用
</div>
<div class="demo-content">
<div class="demo-tabs">
<button
v-for="tab in tabs"
:key="tab.id"
class="tab-button"
:class="{ active: activeTab === tab.id }"
@click="activeTab = tab.id"
>
<span class="tab-icon">{{ tab.icon }}</span>
<span class="tab-name">{{ tab.name }}</span>
</button>
</div>
<Transition
name="fade"
mode="out-in"
>
<div
:key="activeTab"
class="tab-content"
>
<div
v-if="activeTab === 'zustand'"
class="feature-showcase"
>
<div class="feature-card">
<span class="feature-icon">📦</span>
<span class="feature-title">单一 Store</span>
<span class="feature-desc">所有状态集中管理</span>
</div>
<div class="feature-card">
<span class="feature-icon"></span>
<span class="feature-title">极简 API</span>
<span class="feature-desc">无需 Provider 包裹</span>
</div>
<div class="feature-card">
<span class="feature-icon">🎯</span>
<span class="feature-title">细粒度订阅</span>
<span class="feature-desc">只重渲染需要的组件</span>
</div>
</div>
<div
v-if="activeTab === 'zustand'"
class="code-example"
>
<pre class="code-block"><code>// Zustand Store
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({
bears: state.bears + 1
}))
}))
// 在组件中使用
function BearCounter() {
const bears = useStore((state) => state.bears)
return <div>{bears} bears around here</div>
}</code></pre>
</div>
<div
v-if="activeTab === 'jotai'"
class="feature-showcase"
>
<div class="feature-card">
<span class="feature-icon"></span>
<span class="feature-title">原子化</span>
<span class="feature-desc">状态拆分成独立 Atom</span>
</div>
<div class="feature-card">
<span class="feature-icon">🔗</span>
<span class="feature-title">自动依赖</span>
<span class="feature-desc">派生状态自动追踪</span>
</div>
<div class="feature-card">
<span class="feature-icon">📝</span>
<span class="feature-title">TypeScript</span>
<span class="feature-desc">原生类型支持</span>
</div>
</div>
<div
v-if="activeTab === 'jotai'"
class="code-example"
>
<pre class="code-block"><code>// Jotai Atom
import { atom } from 'jotai'
// 基础 Atom
const countAtom = atom(0)
// 派生 Atom
const doubleAtom = atom((get) => get(countAtom) * 2)
// 在组件中使用
function Counter() {
const [count, setCount] = useAtom(countAtom)
const [double] = useAtom(doubleAtom)
return (
&lt;div&gt;
&lt;span&gt;{count}&lt;/span&gt;
&lt;span&gt;{double}&lt;/span&gt;
&lt;/div&gt;
)
}</code></pre>
</div>
</div>
</Transition>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>选择建议</strong>Zustand 适合中小项目API 简洁直观Jotai 适合需要细粒度控制的场景状态更模块化两个都支持 TypeScript不需要 Provider
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const activeTab = ref('zustand')
const tabs = [
{ id: 'zustand', name: 'Zustand', icon: '🐻' },
{ id: 'jotai', name: 'Jotai', icon: '⚛️' }
]
</script>
<style scoped>
.zustand-jotai-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;
}
.demo-content {
margin-bottom: 1rem;
}
.demo-tabs {
display: flex;
gap: 0.75rem;
margin-bottom: 1rem;
border-bottom: 1px solid var(--vp-c-divider);
padding-bottom: 0.75rem;
}
.tab-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 1.2rem;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
cursor: pointer;
transition: all 0.2s ease;
}
.tab-button:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-text-1);
}
.tab-button.active {
background: var(--vp-c-brand);
border-color: var(--vp-c-brand);
color: white;
}
.tab-icon {
font-size: 1rem;
}
.tab-name {
font-weight: 500;
}
.tab-content {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 0.75rem;
}
.feature-showcase {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
}
.feature-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
text-align: center;
}
.feature-icon {
font-size: 2rem;
}
.feature-title {
font-weight: 600;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.feature-desc {
font-size: 0.8rem;
color: var(--vp-c-text-2);
}
.code-example {
background: #1e1e1e;
border-radius: 6px;
padding: 0.75rem;
overflow-x: auto;
}
.code-block {
margin: 0;
}
.code-block code {
font-family: monospace;
font-size: 0.75rem;
line-height: 1.6;
color: #d4d4d4;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
}
.info-box .icon {
margin-right: 0.25rem;
}
@media (max-width: 768px) {
.demo-tabs {
flex-direction: column;
}
.feature-showcase {
grid-template-columns: 1fr;
}
}
</style>