feat: 逻辑优化

This commit is contained in:
digua
2025-11-27 21:16:59 +08:00
parent e19a4474fe
commit 84bfab0b43
7 changed files with 20 additions and 374 deletions
+5
View File
@@ -178,6 +178,11 @@ export const qqJsonParser: ChatParser = {
// 转换时间戳(QQ 导出是毫秒,需要转为秒)
const timestamp = Math.floor(msg.timestamp / 1000)
// 过滤掉不合理的年份(2000年以前)
if (new Date(msg.timestamp).getFullYear() < 2000) {
continue
}
// 确定消息类型
const type = msg.system ? MessageType.SYSTEM : convertMessageType(msg.type, msg.content)
+11 -2
View File
@@ -184,8 +184,17 @@ export const qqTxtParser: ChatParser = {
name,
})
currentSender = { platformId: qqNumber, name }
currentTimestamp = parseDateTime(dateTimeStr)
const timestamp = parseDateTime(dateTimeStr)
// 过滤掉不合理的年份(2000年以前)
if (new Date(timestamp * 1000).getFullYear() < 2000) {
// 如果时间戳无效,标记为 null,后续不添加
currentSender = null
currentTimestamp = 0
} else {
currentSender = { platformId: qqNumber, name }
currentTimestamp = timestamp
}
} else {
// 这是消息内容行
currentContent.push(trimmedLine)
@@ -4,9 +4,9 @@ import { useChatStore } from '@/stores/chat'
import { storeToRefs } from 'pinia'
import type { AnalysisSession, MemberActivity, HourlyActivity, DailyActivity, MessageType } from '@/types/chat'
import UITabs from '@/components/UI/Tabs.vue'
import OverviewTab from './OverviewTab.vue'
import MembersTab from './MembersTab.vue'
import TimelineTab from './TimelineTab.vue'
import OverviewTab from './analysis/OverviewTab.vue'
import MembersTab from './analysis/MembersTab.vue'
import TimelineTab from './analysis/TimelineTab.vue'
const chatStore = useChatStore()
const { currentSessionId } = storeToRefs(chatStore)
-101
View File
@@ -1,101 +0,0 @@
<script setup lang="ts">
import { useChatStore } from '@/stores/chat'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
const chatStore = useChatStore()
const { isImporting, importProgress } = storeToRefs(chatStore)
const isOpen = computed(() => isImporting.value)
const statusIcon = computed(() => {
if (!importProgress.value) return 'i-heroicons-arrow-path'
switch (importProgress.value.stage) {
case 'done':
return 'i-heroicons-check-circle'
case 'error':
return 'i-heroicons-exclamation-circle'
default:
return 'i-heroicons-arrow-path'
}
})
const statusColor = computed(() => {
if (!importProgress.value) return 'text-purple-500'
switch (importProgress.value.stage) {
case 'done':
return 'text-green-500'
case 'error':
return 'text-red-500'
default:
return 'text-purple-500'
}
})
const isSpinning = computed(() => {
if (!importProgress.value) return true
return !['done', 'error'].includes(importProgress.value.stage)
})
function getStageText(): string {
if (!importProgress.value) return '准备中...'
switch (importProgress.value.stage) {
case 'reading':
return '读取文件'
case 'parsing':
return '解析数据'
case 'saving':
return '保存数据'
case 'done':
return '导入完成'
case 'error':
return '导入失败'
default:
return '处理中'
}
}
</script>
<template>
<UModal v-model:open="isOpen" :ui="{ width: 'sm:max-w-md' }">
<template #content>
<div class="p-6 text-center">
<!-- Icon -->
<div class="mb-4 flex justify-center">
<div
class="flex h-16 w-16 items-center justify-center rounded-full bg-gray-100 dark:bg-gray-800"
>
<UIcon
:name="statusIcon"
class="h-8 w-8"
:class="[statusColor, isSpinning ? 'animate-spin' : '']"
/>
</div>
</div>
<!-- Title -->
<h3 class="mb-2 text-lg font-semibold text-gray-900 dark:text-white">
{{ getStageText() }}
</h3>
<!-- Progress -->
<div v-if="importProgress" class="mb-4">
<UProgress
:value="importProgress.progress"
size="sm"
:color="importProgress.stage === 'error' ? 'red' : 'purple'"
/>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
{{ importProgress.message }}
</p>
</div>
<!-- Hint -->
<p v-if="isSpinning" class="text-xs text-gray-400">
请稍候正在处理您的聊天记录...
</p>
</div>
</template>
</UModal>
</template>
+1 -1
View File
@@ -3,7 +3,7 @@ import { useChatStore } from '@/stores/chat'
import { storeToRefs } from 'pinia'
import Sidebar from '@/components/Sidebar.vue'
import WelcomeGuide from '@/components/WelcomeGuide.vue'
import AnalysisDashboard from '@/components/analysis/AnalysisDashboard.vue'
import AnalysisDashboard from '@/components/AnalysisDashboard.vue'
const chatStore = useChatStore()
const { currentSessionId, isInitialized } = storeToRefs(chatStore)
-262
View File
@@ -1,262 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue'
// 表单数据
const formData = ref({
name: '',
email: '',
message: '',
})
// 开关状态
const isDarkMode = ref(false)
const isEnabled = ref(true)
// 下拉选择
const selectedOption = ref('')
const options = [
{ label: '选项一', value: 'option1' },
{ label: '选项二', value: 'option2' },
{ label: '选项三', value: 'option3' },
]
// 标签页
const activeTab = ref('tab1')
const tabs = [
{ label: '概览', value: 'tab1' },
{ label: '分析', value: 'tab2' },
{ label: '设置', value: 'tab3' },
]
// Toast 通知
const toast = useToast()
const showToast = (type: 'success' | 'error' | 'warning' | 'info') => {
const messages = {
success: '操作成功!',
error: '操作失败,请重试',
warning: '请注意此操作',
info: '这是一条提示信息',
}
toast.add({
title: messages[type],
color: type === 'error' ? 'red' : type === 'warning' ? 'yellow' : type === 'success' ? 'green' : 'blue',
})
}
// 模态框
const isModalOpen = ref(false)
// 加载状态
const isLoading = ref(false)
const handleSubmit = async () => {
isLoading.value = true
await new Promise((resolve) => setTimeout(resolve, 2000))
isLoading.value = false
toast.add({ title: '表单提交成功!', color: 'green' })
}
</script>
<template>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 p-8">
<!-- 标题区域 -->
<div class="max-w-6xl mx-auto">
<div class="text-center mb-12">
<h1 class="text-4xl font-bold text-gray-900 dark:text-white mb-4">
🎉 Nuxt UI 组件演示
</h1>
<p class="text-lg text-gray-600 dark:text-gray-400">
ChatLens - 聊天记录分析工具
</p>
</div>
<!-- 按钮组 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">按钮 Buttons</h2>
<div class="flex flex-wrap gap-4">
<UButton>默认按钮</UButton>
<UButton color="primary">主要按钮</UButton>
<UButton color="green">成功按钮</UButton>
<UButton color="red">危险按钮</UButton>
<UButton color="yellow">警告按钮</UButton>
<UButton variant="outline">描边按钮</UButton>
<UButton variant="ghost">幽灵按钮</UButton>
<UButton variant="soft">柔和按钮</UButton>
<UButton :loading="isLoading" @click="handleSubmit">
{{ isLoading ? '加载中...' : '带加载状态' }}
</UButton>
<UButton icon="i-heroicons-arrow-path" />
</div>
</section>
<!-- 表单输入 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">表单 Forms</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-2xl">
<UFormField label="用户名">
<UInput v-model="formData.name" placeholder="请输入用户名" />
</UFormField>
<UFormField label="邮箱">
<UInput v-model="formData.email" type="email" placeholder="请输入邮箱" />
</UFormField>
<UFormField label="选择选项" class="md:col-span-2">
<USelect v-model="selectedOption" :items="options" placeholder="请选择" />
</UFormField>
<UFormField label="留言" class="md:col-span-2">
<UTextarea v-model="formData.message" placeholder="请输入留言内容" :rows="4" />
</UFormField>
</div>
</section>
<!-- 开关与复选框 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">开关 Toggles</h2>
<div class="flex flex-wrap items-center gap-8">
<div class="flex items-center gap-3">
<USwitch v-model="isDarkMode" />
<span class="text-gray-700 dark:text-gray-300">深色模式: {{ isDarkMode ? '开' : '关' }}</span>
</div>
<div class="flex items-center gap-3">
<USwitch v-model="isEnabled" color="green" />
<span class="text-gray-700 dark:text-gray-300">启用功能: {{ isEnabled ? '是' : '否' }}</span>
</div>
<div class="flex items-center gap-3">
<UCheckbox label="记住我" />
</div>
</div>
</section>
<!-- 标签页 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">标签页 Tabs</h2>
<UTabs v-model="activeTab" :items="tabs" class="w-full max-w-xl">
<template #tab1>
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg">
<h3 class="font-medium text-gray-900 dark:text-white mb-2">概览内容</h3>
<p class="text-gray-600 dark:text-gray-400">这里是概览页面的内容区域</p>
</div>
</template>
<template #tab2>
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg">
<h3 class="font-medium text-gray-900 dark:text-white mb-2">分析内容</h3>
<p class="text-gray-600 dark:text-gray-400">这里是分析页面的内容区域</p>
</div>
</template>
<template #tab3>
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg">
<h3 class="font-medium text-gray-900 dark:text-white mb-2">设置内容</h3>
<p class="text-gray-600 dark:text-gray-400">这里是设置页面的内容区域</p>
</div>
</template>
</UTabs>
</section>
<!-- Toast 通知 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">通知 Toast</h2>
<div class="flex flex-wrap gap-4">
<UButton color="green" @click="showToast('success')">成功通知</UButton>
<UButton color="red" @click="showToast('error')">错误通知</UButton>
<UButton color="yellow" @click="showToast('warning')">警告通知</UButton>
<UButton color="blue" @click="showToast('info')">信息通知</UButton>
</div>
</section>
<!-- 模态框 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">模态框 Modal</h2>
<UButton @click="isModalOpen = true">打开模态框</UButton>
<UModal v-model:open="isModalOpen">
<template #header>
<h3 class="text-lg font-semibold">模态框标题</h3>
</template>
<template #body>
<p class="text-gray-600 dark:text-gray-400">
这是模态框的内容区域可以放置任何内容
</p>
</template>
<template #footer>
<div class="flex justify-end gap-3">
<UButton variant="ghost" @click="isModalOpen = false">取消</UButton>
<UButton color="primary" @click="isModalOpen = false">确认</UButton>
</div>
</template>
</UModal>
</section>
<!-- 徽章 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">徽章 Badge</h2>
<div class="flex flex-wrap gap-4">
<UBadge>默认</UBadge>
<UBadge color="green">成功</UBadge>
<UBadge color="red">错误</UBadge>
<UBadge color="yellow">警告</UBadge>
<UBadge color="blue">信息</UBadge>
<UBadge variant="outline">描边</UBadge>
<UBadge variant="soft">柔和</UBadge>
</div>
</section>
<!-- 卡片 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">卡片 Card</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<UCard>
<template #header>
<h3 class="font-semibold">卡片标题</h3>
</template>
<p class="text-gray-600 dark:text-gray-400">这是卡片的内容区域</p>
<template #footer>
<UButton size="sm">查看详情</UButton>
</template>
</UCard>
<UCard>
<template #header>
<h3 class="font-semibold">功能卡片</h3>
</template>
<p class="text-gray-600 dark:text-gray-400">支持自定义头部和底部</p>
<template #footer>
<div class="flex gap-2">
<UButton size="sm" variant="ghost">取消</UButton>
<UButton size="sm">确认</UButton>
</div>
</template>
</UCard>
<UCard>
<template #header>
<h3 class="font-semibold">简洁卡片</h3>
</template>
<p class="text-gray-600 dark:text-gray-400">简洁的卡片展示样式</p>
</UCard>
</div>
</section>
<!-- 进度条 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">进度 Progress</h2>
<div class="space-y-4 max-w-md">
<UProgress :value="30" />
<UProgress :value="60" color="green" />
<UProgress :value="90" color="red" />
<UProgress :value="100" color="blue" />
</div>
</section>
<!-- 骨架屏 -->
<section class="mb-12">
<h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">骨架屏 Skeleton</h2>
<div class="flex items-center gap-4 max-w-md">
<USkeleton class="w-12 h-12 rounded-full" />
<div class="space-y-2 flex-1">
<USkeleton class="h-4 w-3/4" />
<USkeleton class="h-4 w-1/2" />
</div>
</div>
</section>
</div>
<!-- Toast 容器 -->
<UToaster />
</div>
</template>
-5
View File
@@ -7,11 +7,6 @@ export const router = createRouter({
name: 'index',
component: () => import('@/pages/index.vue'),
},
{
path: '/ui',
name: 'ui',
component: () => import('@/pages/ui.vue'),
},
],
history: createWebHashHistory(),
})