Merge branch 'develop' into feature/ui_upgrade_zy

This commit is contained in:
zhaoying
2026-03-20 11:49:00 +08:00
286 changed files with 23406 additions and 5328 deletions

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-06 21:09:42
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-07 15:03:31
* @Last Modified time: 2026-03-19 21:32:34
*/
/**
* File Upload Component
@@ -19,8 +19,8 @@
* - File list management
*
* @component
*/
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
*/
import { useState, useEffect, forwardRef, useImperativeHandle, useMemo } from 'react';
import { Upload, Progress, App, Flex } from 'antd';
import type { UploadProps, UploadFile } from 'antd';
import type { UploadProps as RcUploadProps } from 'antd/es/upload/interface';
@@ -28,6 +28,7 @@ import { useTranslation } from 'react-i18next';
import { request } from '@/utils/request'
import { fileUploadUrlWithoutApiPrefix } from '@/api/fileStorage'
import type { FeaturesConfigForm } from '@/views/ApplicationConfig/types';
interface UploadFilesProps extends Omit<UploadProps, 'onChange'> {
/** Upload API endpoint */
@@ -48,14 +49,14 @@ interface UploadFilesProps extends Omit<UploadProps, 'onChange'> {
disabled?: boolean;
/** File size limit in MB */
fileSize?: number;
/** Allowed file types ['doc', 'xls', 'ppt', 'pdf'] */
fileType?: string[];
/** Auto-upload on file selection, default is true */
isAutoUpload?: boolean;
/** Maximum number of files allowed */
maxCount?: number;
/** Custom file removal callback */
onRemove?: (file: UploadFile) => boolean | void | Promise<boolean | void>;
featureConfig: FeaturesConfigForm['file_upload']
}
const transform_file_type = {
@@ -70,6 +71,12 @@ const transform_file_type = {
'application/vnd.ms-powerpoint': 'document/ppt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'document/pptx',
'application/vnd.ms-excel': 'document/xls',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'document/xlsx',
'text/csv': 'document/csv',
'application/json': 'document/json'
}
// Mapping of file extensions to MIME types
const ALL_FILE_TYPE: {
@@ -87,6 +94,13 @@ const ALL_FILE_TYPE: {
ppt: 'application/vnd.ms-powerpoint',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
csv: 'text/csv',
json: 'application/json',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
@@ -130,11 +144,11 @@ const UploadFiles = forwardRef<UploadFilesRef, UploadFilesProps>(({
onChange,
disabled = false,
fileSize = 5,
fileType = Object.entries(ALL_FILE_TYPE).map(([key]) => key),
isAutoUpload = true,
maxCount = 1,
onRemove: customOnRemove,
requestConfig,
featureConfig,
...props
}, ref) => {
const { t } = useTranslation();
@@ -142,18 +156,37 @@ const UploadFiles = forwardRef<UploadFilesRef, UploadFilesProps>(({
const [fileList, setFileList] = useState<UploadFile[]>(propFileList);
const [accept, setAccept] = useState<string | undefined>();
const fileType = useMemo(() => {
let types: string[] = [];
['image', 'document', 'video', 'audio'].forEach(type => {
if (featureConfig[`${type}_enabled` as keyof FeaturesConfigForm['file_upload']]) {
types = types.concat(featureConfig[`${type}_allowed_extensions` as keyof FeaturesConfigForm['file_upload']] as string[])
}
})
return types
}, [featureConfig])
/**
* Validates file type and size before upload
* @returns Upload.LIST_IGNORE to prevent upload, or true to proceed
*/
const beforeUpload: RcUploadProps['beforeUpload'] = (file) => {
// Validate file size
if (fileSize) {
const isLtMaxSize = (file.size / 1024 / 1024) < fileSize;
if (!isLtMaxSize) {
message.error(t('common.fileSizeTip', { size: fileSize }));
return Upload.LIST_IGNORE;
}
// Determine file category and get max size from featureConfig
const mimePrefix = file.type?.split('/')[0]
const categoryMap: Record<string, keyof FeaturesConfigForm['file_upload']> = {
image: 'image_max_size_mb',
video: 'video_max_size_mb',
audio: 'audio_max_size_mb',
}
const maxSizeKey = categoryMap[mimePrefix] ?? 'document_max_size_mb'
const maxSize = (featureConfig[maxSizeKey] as number) ?? fileSize
const fileSizeMB = file.size / 1024 / 1024
const isLtMaxSize = fileSizeMB < maxSize;
if (!isLtMaxSize) {
message.error(t('common.fileSizeTip', { size: maxSize }));
return Upload.LIST_IGNORE;
}
// Validate file type
if (fileType && fileType.length > 0) {

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-06 21:09:47
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-04 17:47:09
* @Last Modified time: 2026-03-18 21:10:01
*/
/**
* Upload File List Modal Component
@@ -18,25 +18,28 @@
*
* @component
*/
import { forwardRef, useImperativeHandle, useState } from 'react';
import { forwardRef, useImperativeHandle, useState, useMemo } from 'react';
import { Form, Input, Select, Button, Flex } from 'antd';
import { useTranslation } from 'react-i18next';
import type { UploadFileListModalRef } from '../types'
import RbModal from '@/components/RbModal'
import type { FeaturesConfigForm } from '@/views/ApplicationConfig/types';
const FormItem = Form.Item;
interface UploadFileListModalProps {
/** Callback to refresh parent component with new file list */
refresh: (fileList?: any[]) => void;
featureConfig: FeaturesConfigForm['file_upload']
}
/**
* Modal for adding remote files via URL
*/
const UploadFileListModal = forwardRef<UploadFileListModalRef, UploadFileListModalProps>(({
refresh
refresh,
featureConfig
}, ref) => {
const { t } = useTranslation();
const [visible, setVisible] = useState(false);
@@ -79,6 +82,20 @@ const UploadFileListModal = forwardRef<UploadFileListModalRef, UploadFileListMod
handleOpen
}));
const fileTypeOptions = useMemo(() => {
const options = [];
if (featureConfig?.image_enabled) {
options.push({ label: t('memoryConversation.image'), value: 'image' });
}
if (featureConfig?.audio_enabled) {
options.push({ label: t('memoryConversation.audio'), value: 'audio' });
}
if (featureConfig?.video_enabled) {
options.push({ label: t('memoryConversation.video'), value: 'video' });
}
return options;
}, [featureConfig, t])
return (
<RbModal
title={t('memoryConversation.addRemoteFile')}
@@ -98,26 +115,21 @@ const UploadFileListModal = forwardRef<UploadFileListModalRef, UploadFileListMod
<FormItem
{...restField}
name={[name, 'type']}
initialValue="image"
className="rb:mb-0!"
>
<Select
placeholder={t('memoryConversation.fileType')}
options={[
{ label: t('memoryConversation.image'), value: 'image' },
{ label: t('memoryConversation.audio'), value: 'audio' },
{ label: t('memoryConversation.video'), value: 'video' },
]}
className="rb:w-30"
options={fileTypeOptions}
className="rb:w-30!"
/>
</FormItem>
<FormItem
{...restField}
name={[name, 'url']}
rules={[{ required: true, message: t('common.pleaseEnter') }]}
className="rb:mb-0!"
className="rb:mb-0! rb:flex-1!"
>
<Input placeholder={t('memoryConversation.fileUrl')} className="rb:w-82.5!" />
<Input placeholder={t('memoryConversation.fileUrl')} />
</FormItem>
<div
className="rb:w-5 rb:h-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/delete.svg')] rb:hover:bg-[url('@/assets/images/delete_hover.svg')]"