fix: 前端页面架构重构优化
This commit is contained in:
@@ -124,141 +124,7 @@ function handleMenuCommand(command: string, item: ConversationItem<ChatSessionVo
|
|||||||
|
|
||||||
// 折叠/展开侧边栏
|
// 折叠/展开侧边栏
|
||||||
function toggleSidebar() {
|
function toggleSidebar() {
|
||||||
// designStore.setIsCollapseConversationList(!designStore.isCollapseConversationList);
|
designStore.setIsCollapseConversationList(!designStore.isCollapseConversationList);
|
||||||
}
|
|
||||||
|
|
||||||
// 点击logo创建新会话(仅在折叠状态)
|
|
||||||
function handleLogoClick() {
|
|
||||||
if (isCollapsed.value) {
|
|
||||||
handleCreatChat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理右键菜单(折叠状态下使用)
|
|
||||||
function handleContextMenu(event: MouseEvent, item: ConversationItem<ChatSessionVo>) {
|
|
||||||
event.preventDefault();
|
|
||||||
// 在折叠状态下触发删除确认
|
|
||||||
ElMessageBox.confirm('删除后,聊天记录将不可恢复。', '确定删除对话?', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
confirmButtonClass: 'el-button--danger',
|
|
||||||
cancelButtonClass: 'el-button--info',
|
|
||||||
roundButton: true,
|
|
||||||
autofocus: false,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
sessionStore.deleteSessions([item.id!]);
|
|
||||||
nextTick(() => {
|
|
||||||
if (item.id === active.value) {
|
|
||||||
sessionStore.createSessionBtn();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// 取消删除
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 折叠状态下点击更多按钮
|
|
||||||
function handleCollapsedMenuClick(event: MouseEvent, item: ConversationItem<ChatSessionVo>) {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
// 创建一个简单的菜单
|
|
||||||
ElMessageBox({
|
|
||||||
title: '对话操作',
|
|
||||||
message: `
|
|
||||||
<div style="padding: 8px 0;">
|
|
||||||
<div class="menu-item" data-action="rename" style="padding: 8px 12px; cursor: pointer; border-radius: 4px; margin: 4px 0;">
|
|
||||||
<span style="font-size: 14px;">重命名</span>
|
|
||||||
</div>
|
|
||||||
<div class="menu-item" data-action="delete" style="padding: 8px 12px; cursor: pointer; border-radius: 4px; margin: 4px 0; color: #f56c6c;">
|
|
||||||
<span style="font-size: 14px;">删除对话</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
showConfirmButton: false,
|
|
||||||
showCancelButton: false,
|
|
||||||
dangerouslyUseHTMLString: true,
|
|
||||||
customClass: 'collapsed-menu-dialog',
|
|
||||||
closeOnClickModal: true,
|
|
||||||
closeOnPressEscape: true,
|
|
||||||
}).then(() => {
|
|
||||||
// 对话框关闭
|
|
||||||
}).catch(() => {
|
|
||||||
// 对话框关闭
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加菜单项点击事件
|
|
||||||
nextTick(() => {
|
|
||||||
const menuItems = document.querySelectorAll('.menu-item');
|
|
||||||
menuItems.forEach((itemEl) => {
|
|
||||||
itemEl.addEventListener('click', (e) => {
|
|
||||||
const action = (e.currentTarget as HTMLElement).dataset.action;
|
|
||||||
if (action === 'delete') {
|
|
||||||
ElMessageBox.confirm('删除后,聊天记录将不可恢复。', '确定删除对话?', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
confirmButtonClass: 'el-button--danger',
|
|
||||||
cancelButtonClass: 'el-button--info',
|
|
||||||
roundButton: true,
|
|
||||||
autofocus: false,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
sessionStore.deleteSessions([item.id!]);
|
|
||||||
nextTick(() => {
|
|
||||||
if (item.id === active.value) {
|
|
||||||
sessionStore.createSessionBtn();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// 取消删除
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (action === 'rename') {
|
|
||||||
ElMessageBox.prompt('', '编辑对话名称', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
inputErrorMessage: '请输入对话名称',
|
|
||||||
confirmButtonClass: 'el-button--primary',
|
|
||||||
cancelButtonClass: 'el-button--info',
|
|
||||||
roundButton: true,
|
|
||||||
inputValue: item.sessionTitle,
|
|
||||||
autofocus: false,
|
|
||||||
inputValidator: (value) => {
|
|
||||||
return !!value;
|
|
||||||
},
|
|
||||||
}).then(({ value }) => {
|
|
||||||
sessionStore
|
|
||||||
.updateSession({
|
|
||||||
id: item.id!,
|
|
||||||
sessionTitle: value,
|
|
||||||
sessionContent: item.sessionContent,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
ElMessage({
|
|
||||||
type: 'success',
|
|
||||||
message: '修改成功',
|
|
||||||
});
|
|
||||||
nextTick(() => {
|
|
||||||
if (sessionStore.currentSession?.id === item.id) {
|
|
||||||
sessionStore.setCurrentSession({
|
|
||||||
...item,
|
|
||||||
sessionTitle: value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭菜单对话框
|
|
||||||
document.querySelector('.collapsed-menu-dialog .el-message-box__headerbtn')?.dispatchEvent(new Event('click'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -446,7 +312,7 @@ function handleCollapsedMenuClick(event: MouseEvent, item: ConversationItem<Chat
|
|||||||
|
|
||||||
// 折叠状态 - 100px
|
// 折叠状态 - 100px
|
||||||
&.aside-collapsed {
|
&.aside-collapsed {
|
||||||
width: 100px;
|
display: none;
|
||||||
|
|
||||||
.aside-wrapper {
|
.aside-wrapper {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
<!-- 侧边栏折叠按钮 -->
|
<!-- 侧边栏折叠按钮 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
import { SIDE_BAR_WIDTH } from '@/config/index';
|
|
||||||
import { useCollapseToggle } from '@/hooks/useCollapseToggle';
|
|
||||||
import { useDesignStore } from '@/stores';
|
import { useDesignStore } from '@/stores';
|
||||||
|
|
||||||
const { changeCollapse } = useCollapseToggle();
|
// const { changeCollapse } = useCollapseToggle();
|
||||||
const designStore = useDesignStore();
|
const designStore = useDesignStore();
|
||||||
|
|
||||||
function handleChangeCollapse() {
|
function handleChangeCollapse() {
|
||||||
changeCollapse();
|
designStore.setIsCollapseConversationList(!designStore.isCollapseConversationList);
|
||||||
// 每次切换折叠状态,重置安全区状态
|
// 待定
|
||||||
designStore.isSafeAreaHover = false;
|
// changeCollapse();
|
||||||
// 重置首次激活悬停状态
|
// // 每次切换折叠状态,重置安全区状态
|
||||||
designStore.hasActivatedHover = false;
|
// designStore.isSafeAreaHover = false;
|
||||||
if (!designStore.isCollapse) {
|
// // 重置首次激活悬停状态
|
||||||
document.documentElement.style.setProperty(
|
// designStore.hasActivatedHover = false;
|
||||||
`--sidebar-left-container-default-width`,
|
// if (!designStore.isCollapse) {
|
||||||
`${SIDE_BAR_WIDTH}px`,
|
// document.documentElement.style.setProperty(
|
||||||
);
|
// `--sidebar-left-container-default-width`,
|
||||||
}
|
// `${SIDE_BAR_WIDTH}px`,
|
||||||
else {
|
// );
|
||||||
document.documentElement.style.setProperty(`--sidebar-left-container-default-width`, ``);
|
// }
|
||||||
}
|
// else {
|
||||||
|
// document.documentElement.style.setProperty(`--sidebar-left-container-default-width`, ``);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="collapse-container btn-icon-btn" @click="handleChangeCollapse">
|
<div class="collapse-container btn-icon-btn" @click="handleChangeCollapse">
|
||||||
<SvgIcon v-if="!designStore.isCollapse" name="ms-left-panel-close-outline" size="24" />
|
<SvgIcon v-if="!designStore.isCollapseConversationList" name="ms-left-panel-close-outline" size="24" />
|
||||||
<SvgIcon v-if="designStore.isCollapse" name="ms-left-panel-open-outline" size="24" />
|
<SvgIcon v-if="designStore.isCollapseConversationList" name="ms-left-panel-open-outline" size="24" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<!-- 添加新会话按钮 -->
|
<!-- 添加新会话按钮 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { Plus } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import { useSessionStore } from '@/stores/modules/session';
|
import { useSessionStore } from '@/stores/modules/session';
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<!-- 默认消息列表页 -->
|
<!-- 默认消息列表页 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
|
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
|
||||||
import { Loading } from '@element-plus/icons-vue';
|
import { ArrowLeftBold, ArrowRightBold, Loading } from '@element-plus/icons-vue';
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { nextTick, ref, watch } from 'vue';
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
import ModelSelect from '@/components/ModelSelect/index.vue';
|
import ModelSelect from '@/components/ModelSelect/index.vue';
|
||||||
import WelecomeText from '@/components/WelecomeText/index.vue';
|
import WelecomeText from '@/components/WelecomeText/index.vue';
|
||||||
|
import Collapse from '@/layouts/components0/Header/components/Collapse.vue';
|
||||||
|
import CreateChat from '@/layouts/components0/Header/components/CreateChat.vue';
|
||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
import { useFilesStore } from '@/stores/modules/files';
|
import { useFilesStore } from '@/stores/modules/files';
|
||||||
import { useSessionStore } from '@/stores/modules/session';
|
import { useSessionStore } from '@/stores/modules/session';
|
||||||
@@ -16,6 +18,9 @@ const userStore = useUserStore();
|
|||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
const filesStore = useFilesStore();
|
const filesStore = useFilesStore();
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const currentSession = computed(() => sessionStore.currentSession);
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const senderValue = ref(''); // 输入框内容
|
const senderValue = ref(''); // 输入框内容
|
||||||
const senderRef = ref(); // Sender 组件引用
|
const senderRef = ref(); // Sender 组件引用
|
||||||
@@ -106,6 +111,16 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="chat-default">
|
<div class="chat-default">
|
||||||
|
<!-- 头部导航栏 -->
|
||||||
|
<div class="chat-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-left">
|
||||||
|
<Collapse />
|
||||||
|
<CreateChat />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="chat-default-wrap">
|
<div class="chat-default-wrap">
|
||||||
<!-- 欢迎文本 -->
|
<!-- 欢迎文本 -->
|
||||||
<WelecomeText />
|
<WelecomeText />
|
||||||
@@ -185,16 +200,35 @@ watch(
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.chat-default {
|
.chat-default {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
//background: #ff11f3;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
padding: 0 20px;
|
||||||
//padding: 0 0 100px;
|
|
||||||
|
.chat-header {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-default-wrap {
|
.chat-default-wrap {
|
||||||
//background: #0bdcb7;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -276,6 +310,14 @@ watch(
|
|||||||
|
|
||||||
// 响应式设计
|
// 响应式设计
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
.chat-default {
|
||||||
|
padding: 0 12px;
|
||||||
|
|
||||||
|
.chat-header {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chat-default-wrap {
|
.chat-default-wrap {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
min-height: calc(100vh - 120px);
|
min-height: calc(100vh - 120px);
|
||||||
|
|||||||
@@ -13,16 +13,26 @@ import { Sender } from 'vue-element-plus-x';
|
|||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { send } from '@/api';
|
import { send } from '@/api';
|
||||||
import ModelSelect from '@/components/ModelSelect/index.vue';
|
import ModelSelect from '@/components/ModelSelect/index.vue';
|
||||||
import { useGuideTourStore } from '@/stores';
|
import Collapse from '@/layouts/components0/Header/components/Collapse.vue';
|
||||||
|
|
||||||
|
import CreateChat from '@/layouts/components0/Header/components/CreateChat.vue';
|
||||||
|
import TitleEditing from '@/layouts/components0/Header/components/TitleEditing.vue';
|
||||||
|
import { useDesignStore, useGuideTourStore } from '@/stores';
|
||||||
import { useChatStore } from '@/stores/modules/chat';
|
import { useChatStore } from '@/stores/modules/chat';
|
||||||
import { useFilesStore } from '@/stores/modules/files';
|
import { useFilesStore } from '@/stores/modules/files';
|
||||||
import { useModelStore } from '@/stores/modules/model';
|
import { useModelStore } from '@/stores/modules/model';
|
||||||
|
import { useSessionStore } from '@/stores/modules/session';
|
||||||
import { useUserStore } from '@/stores/modules/user';
|
import { useUserStore } from '@/stores/modules/user';
|
||||||
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
|
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
|
||||||
import YMarkdown from '@/vue-element-plus-y/components/XMarkdown/index.vue';
|
import YMarkdown from '@/vue-element-plus-y/components/XMarkdown/index.vue';
|
||||||
import '@/styles/github-markdown.css';
|
import '@/styles/github-markdown.css';
|
||||||
import '@/styles/yixin-markdown.scss';
|
import '@/styles/yixin-markdown.scss';
|
||||||
|
|
||||||
|
// 新增的导入
|
||||||
|
const designStore = useDesignStore();
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
const currentSession = computed(() => sessionStore.currentSession);
|
||||||
|
|
||||||
type MessageItem = BubbleProps & {
|
type MessageItem = BubbleProps & {
|
||||||
key: number;
|
key: number;
|
||||||
role: 'ai' | 'user' | 'assistant';
|
role: 'ai' | 'user' | 'assistant';
|
||||||
@@ -447,6 +457,31 @@ function handleImagePreview(url: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="chat-with-id-container">
|
<div class="chat-with-id-container">
|
||||||
|
<!-- 头部导航栏 -->
|
||||||
|
<div class="chat-header">
|
||||||
|
<div
|
||||||
|
class="overflow-hidden flex h-full items-center flex-row flex-1 w-fit flex-shrink-0 min-w-0"
|
||||||
|
>
|
||||||
|
<div class="w-full flex items-center flex-row">
|
||||||
|
<!-- 左边 -->
|
||||||
|
<div
|
||||||
|
|
||||||
|
class="left-box flex h-full items-center pl-20px gap-12px flex-shrink-0 flex-row"
|
||||||
|
>
|
||||||
|
<Collapse />
|
||||||
|
<CreateChat />
|
||||||
|
<div v-if="currentSession" class="w-0.5px h-30px bg-[rgba(217,217,217)]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中间 -->
|
||||||
|
<div class="middle-box flex-1 min-w-0 ml-12px">
|
||||||
|
<TitleEditing />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 聊天内容区域 -->
|
||||||
<div class="chat-warp">
|
<div class="chat-warp">
|
||||||
<BubbleList ref="bubbleListRef" :list="bubbleItems" max-height="calc(100vh - 240px)">
|
<BubbleList ref="bubbleListRef" :list="bubbleItems" max-height="calc(100vh - 240px)">
|
||||||
<template #header="{ item }">
|
<template #header="{ item }">
|
||||||
@@ -569,14 +604,23 @@ function handleImagePreview(url: string) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
.chat-header {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-warp {
|
.chat-warp {
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100vh - 60px);
|
height: calc(100vh - 120px); // 减去头部高度
|
||||||
.thinking-chain-warp {
|
.thinking-chain-warp {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
@@ -674,12 +718,12 @@ function handleImagePreview(url: string) {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
.footer-token {
|
.footer-token {
|
||||||
background: rgba(1, 183, 86, 0.53);
|
background: rgba(1, 183, 86, 0.53);
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const useSessionStore = defineStore('session', () => {
|
|||||||
try {
|
try {
|
||||||
// 清空当前选中会话信息
|
// 清空当前选中会话信息
|
||||||
setCurrentSession(null);
|
setCurrentSession(null);
|
||||||
router.replace({ name: 'chatConversationWithId' });
|
await router.replace({ name: 'chatConversation' });
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('createSessionBtn错误:', error);
|
console.error('createSessionBtn错误:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user