增加相关功能
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="chat-sidebar">
|
||||
<div class="sidebar-header">
|
||||
<el-button type="primary" class="new-chat-btn" :icon="ChatDotRound" @click="handleNewChat">
|
||||
新建对话
|
||||
</el-button>
|
||||
<el-button :icon="Setting" class="settings-btn" @click="settingsVisible = true" title="AI接入设置" />
|
||||
</div>
|
||||
|
||||
<div class="session-list" v-loading="loading">
|
||||
<div
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="session-item"
|
||||
:class="{ active: currentId === item.id }"
|
||||
@click="handleSelect(item)"
|
||||
>
|
||||
<el-icon class="session-icon"><ChatDotRound /></el-icon>
|
||||
<template v-if="editingId === item.id">
|
||||
<el-input
|
||||
v-model="editTitle"
|
||||
size="small"
|
||||
class="rename-input"
|
||||
@click.stop
|
||||
@keyup.enter="saveRename(item)"
|
||||
@keyup.esc="cancelRename"
|
||||
@blur="saveRename(item)"
|
||||
ref="renameInputRef"
|
||||
/>
|
||||
</template>
|
||||
<span v-else class="session-title" :title="item.title">{{ item.title }}</span>
|
||||
<div class="session-actions">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
class="action-btn"
|
||||
:icon="Edit"
|
||||
@click.stop="startRename(item)"
|
||||
title="重命名"
|
||||
/>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
class="action-btn"
|
||||
:icon="Delete"
|
||||
@click.stop="handleDelete(item)"
|
||||
title="删除"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!loading && list.length === 0" description="暂无对话" :image-size="60" />
|
||||
</div>
|
||||
|
||||
<ProviderDialog v-model="settingsVisible" @change="handleProviderChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ChatDotRound, Setting, Delete, Edit } from '@element-plus/icons-vue'
|
||||
import { getChatSessionList, createChatSession, deleteChatSession, updateChatSession } from '@/api/ai'
|
||||
import ProviderDialog from './providerDialog.vue'
|
||||
|
||||
const emit = defineEmits(['select', 'new', 'rename'])
|
||||
|
||||
const loading = ref(false)
|
||||
const list = ref([])
|
||||
const currentId = ref(null)
|
||||
const settingsVisible = ref(false)
|
||||
|
||||
// 重命名
|
||||
const editingId = ref(null)
|
||||
const editTitle = ref('')
|
||||
const renameInputRef = ref()
|
||||
|
||||
onMounted(() => {
|
||||
fetchList()
|
||||
})
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getChatSessionList()
|
||||
if (res.code === 200) {
|
||||
list.value = res.data.list || []
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '查询失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelect(item) {
|
||||
currentId.value = item.id
|
||||
emit('select', item)
|
||||
}
|
||||
|
||||
async function handleNewChat() {
|
||||
try {
|
||||
const res = await createChatSession({ title: '新对话' })
|
||||
if (res.code === 200) {
|
||||
const newSession = { id: res.data.id, title: res.data.title }
|
||||
list.value.unshift(newSession)
|
||||
currentId.value = newSession.id
|
||||
emit('new', newSession)
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '创建失败')
|
||||
}
|
||||
}
|
||||
|
||||
function startRename(item) {
|
||||
editingId.value = item.id
|
||||
editTitle.value = item.title
|
||||
nextTick(() => {
|
||||
if (renameInputRef.value) {
|
||||
const input = renameInputRef.value[0] || renameInputRef.value
|
||||
if (input && input.focus) input.focus()
|
||||
if (input && input.select) input.select()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function cancelRename() {
|
||||
editingId.value = null
|
||||
editTitle.value = ''
|
||||
}
|
||||
|
||||
async function saveRename(item) {
|
||||
if (editingId.value !== item.id) return
|
||||
const newTitle = editTitle.value.trim()
|
||||
if (!newTitle) {
|
||||
ElMessage.warning('名称不能为空')
|
||||
return
|
||||
}
|
||||
if (newTitle === item.title) {
|
||||
cancelRename()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await updateChatSession(item.id, { title: newTitle })
|
||||
item.title = newTitle
|
||||
ElMessage.success('重命名成功')
|
||||
// 如果是当前会话,通知父组件更新标题
|
||||
if (currentId.value === item.id) {
|
||||
emit('rename', { id: item.id, title: newTitle })
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '重命名失败')
|
||||
} finally {
|
||||
cancelRename()
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(item) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要删除对话「${item.title}」吗?`, '提示', { type: 'warning' })
|
||||
await deleteChatSession(item.id)
|
||||
ElMessage.success('删除成功')
|
||||
list.value = list.value.filter(s => s.id !== item.id)
|
||||
if (currentId.value === item.id) {
|
||||
currentId.value = null
|
||||
emit('select', null)
|
||||
}
|
||||
} catch (e) {
|
||||
if (e !== 'cancel') {
|
||||
ElMessage.error(e.message || '删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleProviderChange() {
|
||||
// 配置变更后不需要特殊处理
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
fetchList,
|
||||
setCurrent: (id) => { currentId.value = id }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chat-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: var(--el-fill-color-light);
|
||||
border-right: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
|
||||
.new-chat-btn {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-btn {
|
||||
width: 36px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.session-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.session-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 4px;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-fill-color);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.session-icon {
|
||||
flex-shrink: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.session-title {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.rename-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.session-actions {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.action-btn {
|
||||
padding: 0;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user