修改了历史问答的样式,修改了通用问答的样式

This commit is contained in:
moon 2025-10-14 09:53:18 +08:00
parent 2d4eeab6a2
commit 9fc56b7393
3 changed files with 120 additions and 38 deletions

View File

@ -18,9 +18,19 @@
</el-icon> </el-icon>
</el-badge> </el-badge>
</el-tooltip> </el-tooltip>
</div>
<!-- 导出PDF按钮 -->
<div class="export-pdf-button " @click="exportChatToPdf">
<el-tooltip content="导出为PDF" placement="left">
<el-icon :size="20">
<Document/>
</el-icon>
</el-tooltip>
</div> </div>
<ChatBox <ChatBox
ref="chatBoxRef"
v-if="isDataLoaded" v-if="isDataLoaded"
:chat-type="chatType" :chat-type="chatType"
:user-id="userId" :user-id="userId"
@ -61,7 +71,7 @@
<div v-else class="no-binding-hint"> <div v-else class="no-binding-hint">
<el-empty description="暂未绑定知识库" :image-size="80" /> <el-empty description="暂未绑定知识库" :image-size="80" />
</div> </div>
<div class="add-knowledge"> <div class="add-knowledge">
<div class="section-title">添加知识库</div> <div class="section-title">添加知识库</div>
<el-select <el-select
@ -90,7 +100,7 @@
</el-select> </el-select>
</div> </div>
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="closeKnowledgeDialog">取消</el-button> <el-button @click="closeKnowledgeDialog">取消</el-button>
@ -110,16 +120,33 @@ import { useAclStore } from '@/store/modules/acl'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { getRecommendations } from '@/api/prologue' import { getRecommendations } from '@/api/prologue'
import { FullScreen, Close, Collection } from '@element-plus/icons-vue' import {FullScreen, Close, Collection, Download, Document} from '@element-plus/icons-vue'
import { import {
getWorkflowAndDatasetTableData, getWorkflowAndDatasetTableData,
getAllUserDatasets, getAllUserDatasets,
addDatasetsToWorkflow, addDatasetsToWorkflow,
removeDatasetsFromWorkflow removeDatasetsFromWorkflow
} from '@/api/functionKnowledgeConfig' } from '@/api/functionKnowledgeConfig'
import { exportElementToPdf } from '@/utils/exportPdf.ts'
const { t, locale } = useI18n() const { t, locale } = useI18n()
const aclStore = useAclStore() const aclStore = useAclStore()
const chatBoxRef = ref<InstanceType<typeof ChatBox>|null>(null)
const exportChatToPdf = async () => {
if (chatBoxRef.value) {
if (chatBoxRef.value.messagesQuantity === 0) {
ElMessage.error('当前没有消息可导出,请开始聊天吧')
return
}
await exportElementToPdf(chatBoxRef.value.messagesContainer, 'chat-history.pdf')
ElMessage.success('导出成功')
}
else {
ElMessage.error('导出失败')
}
}
// //
const placeholder = ref('给AI助手发送消息') const placeholder = ref('给AI助手发送消息')
@ -154,10 +181,10 @@ const fetchAllKnowledgeBases = async () => {
try { try {
isLoadingKnowledgeBases.value = true isLoadingKnowledgeBases.value = true
const response: any = await getAllUserDatasets() const response: any = await getAllUserDatasets()
if (response && response.code === 200 && response.data) { if (response && response.code === 200 && response.data) {
const userDatasets = response.data const userDatasets = response.data
// //
const knowledgeBasesMap = new Map() const knowledgeBasesMap = new Map()
userDatasets.forEach((item: any) => { userDatasets.forEach((item: any) => {
@ -186,17 +213,17 @@ const fetchAllKnowledgeBases = async () => {
const fetchBoundKnowledgeBases = async () => { const fetchBoundKnowledgeBases = async () => {
try { try {
const response: any = await getWorkflowAndDatasetTableData() const response: any = await getWorkflowAndDatasetTableData()
if (response && response.code === 200 && response.data) { if (response && response.code === 200 && response.data) {
// APP_ID // APP_ID
const currentApp = response.data.find((item: any) => item.appId === APP_ID) const currentApp = response.data.find((item: any) => item.appId === APP_ID)
if (currentApp && currentApp.datasetIds && currentApp.datasetIds.length > 0) { if (currentApp && currentApp.datasetIds && currentApp.datasetIds.length > 0) {
boundKnowledgeBases.value = [] boundKnowledgeBases.value = []
for (let i = 0; i < currentApp.datasetIds.length; i++) { for (let i = 0; i < currentApp.datasetIds.length; i++) {
const datasetId = currentApp.datasetIds[i] const datasetId = currentApp.datasetIds[i]
const datasetName = currentApp.datasetNames[i] || datasetId const datasetName = currentApp.datasetNames[i] || datasetId
boundKnowledgeBases.value.push({ boundKnowledgeBases.value.push({
id: datasetId, id: datasetId,
name: datasetName name: datasetName
@ -217,7 +244,7 @@ const fetchBoundKnowledgeBases = async () => {
const openKnowledgeDialog = async () => { const openKnowledgeDialog = async () => {
knowledgeDialogVisible.value = true knowledgeDialogVisible.value = true
selectedKnowledgeBaseIds.value = [] selectedKnowledgeBaseIds.value = []
// //
if (allKnowledgeBases.value.length === 0) { if (allKnowledgeBases.value.length === 0) {
await fetchAllKnowledgeBases() await fetchAllKnowledgeBases()
@ -236,16 +263,16 @@ const confirmBindKnowledgeBases = async () => {
ElMessage.warning('请选择要绑定的知识库') ElMessage.warning('请选择要绑定的知识库')
return return
} }
try { try {
isBinding.value = true isBinding.value = true
// API // API
const response: any = await addDatasetsToWorkflow({ const response: any = await addDatasetsToWorkflow({
appId: APP_ID, appId: APP_ID,
datasetIds: selectedKnowledgeBaseIds.value datasetIds: selectedKnowledgeBaseIds.value
}) })
if (response.code === 200) { if (response.code === 200) {
// //
selectedKnowledgeBaseIds.value.forEach(id => { selectedKnowledgeBaseIds.value.forEach(id => {
@ -254,7 +281,7 @@ const confirmBindKnowledgeBases = async () => {
boundKnowledgeBases.value.push(kb) boundKnowledgeBases.value.push(kb)
} }
}) })
ElMessage.success(`成功绑定 ${selectedKnowledgeBaseIds.value.length} 个知识库`) ElMessage.success(`成功绑定 ${selectedKnowledgeBaseIds.value.length} 个知识库`)
selectedKnowledgeBaseIds.value = [] selectedKnowledgeBaseIds.value = []
closeKnowledgeDialog() closeKnowledgeDialog()
@ -277,7 +304,7 @@ const removeKnowledgeBase = async (knowledgeBaseId: string) => {
appId: APP_ID, appId: APP_ID,
datasetIds: [knowledgeBaseId] datasetIds: [knowledgeBaseId]
}) })
if (response.code === 200) { if (response.code === 200) {
// //
const index = boundKnowledgeBases.value.findIndex(kb => kb.id === knowledgeBaseId) const index = boundKnowledgeBases.value.findIndex(kb => kb.id === knowledgeBaseId)
@ -348,7 +375,7 @@ const handleChatSwitched = (chatIndex: number) => {
// //
const toggleFullscreen = () => { const toggleFullscreen = () => {
isFullscreen.value = !isFullscreen.value isFullscreen.value = !isFullscreen.value
// //
const layoutWrapper = document.querySelector('.vue-admin-better-wrapper') as HTMLElement const layoutWrapper = document.querySelector('.vue-admin-better-wrapper') as HTMLElement
if (layoutWrapper) { if (layoutWrapper) {
@ -375,13 +402,13 @@ const handleEscKey = (event: KeyboardEvent) => {
onMounted(async () => { onMounted(async () => {
console.log('组件已挂载,开始加载数据...') console.log('组件已挂载,开始加载数据...')
// await fetchRecommendations() // await fetchRecommendations()
// //
await fetchBoundKnowledgeBases() await fetchBoundKnowledgeBases()
isDataLoaded.value = true isDataLoaded.value = true
console.log('数据加载完成') console.log('数据加载完成')
// //
window.addEventListener('keydown', handleEscKey) window.addEventListener('keydown', handleEscKey)
}) })
@ -389,7 +416,7 @@ onMounted(async () => {
// //
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('keydown', handleEscKey) window.removeEventListener('keydown', handleEscKey)
// 退 // 退
if (isFullscreen.value) { if (isFullscreen.value) {
const layoutWrapper = document.querySelector('.vue-admin-better-wrapper') as HTMLElement const layoutWrapper = document.querySelector('.vue-admin-better-wrapper') as HTMLElement
@ -587,4 +614,40 @@ onUnmounted(() => {
.main-container.fullscreen-mode ~ .v-modal { .main-container.fullscreen-mode ~ .v-modal {
z-index: 19999 !important; z-index: 19999 !important;
} }
.export-pdf-button {
position: absolute;
top: 16px;
right: 110px;
z-index: 100;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color);
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.export-pdf-button:hover {
background: var(--el-color-primary);
color: white;
border-color: var(--el-color-primary);
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.export-pdf-button :deep(.el-icon) {
transition: color 0.3s ease;
}
.export-pdf-button:hover :deep(.el-icon) {
color: white;
}
.export-pdf-button :deep(.el-badge__content) {
border: none;
}
</style> </style>

View File

@ -289,7 +289,7 @@
<style scoped> <style scoped>
.conversation-detail { .conversation-detail {
max-width: 800px; max-width: 1000px;
margin: 0 auto; margin: 0 auto;
padding: 0; padding: 0;
background: #fff; background: #fff;
@ -547,23 +547,42 @@
font-style: italic; font-style: italic;
} }
/* 滚动条样式 */ /* 表格样式 */
.timeline-container::-webkit-scrollbar { .message-content :deep(table) {
width: 6px; width: 100%;
border-collapse: collapse;
margin: 16px 0;
display: block;
overflow-x: auto;
white-space: nowrap;
} }
.timeline-container::-webkit-scrollbar-track { .message-content :deep(th),
background: #f1f1f1; .message-content :deep(td) {
border-radius: 3px; padding: 8px 12px;
border: 1px solid #eaecef;
text-align: left;
} }
.timeline-container::-webkit-scrollbar-thumb { .message-content :deep(th) {
background: #c1c1c1; background-color: #f6f8fa;
border-radius: 3px; font-weight: 600;
} }
.timeline-container::-webkit-scrollbar-thumb:hover { .message-content :deep(tr:nth-child(even)) {
background: #a8a8a8; background-color: #fafbfc;
}
/* 超过3列的表格特殊处理 */
.message-content :deep(table) {
max-width: 100%;
table-layout: fixed;
}
.message-content :deep(th),
.message-content :deep(td) {
overflow: hidden;
text-overflow: ellipsis;
} }
.export-btn { .export-btn {

View File

@ -463,6 +463,6 @@
.rounded-devlog { .rounded-devlog {
border-radius: 8px; /* 调整圆角大小 */ border-radius: 8px; /* 调整圆角大小 */
overflow: hidden; /* 确保内部内容也被圆角裁剪 */ overflow: hidden; /* 确保内部内容也被圆角裁剪 */
width: 46%; width:52%;
} }
</style> </style>