feat: 初始化文档信息

This commit is contained in:
digua
2025-11-26 20:30:27 +08:00
parent 49ecd8fc1d
commit 807b3925b3
7 changed files with 277 additions and 76 deletions
+1 -1
View File
@@ -19,7 +19,7 @@
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^4.0.0",
"@nuxt/ui": "^4.2.1",
"@vueuse/core": "^14.0.0",
"@vueuse/core": "^13.9.0",
"axios": "^1.13.2",
"dayjs": "^1.11.19",
"electron-updater": "^6.6.2",
+2 -28
View File
@@ -18,8 +18,8 @@ importers:
specifier: ^4.2.1
version: 4.2.1(@babel/parser@7.28.5)(axios@1.13.2)(embla-carousel@8.6.0)(typescript@5.9.2)(vite@6.4.1(@types/node@24.3.0)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.2))(vue-router@4.6.3(vue@3.5.25(typescript@5.9.2)))(vue@3.5.25(typescript@5.9.2))
'@vueuse/core':
specifier: ^14.0.0
version: 14.0.0(vue@3.5.25(typescript@5.9.2))
specifier: ^13.9.0
version: 13.9.0(vue@3.5.25(typescript@5.9.2))
axios:
specifier: ^1.13.2
version: 1.13.2
@@ -1189,11 +1189,6 @@ packages:
peerDependencies:
vue: ^3.5.0
'@vueuse/core@14.0.0':
resolution: {integrity: sha512-d6tKRWkZE8IQElX2aHBxXOMD478fHIYV+Dzm2y9Ag122ICBpNKtGICiXKOhWU3L1kKdttDD9dCMS4bGP3jhCTQ==}
peerDependencies:
vue: ^3.5.0
'@vueuse/integrations@13.9.0':
resolution: {integrity: sha512-SDobKBbPIOe0cVL7QxMzGkuUGHvWTdihi9zOrrWaWUgFKe15cwEcwfWmgrcNzjT6kHnNmWuTajPHoIzUjYNYYQ==}
peerDependencies:
@@ -1245,9 +1240,6 @@ packages:
'@vueuse/metadata@13.9.0':
resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==}
'@vueuse/metadata@14.0.0':
resolution: {integrity: sha512-6yoGqbJcMldVCevkFiHDBTB1V5Hq+G/haPlGIuaFZHpXC0HADB0EN1ryQAAceiW+ryS3niUwvdFbGiqHqBrfVA==}
'@vueuse/shared@10.11.1':
resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
@@ -1259,11 +1251,6 @@ packages:
peerDependencies:
vue: ^3.5.0
'@vueuse/shared@14.0.0':
resolution: {integrity: sha512-mTCA0uczBgurRlwVaQHfG0Ja7UdGe4g9mwffiJmvLiTtp1G4AQyIjej6si/k8c8pUwTfVpNufck+23gXptPAkw==}
peerDependencies:
vue: ^3.5.0
'@xmldom/xmldom@0.8.11':
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
engines: {node: '>=10.0.0'}
@@ -4886,13 +4873,6 @@ snapshots:
'@vueuse/shared': 13.9.0(vue@3.5.25(typescript@5.9.2))
vue: 3.5.25(typescript@5.9.2)
'@vueuse/core@14.0.0(vue@3.5.25(typescript@5.9.2))':
dependencies:
'@types/web-bluetooth': 0.0.21
'@vueuse/metadata': 14.0.0
'@vueuse/shared': 14.0.0(vue@3.5.25(typescript@5.9.2))
vue: 3.5.25(typescript@5.9.2)
'@vueuse/integrations@13.9.0(axios@1.13.2)(fuse.js@7.1.0)(vue@3.5.25(typescript@5.9.2))':
dependencies:
'@vueuse/core': 13.9.0(vue@3.5.25(typescript@5.9.2))
@@ -4908,8 +4888,6 @@ snapshots:
'@vueuse/metadata@13.9.0': {}
'@vueuse/metadata@14.0.0': {}
'@vueuse/shared@10.11.1(vue@3.5.25(typescript@5.9.2))':
dependencies:
vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.2))
@@ -4927,10 +4905,6 @@ snapshots:
dependencies:
vue: 3.5.25(typescript@5.9.2)
'@vueuse/shared@14.0.0(vue@3.5.25(typescript@5.9.2))':
dependencies:
vue: 3.5.25(typescript@5.9.2)
'@xmldom/xmldom@0.8.11': {}
abbrev@1.1.1: {}
+3
View File
@@ -14,11 +14,13 @@ declare module 'vue' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
UApp: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/App.vue')['default']
UAvatar: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Avatar.vue')['default']
UBadge: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue')['default']
UButton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default']
UCard: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default']
UCheckbox: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default']
UFormField: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default']
UIcon: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default']
UInput: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default']
UModal: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default']
UProgress: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Progress.vue')['default']
@@ -28,5 +30,6 @@ declare module 'vue' {
UTabs: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue')['default']
UTextarea: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Textarea.vue')['default']
UToaster: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Toaster.vue')['default']
UTooltip: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.2__26fe4596361b2ba7cfd995b4ae348088/node_modules/@nuxt/ui/dist/runtime/components/Tooltip.vue')['default']
}
}
+108
View File
@@ -0,0 +1,108 @@
<script setup lang="ts">
import { useChatStore } from '@/stores/chat'
import { storeToRefs } from 'pinia'
import { ref } from 'vue'
const chatStore = useChatStore()
const { sessions, currentSessionId } = storeToRefs(chatStore)
const isCollapsed = ref(false)
function toggleSidebar() {
isCollapsed.value = !isCollapsed.value
}
function handleImport() {
// TODO: Implement import logic
console.log('Import clicked')
}
</script>
<template>
<div
class="flex h-full flex-col border-r border-gray-200 bg-gray-50 transition-all duration-300 ease-in-out dark:border-gray-800 dark:bg-gray-900"
:class="[isCollapsed ? 'w-20' : 'w-72']"
>
<!-- Top Section -->
<div class="flex flex-col p-4">
<!-- Header / Toggle -->
<div class="mb-6 flex items-center" :class="[isCollapsed ? 'justify-center' : 'justify-between']">
<div v-if="!isCollapsed" class="text-lg font-semibold text-gray-900 dark:text-white">
ChatLens
</div>
<UButton
icon="i-heroicons-bars-3"
color="gray"
variant="ghost"
size="sm"
@click="toggleSidebar"
/>
</div>
<!-- New Analysis Button -->
<UTooltip :text="isCollapsed ? '新建分析' : ''" :popper="{ placement: 'right' }">
<UButton
block
class="transition-all"
:class="[isCollapsed ? 'px-0' : '']"
color="gray"
variant="solid"
:icon="isCollapsed ? 'i-heroicons-plus' : 'i-heroicons-plus'"
:label="isCollapsed ? '' : '新建分析'"
@click="handleImport"
/>
</UTooltip>
</div>
<!-- Session List -->
<div class="flex-1 overflow-y-auto px-3">
<div v-if="sessions.length === 0 && !isCollapsed" class="py-8 text-center text-sm text-gray-500">
暂无记录
</div>
<div class="space-y-1">
<div v-if="!isCollapsed && sessions.length > 0" class="mb-2 px-2 text-xs font-medium text-gray-500">
最近
</div>
<button
v-for="session in sessions"
:key="session.id"
class="flex w-full items-center rounded-full p-2 text-left transition-colors hover:bg-gray-200 dark:hover:bg-gray-800"
:class="[
currentSessionId === session.id ? 'bg-primary-100 text-primary-900 dark:bg-primary-900/30 dark:text-primary-100' : 'text-gray-700 dark:text-gray-200',
isCollapsed ? 'justify-center' : ''
]"
@click="chatStore.selectSession(session.id)"
>
<UAvatar
:src="session.avatar"
:alt="session.name"
size="sm"
:class="[isCollapsed ? '' : 'mr-3']"
/>
<div v-if="!isCollapsed" class="min-w-0 flex-1">
<p class="truncate text-sm font-medium">
{{ session.name }}
</p>
</div>
</button>
</div>
</div>
<!-- Footer (Optional settings or help) -->
<div class="border-t border-gray-200 p-4 dark:border-gray-800">
<UTooltip :text="isCollapsed ? '设置' : ''" :popper="{ placement: 'right' }">
<UButton
block
color="gray"
variant="ghost"
icon="i-heroicons-cog-6-tooth"
:label="isCollapsed ? '' : '设置'"
:class="[isCollapsed ? 'px-0' : 'justify-start']"
/>
</UTooltip>
</div>
</div>
</template>
+93
View File
@@ -0,0 +1,93 @@
<script setup lang="ts">
const features = [
{
icon: '🏆',
title: '活跃度分析',
desc: '谁是群里的潜水王?',
color: 'text-yellow-500',
bg: 'bg-yellow-50',
delay: '0ms',
},
{
icon: '☁️',
title: '词云生成',
desc: '大家最爱说什么?',
color: 'text-blue-500',
bg: 'bg-blue-50',
delay: '100ms',
},
{
icon: '❤️',
title: '情感分析',
desc: '群聊氛围怎么样?',
color: 'text-pink-500',
bg: 'bg-pink-50',
delay: '200ms',
},
]
function handleImport() {
console.log('Import clicked')
}
function openTutorial(type: 'ios' | 'android') {
console.log('Tutorial:', type)
}
</script>
<template>
<div class="relative flex h-full w-full overflow-hidden bg-white">
<!-- Content Container -->
<div class="relative z-10 flex h-full w-full flex-col items-center justify-center px-4">
<!-- Hero Section -->
<div class="mb-12 text-center">
<div class="mb-6 inline-flex animate-bounce items-center justify-center rounded-3xl bg-white p-4 shadow-lg shadow-purple-100 ring-1 ring-gray-100">
<UIcon name="i-heroicons-sparkles" class="h-8 w-8 text-purple-500" />
</div>
<h1 class="mb-4 bg-gradient-to-r from-purple-600 via-pink-500 to-orange-400 bg-clip-text text-5xl font-black tracking-tight text-transparent sm:text-6xl">
探索你的群聊记忆
</h1>
<p class="text-lg font-medium text-gray-500">
ChatLens 帮你发现那些被遗忘的有趣瞬间
</p>
</div>
<!-- Feature Cards -->
<div class="mb-12 grid max-w-4xl grid-cols-1 gap-6 sm:grid-cols-3">
<div
v-for="feature in features"
:key="feature.title"
class="group relative transform cursor-default rounded-2xl border border-gray-100 bg-white p-6 shadow-sm transition-all duration-300 hover:-translate-y-2 hover:shadow-xl hover:shadow-purple-500/10"
:style="{ animationDelay: feature.delay }"
>
<div class="mb-4 text-4xl transition-transform duration-300 group-hover:scale-110">{{ feature.icon }}</div>
<h3 class="mb-2 text-lg font-bold text-gray-900">{{ feature.title }}</h3>
<p class="text-sm text-gray-500">{{ feature.desc }}</p>
</div>
</div>
<!-- Actions -->
<div class="flex flex-col items-center space-y-8">
<button
class="group relative inline-flex items-center justify-center rounded-full bg-gradient-to-r from-purple-500 to-pink-500 px-8 py-4 text-lg font-bold text-white shadow-lg shadow-purple-500/30 transition-all duration-300 hover:scale-105 hover:shadow-purple-500/50 focus:outline-none focus:ring-4 focus:ring-purple-500/30"
@click="handleImport"
>
<UIcon name="i-heroicons-arrow-up-tray" class="mr-2 h-5 w-5 transition-transform group-hover:-translate-y-1" />
立即导入聊天记录
</button>
<div class="flex items-center space-x-6 text-sm font-medium text-gray-400">
<button class="flex items-center transition-colors hover:text-gray-600" @click="openTutorial('ios')">
<UIcon name="i-simple-icons-apple" class="mr-1.5 h-4 w-4" />
iOS 教程
</button>
<span class="h-1 w-1 rounded-full bg-gray-300"></span>
<button class="flex items-center transition-colors hover:text-gray-600" @click="openTutorial('android')">
<UIcon name="i-simple-icons-android" class="mr-1.5 h-4 w-4" />
Android 教程
</button>
</div>
</div>
</div>
</div>
</template>
+22 -47
View File
@@ -1,55 +1,30 @@
<script setup lang="ts">
import { useChatStore } from '@/stores/chat'
import { storeToRefs } from 'pinia'
import Sidebar from '@/components/Sidebar.vue'
import WelcomeGuide from '@/components/WelcomeGuide.vue'
const chatStore = useChatStore()
const { currentSessionId } = storeToRefs(chatStore)
</script>
<template>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 flex flex-col items-center justify-center p-8">
<div class="text-center max-w-2xl">
<!-- Logo / 标题 -->
<div class="mb-8">
<div class="w-20 h-20 mx-auto mb-6 bg-primary-500 rounded-2xl flex items-center justify-center">
<span class="text-4xl">💬</span>
<div class="flex h-screen w-full overflow-hidden bg-white dark:bg-gray-950">
<!-- Sidebar -->
<Sidebar />
<!-- Main Content -->
<main class="flex-1 overflow-hidden">
<template v-if="!currentSessionId">
<WelcomeGuide />
</template>
<template v-else>
<!-- TODO: Analysis Dashboard -->
<div class="flex h-full items-center justify-center text-gray-500">
分析仪表盘 (开发中...)
</div>
<h1 class="text-4xl font-bold text-gray-900 dark:text-white mb-4">
ChatLens
</h1>
<p class="text-lg text-gray-600 dark:text-gray-400">
聊天记录分析工具 - 让你更好地了解你的聊天数据
</p>
</div>
<!-- 功能入口 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
<UCard class="hover:shadow-lg transition-shadow cursor-pointer">
<div class="text-center p-4">
<div class="text-3xl mb-3">📁</div>
<h3 class="font-semibold text-gray-900 dark:text-white mb-2">导入聊天记录</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">支持多种格式的聊天记录导入</p>
</div>
</UCard>
<UCard class="hover:shadow-lg transition-shadow cursor-pointer">
<div class="text-center p-4">
<div class="text-3xl mb-3">📊</div>
<h3 class="font-semibold text-gray-900 dark:text-white mb-2">数据分析</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">可视化展示聊天数据统计</p>
</div>
</UCard>
</div>
<!-- 快速开始按钮 -->
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<UButton size="lg" color="primary">
开始使用
</UButton>
<UButton size="lg" variant="outline" to="/ui">
组件演示
</UButton>
</div>
<!-- 版本信息 -->
<p class="mt-12 text-sm text-gray-400">
v0.1.0 · Built with Vue 3 + Electron + Nuxt UI
</p>
</div>
</template>
</main>
</div>
</template>
+48
View File
@@ -0,0 +1,48 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export interface ChatSession {
id: string
name: string
avatar?: string
lastMessage?: string
timestamp: number
}
export const useChatStore = defineStore(
'chat',
() => {
const sessions = ref<ChatSession[]>([])
const currentSessionId = ref<string | null>(null)
function addSession(session: ChatSession) {
sessions.value.push(session)
currentSessionId.value = session.id
}
function removeSession(id: string) {
const index = sessions.value.findIndex((s) => s.id === id)
if (index !== -1) {
sessions.value.splice(index, 1)
if (currentSessionId.value === id) {
currentSessionId.value = null
}
}
}
function selectSession(id: string) {
currentSessionId.value = id
}
return {
sessions,
currentSessionId,
addSession,
removeSession,
selectSession,
}
},
{
persist: true,
},
)