feat: 在 web 页面显示版本号和更新日志
This commit is contained in:
120
web/src/lib/components/ChangelogDrawer.svelte
Normal file
120
web/src/lib/components/ChangelogDrawer.svelte
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Drawer from '$lib/components/ui/drawer';
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import X from '@lucide/svelte/icons/x';
|
||||||
|
import Calendar from '@lucide/svelte/icons/calendar';
|
||||||
|
import Tag from '@lucide/svelte/icons/tag';
|
||||||
|
|
||||||
|
let { open = $bindable(false) } = $props();
|
||||||
|
|
||||||
|
// Changelog 内容(从 CHANGELOG.md 解析或硬编码)
|
||||||
|
const changelog = [
|
||||||
|
{
|
||||||
|
version: '1.3.0',
|
||||||
|
date: '2026-01-26',
|
||||||
|
changes: {
|
||||||
|
新增: [
|
||||||
|
'京东账单支持 - 支持京东白条账单上传和清洗',
|
||||||
|
'自动识别京东账单类型(交易流水 ZIP)',
|
||||||
|
'解析京东白条账单 CSV 格式(含还款日期信息)',
|
||||||
|
'京东专属分类映射配置',
|
||||||
|
'支持京东外卖、京东平台商户等商户识别',
|
||||||
|
'上传页面和账单列表页面添加"京东"选项'
|
||||||
|
],
|
||||||
|
优化: [
|
||||||
|
'京东订单智能去重 - 上传京东账单时自动软删除其他来源中的京东订单',
|
||||||
|
'分类推断复核等级优化 - 京东账单引入 LOW 复核等级',
|
||||||
|
'京东平台商户关键词扩展'
|
||||||
|
],
|
||||||
|
技术改进: [
|
||||||
|
'新增京东账单清理器',
|
||||||
|
'新增京东专属配置',
|
||||||
|
'后端新增软删除接口',
|
||||||
|
'新增单元测试(11 个测试用例)'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: '1.2.1',
|
||||||
|
date: '2026-01-23',
|
||||||
|
changes: {
|
||||||
|
优化: [
|
||||||
|
'智能复核快捷确认 - 在复核列表每行添加快捷确认按钮',
|
||||||
|
'无需打开详情页面即可确认分类正确',
|
||||||
|
'自动更新统计数据',
|
||||||
|
'提升复核效率,支持快速批量确认'
|
||||||
|
],
|
||||||
|
文档: ['AGENTS.md 更新 - 精简为 150 行,专为 AI 编程助手设计']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: '1.2.0',
|
||||||
|
date: '2026-01-25',
|
||||||
|
changes: {
|
||||||
|
新增: [
|
||||||
|
'账单删除功能 - 支持在账单详情抽屉中删除账单(软删除)',
|
||||||
|
'删除按钮带二次确认,防止误操作',
|
||||||
|
'已删除的账单在所有查询中自动过滤'
|
||||||
|
],
|
||||||
|
技术改进: [
|
||||||
|
'后端 MongoDB 查询方法添加软删除过滤',
|
||||||
|
'新增 DELETE /api/bills/:id 接口'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Drawer.Root bind:open>
|
||||||
|
<Drawer.Content class="max-h-[90vh]">
|
||||||
|
<Drawer.Header class="border-b">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<Drawer.Title class="text-xl font-semibold">版本更新日志</Drawer.Title>
|
||||||
|
<Button variant="ghost" size="icon" onclick={() => (open = false)}>
|
||||||
|
<X class="size-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Drawer.Header>
|
||||||
|
|
||||||
|
<div class="h-[calc(90vh-8rem)] overflow-y-auto p-6">
|
||||||
|
<div class="space-y-8">
|
||||||
|
{#each changelog as release}
|
||||||
|
<div class="space-y-3">
|
||||||
|
<!-- 版本号和日期 -->
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<Tag class="size-5 text-primary" />
|
||||||
|
<h3 class="text-lg font-semibold">v{release.version}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1.5 text-sm text-muted-foreground">
|
||||||
|
<Calendar class="size-4" />
|
||||||
|
<span>{release.date}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 变更内容 -->
|
||||||
|
<div class="space-y-4 pl-7 border-l-2 border-muted">
|
||||||
|
{#each Object.entries(release.changes) as [category, items]}
|
||||||
|
<div class="space-y-2">
|
||||||
|
<h4 class="text-sm font-semibold text-primary">{category}</h4>
|
||||||
|
<ul class="space-y-1.5 text-sm text-muted-foreground">
|
||||||
|
{#each items as item}
|
||||||
|
<li class="flex gap-2 leading-relaxed">
|
||||||
|
<span class="text-primary mt-1.5">•</span>
|
||||||
|
<span>{item}</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Drawer.Footer class="border-t">
|
||||||
|
<Button variant="outline" onclick={() => (open = false)} class="w-full">关闭</Button>
|
||||||
|
</Drawer.Footer>
|
||||||
|
</Drawer.Content>
|
||||||
|
</Drawer.Root>
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||||
import * as Avatar from '$lib/components/ui/avatar';
|
import * as Avatar from '$lib/components/ui/avatar';
|
||||||
import { Separator } from '$lib/components/ui/separator';
|
import { Separator } from '$lib/components/ui/separator';
|
||||||
|
import ChangelogDrawer from '$lib/components/ChangelogDrawer.svelte';
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import Upload from '@lucide/svelte/icons/upload';
|
import Upload from '@lucide/svelte/icons/upload';
|
||||||
@@ -24,6 +25,10 @@
|
|||||||
import User from '@lucide/svelte/icons/user';
|
import User from '@lucide/svelte/icons/user';
|
||||||
import Bell from '@lucide/svelte/icons/bell';
|
import Bell from '@lucide/svelte/icons/bell';
|
||||||
import Sparkles from '@lucide/svelte/icons/sparkles';
|
import Sparkles from '@lucide/svelte/icons/sparkles';
|
||||||
|
import Info from '@lucide/svelte/icons/info';
|
||||||
|
|
||||||
|
// 从 package.json 导入版本号
|
||||||
|
import pkg from '../../package.json';
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
import {
|
import {
|
||||||
@@ -42,6 +47,7 @@
|
|||||||
let checkingHealth = $state(true);
|
let checkingHealth = $state(true);
|
||||||
let isAuthenticated = $state(false);
|
let isAuthenticated = $state(false);
|
||||||
let currentUser = $state<AuthUser | null>(null);
|
let currentUser = $state<AuthUser | null>(null);
|
||||||
|
let changelogOpen = $state(false);
|
||||||
|
|
||||||
// 订阅认证状态
|
// 订阅认证状态
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@@ -228,9 +234,27 @@
|
|||||||
</Sidebar.Group>
|
</Sidebar.Group>
|
||||||
</Sidebar.Content>
|
</Sidebar.Content>
|
||||||
|
|
||||||
<!-- Footer: 用户信息 -->
|
<!-- Footer: 用户信息 + 版本号 -->
|
||||||
<Sidebar.Footer>
|
<Sidebar.Footer>
|
||||||
<Sidebar.Menu>
|
<Sidebar.Menu>
|
||||||
|
<!-- 版本号 -->
|
||||||
|
<Sidebar.MenuItem>
|
||||||
|
<Sidebar.MenuButton>
|
||||||
|
{#snippet child({ props })}
|
||||||
|
<button
|
||||||
|
{...props}
|
||||||
|
onclick={() => changelogOpen = true}
|
||||||
|
class="w-full"
|
||||||
|
title="查看更新日志"
|
||||||
|
>
|
||||||
|
<Info class="size-4" />
|
||||||
|
<span class="text-xs">v{pkg.version}</span>
|
||||||
|
</button>
|
||||||
|
{/snippet}
|
||||||
|
</Sidebar.MenuButton>
|
||||||
|
</Sidebar.MenuItem>
|
||||||
|
|
||||||
|
<!-- 用户信息 -->
|
||||||
<Sidebar.MenuItem>
|
<Sidebar.MenuItem>
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
@@ -344,4 +368,7 @@
|
|||||||
</main>
|
</main>
|
||||||
</Sidebar.Inset>
|
</Sidebar.Inset>
|
||||||
</Sidebar.Provider>
|
</Sidebar.Provider>
|
||||||
|
|
||||||
|
<!-- Changelog 抽屉 -->
|
||||||
|
<ChangelogDrawer bind:open={changelogOpen} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user