Files
zhxg_app/components/aiChat/HistoryDrawer.vue
2025-08-14 00:36:04 +08:00

399 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- HistoryDrawer.vue -->
<template>
<!-- 抽屉容器visible控制显示 -->
<view v-if="visible" class="drawer-container" @touchmove.stop.prevent>
<!-- 遮罩层点击关闭 -->
<view class="drawer-mask" @click="closeDrawer"></view>
<!-- 抽屉内容区域 -->
<view class="drawer-content">
<!-- 标题区域 -->
<view class="drawer-header">
<text class="title">历史记录</text>
<image src="/static/close.svg" mode="aspectFit" class="close-icon" @click="closeDrawer" />
</view>
<!-- 搜索栏 -->
<view class="search-bar">
<image src="/static/search.svg" mode="aspectFit" class="search-icon" />
<input v-model="searchKeyword" placeholder="搜索聊天记录..." class="search-input" @input="handleSearch" />
<image v-if="searchKeyword" src="/static/clear.svg" class="clear-icon" @click="clearSearch" />
</view>
<!-- 历史列表区域 -->
<scroll-view scroll-y class="history-list">
<!-- 分组渲染历史记录 -->
<block v-for="(group, gIndex) in filteredRecords" :key="gIndex">
<view class="group-title">{{ group.title }}</view>
<view v-for="item in group.list" :key="item.id" class="history-item" @click="onItemClick(item)">
<!-- 日期时间显示 -->
<view class="datetime">
<text class="date">{{ item.date }}</text>
<text class="time">{{ item.time }}</text>
</view>
<!-- 消息内容 -->
<view class="record">
<text class="user-msg" v-html="highlightKeyword(item.content)"></text>
<text class="ai-msg" v-html="highlightKeyword(item.reply)"></text>
</view>
<view class="divider"></view>
</view>
</block>
<!-- 空状态提示 -->
<view v-if="filteredRecords.length === 0" class="empty-tip">
<text>{{ searchKeyword ? '没有找到匹配的记录' : '暂无聊天记录' }}</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import {
getHistory
} from '../../api/aiChat/ai_index.js';
export default {
name: 'HistoryDrawer',
props: {
visible: Boolean
},
data() {
return {
historyRecords: [],
filteredRecords: [],
searchKeyword: '', // 移除了重复定义
loading: false // 新增loading状态
};
},
watch: {
visible(newVal) {
if (newVal) this.loadHistoryRecords();
else this.clearSearch();
}
},
methods: {
closeDrawer() {
this.$emit('close');
},
async loadHistoryRecords() {
this.loading = true;
try {
// 1. 获取当前用户学号
const userNo = uni.getStorageSync('stuNo');
if (!userNo) throw new Error('未获取到用户学号');
// 2. 调用接口获取数据
const res = await getHistory({
user: userNo,
conversationId: '',
limit: 20
});
console.log('接口响应:', res);
// 3. 处理响应数据 - 修正数据结构解析
const list = Array.isArray(res.data?.data) ? res.data.data : [];
console.log('解析后的数据列表:', list);
// 4. 分组处理
const groupMap = {};
list.forEach(item => {
// 修正字段映射
const timestamp = item.created_at || item.create_time || item.timestamp || Date.now() /
1000;
const date = new Date(timestamp * 1000);
const record = {
id: item.id || Math.random().toString(36).slice(2),
date: this.formatDate(date),
time: this.formatTime(date),
content: item.query || item.content || '未知内容',
reply: item.answer || item.reply || '暂无回复',
timestamp: timestamp
};
const title = this.getGroupTitle(date);
(groupMap[title] ||= []).push(record);
});
// 5. 排序并更新数据
this.historyRecords = Object.entries(groupMap).map(([title, arr]) => ({
title,
list: arr.sort((a, b) => b.timestamp - a.timestamp)
}));
this.filteredRecords = [...this.historyRecords];
console.log('最终处理的历史记录:', this.historyRecords);
} catch (e) {
console.error('加载失败:', e);
uni.showToast({
title: `加载失败: ${e.message}`,
icon: 'none',
duration: 3000
});
this.historyRecords = [];
} finally {
this.loading = false;
}
},
// 处理搜索输入
handleSearch() {
const kw = this.searchKeyword.trim().toLowerCase();
if (!kw) return (this.filteredRecords = [...this.historyRecords]);
// 过滤历史记录
this.filteredRecords = this.historyRecords
.map(g => ({
...g,
list: g.list.filter(i =>
i.content.toLowerCase().includes(kw) ||
i.reply.toLowerCase().includes(kw)
)
}))
.filter(g => g.list.length);
},
// 清除搜索
clearSearch() {
this.searchKeyword = '';
this.filteredRecords = [...this.historyRecords];
},
// 高亮搜索关键词
highlightKeyword(text) {
if (!this.searchKeyword || !text) return text;
const kw = this.searchKeyword.trim();
return text.replace(
new RegExp(kw, 'gi'),
`<span class="highlight">${kw}</span>`
);
},
// 获取分组标题(今天/昨天/7天内等
getGroupTitle(date) {
const now = new Date();
const today = new Date(now.setHours(0, 0, 0, 0));
const yesterday = new Date(today).setDate(today.getDate() - 1);
const week = new Date(today).setDate(today.getDate() - 7);
const month = new Date(today).setDate(today.getDate() - 30);
if (date >= today) return '今天';
if (date >= yesterday) return '昨天';
if (date >= week) return '7天内';
if (date >= month) return '30天内';
return '更早';
},
// 格式化日期为YYYY-MM-DD
formatDate(date) {
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date
.getDate()
.toString()
.padStart(2, '0')}`;
},
// 格式化时间为HH:MM
formatTime(date) {
return `${date.getHours().toString().padStart(2, '0')}:${date
.getMinutes()
.toString()
.padStart(2, '0')}`;
},
// 点击历史记录项
onItemClick(item) {
// 移除HTML标签后触发事件
this.$emit('item-click', {
...item,
content: item.content.replace(/<[^>]+>/g, ''),
reply: item.reply.replace(/<[^>]+>/g, '')
});
this.closeDrawer();
}
}
};
</script>
<style scoped>
/* 抽屉容器样式 */
.drawer-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
}
/* 遮罩层样式 */
.drawer-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
/* 抽屉内容区域样式 */
.drawer-content {
position: absolute;
top: 0;
left: 0;
width: 66.67%;
height: 100vh;
background-color: #fff;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
}
/* 头部样式 */
.drawer-header {
padding: 15px 15px 10px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #eee;
height: 50px;
box-sizing: border-box;
}
.title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.close-icon {
width: 24px;
height: 24px;
}
/* 搜索栏样式 */
.search-bar {
display: flex;
align-items: center;
padding: 10px 15px;
background-color: #f5f5f5;
border-bottom: 1px solid #eee;
}
.search-icon {
width: 18px;
height: 18px;
margin-right: 8px;
tint-color: #999;
}
.search-input {
flex: 1;
height: 36px;
background-color: #fff;
border-radius: 18px;
padding: 0 15px;
font-size: 14px;
}
.clear-icon {
width: 16px;
height: 16px;
margin-left: 8px;
tint-color: #999;
}
/* 历史列表样式 */
.history-list {
flex: 1;
overflow-y: auto;
padding: 10px 0;
box-sizing: border-box;
}
/* 分组标题样式 */
.group-title {
font-size: 14px;
color: #888;
padding: 12px 20px;
background-color: #f5f5f5;
}
/* 历史记录项样式 */
.history-item {
padding: 15px 20px;
background-color: #fff;
border-bottom: 1px solid #f0f0f0;
}
/* 日期时间样式 */
.datetime {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.date {
font-size: 13px;
color: #666;
}
.time {
font-size: 12px;
color: #999;
}
/* 消息内容样式 */
.record {
font-size: 14px;
line-height: 1.6;
}
.user-msg {
display: block;
color: #333;
margin-bottom: 6px;
padding: 6px 10px;
background-color: #f9f9f9;
border-radius: 6px;
}
.ai-msg {
display: block;
color: #333;
padding: 6px 10px;
background-color: #eef7ff;
border-radius: 6px;
}
/* 关键词高亮样式 */
.highlight {
color: #ff4d4f;
font-weight: bold;
}
/* 分隔线样式 */
.divider {
height: 8px;
background-color: #f9f9f9;
margin-top: 10px;
border-radius: 4px;
}
/* 空状态提示样式 */
.empty-tip {
text-align: center;
color: #999;
font-size: 16px;
margin-top: 50px;
}
/* 修复输入框占位符样式 */
input::-webkit-input-placeholder {
color: #ccc;
font-size: 14px;
}
</style>