mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-06-16 22:06:52 +08:00
feat: 支持修改聊天记录名称
This commit is contained in:
@@ -371,6 +371,30 @@ export function deleteSession(sessionId: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名会话
|
||||
*/
|
||||
export function renameSession(sessionId: string, newName: string): boolean {
|
||||
const dbPath = getDbPath(sessionId)
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const db = new Database(dbPath)
|
||||
db.pragma('journal_mode = WAL')
|
||||
|
||||
const stmt = db.prepare('UPDATE meta SET name = ?')
|
||||
stmt.run(newName)
|
||||
|
||||
db.close()
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('[Database] Failed to rename session:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库存储目录
|
||||
*/
|
||||
|
||||
@@ -260,6 +260,21 @@ const mainIpcMain = (win: BrowserWindow) => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 重命名会话
|
||||
*/
|
||||
ipcMain.handle('chat:renameSession', async (_, sessionId: string, newName: string) => {
|
||||
try {
|
||||
// 先关闭 Worker 中的数据库连接(确保没有其他进程占用)
|
||||
await worker.closeDatabase(sessionId)
|
||||
// 执行重命名
|
||||
return databaseCore.renameSession(sessionId, newName)
|
||||
} catch (error) {
|
||||
console.error('重命名会话失败:', error)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取可用年份列表
|
||||
*/
|
||||
|
||||
Vendored
+1
@@ -36,6 +36,7 @@ interface ChatApi {
|
||||
getSessions: () => Promise<AnalysisSession[]>
|
||||
getSession: (sessionId: string) => Promise<AnalysisSession | null>
|
||||
deleteSession: (sessionId: string) => Promise<boolean>
|
||||
renameSession: (sessionId: string, newName: string) => Promise<boolean>
|
||||
getAvailableYears: (sessionId: string) => Promise<number[]>
|
||||
getMemberActivity: (sessionId: string, filter?: TimeFilter) => Promise<MemberActivity[]>
|
||||
getMemberNameHistory: (sessionId: string, memberId: number) => Promise<MemberNameHistory[]>
|
||||
|
||||
@@ -90,6 +90,13 @@ const chatApi = {
|
||||
return ipcRenderer.invoke('chat:deleteSession', sessionId)
|
||||
},
|
||||
|
||||
/**
|
||||
* 重命名会话
|
||||
*/
|
||||
renameSession: (sessionId: string, newName: string): Promise<boolean> => {
|
||||
return ipcRenderer.invoke('chat:renameSession', sessionId, newName)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取可用年份列表
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { useChatStore } from '@/stores/chat'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import type { AnalysisSession } from '@/types/chat'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
@@ -17,7 +18,11 @@ const { toggleSidebar } = chatStore
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const deleteConfirmId = ref<string | null>(null)
|
||||
// 重命名相关状态
|
||||
const showRenameModal = ref(false)
|
||||
const renameTarget = ref<AnalysisSession | null>(null)
|
||||
const newName = ref('')
|
||||
const renameInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
// 加载会话列表
|
||||
onMounted(() => {
|
||||
@@ -33,18 +38,59 @@ function formatTime(timestamp: number): string {
|
||||
return dayjs.unix(timestamp).fromNow()
|
||||
}
|
||||
|
||||
function confirmDelete(id: string, event: Event) {
|
||||
event.stopPropagation()
|
||||
deleteConfirmId.value = id
|
||||
// 打开重命名弹窗
|
||||
function openRenameModal(session: AnalysisSession) {
|
||||
renameTarget.value = session
|
||||
newName.value = session.name
|
||||
showRenameModal.value = true
|
||||
// 等待 DOM 更新后聚焦输入框
|
||||
nextTick(() => {
|
||||
renameInputRef.value?.focus()
|
||||
renameInputRef.value?.select()
|
||||
})
|
||||
}
|
||||
|
||||
async function handleDelete(id: string) {
|
||||
await chatStore.deleteSession(id)
|
||||
deleteConfirmId.value = null
|
||||
// 执行重命名
|
||||
async function handleRename() {
|
||||
if (!renameTarget.value || !newName.value.trim()) return
|
||||
|
||||
const success = await chatStore.renameSession(renameTarget.value.id, newName.value.trim())
|
||||
if (success) {
|
||||
showRenameModal.value = false
|
||||
renameTarget.value = null
|
||||
newName.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
function cancelDelete() {
|
||||
deleteConfirmId.value = null
|
||||
// 关闭重命名弹窗
|
||||
function closeRenameModal() {
|
||||
showRenameModal.value = false
|
||||
renameTarget.value = null
|
||||
newName.value = ''
|
||||
}
|
||||
|
||||
// 删除会话
|
||||
async function handleDelete(session: AnalysisSession) {
|
||||
await chatStore.deleteSession(session.id)
|
||||
}
|
||||
|
||||
// 生成右键菜单项
|
||||
function getContextMenuItems(session: AnalysisSession) {
|
||||
return [
|
||||
[
|
||||
{
|
||||
label: '重命名',
|
||||
icon: 'i-lucide-pencil',
|
||||
onSelect: () => openRenameModal(session),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error' as const,
|
||||
onSelect: () => handleDelete(session),
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -86,7 +132,7 @@ function cancelDelete() {
|
||||
</UTooltip>
|
||||
|
||||
<!-- Tools Button -->
|
||||
<UTooltip :text="isCollapsed ? '工具广场' : ''" :popper="{ placement: 'right' }">
|
||||
<UTooltip :text="isCollapsed ? '实用工具' : ''" :popper="{ placement: 'right' }">
|
||||
<UButton
|
||||
:block="!isCollapsed"
|
||||
class="transition-all rounded-full hover:bg-gray-200/60 dark:hover:bg-gray-800 h-12 cursor-pointer mt-2"
|
||||
@@ -100,8 +146,8 @@ function cancelDelete() {
|
||||
variant="ghost"
|
||||
@click="router.push({ name: 'tools' })"
|
||||
>
|
||||
<UIcon name="i-heroicons-wrench-screwdriver" class="h-5 w-5 shrink-0" :class="[isCollapsed ? '' : 'mr-2']" />
|
||||
<span v-if="!isCollapsed" class="truncate">工具广场</span>
|
||||
<UIcon name="i-heroicons-squares-2x2" class="h-5 w-5 shrink-0" :class="[isCollapsed ? '' : 'mr-2']" />
|
||||
<span v-if="!isCollapsed" class="truncate">实用工具</span>
|
||||
</UButton>
|
||||
</UTooltip>
|
||||
</div>
|
||||
@@ -111,8 +157,9 @@ function cancelDelete() {
|
||||
<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 v-if="!isCollapsed && sessions.length > 0" class="mb-2 px-2">
|
||||
<div class="text-xs font-medium text-gray-500">聊天记录</div>
|
||||
<div class="text-[10px] text-gray-400 font-normal mt-0.5">右键删除或重命名</div>
|
||||
</div>
|
||||
|
||||
<UTooltip
|
||||
@@ -121,6 +168,7 @@ function cancelDelete() {
|
||||
:text="isCollapsed ? session.name : ''"
|
||||
:popper="{ placement: 'right' }"
|
||||
>
|
||||
<UContextMenu :items="getContextMenuItems(session)">
|
||||
<div
|
||||
class="group relative flex w-full items-center rounded-full p-2 text-left transition-colors"
|
||||
:class="[
|
||||
@@ -153,44 +201,32 @@ function cancelDelete() {
|
||||
{{ session.messageCount }} 条消息 · {{ formatTime(session.importedAt) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Delete Button -->
|
||||
<div v-if="!isCollapsed" class="shrink-0 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<UPopover v-if="deleteConfirmId === session.id" :open="true" @update:open="cancelDelete">
|
||||
<template #default>
|
||||
<UButton
|
||||
icon="i-heroicons-trash"
|
||||
color="red"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full"
|
||||
@click="(e: Event) => confirmDelete(session.id, e)"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="p-3">
|
||||
<p class="mb-3 text-sm">确定删除此记录?</p>
|
||||
<div class="flex justify-end gap-2">
|
||||
<UButton size="xs" color="red" @click="handleDelete(session.id)">确定删除</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UPopover>
|
||||
<UButton
|
||||
v-else
|
||||
icon="i-heroicons-trash"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full"
|
||||
@click="(e: Event) => confirmDelete(session.id, e)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</UContextMenu>
|
||||
</UTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rename Modal -->
|
||||
<UModal v-model:open="showRenameModal">
|
||||
<template #content>
|
||||
<div class="p-4">
|
||||
<h3 class="mb-3 font-semibold text-gray-900 dark:text-white">重命名</h3>
|
||||
<UInput
|
||||
ref="renameInputRef"
|
||||
v-model="newName"
|
||||
placeholder="请输入新名称"
|
||||
class="mb-4"
|
||||
@keydown.enter="handleRename"
|
||||
/>
|
||||
<div class="flex justify-end gap-2">
|
||||
<UButton size="sm" color="gray" variant="soft" @click="closeRenameModal">取消</UButton>
|
||||
<UButton size="sm" color="primary" :disabled="!newName.trim()" @click="handleRename">确定</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="border-t border-gray-200 p-4 dark:border-gray-800">
|
||||
<UTooltip :text="isCollapsed ? '设置和帮助' : ''" :popper="{ placement: 'right' }">
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ const activeTab = ref('merge')
|
||||
<div class="flex h-full flex-col bg-gray-50 dark:bg-gray-950">
|
||||
<!-- Header -->
|
||||
<div class="border-b border-gray-200 bg-white px-6 py-4 dark:border-gray-800 dark:bg-gray-900">
|
||||
<h1 class="text-xl font-semibold text-gray-900 dark:text-white">工具广场</h1>
|
||||
<h1 class="text-xl font-semibold text-gray-900 dark:text-white">实用工具</h1>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">提供聊天记录处理的实用工具</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -190,6 +190,26 @@ export const useChatStore = defineStore(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名会话
|
||||
*/
|
||||
async function renameSession(id: string, newName: string): Promise<boolean> {
|
||||
try {
|
||||
const success = await window.chatApi.renameSession(id, newName)
|
||||
if (success) {
|
||||
// 更新本地列表中的名称
|
||||
const session = sessions.value.find((s) => s.id === id)
|
||||
if (session) {
|
||||
session.name = newName
|
||||
}
|
||||
}
|
||||
return success
|
||||
} catch (error) {
|
||||
console.error('重命名会话失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除选中状态
|
||||
*/
|
||||
@@ -245,6 +265,7 @@ export const useChatStore = defineStore(
|
||||
importFileFromPath,
|
||||
selectSession,
|
||||
deleteSession,
|
||||
renameSession,
|
||||
clearSelection,
|
||||
toggleSidebar,
|
||||
addCustomKeywordTemplate,
|
||||
|
||||
Reference in New Issue
Block a user