feat: Add base project structure with API and web components
This commit is contained in:
110
web/src/views/MemoryManagement/components/MemoryForm.tsx
Normal file
110
web/src/views/MemoryManagement/components/MemoryForm.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input, App } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { MemoryFormData, Memory, MemoryFormRef } from '../types';
|
||||
import RbModal from '@/components/RbModal'
|
||||
import { createMemoryConfig, updateMemoryConfig } from '@/api/memory'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
interface MemoryFormProps {
|
||||
refresh: () => void;
|
||||
}
|
||||
|
||||
const MemoryForm = forwardRef<MemoryFormRef, MemoryFormProps>(({
|
||||
refresh
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const { message } = App.useApp();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [editingMemory, setEditingMemory] = useState<Memory | null>(null);
|
||||
const [form] = Form.useForm<MemoryFormData>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const values = Form.useWatch([], form);
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
setEditingMemory(null);
|
||||
form.resetFields();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleOpen = (memory?: Memory | null) => {
|
||||
if (memory) {
|
||||
setEditingMemory(memory);
|
||||
// 设置表单值
|
||||
form.setFieldsValue({
|
||||
config_name: memory.config_name,
|
||||
config_desc: memory.config_desc,
|
||||
});
|
||||
} else {
|
||||
form.resetFields();
|
||||
}
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then(() => {
|
||||
setLoading(true)
|
||||
const response = editingMemory?.config_id ? updateMemoryConfig({
|
||||
config_id: editingMemory.config_id,
|
||||
...values
|
||||
}) :createMemoryConfig(values)
|
||||
response.then(() => {
|
||||
if (refresh) {
|
||||
refresh();
|
||||
}
|
||||
handleClose()
|
||||
message.success(editingMemory?.config_id ? t('common.updateSuccess') : t('common.createSuccess'))
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('err', err)
|
||||
});
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
}));
|
||||
|
||||
return (
|
||||
<RbModal
|
||||
title={editingMemory ? t('memory.editConfiguration') : t('memory.createConfiguration')}
|
||||
open={visible}
|
||||
onCancel={handleClose}
|
||||
okText={t('common.save')}
|
||||
onOk={handleSave}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
<FormItem
|
||||
name="config_name"
|
||||
label={t('memory.configurationName')}
|
||||
rules={[{ required: true, message: t('common.pleaseEnter') }]}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="config_desc"
|
||||
label={t('memory.desc')}
|
||||
>
|
||||
<Input.TextArea placeholder={t('common.pleaseEnter')} />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</RbModal>
|
||||
);
|
||||
});
|
||||
|
||||
export default MemoryForm;
|
||||
135
web/src/views/MemoryManagement/index.tsx
Normal file
135
web/src/views/MemoryManagement/index.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { List, Button, Space, App } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import MemoryForm from './components/MemoryForm';
|
||||
import type { Memory, MemoryFormRef } from '@/views/MemoryManagement/types'
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
import StatusTag from '@/components/StatusTag'
|
||||
import { getMemoryConfigList, deleteMemoryConfig } from '@/api/memory'
|
||||
import BodyWrapper from '@/components/Empty/BodyWrapper'
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
import clsx from 'clsx'
|
||||
|
||||
const MemoryManagement: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { message, modal } = App.useApp();
|
||||
const navigate = useNavigate();
|
||||
const [data, setData] = useState<Memory[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const memoryFormRef = useRef<MemoryFormRef>(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadMoreData()
|
||||
}, []);
|
||||
|
||||
const loadMoreData = () => {
|
||||
setLoading(true);
|
||||
getMemoryConfigList()
|
||||
.then((res) => {
|
||||
const response = res as Memory[];
|
||||
const results = Array.isArray(response) ? response : [];
|
||||
setData(results);
|
||||
})
|
||||
.catch(() => {
|
||||
console.error('Failed to load data');
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
// 打开新增标签弹窗
|
||||
const handleEdit = (config?: Memory) => {
|
||||
memoryFormRef.current?.handleOpen(config);
|
||||
}
|
||||
const handleDelete = (item: Memory) => {
|
||||
modal.confirm({
|
||||
title: t('common.confirmDeleteDesc', { name: item.config_name }),
|
||||
okText: t('common.delete'),
|
||||
okType: 'danger',
|
||||
onOk: () => {
|
||||
deleteMemoryConfig(item.config_id)
|
||||
.then(() => {
|
||||
message.success(t('common.deleteSuccess'));
|
||||
loadMoreData();
|
||||
})
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
const handleClick = (id: number, type: string) => {
|
||||
switch (type) {
|
||||
case 'memoryExtractionEngine':
|
||||
navigate(`/memory-extraction-engine/${id}`)
|
||||
break
|
||||
case 'forgottenEngine':
|
||||
navigate(`/forgetting-engine/${id}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rb:text-right rb:mb-[16px]">
|
||||
<Button type="primary" onClick={() => handleEdit()}>
|
||||
{t('memory.createConfiguration')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<BodyWrapper loading={loading} empty={data.length === 0}>
|
||||
<List
|
||||
grid={{ gutter: 16, column: 3 }}
|
||||
loading={loading}
|
||||
dataSource={data}
|
||||
renderItem={(item) => (
|
||||
<List.Item key={item.config_id}>
|
||||
<RbCard
|
||||
title={item.config_name}
|
||||
>
|
||||
<div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-[17px] rb:font-regular rb:mt-[-4px]">{item.config_desc}</div>
|
||||
{['memoryExtractionEngine', 'forgottenEngine'].map((key) => (
|
||||
<div key={key} className="rb:group rb:cursor-pointer rb:bg-[#F0F3F8] rb:h-[40px] rb:rounded-[6px] rb:flex rb:items-center rb:justify-between rb:p-[0_8px_0_12px] rb:mt-[12px] rb:text-[#5B6167] rb:font-medium"
|
||||
onClick={() => handleClick(item.config_id, key)}
|
||||
>
|
||||
{t(`memory.${key}`)}
|
||||
<span className='rb:flex rb:items-center rb:justify-end'>
|
||||
{/* <StatusTag status={item[key] === 'active' ? 'success' : 'error'} text={item[key] === 'active' ? t('memory.active') : t('memory.inactive')} /> */}
|
||||
<div
|
||||
className="rb:w-[16px] rb:h-[16px] rb:ml-[-3px] rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/memory/arrow_right.svg')] rb:group-hover:bg-[url('@/assets/images/memory/arrow_right_hover.svg')]"
|
||||
></div>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
<div className={clsx("rb:mt-[16px] rb:text-[12px] rb:leading-[16px] rb:font-regular rb:text-[#5B6167] rb:flex rb:items-center", {
|
||||
'rb:justify-between': item.updated_at,
|
||||
'rb:justify-end': !item.updated_at
|
||||
})}>
|
||||
{formatDateTime(item.updated_at, 'YYYY-MM-DD HH:mm:ss')}
|
||||
<Space size={16}>
|
||||
<div
|
||||
className="rb:w-[20px] rb:h-[20px] rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/edit.svg')] rb:hover:bg-[url('@/assets/images/edit_hover.svg')]"
|
||||
onClick={() => handleEdit(item)}
|
||||
></div>
|
||||
<div
|
||||
className="rb:w-[20px] rb:h-[20px] rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/delete.svg')] rb:hover:bg-[url('@/assets/images/delete_hover.svg')]"
|
||||
onClick={() => handleDelete(item)}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
</RbCard>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</BodyWrapper>
|
||||
|
||||
<MemoryForm
|
||||
ref={memoryFormRef}
|
||||
refresh={loadMoreData}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemoryManagement;
|
||||
38
web/src/views/MemoryManagement/types.ts
Normal file
38
web/src/views/MemoryManagement/types.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
// 内存管理表单数据类型
|
||||
export interface MemoryFormData {
|
||||
config_id?: number;
|
||||
config_name: string;
|
||||
config_desc?: string;
|
||||
}
|
||||
|
||||
// 内存数据类型
|
||||
export interface Memory {
|
||||
config_id: number;
|
||||
config_name: string;
|
||||
group_id: string;
|
||||
user_id: string;
|
||||
apply_id: string;
|
||||
enable_llm_dedup_blockwise: boolean;
|
||||
enable_llm_disambiguation: boolean;
|
||||
deep_retrieval: boolean;
|
||||
t_type_strict: string;
|
||||
t_name_strict: string;
|
||||
t_overall: string;
|
||||
chunker_strategy: string;
|
||||
statement_granularity: string;
|
||||
include_dialogue_context: boolean;
|
||||
max_context: string;
|
||||
lambda_mem: string;
|
||||
lambda_mem: string;
|
||||
offset: string;
|
||||
state: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
config_desc: string;
|
||||
workspace_id: string;
|
||||
[key: string]: string | number | boolean;
|
||||
}
|
||||
// 定义组件暴露的方法接口
|
||||
export interface MemoryFormRef {
|
||||
handleOpen: (memory?: Memory | null) => void;
|
||||
}
|
||||
Reference in New Issue
Block a user