feat(feedback): 增加反馈消息和对话标题的展开收起功能

在反馈模态框中添加消息内容和对话标题的展开/收起功能,优化长文本显示体验
添加相关样式和交互逻辑,支持自适应显示和折叠
移除消息内容长度限制,显示完整内容
This commit is contained in:
Wenjie Zhang 2025-12-24 13:17:12 +08:00
parent 44ede4d911
commit 15a7a32787
6 changed files with 165 additions and 46 deletions

View File

@ -718,7 +718,7 @@ async def get_all_feedbacks(
"rating": feedback.rating,
"reason": feedback.reason,
"created_at": feedback.created_at.isoformat(),
"message_content": message.content[:100] + ("..." if len(message.content) > 100 else ""),
"message_content": message.content,
"conversation_title": conversation.title,
"agent_id": conversation.agent_id,
}

View File

@ -1067,7 +1067,6 @@ watch(conversations, () => {
display: flex;
flex-direction: column;
overflow-x: hidden;
background: var(--gray-0);
position: relative;
box-sizing: border-box;
overflow-y: scroll;

View File

@ -18,16 +18,22 @@
<a-button
type="text"
size="small"
:icon="h(CopyOutlined)"
@click="copyDatabaseId"
title="复制知识库ID"
/>
>
<template #icon>
<Copy :size="14" />
</template>
</a-button>
<a-button
@click="showEditModal"
type="text"
size="small"
:icon="h(EditOutlined)"
/>
>
<template #icon>
<Pencil :size="14" />
</template>
</a-button>
</div>
</div>
@ -55,7 +61,10 @@
<a-modal v-model:open="editModalVisible" title="编辑知识库信息">
<template #footer>
<a-button danger @click="deleteDatabase" style="margin-right: auto; margin-left: 0;">
<DeleteOutlined /> 删除数据库
<template #icon>
<Trash2 :size="16" style="vertical-align: -3px; margin-right: 4px;" />
</template>
删除数据库
</a-button>
<a-button key="back" @click="editModalVisible = false">取消</a-button>
<a-button key="submit" type="primary" @click="handleEditSubmit">确定</a-button>
@ -97,12 +106,12 @@ import { useRouter } from 'vue-router';
import { useDatabaseStore } from '@/stores/database';
import { getKbTypeLabel, getKbTypeColor } from '@/utils/kb_utils';
import { message } from 'ant-design-vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import {
LeftOutlined,
EditOutlined,
DeleteOutlined,
CopyOutlined,
} from '@ant-design/icons-vue';
Pencil,
Trash2,
Copy,
} from 'lucide-vue-next';
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
import AiTextarea from '@/components/AiTextarea.vue';

View File

