feat: 完成角色卡,智能体配置,加载,历史记录,清空等功能
This commit is contained in:
@@ -14,19 +14,22 @@
|
||||
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar">
|
||||
<view class="navbar-left" @tap="goBack" v-if="showBackButton">
|
||||
<text class="back-icon">←</text>
|
||||
<text class="back-text">返回</text>
|
||||
<view class="navbar-left">
|
||||
<view v-if="showBackButton" @tap="goBack" class="left-btn">
|
||||
<text class="back-icon">←</text>
|
||||
<text class="back-text">返回</text>
|
||||
</view>
|
||||
<text class="clear-btn" @tap="clearContext">清空</text>
|
||||
</view>
|
||||
<view class="navbar-center" :class="{'centered': !showBackButton}">
|
||||
<image class="character-avatar" :src="currentCharacter.avatar" mode="aspectFill" />
|
||||
<text class="character-name">{{ currentCharacter.name }}</text>
|
||||
</view>
|
||||
<view class="navbar-right">
|
||||
<text class="status-dot" :class="{'online': isOnline}"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 聊天消息区域 - 使用 Ant Design X Bubble.List -->
|
||||
<scroll-view
|
||||
class="chat-messages"
|
||||
@@ -257,16 +260,23 @@ const initPage = async () => {
|
||||
currentCharacter.value = {
|
||||
id: 'wei-ai',
|
||||
name: options.characterName || '蔚AI',
|
||||
avatar: options.characterAvatar || '/static/logo.png',
|
||||
avatar: options.characterAvatar || '/static/avatar/icon_hushi.jpg',
|
||||
greeting: decodeURIComponent(options.introMessage || '你好!我是蔚AI,很高兴为您服务!')
|
||||
};
|
||||
|
||||
await loadAIConfigs();
|
||||
await createNewConversation('wei-ai');
|
||||
addMessage('ai', currentCharacter.value.greeting);
|
||||
createNewConversation('wei-ai');
|
||||
|
||||
if (!isLoggedIn.value) {
|
||||
addMessage('system', '您当前处于未登录状态,将使用本地模拟回复。登录后可享受完整的AI对话功能。');
|
||||
// 尝试加载历史消息
|
||||
await loadHistoryMessages();
|
||||
|
||||
// 如果没有历史消息,显示欢迎消息
|
||||
if (messages.value.length === 0) {
|
||||
addMessage('ai', currentCharacter.value.greeting);
|
||||
|
||||
if (!isLoggedIn.value) {
|
||||
addMessage('system', '您当前处于未登录状态,将使用本地模拟回复。登录后可享受完整的AI对话功能。');
|
||||
}
|
||||
}
|
||||
}
|
||||
// AI角色
|
||||
@@ -295,11 +305,18 @@ const initPage = async () => {
|
||||
currentTemplateId.value = parseInt(options.templateId);
|
||||
}
|
||||
|
||||
await createNewConversation(options.roleId);
|
||||
addMessage('ai', currentCharacter.value.greeting);
|
||||
createNewConversation(options.roleId);
|
||||
|
||||
if (!isLoggedIn.value) {
|
||||
addMessage('system', '您当前处于未登录状态,将使用本地模拟回复。登录后可享受完整的AI对话功能。');
|
||||
// 尝试加载历史消息
|
||||
await loadHistoryMessages();
|
||||
|
||||
// 如果没有历史消息,显示欢迎消息
|
||||
if (messages.value.length === 0) {
|
||||
addMessage('ai', currentCharacter.value.greeting);
|
||||
|
||||
if (!isLoggedIn.value) {
|
||||
addMessage('system', '您当前处于未登录状态,将使用本地模拟回复。登录后可享受完整的AI对话功能。');
|
||||
}
|
||||
}
|
||||
}
|
||||
// 默认角色
|
||||
@@ -309,11 +326,18 @@ const initPage = async () => {
|
||||
if (character) {
|
||||
currentCharacter.value = character;
|
||||
await loadAIConfigs();
|
||||
await createNewConversation(characterId);
|
||||
addMessage('ai', character.greeting);
|
||||
createNewConversation(characterId);
|
||||
|
||||
if (!isLoggedIn.value) {
|
||||
addMessage('system', '您当前处于未登录状态,将使用本地模拟回复。登录后可享受完整的AI对话功能。');
|
||||
// 尝试加载历史消息
|
||||
await loadHistoryMessages();
|
||||
|
||||
// 如果没有历史消息,显示欢迎消息
|
||||
if (messages.value.length === 0) {
|
||||
addMessage('ai', character.greeting);
|
||||
|
||||
if (!isLoggedIn.value) {
|
||||
addMessage('system', '您当前处于未登录状态,将使用本地模拟回复。登录后可享受完整的AI对话功能。');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -461,17 +485,115 @@ const sendMessage = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 创建新对话
|
||||
const createNewConversation = async (characterId) => {
|
||||
// 创建或获取会话ID(基于角色持久化存储)
|
||||
const createNewConversation = (characterId) => {
|
||||
// 生成存储key:根据角色类型区分
|
||||
let storageKey = '';
|
||||
if (characterId === 'wei-ai' || !currentCharacter.value.roleId) {
|
||||
// 蔚AI或默认角色
|
||||
storageKey = `session_weiai`;
|
||||
} else {
|
||||
// 剧情角色
|
||||
storageKey = `session_role_${currentCharacter.value.roleId}`;
|
||||
}
|
||||
|
||||
// 尝试从本地存储获取已有的sessionId
|
||||
let existingSessionId = uni.getStorageSync(storageKey);
|
||||
|
||||
if (existingSessionId) {
|
||||
// 已有sessionId,直接使用(保持上下文)
|
||||
conversationId.value = existingSessionId;
|
||||
console.log('使用已有sessionId:', existingSessionId, 'storageKey:', storageKey);
|
||||
} else {
|
||||
// 生成新的sessionId
|
||||
const userId = userStore.userInfo?.openid || userStore.userInfo?.userId || 'guest';
|
||||
const timestamp = Date.now();
|
||||
const newSessionId = `session_${characterId}_${userId}_${timestamp}`;
|
||||
|
||||
// 存储到本地
|
||||
uni.setStorageSync(storageKey, newSessionId);
|
||||
conversationId.value = newSessionId;
|
||||
console.log('创建新sessionId:', newSessionId, 'storageKey:', storageKey);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载历史消息
|
||||
const loadHistoryMessages = async () => {
|
||||
if (!conversationId.value || !isLoggedIn.value) {
|
||||
console.log('没有sessionId或用户未登录,跳过加载历史消息');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await chatAPI.createConversation(characterId);
|
||||
if (result.success) {
|
||||
conversationId.value = result.data.conversationId;
|
||||
console.log('开始加载历史消息,sessionId:', conversationId.value);
|
||||
const result = await chatAPI.getHistoryMessages(conversationId.value);
|
||||
|
||||
if (result.success && result.data && result.data.length > 0) {
|
||||
console.log('获取到历史消息:', result.data.length, '条');
|
||||
|
||||
// 将后端消息格式转换为前端格式,保持与API回复一致的“清理后回复”逻辑
|
||||
const historyMessages = [];
|
||||
|
||||
result.data.forEach(msg => {
|
||||
// 格式化时间
|
||||
let timeStr = '';
|
||||
if (msg.createTime) {
|
||||
const date = new Date(msg.createTime);
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
timeStr = `${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
const messageType = msg.sender === 'assistant' ? 'ai' : (msg.sender === 'user' ? 'user' : 'system');
|
||||
const rawContent = msg.message || '';
|
||||
|
||||
// 与API一致:先清理文本,再根据 & 分段
|
||||
const cleanedContent = cleanText(rawContent);
|
||||
|
||||
if (messageType === 'ai' && cleanedContent.includes('&')) {
|
||||
const segments = cleanedContent
|
||||
.split('&')
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
segments.forEach(segment => {
|
||||
historyMessages.push({
|
||||
type: messageType,
|
||||
content: segment,
|
||||
time: timeStr
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// 非 AI 或无分隔符,直接使用清理后的内容
|
||||
const content = cleanedContent.trim();
|
||||
if (content) {
|
||||
historyMessages.push({
|
||||
type: messageType,
|
||||
content,
|
||||
time: timeStr
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 按时间排序(从旧到新)
|
||||
historyMessages.sort((a, b) => {
|
||||
if (!a.time || !b.time) return 0;
|
||||
return a.time.localeCompare(b.time);
|
||||
});
|
||||
|
||||
// 清空当前消息列表,加载历史消息
|
||||
messages.value = historyMessages;
|
||||
console.log('历史消息加载完成,共', messages.value.length, '条');
|
||||
|
||||
// 滚动到底部
|
||||
await nextTick();
|
||||
scrollToBottom();
|
||||
} else {
|
||||
conversationId.value = `local_${Date.now()}`;
|
||||
console.log('没有历史消息或获取失败');
|
||||
}
|
||||
} catch (error) {
|
||||
conversationId.value = `local_${Date.now()}`;
|
||||
console.error('加载历史消息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -814,6 +936,65 @@ const handleInputFocus = () => {
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
|
||||
|
||||
// 清空对话上下文
|
||||
const clearContext = async () => {
|
||||
uni.showModal({
|
||||
title: '清空对话',
|
||||
content: '确定要清空与该角色的所有对话记录吗?此操作不可恢复。',
|
||||
confirmText: '确定清空',
|
||||
cancelText: '取消',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({ title: '清空中...' });
|
||||
|
||||
// 1. 调用后端清除session(如果用户已登录)
|
||||
if (conversationId.value && isLoggedIn.value) {
|
||||
try {
|
||||
const result = await chatAPI.clearSession(conversationId.value);
|
||||
console.log('后端清除会话结果:', result);
|
||||
} catch (error) {
|
||||
console.log('后端清除失败,继续本地清除:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 删除本地存储的sessionId
|
||||
let storageKey = '';
|
||||
if (currentCharacter.value.id === 'wei-ai' || !currentCharacter.value.roleId) {
|
||||
storageKey = `session_weiai`;
|
||||
} else {
|
||||
storageKey = `session_role_${currentCharacter.value.roleId}`;
|
||||
}
|
||||
uni.removeStorageSync(storageKey);
|
||||
console.log('删除本地sessionId:', storageKey);
|
||||
|
||||
// 3. 生成新的sessionId
|
||||
const characterIdForSession = currentCharacter.value.id || currentCharacter.value.roleId || 'default';
|
||||
createNewConversation(characterIdForSession);
|
||||
|
||||
// 4. 清空消息列表(保留欢迎消息)
|
||||
messages.value = [];
|
||||
addMessage('ai', currentCharacter.value.greeting || '你好!很高兴再次见到你!');
|
||||
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '对话已清空',
|
||||
icon: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('清空对话失败:', error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '清空失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -897,13 +1078,19 @@ page {
|
||||
left: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
gap: 16rpx;
|
||||
color: #f9e076;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
text-shadow: 0 0 10rpx rgba(249, 224, 118, 0.3);
|
||||
}
|
||||
|
||||
.left-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
@@ -938,13 +1125,22 @@ page {
|
||||
.navbar-right {
|
||||
position: absolute;
|
||||
right: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(249, 224, 118, 0.3);
|
||||
.clear-btn {
|
||||
font-size: 28rpx;
|
||||
color: #f9e076;
|
||||
font-weight: 500;
|
||||
text-shadow: 0 0 8rpx rgba(249, 224, 118, 0.3);
|
||||
padding: 8rpx 16rpx;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.clear-btn:active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.status-dot.online {
|
||||
@@ -962,6 +1158,7 @@ page {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 聊天消息区域 */
|
||||
.chat-messages {
|
||||
height: calc(100vh - 100rpx - 200rpx - env(safe-area-inset-top));
|
||||
|
||||
Reference in New Issue
Block a user