diff --git a/backend/ai/urls.py b/backend/ai/urls.py index 5021ef7..ef090bc 100644 --- a/backend/ai/urls.py +++ b/backend/ai/urls.py @@ -9,6 +9,7 @@ router.register(r'ai_model', views.AIModelViewSet) router.register(r'tool', views.ToolViewSet) router.register(r'knowledge', views.KnowledgeViewSet) router.register(r'chat_conversation', views.ChatConversationViewSet) +router.register(r'chat_message', views.ChatMessageViewSet) urlpatterns = [ diff --git a/backend/ai/views/__init__.py b/backend/ai/views/__init__.py index fac792d..7e5fa57 100644 --- a/backend/ai/views/__init__.py +++ b/backend/ai/views/__init__.py @@ -4,10 +4,12 @@ __all__ = [ 'ToolViewSet', 'KnowledgeViewSet', 'ChatConversationViewSet', + 'ChatMessageViewSet', ] from ai.views.ai_api_key import AIApiKeyViewSet from ai.views.ai_model import AIModelViewSet from ai.views.tool import ToolViewSet from ai.views.knowledge import KnowledgeViewSet -from ai.views.chat_conversation import ChatConversationViewSet \ No newline at end of file +from ai.views.chat_conversation import ChatConversationViewSet +from ai.views.chat_message import ChatMessageViewSet \ No newline at end of file diff --git a/backend/ai/views/chat_message.py b/backend/ai/views/chat_message.py new file mode 100644 index 0000000..4c4611e --- /dev/null +++ b/backend/ai/views/chat_message.py @@ -0,0 +1,38 @@ +from rest_framework import serializers + +from ai.models import ChatMessage +from utils.serializers import CustomModelSerializer +from utils.custom_model_viewSet import CustomModelViewSet +from django_filters import rest_framework as filters + + +class ChatMessageSerializer(CustomModelSerializer): + username = serializers.CharField(source='user.username', read_only=True) + """ + AI 聊天消息 序列化器 + """ + class Meta: + model = ChatMessage + fields = '__all__' + read_only_fields = ['id', 'create_time', 'update_time'] + + +class ChatMessageFilter(filters.FilterSet): + + class Meta: + model = ChatMessage + fields = ['id', 'remark', 'creator', 'modifier', 'is_deleted', 'conversation_id', + 'model', 'type', 'reply_id', 'content', 'use_context', 'segment_ids'] + + +class ChatMessageViewSet(CustomModelViewSet): + """ + AI 聊天消息 视图集 + """ + queryset = ChatMessage.objects.filter(is_deleted=False).order_by('-id') + serializer_class = ChatMessageSerializer + filterset_class = ChatMessageFilter + search_fields = ['name'] # 根据实际字段调整 + ordering_fields = ['create_time', 'id'] + ordering = ['-create_time'] + diff --git a/web/apps/web-antd/src/locales/langs/en-US/ai.json b/web/apps/web-antd/src/locales/langs/en-US/ai.json index 775e14c..b5a7415 100644 --- a/web/apps/web-antd/src/locales/langs/en-US/ai.json +++ b/web/apps/web-antd/src/locales/langs/en-US/ai.json @@ -23,5 +23,9 @@ "chat_conversation": { "title": "CHAT Management", "name": "CHAT Management" + }, + "chat_message": { + "title": "MESSAGE Management", + "name": "MESSAGE Management" } } diff --git a/web/apps/web-antd/src/locales/langs/zh-CN/ai.json b/web/apps/web-antd/src/locales/langs/zh-CN/ai.json index b630a70..47ad5b1 100644 --- a/web/apps/web-antd/src/locales/langs/zh-CN/ai.json +++ b/web/apps/web-antd/src/locales/langs/zh-CN/ai.json @@ -23,5 +23,9 @@ "chat_conversation": { "title": "对话列表", "name": "对话列表" + }, + "chat_message": { + "title": "消息列表", + "name": "消息列表" } } diff --git a/web/apps/web-antd/src/models/ai/chat_message.ts b/web/apps/web-antd/src/models/ai/chat_message.ts new file mode 100644 index 0000000..6f5eb1b --- /dev/null +++ b/web/apps/web-antd/src/models/ai/chat_message.ts @@ -0,0 +1,29 @@ +import { BaseModel } from '#/models/base'; + +export namespace AiChatMessageApi { + export interface AiChatMessage { + id: number; + remark: string; + creator: string; + modifier: string; + update_time: string; + create_time: string; + is_deleted: boolean; + conversation_id: number; + user: number; + role: number; + model: string; + model_id: number; + type: string; + reply_id: number; + content: string; + use_context: boolean; + segment_ids: string; + } +} + +export class AiChatMessageModel extends BaseModel { + constructor() { + super('/ai/chat_message/'); + } +} diff --git a/web/apps/web-antd/src/views/ai/chat_conversation/data.ts b/web/apps/web-antd/src/views/ai/chat_conversation/data.ts index e853f39..f29d697 100644 --- a/web/apps/web-antd/src/views/ai/chat_conversation/data.ts +++ b/web/apps/web-antd/src/views/ai/chat_conversation/data.ts @@ -6,8 +6,8 @@ import type { AiChatConversationApi } from '#/models/ai/chat_conversation'; import { z } from '#/adapter/form'; import { $t } from '#/locales'; -import { format_datetime } from '#/utils/date'; -import { op } from '#/utils/permission'; +// import { format_datetime } from '#/utils/date'; +// import { op } from '#/utils/permission'; /** * 获取编辑表单的字段配置 @@ -138,6 +138,10 @@ export function useColumns( field: 'id', title: 'ID', }, + { + field: 'title', + title: '标题', + }, { field: 'username', title: '用户', @@ -168,6 +172,10 @@ export function useColumns( }, name: 'CellOperation', options: [ + { + code: 'view', + text: '查看消息', + }, // op('ai:chat_conversation:edit', 'edit'), // op('ai:chat_conversation:delete', 'delete'), ], diff --git a/web/apps/web-antd/src/views/ai/chat_conversation/list.vue b/web/apps/web-antd/src/views/ai/chat_conversation/list.vue index b603c86..3f87a63 100644 --- a/web/apps/web-antd/src/views/ai/chat_conversation/list.vue +++ b/web/apps/web-antd/src/views/ai/chat_conversation/list.vue @@ -5,19 +5,20 @@ import type { } from '#/adapter/vxe-table'; import type { AiChatConversationApi } from '#/models/ai/chat_conversation'; -import { Page, useVbenModal } from '@vben/common-ui'; -import { Plus } from '@vben/icons'; +import { useRouter } from 'vue-router'; -import { Button, message } from 'ant-design-vue'; +import { Page, useVbenModal } from '@vben/common-ui'; + +import { message } from 'ant-design-vue'; import { useVbenVxeGrid } from '#/adapter/vxe-table'; -import { $t } from '#/locales'; import { AiChatConversationModel } from '#/models/ai/chat_conversation'; import { useColumns, useGridFormSchema } from './data'; import Form from './modules/form.vue'; const formModel = new AiChatConversationModel(); +const router = useRouter(); const [FormModal, formModalApi] = useVbenModal({ connectedComponent: Form, @@ -70,9 +71,19 @@ function onActionClick({ onEdit(row); break; } + case 'view': { + handleMessageDetail(row); + break; + } } } +const handleMessageDetail = (row: AiChatConversationApi.AiChatConversation) => { + router.push({ + path: '/ai/chat_message/', // 目标页面路径 + query: { conversation_id: row.id }, // 传递查询参数 + }); +}; const [Grid, gridApi] = useVbenVxeGrid({ formOptions: { schema: useGridFormSchema(), diff --git a/web/apps/web-antd/src/views/ai/chat_message/data.ts b/web/apps/web-antd/src/views/ai/chat_message/data.ts new file mode 100644 index 0000000..dc5b699 --- /dev/null +++ b/web/apps/web-antd/src/views/ai/chat_message/data.ts @@ -0,0 +1,178 @@ +import type { VxeTableGridOptions } from '@vben/plugins/vxe-table'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { OnActionClickFn } from '#/adapter/vxe-table'; +import type { AiChatMessageApi } from '#/models/ai/chat_message'; + +import { z } from '#/adapter/form'; +import { $t } from '#/locales'; +import { format_datetime } from '#/utils/date'; + +/** + * 获取编辑表单的字段配置 + */ +export function useSchema(): VbenFormSchema[] { + return [ + { + component: 'InputNumber', + fieldName: 'conversation_id', + label: '对话编号', + }, + { + component: 'Input', + fieldName: 'user', + label: '用户', + }, + { + component: 'Input', + fieldName: 'role', + label: '聊天角色', + }, + { + component: 'Input', + fieldName: 'model', + label: '模型标识', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['模型标识'])) + .max(100, $t('ui.formRules.maxLength', ['模型标识', 100])), + }, + { + component: 'Input', + fieldName: 'model_id', + label: '向量模型编号', + }, + { + component: 'Input', + fieldName: 'type', + label: '消息类型', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['消息类型'])) + .max(100, $t('ui.formRules.maxLength', ['消息类型', 100])), + }, + { + component: 'InputNumber', + fieldName: 'reply_id', + label: '回复编号', + }, + { + component: 'Input', + fieldName: 'content', + label: '消息内容', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['消息内容'])) + .max(100, $t('ui.formRules.maxLength', ['消息内容', 100])), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: [ + { label: '开启', value: 1 }, + { label: '关闭', value: 0 }, + ], + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'use_context', + label: '是否携带上下文', + }, + { + component: 'Input', + fieldName: 'segment_ids', + label: '段落编号数组', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['段落编号数组'])) + .max(100, $t('ui.formRules.maxLength', ['段落编号数组', 100])), + }, + { + component: 'Input', + fieldName: 'remark', + label: '备注', + rules: z + .string() + .min(1, $t('ui.formRules.required', ['备注'])) + .max(100, $t('ui.formRules.maxLength', ['备注', 100])), + }, + ]; +} + +/** + * 获取编辑表单的字段配置 + */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'user', + label: '用户', + }, + ]; +} + +/** + * 获取表格列配置 + * @description 使用函数的形式返回列数据而不是直接export一个Array常量,是为了响应语言切换时重新翻译表头 + * @param onActionClick 表格操作按钮点击事件 + */ +export function useColumns( + onActionClick?: OnActionClickFn, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: 'ID', + }, + { + field: 'conversation_id', + title: '对话编号', + }, + { + field: 'username', + title: '用户', + }, + { + field: 'role', + title: '聊天角色', + }, + { + field: 'model', + title: '模型标识', + width: 150, + }, + { + field: 'model_id', + title: '向量模型编号', + }, + { + field: 'type', + title: '消息类型', + }, + { + field: 'reply_id', + title: '回复编号', + }, + { + field: 'content', + title: '消息内容', + width: 200, + }, + { + field: 'use_context', + title: '是否携带上下文', + }, + { + field: 'segment_ids', + title: '段落编号数组', + }, + { + field: 'create_time', + title: '创建时间', + width: 150, + formatter: ({ cellValue }) => format_datetime(cellValue), + }, + ]; +} diff --git a/web/apps/web-antd/src/views/ai/chat_message/list.vue b/web/apps/web-antd/src/views/ai/chat_message/list.vue new file mode 100644 index 0000000..c66e016 --- /dev/null +++ b/web/apps/web-antd/src/views/ai/chat_message/list.vue @@ -0,0 +1,127 @@ + + + diff --git a/web/apps/web-antd/src/views/ai/chat_message/modules/form.vue b/web/apps/web-antd/src/views/ai/chat_message/modules/form.vue new file mode 100644 index 0000000..79b47b1 --- /dev/null +++ b/web/apps/web-antd/src/views/ai/chat_message/modules/form.vue @@ -0,0 +1,79 @@ + + + +