mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-18 01:06:39 +08:00
feat: chat export function is ready
This commit is contained in:
parent
7903eed284
commit
8f1e28c0ab
@ -10,44 +10,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// List 获取会话列表
|
|
||||||
func (h *ChatHandler) List(c *gin.Context) {
|
|
||||||
userId := h.GetInt(c, "user_id", 0)
|
|
||||||
if userId == 0 {
|
|
||||||
resp.ERROR(c, "The parameter 'user_id' is needed.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var items = make([]vo.ChatItem, 0)
|
|
||||||
var chats []model.ChatItem
|
|
||||||
res := h.db.Where("user_id = ?", userId).Order("id DESC").Find(&chats)
|
|
||||||
if res.Error == nil {
|
|
||||||
var roleIds = make([]uint, 0)
|
|
||||||
for _, chat := range chats {
|
|
||||||
roleIds = append(roleIds, chat.RoleId)
|
|
||||||
}
|
|
||||||
var roles []model.ChatRole
|
|
||||||
res = h.db.Find(&roles, roleIds)
|
|
||||||
if res.Error == nil {
|
|
||||||
roleMap := make(map[uint]model.ChatRole)
|
|
||||||
for _, role := range roles {
|
|
||||||
roleMap[role.Id] = role
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, chat := range chats {
|
|
||||||
var item vo.ChatItem
|
|
||||||
err := utils.CopyObject(chat, &item)
|
|
||||||
if err == nil {
|
|
||||||
item.Id = chat.Id
|
|
||||||
item.Icon = roleMap[chat.RoleId].Icon
|
|
||||||
items = append(items, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
resp.SUCCESS(c, items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update 更新会话标题
|
// Update 更新会话标题
|
||||||
func (h *ChatHandler) Update(c *gin.Context) {
|
func (h *ChatHandler) Update(c *gin.Context) {
|
||||||
var data struct {
|
var data struct {
|
||||||
|
71
api/handler/chat_item_handler.go
Normal file
71
api/handler/chat_item_handler.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"chatplus/store/model"
|
||||||
|
"chatplus/store/vo"
|
||||||
|
"chatplus/utils"
|
||||||
|
"chatplus/utils/resp"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List 获取会话列表
|
||||||
|
func (h *ChatHandler) List(c *gin.Context) {
|
||||||
|
userId := h.GetInt(c, "user_id", 0)
|
||||||
|
if userId == 0 {
|
||||||
|
resp.ERROR(c, "The parameter 'user_id' is needed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var items = make([]vo.ChatItem, 0)
|
||||||
|
var chats []model.ChatItem
|
||||||
|
res := h.db.Where("user_id = ?", userId).Order("id DESC").Find(&chats)
|
||||||
|
if res.Error == nil {
|
||||||
|
var roleIds = make([]uint, 0)
|
||||||
|
for _, chat := range chats {
|
||||||
|
roleIds = append(roleIds, chat.RoleId)
|
||||||
|
}
|
||||||
|
var roles []model.ChatRole
|
||||||
|
res = h.db.Find(&roles, roleIds)
|
||||||
|
if res.Error == nil {
|
||||||
|
roleMap := make(map[uint]model.ChatRole)
|
||||||
|
for _, role := range roles {
|
||||||
|
roleMap[role.Id] = role
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, chat := range chats {
|
||||||
|
var item vo.ChatItem
|
||||||
|
err := utils.CopyObject(chat, &item)
|
||||||
|
if err == nil {
|
||||||
|
item.Id = chat.Id
|
||||||
|
item.Icon = roleMap[chat.RoleId].Icon
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
resp.SUCCESS(c, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChatHandler) Detail(c *gin.Context) {
|
||||||
|
chatId := h.GetTrim(c, "chat_id")
|
||||||
|
if utils.IsEmptyValue(chatId) {
|
||||||
|
resp.ERROR(c, "Invalid chatId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatItem model.ChatItem
|
||||||
|
res := h.db.Where("chat_id = ?", chatId).First(&chatItem)
|
||||||
|
if res.Error != nil {
|
||||||
|
resp.ERROR(c, "No chat found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatItemVo vo.ChatItem
|
||||||
|
err := utils.CopyObject(chatItem, &chatItemVo)
|
||||||
|
if err != nil {
|
||||||
|
resp.ERROR(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.SUCCESS(c, chatItemVo)
|
||||||
|
}
|
@ -169,6 +169,7 @@ func main() {
|
|||||||
group := s.Engine.Group("/api/chat/")
|
group := s.Engine.Group("/api/chat/")
|
||||||
group.Any("new", h.ChatHandle)
|
group.Any("new", h.ChatHandle)
|
||||||
group.GET("list", h.List)
|
group.GET("list", h.List)
|
||||||
|
group.GET("detail", h.Detail)
|
||||||
group.POST("update", h.Update)
|
group.POST("update", h.Update)
|
||||||
group.GET("remove", h.Remove)
|
group.GET("remove", h.Remove)
|
||||||
group.GET("history", h.History)
|
group.GET("history", h.History)
|
||||||
|
@ -26,6 +26,12 @@ const routes = [
|
|||||||
meta: {title: 'ChatGPT-智能助手V3'},
|
meta: {title: 'ChatGPT-智能助手V3'},
|
||||||
component: () => import('@/views/ChatPlus.vue'),
|
component: () => import('@/views/ChatPlus.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'chat-export',
|
||||||
|
path: '/chat/export',
|
||||||
|
meta: {title: '导出会话记录'},
|
||||||
|
component: () => import('@/views/ChatExport.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/login',
|
path: '/admin/login',
|
||||||
name: 'admin-login',
|
name: 'admin-login',
|
||||||
|
155
web/src/views/ChatExport.vue
Normal file
155
web/src/views/ChatExport.vue
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="chat-export" v-loading="loading">
|
||||||
|
<div class="chat-box" id="chat-box">
|
||||||
|
<div class="title">
|
||||||
|
<h2>{{ chatTitle }}</h2>
|
||||||
|
<el-button type="success" @click="exportChat" :icon="Promotion">
|
||||||
|
导出 PDF 文档
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="item in chatData" :key="item.id">
|
||||||
|
<chat-prompt
|
||||||
|
v-if="item.type==='prompt'"
|
||||||
|
:icon="item.icon"
|
||||||
|
:created-at="dateFormat(item['created_at'])"
|
||||||
|
:tokens="item['tokens']"
|
||||||
|
:model="model"
|
||||||
|
:content="item.content"/>
|
||||||
|
<chat-reply v-else-if="item.type==='reply'"
|
||||||
|
:icon="item.icon"
|
||||||
|
:org-content="item.orgContent"
|
||||||
|
:created-at="dateFormat(item['created_at'])"
|
||||||
|
:tokens="item['tokens']"
|
||||||
|
:content="item.content"/>
|
||||||
|
</div>
|
||||||
|
</div><!-- end chat box -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import {dateFormat} from "@/utils/libs";
|
||||||
|
import ChatReply from "@/components/ChatReply.vue";
|
||||||
|
import ChatPrompt from "@/components/ChatPrompt.vue";
|
||||||
|
import {nextTick, ref} from "vue";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {httpGet} from "@/utils/http";
|
||||||
|
import 'highlight.js/styles/a11y-dark.css'
|
||||||
|
import hl from "highlight.js";
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import {Promotion} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
const chatData = ref([])
|
||||||
|
const router = useRouter()
|
||||||
|
const chatId = router.currentRoute.value.query['chat_id']
|
||||||
|
const loading = ref(true)
|
||||||
|
const chatTitle = ref('')
|
||||||
|
|
||||||
|
httpGet('/api/chat/history?chat_id=' + chatId).then(res => {
|
||||||
|
const data = res.data
|
||||||
|
if (!data) {
|
||||||
|
loading.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const md = require('markdown-it')({breaks: true});
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].type === "prompt") {
|
||||||
|
chatData.value.push(data[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[i].orgContent = data[i].content;
|
||||||
|
data[i].content = md.render(data[i].content);
|
||||||
|
chatData.value.push(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
hl.configure({ignoreUnescapedHTML: true})
|
||||||
|
const blocks = document.querySelector("#chat-box").querySelectorAll('pre code');
|
||||||
|
blocks.forEach((block) => {
|
||||||
|
hl.highlightElement(block)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
loading.value = false
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error('加载聊天记录失败:' + e.message);
|
||||||
|
})
|
||||||
|
|
||||||
|
httpGet('/api/chat/detail?chat_id=' + chatId).then(res => {
|
||||||
|
chatTitle.value = res.data.title
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("加载会失败: " + e.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
const exportChat = () => {
|
||||||
|
window.print()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="stylus">
|
||||||
|
.chat-export {
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
|
||||||
|
.chat-box {
|
||||||
|
max-width 800px;
|
||||||
|
// 变量定义
|
||||||
|
--content-font-size: 16px;
|
||||||
|
--content-color: #c1c1c1;
|
||||||
|
|
||||||
|
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||||
|
padding: 0 0 50px 0;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align center
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.chat-line {
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.chat-line-inner {
|
||||||
|
.content {
|
||||||
|
padding-top: 0
|
||||||
|
font-size 16px;
|
||||||
|
|
||||||
|
p:first-child {
|
||||||
|
margin-top 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-line-reply {
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
|
||||||
|
.chat-line-inner {
|
||||||
|
display flex
|
||||||
|
|
||||||
|
.copy-reply {
|
||||||
|
display none
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-item {
|
||||||
|
background-color: #f7f7f8;
|
||||||
|
color: #888;
|
||||||
|
padding: 3px 5px;
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-icon {
|
||||||
|
margin-right: 20px
|
||||||
|
|
||||||
|
img {
|
||||||
|
width 30px
|
||||||
|
height 30px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -780,7 +780,9 @@ const exportChat = () => {
|
|||||||
return ElMessage.error("请先选中一个会话")
|
return ElMessage.error("请先选中一个会话")
|
||||||
}
|
}
|
||||||
|
|
||||||
window.open(location.protocol + location.host + '/chat/export?chat_id=' + activeChat.value['chat_id'], '_blank');
|
const url = location.protocol + '//' + location.host + '/chat/export?chat_id=' + activeChat.value['chat_id']
|
||||||
|
// console.log(url)
|
||||||
|
window.open(url, '_blank');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user