@ -48,25 +48,54 @@
</a-tag>
</div>
<!-- 卡片内容消息内容和对话信息 -->
<!-- 卡片内容对话信息消息内容和反馈原因 -->
<div class="card-content">
<div class="message-section">
<div class="message-content">{{ feedback.message_content }}</div>
</div>
<div class="conversation-section">
<!-- 对话标题 -->
<div class="conversation-section" v-if="feedback.conversation_title">
<div class="conversation-info">
<div class="info-item">
<span
class="conversation-title"
:class="{ 'collapsed': !expandedStates.get(`${feedback.id}-conversation`) }"
>
标题{{ feedback.conversation_title }}
</span>
<a-button
v-if="shouldShowConversationExpandButton(feedback.conversation_title)"
type="link"
size="small"
@click="toggleConversationExpand(feedback.id)"
class="expand-button-inline"
>
{{ expandedStates.get(`${feedback.id}-conversation`) ? '收起' : '展开' }}
</a-button>
</div>
<div class="info-item" v-if="!props.agentId">
<span class="label">智能体:</span>
<span class="value">{{ feedback.agent_id }}</span>
</div>
<div class="info-item" v-if="feedback.conversation_title">
<span class="label">对话:</span>
<span class="value">{{ feedback.conversation_title }}</span>
</div>
</div>
</div>
<!-- 消息内容 -->
<div class="message-section">
<div
class="message-content"
:class="{ 'collapsed': !expandedStates.get(`${feedback.id}-message`) }"
>
{{ feedback.message_content }}
</div>
<a-button
v-if="shouldShowExpandButton(feedback.message_content)"
type="link"
size="small"
@click="toggleExpand(feedback.id)"
class="expand-button"
>
{{ expandedStates.get(`${feedback.id}-message`) ? '收起' : '展开全部' }}
</a-button>
</div>
<!-- 反馈原因 -->
<div v-if="feedback.reason" class="reason-section">
<div class="reason-content">{{ feedback.reason }}</div>
@ -98,6 +127,14 @@ import { LikeOutlined, DislikeOutlined, ClockCircleOutlined } from '@ant-design/
import { dashboardApi } from '@/apis/dashboard_api'
import { formatFullDateTime } from '@/utils/time'
//
const CONFIG = {
MESSAGE_MAX_LINES: 8, //
CONVERSATION_MAX_LINES: 2, //
CONVERSATION_MAX_CHARS: 60, //
AVG_CHARS_PER_LINE: 30 //
}
// Props
const props = defineProps({
agentId: {
@ -119,6 +156,9 @@ const feedbackOptions = [
{ label: '点踩', value: 'dislike' }
]
// 使 Map
const expandedStates = ref(new Map())
//
const show = () => {
modalVisible.value = true
@ -128,6 +168,37 @@ const show = () => {
//
defineExpose({ show })
//
const estimateLines = (text) => {
if (!text) return 0
return Math.ceil(text.length / CONFIG.AVG_CHARS_PER_LINE)
}
//
const shouldShowExpandButton = (content) => {
return estimateLines(content) > CONFIG.MESSAGE_MAX_LINES
}
//
const shouldShowConversationExpandButton = (title) => {
if (!title) return false
return title.length > CONFIG.CONVERSATION_MAX_CHARS
}
// /
const toggleExpand = (feedbackId) => {
const key = `${feedbackId}-message`
const currentState = expandedStates.value.get(key) ?? false
expandedStates.value.set(key, !currentState)
}
// /
const toggleConversationExpand = (feedbackId) => {
const key = `${feedbackId}-conversation`
const currentState = expandedStates.value.get(key) ?? false
expandedStates.value.set(key, !currentState)
}
//
const loadFeedbacks = async () => {
loadingFeedbacks.value = true
@ -139,9 +210,12 @@ const loadFeedbacks = async () => {
const response = await dashboardApi.getFeedbacks(params)
feedbacks.value = response
//
expandedStates.value.clear()
} catch (error) {
console.error('加载反馈列表失败:', error)
message.error('加载反馈列表失败')
message.error('加载反馈列表失败,请稍后重试')
feedbacks.value = []
} finally {
loadingFeedbacks.value = false
}
@ -268,6 +342,25 @@ watch(() => props.agentId, () => {
line-height: 1.4;
color: var(--gray-800);
word-break: break-word;
overflow: hidden;
transition: max-height 0.3s ease;
}
.message-content.collapsed {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 8;
line-clamp: 8;
overflow: hidden;
text-overflow: ellipsis;
}
.expand-button {
padding: 0;
height: auto;
font-size: 12px;
margin-top: 8px;
color: var(--main-color);
}
.conversation-section {
@ -277,7 +370,7 @@ watch(() => props.agentId, () => {
.conversation-info {
display: flex;
flex-direction: column;
gap: 4px;
gap: 8px;
}
.info-item {
@ -297,6 +390,41 @@ watch(() => props.agentId, () => {
font-weight: 400;
word-break: break-all;
}
//
.conversation-title {
display: block;
color: var(--gray-700);
font-size: 13px;
font-weight: 500;
line-height: 1.4;
word-break: break-word;
transition: all 0.3s ease;
&.collapsed {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
}
// info-item
&:has(.conversation-title) {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
}
.expand-button-inline {
padding: 0;
height: auto;
font-size: 11px;
color: var(--main-color);
align-self: flex-start;
}
.reason-section {
@ -346,14 +474,8 @@ watch(() => props.agentId, () => {
gap: 12px;
}
.feedback-card {
margin-bottom: 0;
}
.card-header {
padding: 10px 12px;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
@ -362,19 +484,6 @@ watch(() => props.agentId, () => {
gap: 10px;
}
.conversation-info {
.info-item {
flex-direction: column;
align-items: flex-start;
gap: 2px;
.label {
min-width: auto;
margin-right: 0;
}
}
}
.card-footer {
padding: 6px 12px;
}

View File

@ -94,6 +94,8 @@ onMounted(async () => {
getRemoteConfig()
getRemoteDatabase()
fetchGithubStars() // Fetch GitHub stars on mount
//
taskerStore.loadTasks()
})
// 使 vue3 setup composition API

View File

@ -47,7 +47,7 @@
<span :style="{ color: !isEvaluationSupported ? 'var(--gray-400)' : '' }">
RAG评估
<a-tooltip v-if="!isEvaluationSupported" title="仅支持 Milvus 类型的知识库">
<InfoCircleOutlined style="margin-left: 4px;" />
<Info :size="14" style="margin-left: 4px; vertical-align: middle;" />
</a-tooltip>
</span>
</template>
@ -62,7 +62,7 @@
<span :style="{ color: !isEvaluationSupported ? 'var(--gray-400)' : '' }">
评估基准
<a-tooltip v-if="!isEvaluationSupported" title="仅支持 Milvus 类型的知识库">
<InfoCircleOutlined style="margin-left: 4px;" />
<Info :size="14" style="margin-left: 4px; vertical-align: middle;" />
</a-tooltip>
</span>
</template>
@ -93,7 +93,7 @@ import { onMounted, reactive, ref, watch, onUnmounted, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useDatabaseStore } from '@/stores/database';
import { useTaskerStore } from '@/stores/tasker';
import { InfoCircleOutlined } from '@ant-design/icons-vue';
import { Info } from 'lucide-vue-next';
import KnowledgeBaseCard from '@/components/KnowledgeBaseCard.vue';
import FileTable from '@/components/FileTable.vue';
import FileDetailModal from '@/components/FileDetailModal.vue';