<template>
|
<DialogAi title="" v-model="dialogVisible" width="1200" custom-class="transparent-dialog">
|
<!-- 搜索工作栏 -->
|
<el-form
|
class="-mb-15px query-area"
|
:model="queryParams"
|
ref="queryFormRef"
|
:inline="true"
|
label-width="68px"
|
>
|
<el-form-item label="对话时间" prop="createTime">
|
<el-date-picker
|
v-model="queryParams.createTime"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
type="datetimerange"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
class="!w-360px transparent-date-picker-popper"
|
/>
|
</el-form-item>
|
<el-form-item>
|
<el-button @click="handleQuery">
|
<Icon icon="ep:search" class="mr-5px"/>
|
搜索
|
</el-button>
|
<el-button @click="resetQuery">
|
<Icon icon="ep:refresh" class="mr-5px"/>
|
重置
|
</el-button>
|
</el-form-item>
|
</el-form>
|
<!-- 对话详情 -->
|
<el-container class="detail-container">
|
<el-header class="header">
|
<div class="title">
|
{{ activeConversation?.title ? activeConversation?.title : '' }}
|
<span v-if="total">({{ total }})</span>
|
</div>
|
<div class="btns" v-if="activeConversation">
|
<el-button size="small" class="btn" @click="handlerMessageClear">
|
<Icon icon="heroicons-outline:archive-box-x-mark" color="#73C4FF" />
|
</el-button>
|
<el-button size="small" class="btn" @click="handleGoBottomMessage">
|
<Icon icon="ep:download" color="#73C4FF" />
|
</el-button>
|
<el-button size="small" class="btn" @click="handleGoTopMessage">
|
<Icon icon="ep:top" color="#73C4FF" />
|
</el-button>
|
</div>
|
</el-header>
|
|
<!-- main:消息列表 -->
|
<el-main class="main-container">
|
<div class="message-container">
|
<!-- 情况一:无聊天对话时 -->
|
<ConversationListEmpty
|
v-if="!activeConversation"
|
/>
|
<!-- 情况二:消息列表为空 -->
|
<MessageListEmpty
|
v-if="activeMessageList.length === 0 && activeConversation"
|
/>
|
<!-- 情况三:消息列表不为空 -->
|
<HistoryMessageList
|
v-if="activeMessageList.length > 0"
|
ref="messageRef"
|
:conversation="activeConversation"
|
:list="activeMessageList"
|
:gotoManualMethod="gotoManual"
|
/>
|
</div>
|
</el-main>
|
</el-container>
|
<!-- 分页 -->
|
<Pagination
|
:total="total"
|
v-model:page="queryParams.pageNo"
|
v-model:limit="queryParams.pageSize"
|
@pagination="handleQuery"
|
/>
|
</DialogAi>
|
</template>
|
|
<script setup lang="ts">
|
import {ChatMessageApi, ChatMessageVO} from '@/api/ai/chat/message'
|
import { ChatConversationVO } from '@/api/ai/chat/conversation'
|
import HistoryMessageList from './HistoryMessageList.vue'
|
import MessageListEmpty from './MessageListEmpty.vue'
|
import ConversationListEmpty from '../conversation/ConversationListEmpty.vue'
|
import {formatDate} from "@vueuse/core";
|
import {ref} from "vue";
|
|
/** AI 聊天对话 列表 */
|
defineOptions({ name: 'HistoryMessageDialog' })
|
|
// 接收父组件传递的方法
|
const props = defineProps({
|
parentMethod: Function,
|
gotoManualMethod: Function
|
});
|
|
// 定义发射事件
|
const emit = defineEmits(['gotoManualMethod'])
|
|
const message = useMessage() // 消息弹窗
|
const total = ref(0) // 历史建议列表
|
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
const queryFormRef = ref() // 搜索的表单
|
const queryParams = reactive({
|
pageNo: 1,
|
pageSize: 10,
|
createTime: [],
|
})
|
|
// 聊天对话
|
const activeConversation = ref<ChatConversationVO | null>(null) // 选中的 Conversation
|
|
// 消息列表
|
const messageRef = ref()
|
const activeMessageList = ref<ChatMessageVO[]>([]) // 选中对话的消息列表
|
|
|
/** 打开弹窗 */
|
const open = async (messages: ChatMessageVO[], conversation: ChatConversationVO, activeHistoryMessageTotal: number) => {
|
dialogVisible.value = true
|
total.value = activeHistoryMessageTotal
|
await nextTick() // 等待弹窗DOM挂载
|
activeMessageList.value = messages
|
activeConversation.value = conversation
|
}
|
|
/** 处理查询时间段 */
|
const dealDate = async () => {
|
const currentDate = new Date();
|
const previousDate = new Date(currentDate.getTime() - 2 * 60 * 60 * 1000);
|
queryParams.createTime[0] = formatDate(previousDate, 'YYYY-MM-DD HH:mm:ss');
|
queryParams.createTime[1] = formatDate(currentDate, 'YYYY-MM-DD HH:mm:ss');
|
return queryParams;
|
}
|
|
defineExpose({ open, dealDate }) // 提供方法给 parent 调用
|
|
/** 回到 message 列表的顶部 */
|
const handleGoTopMessage = () => {
|
messageRef.value.handlerGoTop()
|
}
|
|
/** 回到 message 列表的底部 */
|
const handleGoBottomMessage = () => {
|
messageRef.value.handleGoBottom()
|
}
|
|
/** 处理 message 清空 */
|
const handlerMessageClear = async () => {
|
if (!activeConversation.value) {
|
return
|
}
|
try {
|
// 确认提示
|
await message.delConfirm('确认清空对话消息?')
|
// 清空对话
|
await ChatMessageApi.deleteByConversationId(activeConversation.value.id)
|
// 刷新 message 列表
|
activeMessageList.value = []
|
} catch {}
|
}
|
|
const gotoManual = async (item: ChatMessageVO) => {
|
emit('gotoManualMethod', item) // 发送数据给父组件
|
}
|
|
/** 搜索按钮操作 */
|
const handleQuery = async () => {
|
if (props.parentMethod) {
|
// props.parentMethod({ data: queryParams }); // 可传递参数
|
let pageResult = await props.parentMethod(queryParams)
|
activeMessageList.value = pageResult.list
|
total.value = pageResult.total
|
}
|
}
|
|
/** 重置按钮操作 */
|
const resetQuery = () => {
|
queryFormRef.value.resetFields()
|
queryParams.pageNo = 1
|
handleQuery()
|
}
|
|
/** 初始化 **/
|
onMounted(async () => {
|
// await dealDate()
|
})
|
</script>
|
|
<style lang="scss" scoped>
|
|
.query-area {
|
margin-top: 10px;
|
float: right;
|
:deep(.el-form-item__label) {
|
color: #73C4FF;
|
}
|
:deep(.el-date-editor .el-icon) {
|
color: #DBEEFF;
|
}
|
:deep(.el-date-editor .el-range-input) {
|
color: rgba(219, 238, 255, 0.5);
|
}
|
/* 移除所有输入框边框 */
|
:deep(.el-form-item .el-input__wrapper) {
|
border: none !important;
|
box-shadow: none !important;
|
background: rgba(255,255,255,0.1) !important; /* 保留浅色背景 */
|
}
|
/* 所有状态通用透明背景 */
|
:deep(.el-button) {
|
background: transparent !important;
|
border-color: currentColor; /* 保持与文字同色 */
|
color: #409EFF; /* 蓝色文字 */
|
}
|
|
/* 悬停状态 */
|
:deep(.el-button:hover) {
|
background: rgba(0, 0, 0, 0.5) !important; /* 轻微悬停反馈 */
|
}
|
|
/* 点击状态 */
|
:deep(.el-button:active) {
|
background: rgba(0, 0, 0, 0.8) !important;
|
}
|
}
|
|
// 头部
|
.detail-container {
|
display: flex;
|
flex-direction: column;
|
width: 100%;
|
height: 75vh;
|
background-color: rgba(0, 0, 0, 0); /* 透明背景 */
|
z-index: 1;
|
.header {
|
display: flex;
|
flex-direction: row;
|
align-items: center;
|
justify-content: space-between;
|
box-shadow: 0 0 0 0 #dcdfe6;
|
|
.title {
|
font-size: 18px;
|
font-weight: bold;
|
color: gold;
|
}
|
|
.btns {
|
display: flex;
|
width: 300px;
|
flex-direction: row;
|
justify-content: flex-end;
|
|
.btn {
|
padding: 10px;
|
}
|
|
/* 所有状态通用透明背景 */
|
:deep(.el-button) {
|
background: transparent !important;
|
border-color: currentColor; /* 保持与文字同色 */
|
color: #409EFF; /* 蓝色文字 */
|
}
|
|
/* 悬停状态 */
|
:deep(.el-button:hover) {
|
background: rgba(0, 0, 0, 0.05) !important; /* 轻微悬停反馈 */
|
}
|
|
/* 点击状态 */
|
:deep(.el-button:active) {
|
background: rgba(0, 0, 0, 0.1) !important;
|
}
|
/* 禁用状态 */
|
:deep(.el-button.is-disabled) {
|
opacity: 0.6;
|
background: transparent !important;
|
}
|
}
|
}
|
}
|
|
// main 容器
|
.main-container {
|
padding: 0;
|
position: relative;
|
overflow: hidden; /* 隐藏外层滚动条 */
|
width: 100%;
|
|
.message-container {
|
position: absolute;
|
top: 0;
|
bottom: 0;
|
left: 0;
|
right: 0;
|
overflow-y: hidden;
|
/* Firefox */
|
scrollbar-width: thin;
|
scrollbar-color: rgba(0, 0, 0, 0.15) transparent;
|
|
/* WebKit */
|
&::-webkit-scrollbar {
|
width: 6px;
|
background: transparent;
|
}
|
&::-webkit-scrollbar-thumb {
|
border-radius: 4px;
|
background: rgba(0, 0, 0, 0.15);
|
transition: background 0.3s;
|
&:hover { background: rgba(0, 0, 0, 0.25); }
|
}
|
}
|
}
|
|
// 底部
|
.footer-container {
|
display: flex;
|
flex-direction: column;
|
height: 114px;
|
margin-left: 10px;
|
padding: 0;
|
|
// 输入框
|
.input-container {
|
display: flex;
|
flex-direction: column;
|
height: auto;
|
width: 876px;
|
margin: 0;
|
padding: 0;
|
overflow-y: auto; /* 垂直方向溢出时显示滚动条 */
|
overflow-x: hidden; /* 水平方向隐藏滚动条 */
|
/* Firefox */
|
scrollbar-width: thin;
|
scrollbar-color: rgba(0, 0, 0, 0.15) transparent;
|
|
/* WebKit */
|
&::-webkit-scrollbar {
|
width: 6px;
|
background: transparent;
|
}
|
&::-webkit-scrollbar-thumb {
|
border-radius: 4px;
|
background: rgba(0, 0, 0, 0.15);
|
transition: background 0.3s;
|
&:hover { background: rgba(0, 0, 0, 0.25); }
|
}
|
|
.prompt-from {
|
display: flex;
|
flex-direction: column;
|
padding: 9px 10px;
|
width: 876px;
|
height: 114px;
|
background: rgba(115,196,255,0.05);
|
border-radius: 4px 4px 4px 4px;
|
border: 1px solid #73C4FF;
|
}
|
|
.prompt-input {
|
width: 876px;
|
height: 113.55px;
|
font-weight: 400;
|
font-size: 14px;
|
background-color: rgba(219,238,255,0);
|
line-height: 21px;
|
text-align: left;
|
font-style: normal;
|
text-transform: none;
|
border: 0;
|
color: rgba(219,238,255,0.6);
|
}
|
|
.prompt-input:focus {
|
outline: none;
|
}
|
|
.prompt-btns {
|
display: flex;
|
justify-content: space-between;
|
padding-bottom: 0;
|
padding-top: 5px;
|
|
.content {
|
/* 默认状态 */
|
.el-button {
|
background: transparent !important;
|
border-color: rgba(115, 196, 255, 0.5);
|
color: #73C4FF;
|
border-radius: 15px !important;
|
}
|
|
/* 上下文图标处理 */
|
.content-icon {
|
color: blue; /* 图标颜色 */
|
font-size: 18px;
|
margin-right: 10px;
|
background: url("@/assets/ai/zhuanlu/content.png");
|
vertical-align: middle;
|
}
|
|
/* 选中状态 */
|
.active-button {
|
background: #409eff !important;
|
border-color: #409eff !important;
|
color: white !important;
|
.content-icon {
|
background: url("@/assets/ai/zhuanlu/content_select.png");
|
vertical-align: middle;
|
}
|
}
|
|
/* 按钮组间距处理 */
|
.button-group .el-button {
|
margin-left: 0;
|
border-radius: 4px;
|
}
|
|
/* 悬停效果 */
|
.el-button:not(.active-button):hover {
|
border-color: rgba(115,196,255,0.5);
|
color: #409eff;
|
}
|
}
|
.message {
|
/* 所有状态通用透明背景 */
|
:deep(.el-button) {
|
background: rgba(73, 254, 210, 0.8) !important;
|
border-color: currentColor; /* 保持与文字同色 */
|
font-family: Alimama ShuHeiTi, Alimama ShuHeiTi;
|
font-weight: bold;
|
font-size: 16px;
|
color: #123C4E;
|
clip-path: polygon(
|
0 0,
|
100% 0,
|
100% 100%,
|
10px 100%, /* 右下方向留出10px */
|
0 calc(100% - 10px) /* 左上方向留出10px */
|
);
|
position: relative;
|
padding-left: 15px; /* 增加右侧留白 */
|
}
|
|
/* 悬停状态 */
|
:deep(.el-button:hover) {
|
background: rgba(73, 254, 210, 0.6) !important; /* 轻微悬停反馈 */
|
}
|
|
/* 点击状态 */
|
:deep(.el-button:active) {
|
background: rgba(73, 254, 210, 1) !important;
|
}
|
|
/* 禁用状态 */
|
:deep(.el-button.is-disabled) {
|
opacity: 0.6;
|
background: transparent !important;
|
}
|
|
/* 核心样式覆盖 */
|
:deep(.el-switch__core) {
|
background: transparent !important;
|
border-radius: 0 0 15px 0 !important;
|
border: none !important;
|
height: 40px !important;
|
}
|
|
/* 按钮内容容器 */
|
.button-content {
|
display: flex;
|
align-items: center;
|
padding: 0 15px;
|
height: 100%;
|
}
|
}
|
}
|
|
/* 下拉组件 */
|
:deep(.el-select) {
|
/* 下拉箭头 */
|
.el-select__caret {
|
color: #73C4FF !important; /* 匹配图中的浅蓝箭头 */
|
font-size: 16px !important;
|
}
|
}
|
|
/* 深度选择器调整边框细节 */
|
:deep(.el-select__wrapper) {
|
background-color: transparent !important;
|
border-radius: 6px; /* 圆角大小 */
|
border-width: 1.5px; /* 边框粗细 */
|
box-shadow: 0 0 0 1px #1E5A86 !important; /* 聚焦阴影 */
|
}
|
}
|
}
|
|
.el-pagination {
|
//--el-pagination-button-bg-color: transparent;
|
opacity: 0.6;
|
:deep(.el-pagination__total) {
|
color: white;
|
}
|
:deep(.el-pager) {
|
color: rgba(3,27,21);
|
font-weight: bold;
|
}
|
:deep(.el-pagination__jump) {
|
color: white;
|
}
|
:deep(.el-select__popper) {
|
background-color: transparent;
|
}
|
:deep(.el-scrollbar) {
|
--el-scrollbar-opacity: 0.8;
|
--el-scrollbar-bg-color: transparent;
|
}
|
}
|
</style>
|