feat: 添加主页组件并设为默认首页

- 新增 HomePage.vue 主页组件 (玻璃态设计风格)
- GlobalSidebar logo 改为可点击跳转主页
- 默认页面从 writer 改为 home
- 新增玻璃态 CSS 变量支持

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
empty
2026-01-12 03:56:40 +08:00
parent 2c664f77d8
commit 4212b63df2
5 changed files with 621 additions and 27 deletions

View File

@@ -5,7 +5,11 @@
<!-- 主体区域 -->
<main class="flex-1 flex overflow-hidden relative">
<!-- 主页 -->
<HomePage v-if="currentPage === 'home'" />
<!-- 持久化布局面板 -->
<template v-else>
<!-- 左侧/中间配置侧边栏 -->
<WriterPanel v-if="currentPage === 'writer'" />
<AnalysisPanel v-else-if="currentPage === 'analysis'" />
@@ -25,7 +29,7 @@
<MainContent v-if="currentPage !== 'compare' && currentPage !== 'rewrite' && currentPage !== 'diffAnnotation'" />
<!-- 侧滑浮层面板 (仅文稿页) -->
<DocumentVersionPanel
<DocumentVersionPanel
v-if="currentPage === 'documents'"
:visible="showVersionPanel"
:document-id="selectedDocumentId"
@@ -33,6 +37,7 @@
@close="showVersionPanel = false"
@restore="handleVersionRestore"
/>
</template>
</main>
</div>
</template>
@@ -41,6 +46,7 @@
import { ref, computed } from 'vue'
import { useAppStore } from './stores/app'
import GlobalSidebar from './components/GlobalSidebar.vue'
import HomePage from './components/HomePage.vue'
import WriterPanel from './components/WriterPanel.vue'
import AnalysisPanel from './components/AnalysisPanel.vue'
import DocumentsPanel from './components/DocumentsPanel.vue'

View File

