Skip to content

Commit

Permalink
feat: add session edit/search/sort (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
orangelckc committed Mar 23, 2023
1 parent e4361d3 commit bb88573
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 21 deletions.
119 changes: 106 additions & 13 deletions src/components/Function/components/HistoryDrawer.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,103 @@
<script setup lang="ts">
import { useSessionStore } from '@/stores'
import { IconDelete } from '@arco-design/web-vue/es/icon'
import { Message } from '@arco-design/web-vue'
import {
IconDelete,
IconEdit,
IconCheck,
IconClose
} from '@arco-design/web-vue/es/icon'
import type { SessionPayload } from '@/types'
import { Message } from '@arco-design/web-vue'
const props = defineProps<{ visible: boolean; setVisible: () => void }>()
const sessionStore = useSessionStore()
const { sessionList, currentSession, isThinking } = storeToRefs(sessionStore)
const { switchSession, getSessionList, deleteSession } = sessionStore
const { sessionList, currentSession } = storeToRefs(sessionStore)
const { switchSession, getSessionList, deleteSession, updateSession } =
sessionStore
const search = ref('')
const searchRef = ref<HTMLInputElement>()
const filterList = computed(() =>
sessionList.value.filter((item) => item.title.includes(search.value))
)
const renderList = computed(() =>
search.value ? filterList.value : sessionList.value
)
const isEdit = computed(() => sessionList.value.some((item) => item.isEdit))
const handleClick = (item: SessionPayload) => {
if (isEdit.value) return Message.info('请先完成当前编辑')
switchSession(item)
search.value = ''
props.setVisible()
}
const handleOpen = () => {
if (!currentSession.value) return
searchRef.value?.focus()
// 列表滚动到当前的会话
const el = document.getElementById(currentSession.value.id)
if (!el) return
el.scrollIntoView()
}
const handleClose = () => {
if (isEdit.value) return Message.info('请先完成当前编辑')
search.value = ''
props.setVisible()
}
const handleEdit = (item: SessionPayload) => {
if (isEdit.value) return Message.info(`请先完成当前编辑`)
item.isEdit = true
}
const handleUpdate = (item: SessionPayload) => {
handleCancle(item)
updateSession(item)
}
const handleCancle = (item: SessionPayload) => {
if (!item.title.trim()) return Message.info(`标题不能为空`)
item.isEdit = false
}
</script>

<template>
<a-drawer
:visible="visible"
placement="left"
:footer="false"
:width="300"
class="history-drawer"
@cancel="setVisible"
@cancel="handleClose"
@before-open="getSessionList"
@open="handleOpen"
unmountOnClose
>
<!-- 加入全部清除的按钮 -->
<template #title> 会话历史 </template>
<ul class="flex flex-col gap-2">
<ul class="flex flex-col gap-2 scroll-smooth" v-if="renderList.length">
<li
v-for="item in sessionList"
v-for="item in renderList"
class="transition-300 group flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-[var(--color-fill-2)]"
:class="{
'bg-[rgb(var(--blue-6))]! text-white': item.id === currentSession?.id
}"
:key="item.id"
:id="item.id"
>
<!-- TODO: 优化此处宽度的计算,这样写有点鸡肋 -->
<div class="flex w-[calc(100%-16px)] items-center gap-2">
Expand All @@ -46,19 +106,52 @@ const handleClick = (item: SessionPayload) => {
class="flex w-[calc(100%-56px)] flex-col gap-3 leading-none"
@click="handleClick(item)"
>
<div class="truncate">
<a-input
v-model="item.title"
class="w-[150px] rounded-sm"
size="mini"
@click.stop
allow-clear
v-if="item.isEdit"
/>
<div class="truncate" v-else>
{{ item.title }}
</div>
<span> 与 {{ item.name }} 的聊天 </span>
</div>
</div>
<IconDelete
class="text-5 opacity-0 group-hover:opacity-100"
@click="deleteSession(item)"
/>
<div
v-if="!item.isEdit"
class="text-5 flex gap-2 opacity-0 group-hover:opacity-100"
>
<IconEdit @click="handleEdit(item)" />
<IconDelete
@click="deleteSession(item)"
:class="{
'pointer-events-none opacity-50': item.id === currentSession?.id
}"
/>
</div>
<div v-else class="text-5 flex gap-2 opacity-0 group-hover:opacity-100">
<IconCheck @click="handleUpdate(item)" />
<IconClose @click="handleCancle(item)" />
</div>
</li>
</ul>
<a-empty v-else class="mt-1/2" />
<template #footer>
<a-input-search
ref="searchRef"
v-model="search"
placeholder="搜索对话"
allow-clear
class="rounded-md"
/>
</template>
</a-drawer>
</template>
Expand Down
11 changes: 10 additions & 1 deletion src/sqls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const executeSQL = async (sql: string) => {
export const initSQL = async () => {
await executeSQL(
`
CREATE TABLE IF NOT EXISTS session (id TEXT, title TEXT, role_id INTEGER);
CREATE TABLE IF NOT EXISTS session (id TEXT, title TEXT, role_id INTEGER, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE IF NOT EXISTS session_data (id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, message TEXT, is_ask INTEGER, is_memory INTEGER, message_type TEXT, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE IF NOT EXISTS role (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, is_default INTEGER DEFAULT false);
CREATE TABLE IF NOT EXISTS credit (id INTEGER PRIMARY KEY AUTOINCREMENT, history_id INTEGER, token_cost INTEGER, api_key TEXT);
Expand All @@ -76,6 +76,15 @@ export const initSQL = async () => {
description: DEFAULT_ROLE.description,
is_default: true
})

// 发版之后的表更新操作,只能对已存在的表进行增加列,不能删除列
// 1.2023-03-22 在session表中添加update_time列,记录对话的最后一次更新时间
const sessionTable = (await executeSQL(
'SELECT * FROM session LIMIT 1;'
)) as any[]
if (sessionTable[0].update_time === undefined) {
await executeSQL(`ALTER TABLE session ADD COLUMN update_time TIMESTAMP;`)
}
}

/**
Expand Down
22 changes: 15 additions & 7 deletions src/stores/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ export const useSessionStore = defineStore(
const getSessionList = async () => {
// TODO: 优化 sql
const sql =
'SELECT session.*,role.name FROM session LEFT JOIN role ON role.id=session.role_id;'
'SELECT session.*, role.name FROM session LEFT JOIN role ON role.id=session.role_id ORDER BY session.update_time DESC;'

sessionList.value = (await executeSQL(sql)) as any[]
const res = (await executeSQL(sql)) as SessionPayload[]
sessionList.value = res.map((item) => ({ ...item, isEdit: false }))
}

// 获取当前会话数据
Expand Down Expand Up @@ -73,11 +74,8 @@ export const useSessionStore = defineStore(
const { currentRole } = useRoleStore()

if (!isExist && currentRole?.id) {
await insertSQL('session', {
id: currentSession.value.id,
title: data.content,
role_id: currentRole.id
})
currentSession.value.title = data.content
await insertSQL('session', currentSession.value)
}

const { isMemory } = useSettingsStore()
Expand All @@ -96,6 +94,15 @@ export const useSessionStore = defineStore(
// 更新当前对话数据
const updateSessionData = async (payload: SessionData) => {
await updateSQL('session_data', payload)
await updateSession(currentSession.value!)
}

// 更新会话信息
const updateSession = async (payload: SessionPayload) => {
const sql = `UPDATE session SET title = '${
payload.title
}', update_time = '${Date.now()}' WHERE id = '${payload.id}';`
await executeSQL(sql)
}

// 删除会话
Expand Down Expand Up @@ -147,6 +154,7 @@ export const useSessionStore = defineStore(
switchSession,
getSessionList,
deleteSession,
updateSession,
chatController
}
},
Expand Down
2 changes: 2 additions & 0 deletions src/types/sql.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export interface SessionPayload {
id: string
title: string
role_id: number
update_time?: string
name?: string
isEdit?: boolean
}

export interface RolePayload {
Expand Down

0 comments on commit bb88573

Please sign in to comment.