refactor(chat): 重构PC端聊天界面布局结构

- 替换原有的flex布局为新的LayoutContainer和ContentContainer组件
- 添加顶部应用标题栏,包含ChinaMobileIcon和AI-RAG标题
- 移除原有的左侧折叠按钮,整合到新的布局容器中
- 调整右侧聊天区域样式,优化背景图片显示方式
- 更新CSS样式以适配新布局结构和高度计算
- 导入新的布局组件并调整现有组件的嵌套关系
v3.2
tanlianwang 2026-03-09 10:14:13 +08:00
parent d5d1e71bbb
commit c63485d53f
1 changed files with 90 additions and 109 deletions

View File

@ -19,8 +19,17 @@
), ),
}" }"
> >
<div class="flex h-full w-full"> <div class="app-top-bar-container border-b flex-center">
<div class="chat-pc__left"> <div class="flex-between w-full align-center" style="padding: 0 16px;">
<div style="display: flex; align-items: center;">
<ChinaMobileIcon />
<h1 style="font-size: 18px; font-weight: 600; margin: 0;">AI-RAG</h1>
</div>
</div>
</div>
<LayoutContainer showCollapse resizable class="application-manage">
<template #left>
<h4 class="p-12-16 pb-0 mt-12">{{ $t('chat.title') }}</h4>
<HistoryPanel <HistoryPanel
:application-detail="applicationDetail" :application-detail="applicationDetail"
:chat-log-data="chatLogData" :chat-log-data="chatLogData"
@ -91,26 +100,10 @@
</el-dropdown> </el-dropdown>
</div> </div>
</HistoryPanel> </HistoryPanel>
<el-button </template>
v-if="!common.isMobile()" <ContentContainer>
class="pc-collapse cursor" <template #header>
circle <div class="flex-between w-full">
@click="isPcCollapse = !isPcCollapse"
>
<el-icon>
<component :is="isPcCollapse ? 'ArrowRightBold' : 'ArrowLeftBold'" />
</el-icon>
</el-button>
</div>
<div
class="chat-pc__right chat-background"
:style="{
backgroundImage: `url(${applicationDetail?.chat_background})`,
'--execution-detail-panel-width': rightPanelSize + 'px',
}"
>
<div style="flex: 1; width: calc(100% - var(--execution-detail-panel-width))">
<div class="p-16-24 flex-between">
<h4 class="ellipsis-1" style="width: 66%"> <h4 class="ellipsis-1" style="width: 66%">
{{ currentChatName }} {{ currentChatName }}
</h4> </h4>
@ -147,70 +140,78 @@
</el-dropdown> </el-dropdown>
</span> </span>
</div> </div>
<div class="right-height chat-width"> </template>
<AiChat <div
ref="AiChatRef" class="chat-background"
v-model:applicationDetails="applicationDetail" :style="{
:available="applicationAvailable" backgroundImage: `url(${applicationDetail?.chat_background})`,
type="ai-chat" '--execution-detail-panel-width': rightPanelSize + 'px',
:appId="applicationDetail?.id" }"
:record="currentRecordList" style="display: flex; height: 100%;"
:chatId="currentChatId" >
executionIsRightPanel <div style="flex: 1; width: calc(100% - var(--execution-detail-panel-width))">
@refresh="refresh" <div class="right-height chat-width">
@scroll="handleScroll" <AiChat
@open-execution-detail="openExecutionDetail" ref="AiChatRef"
@openParagraph="openKnowledgeSource" v-model:applicationDetails="applicationDetail"
@openParagraphDocument="openParagraphDocument" :available="applicationAvailable"
> type="ai-chat"
</AiChat> :appId="applicationDetail?.id"
</div> :record="currentRecordList"
</div> :chatId="currentChatId"
<div class="execution-detail-panel" :resizable="false" collapsible> executionIsRightPanel
<div class="p-16 flex-between border-b"> @refresh="refresh"
<h4 class="medium ellipsis" :title="rightPanelTitle">{{ rightPanelTitle }}</h4> @scroll="handleScroll"
@open-execution-detail="openExecutionDetail"
<div class="flex align-center"> @openParagraph="openKnowledgeSource"
<span v-if="rightPanelType === 'paragraphDocument'" class="mr-4"> @openParagraphDocument="openParagraphDocument"
<a >
:href=" </AiChat>
getFileUrl(rightPanelDetail?.meta?.source_file_id) ||
rightPanelDetail?.meta?.source_url
"
target="_blank"
class="ellipsis-1"
:title="rightPanelDetail?.document_name?.trim()"
>
<el-button text>
<AppIcon iconName="app-pdf-export" class="cursor"></AppIcon>
</el-button>
</a>
</span>
<!-- <span v-if="rightPanelType === 'paragraphDocument'">
<el-button text> <app-icon iconName="app-export" size="20" /></el-button>
</span> -->
<span>
<el-button text @click="closeExecutionDetail">
<el-icon size="20"><Close /></el-icon
></el-button>
</span>
</div> </div>
</div> </div>
<div class="execution-detail-content" v-loading="rightPanelLoading"> <div class="execution-detail-panel" :resizable="false" collapsible>
<ParagraphSourceContent <div class="p-16 flex-between border-b">
v-if="rightPanelType === 'knowledgeSource'" <h4 class="medium ellipsis" :title="rightPanelTitle">{{ rightPanelTitle }}</h4>
:detail="rightPanelDetail"
/> <div class="flex align-center">
<ExecutionDetailContent <span v-if="rightPanelType === 'paragraphDocument'" class="mr-4">
v-if="rightPanelType === 'executionDetail'" <a
:detail="executionDetail" :href="
:appType="applicationDetail?.type" getFileUrl(rightPanelDetail?.meta?.source_file_id) ||
/> rightPanelDetail?.meta?.source_url
<ParagraphDocumentContent :detail="rightPanelDetail" v-else /> "
target="_blank"
class="ellipsis-1"
:title="rightPanelDetail?.document_name?.trim()"
>
<el-button text>
<AppIcon iconName="app-pdf-export" class="cursor"></AppIcon>
</el-button>
</a>
</span>
<span>
<el-button text @click="closeExecutionDetail">
<el-icon size="20"><Close /></el-icon>
</el-button>
</span>
</div>
</div>
<div class="execution-detail-content" v-loading="rightPanelLoading">
<ParagraphSourceContent
v-if="rightPanelType === 'knowledgeSource'"
:detail="rightPanelDetail"
/>
<ExecutionDetailContent
v-if="rightPanelType === 'executionDetail'"
:detail="executionDetail"
:appType="applicationDetail?.type"
/>
<ParagraphDocumentContent :detail="rightPanelDetail" v-else />
</div>
</div> </div>
</div> </div>
</div> </ContentContainer>
</div> </LayoutContainer>
<ResetPassword <ResetPassword
ref="resetPasswordRef" ref="resetPasswordRef"
@ -240,6 +241,9 @@ import HistoryPanel from '@/views/chat/component/HistoryPanel.vue'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { getFileUrl } from '@/utils/common' import { getFileUrl } from '@/utils/common'
import PdfExport from '@/components/pdf-export/index.vue' import PdfExport from '@/components/pdf-export/index.vue'
import ChinaMobileIcon from '@/components/china-mobile-icon/index.vue'
import LayoutContainer from '@/components/layout-container/index.vue'
import ContentContainer from '@/components/layout-container/ContentContainer.vue'
useResize() useResize()
const pdfExportRef = ref<InstanceType<typeof PdfExport>>() const pdfExportRef = ref<InstanceType<typeof PdfExport>>()
@ -534,30 +538,12 @@ function closeExecutionDetail() {
overflow: hidden; overflow: hidden;
background: #eef1f4; background: #eef1f4;
&__left { .chat-background {
position: relative;
z-index: 1;
.pc-collapse {
position: absolute;
top: 20px;
right: -13px;
box-shadow: 0px 5px 10px 0px var(--app-text-color-light-1);
z-index: 1;
width: 24px;
height: 24px;
}
}
&__right {
flex: 1;
overflow: hidden;
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
display: flex;
.right-height { .right-height {
height: calc(100vh - 60px); height: calc(100vh - var(--app-header-height) - 120px);
} }
:deep(.execution-detail-panel) { :deep(.execution-detail-panel) {
@ -584,13 +570,8 @@ function closeExecutionDetail() {
margin: 0 auto; margin: 0 auto;
} }
.chat-pc__right { .execution-detail-panel {
width: calc(100vw - 280px); width: var(--execution-detail-panel-width, 400px);
--execution-detail-panel-width: 400px;
.execution-detail-panel {
width: var(--execution-detail-panel-width, 400px);
}
} }
@media only screen and (max-width: 1000px) { @media only screen and (max-width: 1000px) {