@@ -1,13 +1,18 @@
<template>
<aside class="sidebar">
<!-- Logo/Home -->
<div class="sidebar-logo" title="AI 写作工坊">
<button
class="sidebar-logo"
:class="{ active: currentPage === 'home' }"
title="主页"
@click="switchPage('home')"
>
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</button>
<!-- 导航项 -->
<nav class="sidebar-nav">
@@ -99,6 +104,10 @@ const switchPage = (page) => {
margin-bottom: var(--space-8);
color: var(--accent-primary);
transition: all var(--transition-normal);
background: transparent;
border: none;
border-radius: var(--radius-lg);
cursor: pointer;
}
.sidebar-logo:hover {
@@ -106,6 +115,10 @@ const switchPage = (page) => {
transform: scale(1.05);
}
.sidebar-logo.active {
background: var(--info-bg);
}
.sidebar-logo svg {
width: 100%;
height: 100%;

541
src/components/HomePage.vue Normal file
View File

@@ -0,0 +1,541 @@
<template>
<div class="home-page">
<!-- Dynamic Background Decor -->
<div class="bg-decor">
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<div class="blob blob-3"></div>
</div>
<!-- Hero Section -->
<header class="home-hero glass">
<div class="hero-content">
<div class="hero-icon-wrapper">
<div class="hero-icon-glow"></div>
<div class="hero-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
<h1 class="hero-title">AI 写作工坊</h1>
<p class="hero-subtitle">结构化内容生成平台</p>
<p class="hero-description">
通过智能范式系统实现高质量规范化的内容生成
</p>
</div>
</header>
<!-- Stats Section -->
<section class="home-stats">
<div v-for="stat in statConfig" :key="stat.label" class="stat-card glass-card">
<div class="stat-icon" :style="{ color: stat.color }">
<IconLibrary :name="stat.icon" :size="24" />
</div>
<div class="stat-info">
<span class="stat-value text-glow" :style="{ '--glow-color': stat.color }">{{ stats[stat.key] }}</span>
<span class="stat-label">{{ stat.label }}</span>
</div>
</div>
</section>
<!-- Content Sections Grid -->
<div class="content-grid">
<!-- Quick Actions -->
<section class="home-section quick-actions-section">
<h2 class="section-title">
<IconLibrary name="sparkles" :size="18" />
<span>快速开始</span>
</h2>
<div class="action-list">
<button
v-for="action in quickActions"
:key="action.id"
@click="navigateTo(action.id)"
class="action-card glass-card"
>
<div class="action-icon-box" :style="{ background: action.gradient }">
<IconLibrary :name="action.icon" :size="24" />
</div>
<div class="action-content">
<h3 class="action-title">{{ action.title }}</h3>
<p class="action-desc">{{ action.description }}</p>
</div>
<div class="action-arrow-box">
<IconLibrary name="expand" :size="16" class="action-arrow" />
</div>
</button>
</div>
</section>
<!-- All Features -->
<section class="home-section all-features-section">
<h2 class="section-title">
<IconLibrary name="list" :size="18" />
<span>全部功能</span>
</h2>
<div class="feature-grid">
<button
v-for="feature in features"
:key="feature.id"
@click="navigateTo(feature.id)"
class="feature-card glass-card"
>
<div class="feature-icon-wrapper">
<IconLibrary :name="feature.icon" :size="24" />
</div>
<span class="feature-name">{{ feature.name }}</span>
</button>
</div>
</section>
</div>
<!-- Footer -->
<footer class="home-footer">
<div class="footer-divider"></div>
<p class="footer-text">Pro · 基于 DeepSeek API · 极简高效</p>
</footer>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useAppStore } from '../stores/app'
import { useDatabaseStore } from '../stores/database'
import { getParadigmList } from '../config/paradigms'
import IconLibrary from './icons/IconLibrary.vue'
const appStore = useAppStore()
const dbStore = useDatabaseStore()
// 统计数据
const stats = ref({
documents: 0,
materials: 0,
paradigms: 0
})
const statConfig = [
{ key: 'documents', label: '文稿', icon: 'folder', color: '#60a5fa' },
{ key: 'materials', label: '素材', icon: 'chart', color: '#34d399' },
{ key: 'paradigms', label: '范式', icon: 'analysis', color: '#c084fc' }
]
// 快速操作
const quickActions = [
{
id: 'writer',
title: 'AI 写作',
description: '智能生成高质量内容',
icon: 'edit',
gradient: 'linear-gradient(135deg, #3b82f6, #6366f1)'
},
{
id: 'rewrite',
title: '范式润色',
description: '按范式标准优化文章',
icon: 'sparkles',
gradient: 'linear-gradient(135deg, #f59e0b, #ef4444)'
},
{
id: 'analysis',
title: '范式库',
description: '管理和创建写作范式',
icon: 'analysis',
gradient: 'linear-gradient(135deg, #10b981, #06b6d4)'
}
]
// 全部功能
const features = [
{ id: 'writer', name: 'AI 写作', icon: 'edit' },
{ id: 'analysis', name: '范式库', icon: 'analysis' },
{ id: 'paradigmWriter', name: '范式写作', icon: 'article' },
{ id: 'documents', name: '文稿库', icon: 'folder' },
{ id: 'materials', name: '素材库', icon: 'chart' },
{ id: 'rewrite', name: '范式润色', icon: 'sparkles' },
{ id: 'compare', name: '对照检查', icon: 'compare' },
{ id: 'diffAnnotation', name: '差异标注', icon: 'chart' }
]
// 导航
const navigateTo = (page) => {
appStore.setCurrentPage(page)
}
// 加载统计数据
onMounted(() => {
// 获取范式数量
const defaultParadigms = getParadigmList()
const customParadigms = JSON.parse(localStorage.getItem('customParadigms') || '[]')
stats.value.paradigms = defaultParadigms.length + customParadigms.length
// 获取文稿和素材数量
stats.value.documents = dbStore.documents?.length || 0
stats.value.materials = dbStore.references?.length || 0
})
</script>
<style scoped>
.home-page {
flex: 1;
overflow-y: auto;
padding: var(--space-8);
background: var(--bg-primary);
position: relative;
z-index: 1;
}
/* Background Decor */
.bg-decor {
position: fixed;
inset: 0;
overflow: hidden;
z-index: -1;
pointer-events: none;
}
.blob {
position: absolute;
width: 400px;
height: 400px;
background: radial-gradient(circle, rgba(96, 165, 250, 0.15) 0%, transparent 70%);
border-radius: 50%;
filter: blur(60px);
animation: float 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
}
.blob-1 { top: -100px; left: -100px; animation-delay: 0s; }
.blob-2 { bottom: -150px; right: -100px; background: radial-gradient(circle, rgba(192, 132, 252, 0.1) 0%, transparent 70%); animation-delay: -5s; }
.blob-3 { top: 20%; right: 10%; background: radial-gradient(circle, rgba(52, 211, 153, 0.08) 0%, transparent 70%); animation-delay: -10s; }
@keyframes float {
from { transform: translate(0, 0) scale(1); }
to { transform: translate(100px, 50px) scale(1.1); }
}
/* Glass Effect */
.glass {
background: var(--glass-bg);
backdrop-filter: blur(var(--glass-blur));
-webkit-backdrop-filter: blur(var(--glass-blur));
border: 1px solid var(--glass-border);
box-shadow: var(--shadow-xl);
}
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(var(--glass-blur));
-webkit-backdrop-filter: blur(var(--glass-blur));
border: 1px solid var(--glass-border);
transition: all var(--transition-normal);
}
.glass-card:hover {
background: var(--glass-bg-hover);
border-color: rgba(255, 255, 255, 0.2);
transform: translateY(-4px);
box-shadow: var(--shadow-xl), var(--shadow-glow);
}
/* Hero Section */
.home-hero {
text-align: center;
padding: var(--space-12) var(--space-6);
margin-bottom: var(--space-10);
border-radius: var(--radius-2xl);
position: relative;
overflow: hidden;
}
.home-hero::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(96, 165, 250, 0.05) 0%, transparent 50%, rgba(192, 132, 252, 0.05) 100%);
}
.hero-content {
position: relative;
z-index: 2;
max-width: 600px;
margin: 0 auto;
}
.hero-icon-wrapper {
position: relative;
display: inline-flex;
margin-bottom: var(--space-6);
}
.hero-icon-glow {
position: absolute;
inset: -10px;
background: var(--accent-primary);
filter: blur(20px);
opacity: 0.3;
border-radius: var(--radius-xl);
}
.hero-icon {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
background: linear-gradient(135deg, var(--accent-primary), #c084fc);
border-radius: var(--radius-xl);
color: white;
box-shadow: 0 10px 25px rgba(96, 165, 250, 0.4);
}
.hero-title {
font-size: var(--text-3xl);
font-weight: var(--font-bold);
color: var(--text-primary);
margin-bottom: var(--space-2);
letter-spacing: -0.02em;
}
.hero-subtitle {
font-size: var(--text-lg);
font-weight: var(--font-medium);
color: var(--accent-primary);
margin-bottom: var(--space-4);
background: linear-gradient(90deg, var(--accent-primary), #c084fc);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hero-description {
font-size: var(--text-base);
color: var(--text-secondary);
line-height: 1.6;
}
/* Stats Section */
.home-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-6);
margin-bottom: var(--space-10);
}
.stat-card {
display: flex;
align-items: center;
gap: var(--space-4);
padding: var(--space-6);
border-radius: var(--radius-xl);
}
.stat-icon {
display: flex;
align-items: center;
justify-content: center;
width: 52px;
height: 52px;
background: rgba(255, 255, 255, 0.05);
border-radius: var(--radius-lg);
flex-shrink: 0;
}
.stat-info {
display: flex;
flex-direction: column;
}
.stat-value {
font-size: var(--text-2xl);
font-weight: var(--font-bold);
color: var(--text-primary);
line-height: 1;
margin-bottom: var(--space-1);
}
.text-glow {
text-shadow: 0 0 10px var(--glow-color, rgba(96, 165, 250, 0.3));
}
.stat-label {
font-size: var(--text-xs);
color: var(--text-muted);
font-weight: var(--font-medium);
text-transform: uppercase;
letter-spacing: 0.1em;
}
/* Content Grid */
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-8);
}
.section-title {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-sm);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-5);
text-transform: uppercase;
letter-spacing: 0.05em;
opacity: 0.8;
}
/* Quick Actions */
.action-list {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.action-card {
display: flex;
align-items: center;
gap: var(--space-5);
padding: var(--space-5);
border-radius: var(--radius-xl);
cursor: pointer;
text-align: left;
width: 100%;
}
.action-icon-box {
display: flex;
align-items: center;
justify-content: center;
width: 56px;
height: 56px;
border-radius: var(--radius-lg);
color: white;
flex-shrink: 0;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}
.action-content {
flex: 1;
min-width: 0;
}
.action-title {
font-size: var(--text-base);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-1);
}
.action-desc {
font-size: var(--text-sm);
color: var(--text-muted);
}
.action-arrow-box {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-full);
background: rgba(255, 255, 255, 0.05);
color: var(--text-muted);
transition: all var(--transition-fast);
}
.action-card:hover .action-arrow-box {
background: var(--accent-primary);
color: white;
transform: translateX(4px);
}
/* Feature Grid */
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
gap: var(--space-4);
}
.feature-card {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-3);
padding: var(--space-6) var(--space-4);
border-radius: var(--radius-xl);
cursor: pointer;
}
.feature-icon-wrapper {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
background: rgba(255, 255, 255, 0.05);
border-radius: var(--radius-lg);
color: var(--text-secondary);
transition: all var(--transition-normal);
}
.feature-card:hover .feature-icon-wrapper {
background: var(--accent-primary);
color: white;
transform: scale(1.1) rotate(5deg);
}
.feature-name {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-secondary);
text-align: center;
}
.feature-card:hover .feature-name {
color: var(--text-primary);
}
/* Footer */
.home-footer {
margin-top: var(--space-12);
text-align: center;
}
.footer-divider {
height: 1px;
background: linear-gradient(90deg, transparent, var(--border-default), transparent);
margin-bottom: var(--space-6);
}
.footer-text {
font-size: var(--text-xs);
color: var(--text-muted);
letter-spacing: 0.02em;
}
/* Responsive */
@media (max-width: 1024px) {
.content-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.home-stats {
grid-template-columns: 1fr;
gap: var(--space-4);
}
.hero-title {
font-size: var(--text-2xl);
}
.feature-grid {
grid-template-columns: repeat(2, 1fr);
}
}
</style>

