526 lines
13 KiB
Vue
526 lines
13 KiB
Vue
<template>
|
||
<u-popup v-model="showPopup" width="550rpx">
|
||
<view class="drawer-container">
|
||
<view class="drawer-header">
|
||
<view class="header-top">
|
||
<view class="header-tabs">
|
||
<u-tabs
|
||
:list="tabList"
|
||
:is-scroll="false"
|
||
:current="currentTab"
|
||
@change="changeTab"
|
||
bg-color="transparent"
|
||
active-color="#4F6AFF"
|
||
inactive-color="#505866"
|
||
bar-width="80"
|
||
font-size="32"
|
||
:bold="true"
|
||
></u-tabs>
|
||
</view>
|
||
<view class="header-icon" @click="handleHistoryClick">
|
||
<u-icon name="clock" size="40" color="#333333"></u-icon>
|
||
</view>
|
||
</view>
|
||
|
||
<view
|
||
class="drawer-title header-bottom"
|
||
@click="handleCreateConversation"
|
||
>
|
||
<u-icon
|
||
class="drawer-title-icon"
|
||
name="plus"
|
||
size="32rpx"
|
||
color="#666666"
|
||
></u-icon>
|
||
新建对话
|
||
</view>
|
||
</view>
|
||
<scroll-view
|
||
scroll-y
|
||
class="chat-history-list"
|
||
:scroll-into-view="scrollToView"
|
||
:show-scrollbar="false"
|
||
scroll-with-animation="true"
|
||
>
|
||
<!-- 使用新的数据结构渲染聊天历史 -->
|
||
<view
|
||
v-for="(group, groupIndex) in chatHistoryList3"
|
||
:key="'group-' + group.id + groupIndex"
|
||
@click="closePopover"
|
||
>
|
||
<!-- 日期标题 -->
|
||
<view class="chat-day">
|
||
<text class="day-text">{{ group.id }}</text>
|
||
</view>
|
||
|
||
<!-- 该日期下的对话列表 -->
|
||
<view
|
||
class="chat-item"
|
||
v-for="(item, index) in group.conversation"
|
||
:key="'conv-' + groupIndex + '-' + index"
|
||
:id="'chat-item-' + item.id"
|
||
:class="{
|
||
'chat-item-active': item.isActiveChat,
|
||
}"
|
||
@click.stop="
|
||
selectChatItem(groupIndex, index, item.id, item.conversationId)
|
||
"
|
||
>
|
||
<view class="chat-item-content">
|
||
<text class="chat-text u-line-1">{{ item.title }}</text>
|
||
<view
|
||
class="more-icon"
|
||
v-if="item.isActiveChat"
|
||
:id="'more-' + item.id"
|
||
@click.stop="togglePopover(item.id)"
|
||
>
|
||
<image
|
||
style="width: 40rpx; height: 40rpx"
|
||
src="/static/common/images/icon-more-white.png"
|
||
></image>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Popover菜单 -->
|
||
<view
|
||
class="popover-menu"
|
||
v-if="activePopoverId === item.id"
|
||
@click.stop
|
||
>
|
||
<view
|
||
class="popover-item"
|
||
@click.stop="
|
||
selectChatItem(
|
||
groupIndex,
|
||
index,
|
||
item.id,
|
||
item.conversationId
|
||
)
|
||
"
|
||
>
|
||
<u-icon
|
||
name="chat"
|
||
color="#4f6aff"
|
||
size="32"
|
||
custom-style="margin-right: 12rpx"
|
||
></u-icon>
|
||
<text class="popover-text" style="color: #4f6aff">回复</text>
|
||
</view>
|
||
<view class="popover-item" @click="handleDelete(item)">
|
||
<u-icon
|
||
name="trash"
|
||
color="#fa3534"
|
||
size="32"
|
||
custom-style="margin-right: 12rpx"
|
||
></u-icon>
|
||
<text class="popover-text" style="color: #fa3534">删除</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 添加底部空白区域 -->
|
||
<view class="bottom-space"></view>
|
||
</scroll-view>
|
||
|
||
<view class="drawer-footer">
|
||
<view class="user-info">
|
||
<image class="user-avatar" :src="headSculptureUrl"></image>
|
||
<text class="user-name">{{ userName || "晓德塔," }}</text>
|
||
</view>
|
||
<view class="settings" @click="handleSettingsClick">
|
||
<u-icon name="setting" size="40rpx" color="#999"></u-icon>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 删除确认弹窗:使用 uView 的 u-modal 组件实现 -->
|
||
<!-- <u-modal
|
||
border-radius="20rpx"
|
||
v-model="deleteConfirmShow"
|
||
title="提示"
|
||
content="删除后,该对话将不可恢复。确认删除吗?"
|
||
:show-cancel-button="true"
|
||
@confirm="confirmDelete"
|
||
@cancel="cancelDelete"
|
||
></u-modal> -->
|
||
</view>
|
||
</u-popup>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: "ChatHistory",
|
||
props: {
|
||
show: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
chatHistoryList3: {
|
||
type: Array,
|
||
default: () => [],
|
||
},
|
||
activeIndex: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
userName: {
|
||
type: String,
|
||
default: "",
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
baseUrl: "",
|
||
showPopup: false,
|
||
currentActiveGroup: -1,
|
||
currentActiveIndex: -1,
|
||
activeItemId: "", // 存储当前激活项的ID
|
||
scrollToView: "", // 用于scroll-into-view属性
|
||
tabList: [
|
||
{
|
||
name: "AI咨询",
|
||
},
|
||
{
|
||
name: "人工咨询",
|
||
},
|
||
],
|
||
currentTab: 0,
|
||
activePopoverId: null, // 当前打开的popover对应的itemId
|
||
// 删除确认弹窗的显示状态与目标项
|
||
// deleteConfirmShow: false,
|
||
// deleteTargetItem: null,
|
||
};
|
||
},
|
||
|
||
computed: {
|
||
headSculptureUrl() {
|
||
if (this.vuex_user.HeadSculptureUrl) {
|
||
return this.baseUrl + "/" + this.vuex_user.HeadSculptureUrl;
|
||
}
|
||
|
||
return "/static/common/images/avatar.png";
|
||
},
|
||
},
|
||
|
||
watch: {
|
||
show: {
|
||
handler(newVal) {
|
||
this.showPopup = newVal;
|
||
// 弹层每次打开时,收起右侧“回复/删除”浮窗
|
||
if (newVal) {
|
||
this.activePopoverId = null;
|
||
}
|
||
},
|
||
immediate: true,
|
||
},
|
||
showPopup(val) {
|
||
if (val !== this.show) {
|
||
this.$emit("update:show", val);
|
||
}
|
||
// 兼容从内部改变显示状态的场景,打开时关闭浮窗
|
||
if (val) {
|
||
this.baseUrl = this.$u.http.config.baseUrl;
|
||
this.activePopoverId = null;
|
||
}
|
||
},
|
||
// 监听聊天历史数据变化,找到激活项并滚动
|
||
chatHistoryList3: {
|
||
handler() {
|
||
this.$nextTick(() => {
|
||
this.scrollToActiveItem();
|
||
});
|
||
},
|
||
deep: true,
|
||
immediate: true,
|
||
},
|
||
},
|
||
methods: {
|
||
// 处理历史记录点击事件
|
||
handleHistoryClick() {
|
||
uni.navigateTo({
|
||
url: "/pages/home/history/history",
|
||
});
|
||
},
|
||
// 处理设置点击事件
|
||
handleSettingsClick() {
|
||
uni.navigateTo({
|
||
url: "/pages/home/userSetting/index",
|
||
});
|
||
},
|
||
// 关闭Popover
|
||
closePopover() {
|
||
this.activePopoverId = null;
|
||
},
|
||
// 切换Popover显示状态
|
||
togglePopover(itemId) {
|
||
if (this.activePopoverId === itemId) {
|
||
this.activePopoverId = null;
|
||
return;
|
||
}
|
||
|
||
this.activePopoverId = itemId;
|
||
},
|
||
// 处理回复(暂时无用)
|
||
// handleReply(item) {
|
||
// this.closePopover();
|
||
// // 这里添加回复逻辑,比如跳转或弹窗
|
||
// console.log("Reply to:", item);
|
||
// this.$emit("reply-conversation", item);
|
||
// },
|
||
// 处理删除
|
||
handleDelete(item) {
|
||
// 关闭右侧浮窗,弹出确认弹窗
|
||
this.closePopover();
|
||
// this.deleteTargetItem = item;
|
||
// this.deleteConfirmShow = true;
|
||
|
||
this.$u.api
|
||
.DeleteDialogueManagement({
|
||
id: [item.id],
|
||
})
|
||
.then((res) => {
|
||
if (res.succeed) {
|
||
this.$u.toast("删除成功");
|
||
// 刷新聊天历史数据
|
||
this.$emit("refresh-chat-history");
|
||
} else {
|
||
this.$u.toast(res.msg || "删除失败");
|
||
}
|
||
});
|
||
},
|
||
// // 确认删除:确认后发出删除事件并重置状态
|
||
// confirmDelete() {
|
||
// if (this.deleteTargetItem) {
|
||
// this.$emit("delete-conversation", this.deleteTargetItem);
|
||
// }
|
||
// this.deleteTargetItem = null;
|
||
// this.deleteConfirmShow = false;
|
||
// },
|
||
// // 取消删除:仅关闭弹窗并清空目标
|
||
// cancelDelete() {
|
||
// this.deleteConfirmShow = false;
|
||
// this.deleteTargetItem = null;
|
||
// },
|
||
// 滚动到激活的聊天项
|
||
scrollToActiveItem() {
|
||
// 查找激活的聊天项
|
||
let activeItemFound = false;
|
||
|
||
for (
|
||
let groupIndex = 0;
|
||
groupIndex < this.chatHistoryList3.length;
|
||
groupIndex++
|
||
) {
|
||
const group = this.chatHistoryList3[groupIndex];
|
||
for (let index = 0; index < group.conversation.length; index++) {
|
||
const item = group.conversation[index];
|
||
if (item.isActiveChat) {
|
||
activeItemFound = true;
|
||
this.activeItemId = `chat-item-${item.id}`;
|
||
// 设置scrollToView的值,使scroll-view自动滚动到该元素
|
||
this.scrollToView = this.activeItemId;
|
||
break;
|
||
}
|
||
}
|
||
if (activeItemFound) break;
|
||
}
|
||
},
|
||
|
||
selectChatItem(groupIndex, index, id, conversationId) {
|
||
// this.currentActiveGroup = groupIndex;
|
||
// this.currentActiveIndex = index;
|
||
console.log("selectChatItem", groupIndex, index, id, conversationId);
|
||
|
||
// 向父组件发送选中的对话信息
|
||
this.$emit("select-conversation", {
|
||
id,
|
||
conversationId,
|
||
});
|
||
},
|
||
|
||
handleCreateConversation() {
|
||
this.$emit("create-conversation");
|
||
},
|
||
changeTab(index) {
|
||
this.currentTab = index;
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.drawer-container {
|
||
padding: 32rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
background-color: #ffffff;
|
||
|
||
.drawer-header {
|
||
.header-top {
|
||
height: 80rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.header-tabs {
|
||
width: 300rpx;
|
||
}
|
||
.header-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
}
|
||
}
|
||
.header-bottom {
|
||
padding: 16rpx 32rpx;
|
||
}
|
||
|
||
.drawer-title {
|
||
font-family: DouyinSans;
|
||
font-weight: bold;
|
||
font-size: 32rpx;
|
||
color: #666666;
|
||
|
||
.drawer-title-icon {
|
||
margin-right: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.chat-history-list {
|
||
flex: 1;
|
||
height: calc(100vh - 420rpx);
|
||
padding-bottom: 20rpx;
|
||
|
||
.chat-day {
|
||
display: flex;
|
||
margin: 20rpx 0 20rpx 30rpx;
|
||
|
||
.day-text {
|
||
font-size: 24rpx;
|
||
color: #999999;
|
||
font-family: PingFang SC;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
}
|
||
|
||
.chat-item {
|
||
padding: 24rpx 24rpx 24rpx 30rpx;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 4rpx;
|
||
position: relative;
|
||
// overflow: hidden; // 移除overflow hidden以便显示popover
|
||
|
||
.chat-item-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
width: 100%;
|
||
|
||
.more-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
}
|
||
}
|
||
|
||
.chat-text {
|
||
font-size: 28rpx;
|
||
color: #303030;
|
||
font-family: PingFang SC;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
&-active {
|
||
background-color: #4f6aff;
|
||
box-shadow: 0 2rpx 8rpx rgba(79, 106, 255, 0.3);
|
||
|
||
.chat-text {
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.popover-menu {
|
||
width: 200rpx;
|
||
// 相对当前 chat-item 进行绝对定位,随 item 滚动
|
||
position: absolute;
|
||
right: 10rpx;
|
||
top: 80rpx;
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgba(79, 106, 255, 0.02),
|
||
rgba(215, 237, 237, 0.01)
|
||
);
|
||
box-shadow: 0px 2px 8px 0px rgba(91, 92, 94, 0.3);
|
||
background-color: #ffffff;
|
||
border-radius: 20rpx;
|
||
padding: 10rpx 0;
|
||
z-index: 10005;
|
||
|
||
.popover-item {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20rpx 30rpx;
|
||
|
||
&:active {
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.popover-text {
|
||
font-size: 28rpx;
|
||
font-family: PingFang SC;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.bottom-space {
|
||
// 增加底部留白,避免最底部项的浮层阴影被裁剪
|
||
height: 200rpx;
|
||
width: 100%;
|
||
}
|
||
|
||
.drawer-footer {
|
||
margin-top: 20rpx;
|
||
height: 130rpx;
|
||
border: 1rpx solid #eeeeee;
|
||
border-radius: 12rpx;
|
||
padding: 0 30rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
.user-info {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.user-avatar {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
border-radius: 50%;
|
||
margin-right: 30rpx;
|
||
}
|
||
|
||
.user-name {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
font-family: DouyinSans;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.settings {
|
||
padding: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
</style>
|