优化会话列表名称,添加复制和转发,转发待完善

This commit is contained in:
2026-08-07 17:58:36 +08:00
parent 53ca1cd851
commit 96d58abc2f
21 changed files with 1845 additions and 109 deletions

View File

@@ -400,6 +400,191 @@
background: #e0e0e0;
}
/* ========== 转发消息弹窗 ========== */
.forward-modal-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.45);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
z-index: 1001;
display: flex;
align-items: center;
justify-content: center;
}
.forward-modal {
width: 85%;
max-height: 72%;
background-color: #fff;
border-radius: 24rpx;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.12);
}
.forward-modal-header {
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding: 28rpx 30rpx 20rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.forward-modal-title {
font-size: 34rpx;
font-weight: 700;
color: #1a1a2e;
}
.forward-modal-close {
position: absolute;
right: 24rpx;
top: 50%;
transform: translateY(-50%);
}
.forward-preview-box {
margin: 20rpx 30rpx 8rpx;
padding: 20rpx 24rpx;
background: #f7f8fc;
border-radius: 14rpx;
border-left: 6rpx solid #007aff;
}
.forward-preview-label {
font-size: 24rpx;
color: #999;
display: block;
margin-bottom: 8rpx;
}
.forward-preview-text {
font-size: 28rpx;
color: #333;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
.forward-select-label {
font-size: 26rpx;
color: #666;
padding: 12rpx 30rpx 8rpx;
display: block;
}
.forward-conv-list {
flex: 1;
max-height: 400rpx;
padding: 0 20rpx;
}
.forward-empty {
display: flex;
justify-content: center;
align-items: center;
height: 160rpx;
color: #999;
font-size: 26rpx;
}
.forward-conv-item {
display: flex;
align-items: center;
padding: 22rpx 16rpx;
border-radius: 16rpx;
margin-bottom: 8rpx;
background: #fafafa;
transition: background 0.2s;
}
.forward-conv-item:active {
background: #eef4ff;
}
.forward-conv-selected {
background: #e8f0fe;
border: 2rpx solid #007aff;
}
.forward-conv-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 14rpx;
background: #e8f0fe;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
flex-shrink: 0;
}
.forward-conv-info {
flex: 1;
min-width: 0;
}
.forward-conv-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.forward-conv-check {
flex-shrink: 0;
margin-left: 12rpx;
}
.forward-modal-footer {
padding: 20rpx 30rpx 30rpx;
border-top: 2rpx solid #f0f0f0;
display: flex;
justify-content: flex-end;
gap: 20rpx;
}
.forward-modal-btn {
padding: 18rpx 48rpx;
border-radius: 16rpx;
font-size: 28rpx;
font-weight: 500;
}
.forward-btn-cancel {
background: #f0f0f0;
color: #666;
}
.forward-btn-cancel:active {
background: #e0e0e0;
}
.forward-btn-confirm {
background: #007aff;
color: #fff;
}
.forward-btn-confirm:active {
background: #005fcc;
}
.forward-btn-disabled {
background: #b0d0f0;
color: #fff;
pointer-events: none;
}
/* ==========新建聊天列表相关====== */
.close-option-card {
width: 100%;
@@ -619,7 +804,7 @@
.chat-message {
display: flex;
align-items: flex-start;
flex-direction: column;
margin: 14rpx 0;
box-sizing: border-box;
animation: msgFadeIn 0.3s ease;
@@ -631,6 +816,17 @@
}
.message-user {
align-items: flex-end;
}
/* avatar + 气泡行(上方) */
.message-row {
display: flex;
align-items: flex-start;
width: 100%;
}
.message-user .message-row {
flex-direction: row-reverse;
}
@@ -675,6 +871,9 @@
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
background: #ffffff;
border: 1rpx solid #eeeef5;
/* 启用文本选择复制 */
user-select: text;
-webkit-user-select: text;
}
.chat-content-user {
@@ -694,6 +893,81 @@
border-bottom-left-radius: 8rpx;
}
/* 消息操作栏 */
.message-actions {
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 16rpx;
padding-top: 12rpx;
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
gap: 24rpx;
}
.message-actions-user {
justify-content: flex-start;
border-top-color: rgba(255, 255, 255, 0.3);
}
.message-action-item {
display: flex;
align-items: center;
gap: 6rpx;
padding: 8rpx 16rpx;
border-radius: 12rpx;
transition: all 0.2s ease;
}
.message-action-item:active {
background: rgba(0, 0, 0, 0.05);
transform: scale(0.95);
}
.message-actions-user .message-action-item:active {
background: rgba(255, 255, 255, 0.2);
}
.action-text {
font-size: 22rpx;
color: #999;
}
.message-actions-user .action-text {
color: rgba(255, 255, 255, 0.8);
}
/* 消息转发弹窗 */
.msg-actions {
display: flex;
align-items: center;
gap: 16rpx;
margin-top: 10rpx;
}
.msg-actions-user {
justify-content: flex-end;
}
.msg-action-btn {
display: flex;
align-items: center;
gap: 6rpx;
padding: 4rpx 0;
}
.msg-action-icon {
font-size: 26rpx;
}
.msg-action-label {
font-size: 22rpx;
color: #888;
}
.msg-action-forward {
margin-left: 8rpx;
}
/* 图片消息 */
.message-image {
max-width: 300rpx;

View File

@@ -78,6 +78,54 @@
</view>
</view>
<!-- 转发消息弹窗 -->
<view class="forward-modal-mask" v-if="showForwardPopup" @click="cancelForward">
<view class="forward-modal" @click.stop>
<view class="forward-modal-header">
<text class="forward-modal-title">转发消息</text>
<view class="forward-modal-close" @click="cancelForward">
<uni-icons type="closeempty" size="20" color="#999"></uni-icons>
</view>
</view>
<!-- 被转发消息的预览 -->
<!-- <view class="forward-preview-box">
<text class="forward-preview-label">消息内容</text>
<text class="forward-preview-text">{{ forwardPreview }}</text>
</view> -->
<text class="forward-select-label">选择目标会话</text>
<scroll-view class="forward-conv-list" scroll-y="true">
<view v-if="forwardConversations.length === 0" class="forward-empty">
<text>暂无其他会话</text>
</view>
<view v-else
v-for="conv in forwardConversations"
:key="conv._id"
class="forward-conv-item"
:class="{ 'forward-conv-selected': forwardSelectedId === conv._id }"
@click="forwardSelectedId = conv._id"
>
<view class="forward-conv-avatar">
<uni-icons type="chat-filled" size="24" color="#007aff"></uni-icons>
</view>
<view class="forward-conv-info">
<text class="forward-conv-name">{{ getConvName(conv) }}</text>
</view>
<view v-if="forwardSelectedId === conv._id" class="forward-conv-check">
<uni-icons type="checkmarkempty" size="20" color="#007aff"></uni-icons>
</view>
</view>
</scroll-view>
<view class="forward-modal-footer">
<view class="forward-modal-btn forward-btn-cancel" @click="cancelForward">
<text>取消</text>
</view>
<view class="forward-modal-btn forward-btn-confirm" :class="{ 'forward-btn-disabled': !forwardSelectedId }" @click="confirmForward">
<text>确认转发</text>
</view>
</view>
</view>
</view>
<view class="chat-page page-container">
<!-- 弹窗出来时的笼罩层 -->
<view v-if="isChatSidebar" class="mask" @click="handleChatSidebar"></view>
@@ -159,6 +207,7 @@
<view class="chat-content"
:class="{'chat-content-user':message.role==='user', 'chat-content-assistant':message.role==='assistant' || message.role==='tool'}"
@click="handleChatContentClick($event, message)"
@longpress="handleLongPress(message)"
v-if="(message.content && String(message.content).trim() !== '') || message._streaming || (message.yxd_files && message.yxd_files.length > 0) || (message.role === 'tool' && message.yxd_tool_name === 'sendFrom')">
<!-- 流式加载中显示跳动的点 -->
<view
@@ -231,6 +280,17 @@
</view>
</view>
</template>
<!-- 操作栏复制转发仅非流式非用户上传文件 sendFrom 工具消息时显示 -->
<view v-if="!message._streaming && message.role !== 'tool'" class="message-actions" :class="{'message-actions-user': message.role === 'user'}">
<view class="message-action-item" @click.stop="copyMessage(message)">
<view class="iconfont icon-fuzhi1" color="#333"></view>
<text class="action-text">复制</text>
</view>
<view class="message-action-item" @click.stop="forwardMessage(message)">
<view class="iconfont icon-zhuanfa_3" color="#333"></view>
<text class="action-text">转发</text>
</view>
</view>
</view>
</view>
</template>
@@ -249,7 +309,8 @@
<view v-else class="iconfont icon-yonghuziliao"></view>
</view>
<view class="chat-content" :class="{'chat-content-user':message.sender === UserId}"
@click="handleChatContentClick($event, message)">
@click="handleChatContentClick($event, message)"
@longpress="handleLongPress(message)">
<view v-if="message.content && String(message.content).trim() !== ''"
v-html="pareseMarkdown(message.content)"></view>
<!-- 2. 图片 + 文件 渲染核心新增 -->
@@ -271,6 +332,17 @@
</view>
</view>
</view>
<!-- 好友消息操作栏 -->
<view v-if="message.content && String(message.content).trim() !== ''" class="message-actions" :class="{'message-actions-user': message.sender === UserId}">
<view class="message-action-item" @click.stop="copyMessage(message)">
<uni-icons type="copy" size="14" color="#333"></uni-icons>
<text class="action-text">复制</text>
</view>
<view class="message-action-item" @click.stop="forwardMessage(message)">
<uni-icons type="redo" size="14" color="#999"></uni-icons>
<text class="action-text">转发</text>
</view>
</view>
</view>
</view>
<!-- 与群聊的对话内容展示 -->
@@ -288,7 +360,8 @@
<view v-else class="iconfont icon-yonghuziliao"></view>
</view>
<view class="chat-content" :class="{'chat-content-user':message.sender === UserId}"
@click="handleChatContentClick($event, message)">
@click="handleChatContentClick($event, message)"
@longpress="handleLongPress(message)">
<view v-if="message.message && String(message.message).trim() !== ''"
v-html="pareseMarkdown(message.message)"></view>
<!-- 2. 图片 + 文件 渲染核心新增 -->
@@ -310,6 +383,17 @@
</view>
</view>
</view>
<!-- 群聊消息操作栏 -->
<view v-if="message.message && String(message.message).trim() !== ''" class="message-actions" :class="{'message-actions-user': message.sender === UserId}">
<view class="message-action-item" @click.stop="copyMessage({content: message.message})">
<uni-icons type="copy" size="14" color="#333"></uni-icons>
<text class="action-text">复制</text>
</view>
<view class="message-action-item" @click.stop="forwardMessage({content: message.message})">
<uni-icons type="redo" size="14" color="#999"></uni-icons>
<text class="action-text">转发</text>
</view>
</view>
</view>
</view>
<!-- 底部锚点两个交替切换确保流式时每次都能触发滚动到底部 -->
@@ -388,8 +472,8 @@
<script setup>
import {
computed,
getCurrentInstance,
computed,
nextTick,
onMounted,
reactive,
@@ -863,6 +947,131 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
agentList.value = []
}
// 复制消息内容
const copyMessage = (message) => {
// 提取纯文本内容用于复制
const textContent = parseFileInfo(message.content).textContent || message.content || ''
uni.setClipboardData({
data: textContent,
success: () => {
uni.showToast({ title: '已复制到剪贴板', icon: 'success' })
},
fail: () => {
uni.showToast({ title: '复制失败', icon: 'none' })
}
})
}
// 长按气泡处理 - 直接复制内容
const handleLongPress = (message) => {
// 流式消息不处理
// if (message._streaming) return
// // 适配不同消息格式
// let textContent = ''
// if (message.content && parseFileInfo(message.content).textContent) {
// // AI 对话格式(含文件信息)
// textContent = parseFileInfo(message.content).textContent
// } else if (message.content) {
// // 好友对话格式
// textContent = message.content
// } else if (message.message) {
// // 群聊对话格式
// textContent = message.message
// }
// if (!textContent || String(textContent).trim() === '') return
// uni.setClipboardData({
// data: textContent,
// success: () => {
// uni.showToast({ title: '已复制到剪贴板', icon: 'success' })
// },
// fail: () => {
// uni.showToast({ title: '复制失败', icon: 'none' })
// }
// })
}
// ===== 转发消息 =====
const showForwardPopup = ref(false)
const forwardTargetMessage = ref(null)
const forwardSelectedId = ref('')
// 可转发的会话列表(排除当前会话)
const forwardConversations = computed(() => {
return UserConversations.value.filter(c => c._id !== currentSessionId.value)
})
// 转发内容预览
const forwardPreview = computed(() => {
if (!forwardTargetMessage.value) return ''
const obj = parseFileInfo(forwardTargetMessage.value.content || '')
return obj.textContent || ''
})
// 打开转发弹窗
const forwardMessage = (message) => {
if (ChatType.value !== 0) {
uni.showToast({ title: '仅支持在 AI 会话中转发', icon: 'none', duration: 1500 })
return
}
forwardTargetMessage.value = message
forwardSelectedId.value = ''
// 确保会话列表已加载
if (forwardConversations.value.length === 0 && UserConversations.value.length <= 1) {
uni.showToast({ title: '暂无其他会话可转发', icon: 'none', duration: 1500 })
return
}
showForwardPopup.value = true
}
// 取消转发
const cancelForward = () => {
showForwardPopup.value = false
forwardTargetMessage.value = null
forwardSelectedId.value = ''
}
// 格式化会话显示名称
const getConvName = (conv) => {
// 优先智能体名称
if (conv.agent_data?.title) return conv.agent_data.title
// 其次 title排除文件信息格式
if (conv.title && !conv.title.startsWith('[文件信息')) return conv.title
// 最后用创建时间
if (conv.created_at) {
const t = conv.created_at
const pad = (n) => String(n).padStart(2, '0')
return `${t.year}${t.month}${t.day}${pad(t.hour)}:${pad(t.minute)}:${pad(t.second)}`
}
return '未命名会话'
}
// 确认转发
const confirmForward = async () => {
if (!forwardSelectedId.value || !forwardTargetMessage.value) {
uni.showToast({ title: '请选择目标会话', icon: 'none' })
return
}
try {
uni.showLoading({ title: '转发中...' })
const message = forwardTargetMessage.value
const textContent = parseFileInfo(message.content || '').textContent || message.content || ''
const forwardContent = `[转发消息] ${textContent}`
await addMessageDict(userToken.value, message.role || 'user', forwardSelectedId.value, forwardContent)
uni.hideLoading()
uni.showToast({ title: '转发成功', icon: 'success', duration: 1500 })
// 刷新会话列表以更新 preview
await takeUserConversations()
cancelForward()
} catch (error) {
uni.hideLoading()
console.error('转发失败:', error)
uni.showToast({ title: `转发失败: ${error}`, icon: 'none', duration: 2000 })
}
}
const handleAgentSelect = async (agent) => {
closeAgentModal()
uni.showLoading({ title: '正在创建智能体...', mask: true })
@@ -2517,4 +2726,193 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
color: #fff;
font-weight: 500;
}
/* ========== 转发消息弹窗 ========== */
.forward-modal-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.45);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
z-index: 1001;
display: flex;
align-items: center;
justify-content: center;
}
.forward-modal {
width: 85%;
max-height: 72%;
background-color: #fff;
border-radius: 24rpx;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.12);
}
.forward-modal-header {
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding: 28rpx 30rpx 20rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.forward-modal-title {
font-size: 34rpx;
font-weight: 700;
color: #1a1a2e;
}
.forward-modal-close {
position: absolute;
right: 24rpx;
top: 50%;
transform: translateY(-50%);
}
.forward-preview-box {
margin: 20rpx 30rpx 8rpx;
padding: 20rpx 24rpx;
background: #f7f8fc;
border-radius: 14rpx;
border-left: 6rpx solid #007aff;
flex-shrink: 0;
}
.forward-preview-label {
font-size: 24rpx;
color: #999;
display: block;
margin-bottom: 8rpx;
}
.forward-preview-text {
font-size: 28rpx;
color: #333;
word-break: break-all;
max-height: 160rpx;
overflow-y: auto;
line-height: 1.5;
}
.forward-select-label {
font-size: 26rpx;
color: #666;
padding: 12rpx 30rpx 8rpx;
display: block;
flex-shrink: 0;
}
.forward-conv-list {
flex: 1;
height: 0;
min-height: 500rpx;
padding: 0 20rpx;
box-sizing: border-box;
}
.forward-empty {
display: flex;
justify-content: center;
align-items: center;
height: 160rpx;
color: #999;
font-size: 26rpx;
}
.forward-conv-item {
display: flex;
align-items: center;
padding: 22rpx 16rpx;
border-radius: 16rpx;
margin-bottom: 8rpx;
background: #fafafa;
transition: background 0.2s;
}
.forward-conv-item:active {
background: #eef4ff;
}
.forward-conv-selected {
background: #e8f0fe;
border: 2rpx solid #007aff;
}
.forward-conv-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 14rpx;
background: #e8f0fe;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
flex-shrink: 0;
}
.forward-conv-info {
flex: 1;
min-width: 0;
}
.forward-conv-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.forward-conv-check {
flex-shrink: 0;
margin-left: 12rpx;
}
.forward-modal-footer {
padding: 20rpx 30rpx 30rpx;
border-top: 2rpx solid #f0f0f0;
display: flex;
justify-content: flex-end;
gap: 20rpx;
flex-shrink: 0;
}
.forward-modal-btn {
padding: 18rpx 48rpx;
border-radius: 16rpx;
font-size: 28rpx;
font-weight: 500;
}
.forward-btn-cancel {
background: #f0f0f0;
color: #666;
}
.forward-btn-cancel:active {
background: #e0e0e0;
}
.forward-btn-confirm {
background: #007aff;
color: #fff;
}
.forward-btn-confirm:active {
background: #005fcc;
}
.forward-btn-disabled {
background: #b0d0f0;
color: #fff;
pointer-events: none;
}
</style>