View File

@@ -7,7 +7,7 @@ import { PARADIGMS, getParadigmById, buildParadigmConstraints } from '../config/
export const useAppStore = defineStore('app', () => {
// 页面状态
const currentPage = ref('writer') // 'writer' 或 'analysis'
const currentPage = ref('home') // 默认进入主页
// 模型服务商选择
const selectedProviderId = ref(getDefaultProvider().id)

View File

@@ -3,29 +3,45 @@
/* ===== 深色模式(默认) ===== */
:root {
/* 背景色 */
--bg-primary: #0f172a; /* 页面主背景 */
--bg-secondary: #1e293b; /* 卡片/面板背景 */
--bg-elevated: #334155; /* 悬浮元素背景 */
--bg-sunken: #0f172a; /* 凹陷元素背景 */
--bg-primary: #0f172a;
/* 页面主背景 */
--bg-secondary: #1e293b;
/* 卡片/面板背景 */
--bg-elevated: #334155;
/* 悬浮元素背景 */
--bg-sunken: #0f172a;
/* 凹陷元素背景 */
/* 边框色 */
--border-default: #334155; /* 默认边框 */
--border-subtle: #1e293b; /* 微妙边框 */
--border-strong: #475569; /* 强调边框 */
--border-focus: #60a5fa; /* 焦点边框 */
--border-default: #334155;
/* 默认边框 */
--border-subtle: #1e293b;
/* 微妙边框 */
--border-strong: #475569;
/* 强调边框 */
--border-focus: #60a5fa;
/* 焦点边框 */
/* 文本色 */
--text-primary: #f1f5f9; /* 主要内容 */
--text-secondary: #94a3b8; /* 要内容 */
--text-muted: #64748b; /* 禁用/提示文本 */
--text-inverse: #0f172a; /* 反色文本 */
--text-primary: #f1f5f9;
/* 要内容 */
--text-secondary: #94a3b8;
/* 次要内容 */
--text-muted: #64748b;
/* 禁用/提示文本 */
--text-inverse: #0f172a;
/* 反色文本 */
/* 语义色 */
--accent-primary: #60a5fa; /* 主操作色 - 蓝色 */
--accent-primary: #60a5fa;
/* 主操作色 - 蓝色 */
--accent-primary-hover: #3b82f6;
--accent-success: #34d399; /* 成功 - 绿色 */
--accent-warning: #fbbf24; /* 警告 - 色 */
--accent-danger: #f87171; /* 危险 - 红色 */
--accent-success: #34d399;
/* 成功 - 绿色 */
--accent-warning: #fbbf24;
/* 警告 - 黄色 */
--accent-danger: #f87171;
/* 危险 - 红色 */
/* 语义背景色(带透明度) */
--info-bg: rgba(96, 165, 250, 0.1);
@@ -39,12 +55,20 @@
--font-mono: 'JetBrains Mono', 'SF Mono', Monaco, monospace;
/* 字号 */
--text-xs: 0.6875rem; /* 11px - 标签、提示 */
--text-sm: 0.8125rem; /* 13px - 次要内容 */
--text-base: 0.9375rem; /* 15px - 正文 */
--text-lg: 1.125rem; /* 18px - 小标题 */
--text-xl: 1.375rem; /* 22px - 页面标题 */
--text-2xl: 1.75rem; /* 28px - 大标题 */
--text-xs: 0.6875rem;
/* 11px - 标签、提示 */
--text-sm: 0.8125rem;
/* 13px - 次要内容 */
--text-base: 0.9375rem;
/* 15px - 正文 */
--text-lg: 1.125rem;
/* 18px - 小标题 */
--text-xl: 1.375rem;
/* 22px - 页面标题 */
--text-2xl: 1.75rem;
/* 28px - 大标题 */
--text-3xl: 2.25rem;
/* 36px - 超大标题 */
/* 行高 */
--leading-tight: 1.2;
@@ -81,6 +105,16 @@
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.4);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.4);
--shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.3);
--shadow-glow: 0 0 15px rgba(96, 165, 250, 0.3);
--shadow-glow-strong: 0 0 25px rgba(96, 165, 250, 0.5);
/* 玻璃拟态 (Glassmorphism) */
--glass-bg: rgba(30, 41, 59, 0.7);
--glass-bg-hover: rgba(51, 65, 85, 0.8);
--glass-border: rgba(255, 255, 255, 0.1);
--glass-blur: 12px;
/* 过渡 */
--transition-fast: 0.15s ease;
@@ -95,4 +129,4 @@
--z-modal: 1050;
--z-popover: 1060;
--z-tooltip: 1070;
}
}