commit
ef7a48ace8
|
@ -52,3 +52,4 @@ management/models--opendatalab--PDF-Extract-Kit-1.0
|
|||
management/models--hantian--layoutreader
|
||||
docker/models
|
||||
management/web/types/auto
|
||||
node_modules/.cache/logger/umi.log
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import database
|
||||
import jwt
|
||||
import os
|
||||
from flask import Flask, jsonify, request
|
||||
from flask import Flask, request
|
||||
from flask_cors import CORS
|
||||
from datetime import datetime, timedelta
|
||||
from routes import register_routes
|
||||
|
|
|
@ -7,6 +7,7 @@ teams_bp = Blueprint('teams', __name__, url_prefix='/api/v1/teams')
|
|||
tenants_bp = Blueprint('tenants', __name__, url_prefix='/api/v1/tenants')
|
||||
files_bp = Blueprint('files', __name__, url_prefix='/api/v1/files')
|
||||
knowledgebase_bp = Blueprint('knowledgebases', __name__, url_prefix='/api/v1/knowledgebases')
|
||||
conversation_bp = Blueprint('conversation', __name__, url_prefix='/api/v1/conversation')
|
||||
|
||||
# 导入路由
|
||||
from .users.routes import *
|
||||
|
@ -14,6 +15,7 @@ from .teams.routes import *
|
|||
from .tenants.routes import *
|
||||
from .files.routes import *
|
||||
from .knowledgebases.routes import *
|
||||
from .conversation.routes import *
|
||||
|
||||
|
||||
def register_routes(app):
|
||||
|
@ -23,3 +25,4 @@ def register_routes(app):
|
|||
app.register_blueprint(tenants_bp)
|
||||
app.register_blueprint(files_bp)
|
||||
app.register_blueprint(knowledgebase_bp)
|
||||
app.register_blueprint(conversation_bp)
|
|
@ -0,0 +1,63 @@
|
|||
from flask import jsonify, request
|
||||
from services.conversation.service import get_conversations_by_user_id, get_messages_by_conversation_id, get_conversation_detail
|
||||
from .. import conversation_bp
|
||||
|
||||
|
||||
@conversation_bp.route("", methods=["GET"])
|
||||
def get_conversations():
|
||||
"""获取对话列表的API端点,支持分页和条件查询"""
|
||||
try:
|
||||
# 获取查询参数
|
||||
user_id = request.args.get("user_id")
|
||||
page = int(request.args.get("page", 1))
|
||||
size = int(request.args.get("size", 20))
|
||||
sort_by = request.args.get("sort_by", "update_time")
|
||||
sort_order = request.args.get("sort_order", "desc")
|
||||
|
||||
# 参数验证
|
||||
if not user_id:
|
||||
return jsonify({"code": 400, "message": "用户ID不能为空"}), 400
|
||||
|
||||
# 调用服务函数获取分页和筛选后的对话数据
|
||||
conversations, total = get_conversations_by_user_id(user_id, page, size, sort_by, sort_order)
|
||||
|
||||
# 返回符合前端期望格式的数据
|
||||
return jsonify({"code": 0, "data": {"list": conversations, "total": total}, "message": "获取对话列表成功"})
|
||||
except Exception as e:
|
||||
# 错误处理
|
||||
return jsonify({"code": 500, "message": f"获取对话列表失败: {str(e)}"}), 500
|
||||
|
||||
|
||||
@conversation_bp.route("/<conversation_id>/messages", methods=["GET"])
|
||||
def get_messages(conversation_id):
|
||||
"""获取特定对话的消息列表"""
|
||||
try:
|
||||
# 获取查询参数
|
||||
page = int(request.args.get("page", 1))
|
||||
size = int(request.args.get("size", 30))
|
||||
|
||||
# 调用服务函数获取消息数据
|
||||
messages, total = get_messages_by_conversation_id(conversation_id, page, size)
|
||||
|
||||
# 返回符合前端期望格式的数据
|
||||
return jsonify({"code": 0, "data": {"list": messages, "total": total}, "message": "获取消息列表成功"})
|
||||
except Exception as e:
|
||||
# 错误处理
|
||||
return jsonify({"code": 500, "message": f"获取消息列表失败: {str(e)}"}), 500
|
||||
|
||||
|
||||
@conversation_bp.route("/<conversation_id>", methods=["GET"])
|
||||
def get_conversation(conversation_id):
|
||||
"""获取特定对话的详细信息"""
|
||||
try:
|
||||
# 调用服务函数获取对话详情
|
||||
conversation = get_conversation_detail(conversation_id)
|
||||
|
||||
if not conversation:
|
||||
return jsonify({"code": 404, "message": "对话不存在"}), 404
|
||||
|
||||
# 返回符合前端期望格式的数据
|
||||
return jsonify({"code": 0, "data": conversation, "message": "获取对话详情成功"})
|
||||
except Exception as e:
|
||||
# 错误处理
|
||||
return jsonify({"code": 500, "message": f"获取对话详情失败: {str(e)}"}), 500
|
|
@ -0,0 +1,267 @@
|
|||
import mysql.connector
|
||||
from database import DB_CONFIG
|
||||
|
||||
|
||||
def get_conversations_by_user_id(user_id, page=1, size=20, sort_by="update_time", sort_order="desc"):
|
||||
"""
|
||||
根据用户ID获取对话列表
|
||||
|
||||
参数:
|
||||
user_id (str): 用户ID
|
||||
page (int): 当前页码
|
||||
size (int): 每页大小
|
||||
sort_by (str): 排序字段
|
||||
sort_order (str): 排序方式 (asc/desc)
|
||||
|
||||
返回:
|
||||
tuple: (对话列表, 总数)
|
||||
"""
|
||||
try:
|
||||
conn = mysql.connector.connect(**DB_CONFIG)
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
|
||||
# 直接使用user_id作为tenant_id
|
||||
tenant_id = user_id
|
||||
|
||||
print(f"查询用户ID: {user_id}, 租户ID: {tenant_id}")
|
||||
|
||||
# 查询总记录数
|
||||
count_sql = """
|
||||
SELECT COUNT(*) as total
|
||||
FROM dialog d
|
||||
WHERE d.tenant_id = %s
|
||||
"""
|
||||
cursor.execute(count_sql, (tenant_id,))
|
||||
total = cursor.fetchone()["total"]
|
||||
|
||||
print(f"查询到总记录数: {total}")
|
||||
|
||||
# 计算分页偏移量
|
||||
offset = (page - 1) * size
|
||||
|
||||
# 确定排序方向
|
||||
sort_direction = "DESC" if sort_order.lower() == "desc" else "ASC"
|
||||
|
||||
# 执行分页查询
|
||||
query = f"""
|
||||
SELECT
|
||||
d.id,
|
||||
d.name,
|
||||
d.create_date,
|
||||
d.update_date,
|
||||
d.tenant_id
|
||||
FROM
|
||||
dialog d
|
||||
WHERE
|
||||
d.tenant_id = %s
|
||||
ORDER BY
|
||||
d.{sort_by} {sort_direction}
|
||||
LIMIT %s OFFSET %s
|
||||
"""
|
||||
|
||||
print(f"执行查询: {query}")
|
||||
print(f"参数: tenant_id={tenant_id}, size={size}, offset={offset}")
|
||||
|
||||
cursor.execute(query, (tenant_id, size, offset))
|
||||
results = cursor.fetchall()
|
||||
|
||||
print(f"查询结果数量: {len(results)}")
|
||||
|
||||
# 获取每个对话的最新消息
|
||||
conversations = []
|
||||
for dialog in results:
|
||||
# 查询对话的所有消息
|
||||
conv_query = """
|
||||
SELECT id, message, name
|
||||
FROM conversation
|
||||
WHERE dialog_id = %s
|
||||
ORDER BY create_date DESC
|
||||
"""
|
||||
cursor.execute(conv_query, (dialog["id"],))
|
||||
conv_results = cursor.fetchall()
|
||||
|
||||
latest_message = ""
|
||||
conversation_name = dialog["name"] # 默认使用dialog的name
|
||||
if conv_results and len(conv_results) > 0:
|
||||
# 获取最新的一条对话记录
|
||||
latest_conv = conv_results[0]
|
||||
# 如果conversation有name,优先使用conversation的name
|
||||
if latest_conv and latest_conv.get("name"):
|
||||
conversation_name = latest_conv["name"]
|
||||
|
||||
if latest_conv and latest_conv["message"]:
|
||||
# 获取最后一条消息内容
|
||||
messages = latest_conv["message"]
|
||||
if messages and len(messages) > 0:
|
||||
# 检查消息类型,处理字符串和字典两种情况
|
||||
if isinstance(messages[-1], dict):
|
||||
latest_message = messages[-1].get("content", "")
|
||||
elif isinstance(messages[-1], str):
|
||||
latest_message = messages[-1]
|
||||
else:
|
||||
latest_message = str(messages[-1])
|
||||
|
||||
conversations.append(
|
||||
{
|
||||
"id": dialog["id"],
|
||||
"name": conversation_name,
|
||||
"latestMessage": latest_message[:100] + "..." if len(latest_message) > 100 else latest_message,
|
||||
"createTime": dialog["create_date"].strftime("%Y-%m-%d %H:%M:%S") if dialog["create_date"] else "",
|
||||
"updateTime": dialog["update_date"].strftime("%Y-%m-%d %H:%M:%S") if dialog["update_date"] else "",
|
||||
}
|
||||
)
|
||||
|
||||
# 关闭连接
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return conversations, total
|
||||
|
||||
except mysql.connector.Error as err:
|
||||
print(f"数据库错误: {err}")
|
||||
# 更详细的错误日志
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return [], 0
|
||||
except Exception as e:
|
||||
print(f"未知错误: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return [], 0
|
||||
|
||||
|
||||
def get_messages_by_conversation_id(conversation_id, page=1, size=30):
|
||||
"""
|
||||
获取特定对话的详细信息
|
||||
|
||||
参数:
|
||||
conversation_id (str): 对话ID
|
||||
page (int): 当前页码
|
||||
size (int): 每页大小
|
||||
|
||||
返回:
|
||||
tuple: (对话详情, 总数)
|
||||
"""
|
||||
try:
|
||||
conn = mysql.connector.connect(**DB_CONFIG)
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
|
||||
# 查询对话信息
|
||||
query = """
|
||||
SELECT *
|
||||
FROM conversation
|
||||
WHERE dialog_id = %s
|
||||
ORDER BY create_date DESC
|
||||
"""
|
||||
cursor.execute(query, (conversation_id,))
|
||||
result = cursor.fetchall() # 确保读取所有结果
|
||||
|
||||
if not result:
|
||||
print(f"未找到对话ID: {conversation_id}")
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return None, 0
|
||||
|
||||
# 获取第一条记录作为对话详情
|
||||
conversation = None
|
||||
if len(result) > 0:
|
||||
conversation = {
|
||||
"id": result[0]["id"],
|
||||
"dialogId": result[0].get("dialog_id", ""),
|
||||
"createTime": result[0]["create_date"].strftime("%Y-%m-%d %H:%M:%S") if result[0].get("create_date") else "",
|
||||
"updateTime": result[0]["update_date"].strftime("%Y-%m-%d %H:%M:%S") if result[0].get("update_date") else "",
|
||||
"messages": result[0].get("message", []),
|
||||
}
|
||||
|
||||
# 打印调试信息
|
||||
print(f"获取到对话详情: ID={conversation_id}")
|
||||
print(f"消息长度: {len(conversation['messages']) if conversation and conversation.get('messages') else 0}")
|
||||
|
||||
# 关闭连接
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
# 返回对话详情和消息总数
|
||||
total = len(conversation["messages"]) if conversation and conversation.get("messages") else 0
|
||||
return conversation, total
|
||||
|
||||
except mysql.connector.Error as err:
|
||||
print(f"数据库错误: {err}")
|
||||
# 更详细的错误日志
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return None, 0
|
||||
except Exception as e:
|
||||
print(f"未知错误: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return None, 0
|
||||
|
||||
|
||||
def get_conversation_detail(conversation_id):
|
||||
"""
|
||||
获取特定对话的详细信息
|
||||
|
||||
参数:
|
||||
conversation_id (str): 对话ID
|
||||
|
||||
返回:
|
||||
dict: 对话详情
|
||||
"""
|
||||
try:
|
||||
conn = mysql.connector.connect(**DB_CONFIG)
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
|
||||
# 查询对话信息
|
||||
query = """
|
||||
SELECT c.*, d.name as dialog_name, d.icon as dialog_icon
|
||||
FROM conversation c
|
||||
LEFT JOIN dialog d ON c.dialog_id = d.id
|
||||
WHERE c.id = %s
|
||||
"""
|
||||
cursor.execute(query, (conversation_id,))
|
||||
result = cursor.fetchone()
|
||||
|
||||
if not result:
|
||||
print(f"未找到对话ID: {conversation_id}")
|
||||
return None
|
||||
|
||||
# 格式化对话详情
|
||||
conversation = {
|
||||
"id": result["id"],
|
||||
"name": result.get("name", ""),
|
||||
"dialogId": result.get("dialog_id", ""),
|
||||
"dialogName": result.get("dialog_name", ""),
|
||||
"dialogIcon": result.get("dialog_icon", ""),
|
||||
"createTime": result["create_date"].strftime("%Y-%m-%d %H:%M:%S") if result.get("create_date") else "",
|
||||
"updateTime": result["update_date"].strftime("%Y-%m-%d %H:%M:%S") if result.get("update_date") else "",
|
||||
"messages": result.get("message", []),
|
||||
}
|
||||
|
||||
# 打印调试信息
|
||||
print(f"获取到对话详情: ID={conversation_id}")
|
||||
print(f"消息数量: {len(conversation['messages']) if conversation['messages'] else 0}")
|
||||
|
||||
# 关闭连接
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return conversation
|
||||
|
||||
except mysql.connector.Error as err:
|
||||
print(f"数据库错误: {err}")
|
||||
# 更详细的错误日志
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"未知错误: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return None
|
|
@ -205,7 +205,7 @@ class KnowledgebaseService:
|
|||
SELECT llm_name
|
||||
FROM tenant_llm
|
||||
WHERE model_type = 'embedding'
|
||||
ORDER BY create_time ASC
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
cursor.execute(query_embedding_model)
|
||||
|
@ -213,6 +213,9 @@ class KnowledgebaseService:
|
|||
|
||||
if embedding_model and embedding_model.get('llm_name'):
|
||||
dynamic_embd_id = embedding_model['llm_name']
|
||||
# 对硅基流动平台进行特异性处理
|
||||
if dynamic_embd_id == "netease-youdao/bce-embedding-base_v1":
|
||||
dynamic_embd_id = "BAAI/bge-m3"
|
||||
print(f"动态获取到的 embedding 模型 ID: {dynamic_embd_id}")
|
||||
else:
|
||||
dynamic_embd_id = default_embd_id
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
|
||||
|
||||
<!-- 气泡主体(深色区) -->
|
||||
<path d="M832 192H320c-70.7 0-128 57.3-128 128v448c0 70.7 57.3 128 128 128h384l160 160V896c70.7 0 128-57.3 128-128V320c0-70.7-57.3-128-128-128z" fill="#3B78E7"/>
|
||||
|
||||
<!-- 气泡高光(浅色区) -->
|
||||
<path d="M832 192H320c-35.3 0-64 28.7-64 64v448c0 35.3 28.7 64 64 64h384l160 160V768c35.3 0 64-28.7 64-64V320c0-35.3-28.7-64-64-64z" fill="#4D90FE"/>
|
||||
|
||||
<!-- 消息横条1(长条) -->
|
||||
<path d="M256 384h512v64H256z" fill="#FFFFFF" opacity="0.8"/>
|
||||
|
||||
<!-- 消息横条2(中长条) -->
|
||||
<path d="M256 512h384v64H256z" fill="#FFFFFF" opacity="0.9"/>
|
||||
|
||||
<!-- 消息横条3(短条) -->
|
||||
<path d="M256 640h256v64H256z" fill="#FFFFFF"/>
|
||||
|
||||
<!-- 消息横条4(长条) -->
|
||||
<path d="M256 768h448v64H256z" fill="#FFFFFF" opacity="0.7"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 893 B |
|
@ -0,0 +1,709 @@
|
|||
<script lang="ts" setup>
|
||||
import type { CreateOrUpdateTableRequestData, TableData } from "@@/apis/tables/type"
|
||||
import type { FormInstance, FormRules } from "element-plus"
|
||||
import { createTableDataApi, deleteTableDataApi, getTableDataApi, resetPasswordApi, updateTableDataApi } from "@@/apis/tables"
|
||||
import { usePagination } from "@@/composables/usePagination"
|
||||
import { ChatDotRound, CirclePlus, Delete, Edit, Key, Refresh, RefreshRight, Search, User } from "@element-plus/icons-vue"
|
||||
import axios from "axios"
|
||||
import { cloneDeep } from "lodash-es"
|
||||
|
||||
defineOptions({
|
||||
// 命名当前组件
|
||||
name: "ConversationManagement"
|
||||
})
|
||||
|
||||
// #region 用户数据
|
||||
const userList = ref<TableData[]>([])
|
||||
const searchData = reactive({
|
||||
username: "",
|
||||
email: ""
|
||||
})
|
||||
|
||||
// 用户列表滚动加载相关
|
||||
const userLoading = ref(false)
|
||||
const userHasMore = ref(true)
|
||||
const userPage = ref(1)
|
||||
const userPageSize = 20
|
||||
|
||||
// 排序状态
|
||||
const sortData = reactive({
|
||||
sortBy: "create_date",
|
||||
sortOrder: "desc" // 默认排序顺序 (最新创建的在前)
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取用户列表数据
|
||||
* @param isLoadMore 是否为加载更多操作
|
||||
*/
|
||||
function getUserData(isLoadMore = false) {
|
||||
if (!isLoadMore) {
|
||||
userPage.value = 1
|
||||
userList.value = []
|
||||
}
|
||||
|
||||
userLoading.value = true
|
||||
getTableDataApi({
|
||||
currentPage: userPage.value,
|
||||
size: userPageSize,
|
||||
username: searchData.username,
|
||||
email: searchData.email,
|
||||
sort_by: sortData.sortBy,
|
||||
sort_order: sortData.sortOrder
|
||||
}).then(({ data }) => {
|
||||
if (isLoadMore) {
|
||||
userList.value = [...userList.value, ...data.list]
|
||||
} else {
|
||||
userList.value = data.list
|
||||
}
|
||||
|
||||
// 判断是否还有更多数据
|
||||
userHasMore.value = userList.value.length < data.total
|
||||
}).catch(() => {
|
||||
if (!isLoadMore) {
|
||||
userList.value = []
|
||||
}
|
||||
}).finally(() => {
|
||||
userLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多用户
|
||||
*/
|
||||
function loadMoreUsers() {
|
||||
if (userLoading.value || !userHasMore.value) return
|
||||
|
||||
userPage.value++
|
||||
getUserData(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听用户列表滚动事件
|
||||
* @param event 滚动事件
|
||||
*/
|
||||
function handleUserListScroll(event) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = event.target
|
||||
// 当滚动到距离底部100px时,加载更多数据
|
||||
if (scrollHeight - scrollTop - clientHeight < 100 && userHasMore.value && !userLoading.value) {
|
||||
loadMoreUsers()
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region 对话数据
|
||||
// 定义对话数据类型
|
||||
interface ConversationData {
|
||||
id: string // 修改为string类型
|
||||
name: string // 修改title为name
|
||||
latestMessage: string // 添加latestMessage字段
|
||||
createTime: string // 修改create_time为createTime
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
// 定义消息数据类型
|
||||
interface MessageData {
|
||||
id: number
|
||||
conversation_id: number
|
||||
role: string
|
||||
content: string
|
||||
create_time: string
|
||||
}
|
||||
|
||||
const conversationList = ref<ConversationData[]>([])
|
||||
const messageList = ref<MessageData[]>([])
|
||||
const conversationLoading = ref(false)
|
||||
const messageLoading = ref(false)
|
||||
|
||||
// 对话列表滚动加载相关
|
||||
const conversationHasMore = ref(true)
|
||||
const conversationPage = ref(1)
|
||||
const conversationPageSize = 20
|
||||
|
||||
// 消息列表滚动加载相关
|
||||
const messageHasMore = ref(true)
|
||||
const messagePage = ref(1)
|
||||
const messagePageSize = 30
|
||||
|
||||
// 当前选中的用户和对话
|
||||
const selectedUser = ref<TableData | null>(null)
|
||||
const selectedConversation = ref<ConversationData | null>(null)
|
||||
|
||||
/**
|
||||
* 选择用户
|
||||
* @param user 用户数据
|
||||
*/
|
||||
function selectUser(user: TableData) {
|
||||
selectedUser.value = user
|
||||
selectedConversation.value = null
|
||||
messageList.value = []
|
||||
conversationPage.value = 1
|
||||
conversationHasMore.value = true
|
||||
getConversationsByUserId(user.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择对话
|
||||
* @param conversation 对话数据
|
||||
*/
|
||||
function selectConversation(conversation: ConversationData) {
|
||||
selectedConversation.value = conversation
|
||||
messagePage.value = 1
|
||||
messageHasMore.value = true
|
||||
messageList.value = []
|
||||
getMessagesByConversationId(conversation.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的对话列表
|
||||
* @param userId 用户ID
|
||||
* @param isLoadMore 是否为加载更多操作
|
||||
*/
|
||||
function getConversationsByUserId(userId: number, isLoadMore = false) {
|
||||
conversationLoading.value = true
|
||||
// 调用获取对话列表API
|
||||
axios.get(`/api/v1/conversation`, {
|
||||
params: {
|
||||
user_id: userId,
|
||||
page: conversationPage.value,
|
||||
size: conversationPageSize,
|
||||
sort_by: "update_time",
|
||||
sort_order: "desc"
|
||||
}
|
||||
}).then((response) => {
|
||||
const data = response.data.data
|
||||
|
||||
if (isLoadMore) {
|
||||
conversationList.value = [...conversationList.value, ...(data.list || [])]
|
||||
} else {
|
||||
conversationList.value = data.list || []
|
||||
}
|
||||
|
||||
// 判断是否还有更多数据
|
||||
conversationHasMore.value = conversationList.value.length < (data.total || 0)
|
||||
}).catch((error) => {
|
||||
console.error("获取对话列表失败:", error)
|
||||
ElMessage.error("获取对话列表失败")
|
||||
if (!isLoadMore) {
|
||||
conversationList.value = []
|
||||
}
|
||||
}).finally(() => {
|
||||
conversationLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多对话
|
||||
*/
|
||||
function loadMoreConversations() {
|
||||
if (conversationLoading.value || !conversationHasMore.value || !selectedUser.value) return
|
||||
|
||||
conversationPage.value++
|
||||
getConversationsByUserId(selectedUser.value.id, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听对话列表滚动事件
|
||||
* @param event 滚动事件
|
||||
*/
|
||||
function handleConversationListScroll(event) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = event.target
|
||||
// 当滚动到距离底部100px时,加载更多数据
|
||||
if (scrollHeight - scrollTop - clientHeight < 100 && conversationHasMore.value && !conversationLoading.value) {
|
||||
loadMoreConversations()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话的消息列表
|
||||
* @param conversationId 对话ID
|
||||
* @param isLoadMore 是否为加载更多操作
|
||||
*/
|
||||
function getMessagesByConversationId(conversationId: string, isLoadMore = false) {
|
||||
messageLoading.value = true
|
||||
|
||||
// 调用获取消息列表API
|
||||
axios.get(`/api/v1/conversation/${conversationId}/messages`, {
|
||||
params: {
|
||||
page: messagePage.value,
|
||||
size: messagePageSize,
|
||||
sort_by: "create_time",
|
||||
sort_order: "asc" // 按时间正序排列,旧消息在前
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
const data = response.data
|
||||
// 在控制台输出获取到的消息数据
|
||||
console.log("获取到的消息数据:", data)
|
||||
|
||||
// 处理消息数据
|
||||
let processedMessages = []
|
||||
|
||||
if (data.data && data.data.list) {
|
||||
const conversation = data.data.list
|
||||
|
||||
// 检查messages字段是否为字符串,如果是则解析为JSON对象
|
||||
if (conversation.messages && typeof conversation.messages === "string") {
|
||||
try {
|
||||
const parsedMessages = JSON.parse(conversation.messages)
|
||||
|
||||
// 格式化消息数据
|
||||
processedMessages = parsedMessages.map((msg, index) => {
|
||||
return {
|
||||
id: msg.id || `msg-${index}`,
|
||||
conversation_id: conversationId,
|
||||
role: msg.role || "unknown",
|
||||
content: msg.content || "",
|
||||
create_time: msg.created_at ? new Date(msg.created_at * 1000).toISOString() : conversation.createTime
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("解析消息数据失败:", error)
|
||||
processedMessages = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("处理后的消息数据:", processedMessages)
|
||||
|
||||
if (isLoadMore) {
|
||||
// 防止重复加载:检查新消息是否已存在
|
||||
const existingIds = new Set(messageList.value.map(msg => msg.id))
|
||||
const uniqueNewMessages = processedMessages.filter(msg => !existingIds.has(msg.id))
|
||||
|
||||
// 追加新的唯一消息
|
||||
messageList.value = [...messageList.value, ...uniqueNewMessages]
|
||||
console.log(`加载了 ${uniqueNewMessages.length} 条新消息,过滤掉 ${processedMessages.length - uniqueNewMessages.length} 条重复消息`)
|
||||
} else {
|
||||
messageList.value = processedMessages
|
||||
}
|
||||
|
||||
// 判断是否还有更多数据
|
||||
messageHasMore.value = messageList.value.length < (data.data.total || 0)
|
||||
|
||||
// 如果不是加载更多,滚动到底部
|
||||
if (!isLoadMore && messageList.value.length > 0) {
|
||||
setTimeout(() => {
|
||||
const messageListEl = document.querySelector(".message-list")
|
||||
if (messageListEl) {
|
||||
messageListEl.scrollTop = messageListEl.scrollHeight
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("获取消息列表失败:", error)
|
||||
ElMessage.error("获取消息列表失败")
|
||||
if (!isLoadMore) {
|
||||
messageList.value = []
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
messageLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染消息内容,处理图片和链接
|
||||
* @param content 消息内容
|
||||
* @returns 处理后的HTML内容
|
||||
*/
|
||||
function renderMessageContent(content) {
|
||||
if (!content) return ""
|
||||
|
||||
// 处理Markdown格式的图片
|
||||
let processedContent = content.replace(/!\[.*?\]\((.*?)\)/g, "<img src=\"$1\" class=\"message-image\" />")
|
||||
|
||||
// 处理换行符
|
||||
processedContent = processedContent.replace(/\n/g, "<br>")
|
||||
|
||||
return processedContent
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载更多消息
|
||||
*/
|
||||
function loadMoreMessages() {
|
||||
if (messageLoading.value || !messageHasMore.value || !selectedConversation.value) return
|
||||
|
||||
messagePage.value++
|
||||
getMessagesByConversationId(selectedConversation.value.id, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听消息列表滚动事件
|
||||
* @param event 滚动事件
|
||||
*/
|
||||
function handleMessageListScroll(event) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = event.target
|
||||
// 当滚动到距离底部100px时,加载更多数据(向下滚动加载更多)
|
||||
if (scrollHeight - scrollTop - clientHeight < 100 && messageHasMore.value && !messageLoading.value) {
|
||||
loadMoreMessages()
|
||||
}
|
||||
}
|
||||
|
||||
// 初始加载用户数据
|
||||
onMounted(() => {
|
||||
getUserData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 多级卡片区域 -->
|
||||
<div class="conversation-cards-container">
|
||||
<!-- 第一个卡片:用户列表 -->
|
||||
<el-card shadow="hover" class="user-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>用户列表</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="user-list" @scroll="handleUserListScroll">
|
||||
<div
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
class="user-item"
|
||||
:class="{ active: selectedUser?.id === user.id }"
|
||||
@click="selectUser(user)"
|
||||
>
|
||||
<el-avatar :size="32" :icon="User" />
|
||||
<div class="user-info">
|
||||
<div class="username">
|
||||
{{ user.username }}
|
||||
</div>
|
||||
<div class="email">
|
||||
{{ user.email }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="userLoading" class="loading-more">
|
||||
<el-icon class="loading-icon">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
<el-empty v-if="userList.length === 0 && !userLoading" description="暂无用户数据" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 第二个卡片:对话标题列表 -->
|
||||
<el-card shadow="hover" class="conversation-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>对话列表</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="conversation-list" @scroll="handleConversationListScroll">
|
||||
<template v-if="selectedUser">
|
||||
<div
|
||||
v-for="conversation in conversationList"
|
||||
:key="conversation.id"
|
||||
class="conversation-item"
|
||||
:class="{ active: selectedConversation?.id === conversation.id }"
|
||||
@click="selectConversation(conversation)"
|
||||
>
|
||||
<div class="conversation-icon">
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
</div>
|
||||
<div class="conversation-info">
|
||||
<div class="conversation-title">
|
||||
{{ conversation.name }}
|
||||
</div>
|
||||
<div class="conversation-meta">
|
||||
<span>{{ new Date(conversation.updateTime).toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="conversationLoading" class="loading-more">
|
||||
<el-icon class="loading-icon">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
<el-empty v-if="conversationList.length === 0 && !conversationLoading" description="暂无对话数据" />
|
||||
</template>
|
||||
<el-empty v-else description="请先选择用户" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 第三个卡片:对话内容 -->
|
||||
<el-card shadow="hover" class="message-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>对话标题: {{ selectedConversation?.name || '未选择对话' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="message-list" @scroll="handleMessageListScroll">
|
||||
<template v-if="selectedConversation">
|
||||
<div
|
||||
v-for="message in messageList"
|
||||
:key="message.id"
|
||||
class="message-item"
|
||||
:class="{ 'user-message': message.role === 'user', 'assistant-message': message.role === 'assistant' }"
|
||||
>
|
||||
<div class="message-header">
|
||||
<span class="message-role">{{ message.role === 'user' ? '用户' : '助手' }}</span>
|
||||
<span class="message-time">{{ new Date(message.create_time).toLocaleString() }}</span>
|
||||
</div>
|
||||
<div class="message-content" v-html="renderMessageContent(message.content)" />
|
||||
</div>
|
||||
<el-empty v-if="messageList.length === 0 && !messageLoading" description="暂无消息数据" />
|
||||
<!-- 加载提示 -->
|
||||
<!-- <div v-if="messageHasMore" class="loading-more bottom-loading">
|
||||
<el-icon class="loading-icon" :class="{ 'is-loading': messageLoading }">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<span>{{ messageLoading ? '加载中...' : '向下滚动加载更多' }}</span>
|
||||
</div> -->
|
||||
</template>
|
||||
<el-empty v-else description="请先选择对话" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-wrapper {
|
||||
margin-bottom: 20px;
|
||||
:deep(.el-card__body) {
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-cards-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
height: calc(100vh - 240px);
|
||||
min-height: 750px;
|
||||
overflow-x: auto; /* 添加水平滚动 */
|
||||
padding-bottom: 10px; /* 添加底部内边距,避免滚动条遮挡内容 */
|
||||
}
|
||||
|
||||
.user-card {
|
||||
width: 25%;
|
||||
min-width: 250px; /* 设置最小宽度,避免卡片过小 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0; /* 防止卡片被压缩 */
|
||||
}
|
||||
|
||||
.conversation-card {
|
||||
width: 25%;
|
||||
min-width: 250px; /* 设置最小宽度,避免卡片过小 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0; /* 防止卡片被压缩 */
|
||||
}
|
||||
|
||||
.message-card {
|
||||
flex: 1;
|
||||
min-width: 300px; /* 设置最小宽度,避免卡片过小 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.user-list,
|
||||
.conversation-list,
|
||||
.message-list {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
max-height: calc(100vh - 300px); /* 设置最大高度,确保内容可滚动 */
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px; /* 添加水平滚动条高度 */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: var(--el-border-color-darker);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 14px;
|
||||
|
||||
.loading-icon {
|
||||
margin-right: 6px;
|
||||
animation: rotating 2s linear infinite;
|
||||
}
|
||||
|
||||
&.top-loading {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.bottom-loading {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.user-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
|
||||
.username {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.email {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.conversation-icon {
|
||||
font-size: 20px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.conversation-info {
|
||||
margin-left: 10px;
|
||||
flex: 1;
|
||||
|
||||
.conversation-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.conversation-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-item {
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&.user-message {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
&.assistant-message {
|
||||
background-color: var(--el-fill-color-light);
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.message-role {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.message-content {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
.message-image {
|
||||
max-width: 100%;
|
||||
border-radius: 4px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 添加全局滚动条样式 -->
|
||||
<style lang="scss">
|
||||
/* 全局滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-border-color-darker);
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
|
@ -134,6 +134,24 @@ export const constantRoutes: RouteRecordRaw[] = [
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/conversation",
|
||||
component: Layouts,
|
||||
redirect: "/conversation/index",
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
component: () => import("@/pages/conversation/index.vue"),
|
||||
name: "conversation",
|
||||
meta: {
|
||||
title: "用户会话管理",
|
||||
svgIcon: "conversation",
|
||||
affix: false,
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -9,18 +9,18 @@ declare module 'vue' {
|
|||
export interface GlobalComponents {
|
||||
SvgIcon: import("vue").DefineComponent<{
|
||||
name: {
|
||||
type: import("vue").PropType<"dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
type: import("vue").PropType<"conversation" | "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
default: string;
|
||||
required: true;
|
||||
};
|
||||
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
name: {
|
||||
type: import("vue").PropType<"dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
type: import("vue").PropType<"conversation" | "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
default: string;
|
||||
required: true;
|
||||
};
|
||||
}>>, {
|
||||
name: "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management";
|
||||
name: "conversation" | "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management";
|
||||
}>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,20 @@
|
|||
declare module '~virtual/svg-component' {
|
||||
const SvgIcon: import("vue").DefineComponent<{
|
||||
name: {
|
||||
type: import("vue").PropType<"dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
type: import("vue").PropType<"conversation" | "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
default: string;
|
||||
required: true;
|
||||
};
|
||||
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
name: {
|
||||
type: import("vue").PropType<"dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
type: import("vue").PropType<"conversation" | "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management">;
|
||||
default: string;
|
||||
required: true;
|
||||
};
|
||||
}>>, {
|
||||
name: "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management";
|
||||
name: "conversation" | "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management";
|
||||
}>;
|
||||
export const svgNames: ["dashboard", "file", "fullscreen-exit", "fullscreen", "kb", "keyboard-down", "keyboard-enter", "keyboard-esc", "keyboard-up", "search", "team-management", "user-config", "user-management"];
|
||||
export type SvgName = "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management";
|
||||
export const svgNames: ["conversation", "dashboard", "file", "fullscreen-exit", "fullscreen", "kb", "keyboard-down", "keyboard-enter", "keyboard-esc", "keyboard-up", "search", "team-management", "user-config", "user-management"];
|
||||
export type SvgName = "conversation" | "dashboard" | "file" | "fullscreen-exit" | "fullscreen" | "kb" | "keyboard-down" | "keyboard-enter" | "keyboard-esc" | "keyboard-up" | "search" | "team-management" | "user-config" | "user-management";
|
||||
export default SvgIcon;
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ import classNames from 'classnames';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'umi';
|
||||
|
||||
import OperateDropdown from '@/components/operate-dropdown';
|
||||
// import OperateDropdown from '@/components/operate-dropdown';
|
||||
import { useTheme } from '@/components/theme-provider';
|
||||
import { useDeleteKnowledge } from '@/hooks/knowledge-hooks';
|
||||
// import { useDeleteKnowledge } from '@/hooks/knowledge-hooks';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import styles from './index.less';
|
||||
|
||||
|
@ -26,11 +26,11 @@ const KnowledgeCard = ({ item }: IProps) => {
|
|||
const { t } = useTranslation();
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
const { theme } = useTheme();
|
||||
const { deleteKnowledge } = useDeleteKnowledge();
|
||||
// const { deleteKnowledge } = useDeleteKnowledge();
|
||||
|
||||
const removeKnowledge = async () => {
|
||||
return deleteKnowledge(item.id);
|
||||
};
|
||||
// const removeKnowledge = async () => {
|
||||
// return deleteKnowledge(item.id);
|
||||
// };
|
||||
|
||||
const handleCardClick = () => {
|
||||
navigate(`/knowledge/${KnowledgeRouteKey.Dataset}?id=${item.id}`, {
|
||||
|
@ -50,7 +50,7 @@ const KnowledgeCard = ({ item }: IProps) => {
|
|||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<Avatar size={34} icon={<UserOutlined />} src={item.avatar} />
|
||||
<OperateDropdown deleteItem={removeKnowledge}></OperateDropdown>
|
||||
{/* <OperateDropdown deleteItem={removeKnowledge}></OperateDropdown> */}
|
||||
</div>
|
||||
<div className={styles.titleWrapper}>
|
||||
<span
|
||||
|
|
Loading…
Reference in New Issue