detail.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <template>
  2. <div class="store-page">
  3. <Breadcrumb />
  4. <a-card title="AI 对话详情" :loading="loading">
  5. <a-descriptions :column="{ xs: 1, sm: 2 }" bordered>
  6. <a-descriptions-item label="会话编号">{{ data.conversation_no || '-' }}</a-descriptions-item>
  7. <a-descriptions-item label="内部ID">{{ data.id || '-' }}</a-descriptions-item>
  8. <a-descriptions-item label="标题">{{ data.title || '-' }}</a-descriptions-item>
  9. <a-descriptions-item label="用户">{{ data.username || '-' }}</a-descriptions-item>
  10. <a-descriptions-item label="用户UUID">{{ data.create_user || '-' }}</a-descriptions-item>
  11. <a-descriptions-item label="状态">{{ data.state === 1 ? '正常' : '已删除' }}</a-descriptions-item>
  12. <a-descriptions-item label="更新时间">{{ formatTime(data.update_time) }}</a-descriptions-item>
  13. </a-descriptions>
  14. </a-card>
  15. <a-card title="消息记录" class="message-card">
  16. <div v-if="!data.messages?.length" class="empty">暂无消息</div>
  17. <div v-for="message in data.messages" :key="message.id" class="message">
  18. <div class="message__head">
  19. <a-avatar :size="34">
  20. <img v-if="message.role === 'assistant'" src="@/assets/img/ai.png" alt="小妍助理" />
  21. <icon-user v-else-if="message.role === 'user'" />
  22. <icon-info-circle v-else />
  23. </a-avatar>
  24. <div>
  25. <div class="message__name">
  26. {{ roleLabel(message.role) }}
  27. <a-tag v-if="message.status === 'error'" color="red" size="small">异常</a-tag>
  28. </div>
  29. <div class="message__time">{{ formatTime(message.create_time) }}</div>
  30. </div>
  31. </div>
  32. <div v-if="message.content" class="message__content">{{ message.content }}</div>
  33. <div v-if="message.images?.length" class="message__images">
  34. <a-image v-for="url in message.images" :key="url" :src="url" width="120" height="120" fit="cover" />
  35. </div>
  36. <div v-if="message.error_msg" class="message__error">{{ message.error_msg }}</div>
  37. </div>
  38. </a-card>
  39. </div>
  40. </template>
  41. <script setup>
  42. import { onMounted, ref } from 'vue'
  43. import { useRoute } from 'vue-router'
  44. import { Notification } from '@arco-design/web-vue'
  45. import { adminGetAIChatConversationDetail } from '@/api/aiChat'
  46. const route = useRoute()
  47. const loading = ref(false)
  48. const data = ref({})
  49. const roleLabel = (role) => ({ user: '用户', assistant: '小妍助理', system: '系统' }[role] || role)
  50. const formatTime = (time) => time ? new Date(Number(time)).toLocaleString('zh-CN', { hour12: false }) : '-'
  51. const fetchDetail = async () => {
  52. loading.value = true
  53. try {
  54. const res = await adminGetAIChatConversationDetail({ id: route.params.id })
  55. if (!res || res.code !== 0) return Notification.error({ title: '获取 AI 对话详情失败', content: res?.msg || '请稍后再试' })
  56. data.value = res.data || {}
  57. } finally {
  58. loading.value = false
  59. }
  60. }
  61. onMounted(fetchDetail)
  62. </script>
  63. <style scoped lang="less">
  64. @import '@/styles/store-theme.less';
  65. .message-card {
  66. margin-top: 15px;
  67. }
  68. .empty {
  69. padding: 24px;
  70. text-align: center;
  71. color: @store-text-muted;
  72. }
  73. .message {
  74. padding: 14px 0;
  75. border-bottom: 1px solid var(--color-border-2);
  76. &:last-child {
  77. border-bottom: 0;
  78. }
  79. &__head {
  80. display: flex;
  81. align-items: flex-start;
  82. gap: 10px;
  83. }
  84. &__name {
  85. display: flex;
  86. align-items: center;
  87. gap: 8px;
  88. font-weight: 650;
  89. color: @store-primary;
  90. }
  91. &__time {
  92. margin-top: 2px;
  93. font-size: 12px;
  94. color: @store-text-muted;
  95. }
  96. &__content {
  97. margin: 10px 0 0 44px;
  98. max-width: 900px;
  99. white-space: pre-wrap;
  100. word-break: break-word;
  101. line-height: 1.7;
  102. }
  103. &__images {
  104. display: flex;
  105. flex-wrap: wrap;
  106. gap: 8px;
  107. margin: 10px 0 0 44px;
  108. }
  109. &__error {
  110. margin: 8px 0 0 44px;
  111. color: rgb(var(--red-6));
  112. }
  113. }
  114. </style>