- 为聊天历史面板添加加载更多功能,支持分页加载会话列表 - 优化会话列表按时间降序排序和日期分组逻辑 - 统一复制功能使用工具函数,改进错误处理 - 修复兑换码管理菜单路径缺少斜杠的问题 - 优化Dify服务用户标识生成,按agentId隔离会话 - 重构Dify服务扣费逻辑,提取通用处理方法
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
AlertDialogTitle
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import { sendChatStream } from '@/api/agent'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
import HistoryPanel from './HistoryPanel.vue'
|
||||
import ChatDrawerHeader from './ChatDrawerHeader.vue'
|
||||
import ChatDrawerEmpty from './ChatDrawerEmpty.vue'
|
||||
@@ -90,10 +91,10 @@ const handleGenerate = async () => {
|
||||
}
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(generatedContent.value)
|
||||
const success = await copyToClipboard(generatedContent.value)
|
||||
if (success) {
|
||||
toast.success('已复制')
|
||||
} catch {
|
||||
} else {
|
||||
toast.error('复制失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,18 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load More -->
|
||||
<div v-if="hasMore" class="load-more">
|
||||
<button
|
||||
class="load-more-btn"
|
||||
:disabled="loading"
|
||||
@click="loadConversations(true)"
|
||||
>
|
||||
<Icon v-if="loading" icon="lucide:loader-2" class="size-4 animate-spin" />
|
||||
<span>{{ loading ? '加载中...' : '加载更多' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -171,8 +183,10 @@ const conversationList = ref([])
|
||||
const selectedConversation = ref(null)
|
||||
const messageList = ref([])
|
||||
const messageLoading = ref(false)
|
||||
const lastId = ref(null)
|
||||
const hasMore = ref(true)
|
||||
|
||||
// 按日期分组
|
||||
// 按日期分组(先排序再分组)
|
||||
const groupedConversations = computed(() => {
|
||||
const today = dayjs().startOf('day')
|
||||
const yesterday = dayjs().subtract(1, 'day').startOf('day')
|
||||
@@ -185,7 +199,14 @@ const groupedConversations = computed(() => {
|
||||
older: { label: '更早', items: [] }
|
||||
}
|
||||
|
||||
conversationList.value.forEach(item => {
|
||||
// 按时间降序排序
|
||||
const sortedList = [...conversationList.value].sort((a, b) => {
|
||||
const timeA = (a.updatedAt || a.createdAt) * 1000
|
||||
const timeB = (b.updatedAt || b.createdAt) * 1000
|
||||
return timeB - timeA
|
||||
})
|
||||
|
||||
sortedList.forEach(item => {
|
||||
const date = dayjs((item.updatedAt || item.createdAt) * 1000)
|
||||
if (date.isAfter(today)) {
|
||||
groups.today.items.push(item)
|
||||
@@ -202,13 +223,27 @@ const groupedConversations = computed(() => {
|
||||
})
|
||||
|
||||
// Methods
|
||||
const loadConversations = async () => {
|
||||
const loadConversations = async (loadMore = false) => {
|
||||
if (!props.agentId) return
|
||||
if (loadMore && !hasMore.value) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getConversations({ agentId: props.agentId, limit: 50 })
|
||||
const params = { agentId: props.agentId, limit: 20 }
|
||||
if (loadMore && lastId.value) {
|
||||
params.lastId = lastId.value
|
||||
}
|
||||
const res = await getConversations(params)
|
||||
if (res.code === 0) {
|
||||
conversationList.value = res.data?.data || []
|
||||
const data = res.data?.data || []
|
||||
if (loadMore) {
|
||||
conversationList.value = [...conversationList.value, ...data]
|
||||
} else {
|
||||
conversationList.value = data
|
||||
}
|
||||
// 更新分页状态
|
||||
lastId.value = data.length > 0 ? data[data.length - 1].id : null
|
||||
hasMore.value = data.length === 20 && res.data?.hasMore !== false
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载会话列表失败:', e)
|
||||
@@ -249,7 +284,11 @@ const handleClose = () => {
|
||||
|
||||
const copyContent = async (content) => {
|
||||
const success = await copyToClipboard(content)
|
||||
success ? toast.success('已复制到剪贴板') : toast.error('复制失败')
|
||||
if (success) {
|
||||
toast.success('已复制到剪贴板')
|
||||
} else {
|
||||
toast.error('复制失败')
|
||||
}
|
||||
}
|
||||
|
||||
const formatTime = (timestamp) => {
|
||||
@@ -257,15 +296,10 @@ const formatTime = (timestamp) => {
|
||||
const date = dayjs(timestamp * 1000)
|
||||
const now = dayjs()
|
||||
|
||||
if (date.isSame(now, 'day')) {
|
||||
return date.format('HH:mm')
|
||||
} else if (date.isSame(now.subtract(1, 'day'), 'day')) {
|
||||
return '昨天 ' + date.format('HH:mm')
|
||||
} else if (date.isAfter(now.subtract(7, 'day'))) {
|
||||
return date.format('dddd HH:mm')
|
||||
} else {
|
||||
return date.format('MM-DD HH:mm')
|
||||
}
|
||||
if (date.isSame(now, 'day')) return date.format('HH:mm')
|
||||
if (date.isSame(now.subtract(1, 'day'), 'day')) return '昨天 ' + date.format('HH:mm')
|
||||
if (date.isAfter(now.subtract(7, 'day'))) return date.format('dddd HH:mm')
|
||||
return date.format('MM-DD HH:mm')
|
||||
}
|
||||
|
||||
// Watch
|
||||
@@ -530,6 +564,36 @@ watch(() => props.visible, (val) => {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.load-more-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid var(--color-gray-200);
|
||||
background: var(--color-bg-card);
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
color: var(--color-gray-600);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
border-color: var(--color-primary-300);
|
||||
color: var(--color-primary-500);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-group {
|
||||
animation: groupFadeIn 0.4s ease-out backwards;
|
||||
animation-delay: calc(var(--group-index) * 0.1s);
|
||||
|
||||
Reference in New Issue
Block a user