This commit is contained in:
@@ -14,6 +14,8 @@ VITE_UPLOAD_TYPE=server
|
|||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
||||||
|
VITE_API_URL_PREFIX=/ai/sample/create
|
||||||
|
|
||||||
# 是否删除debugger
|
# 是否删除debugger
|
||||||
VITE_DROP_DEBUGGER=false
|
VITE_DROP_DEBUGGER=false
|
||||||
|
|
||||||
|
|||||||
14
mock/ap.ts
Normal file
14
mock/ap.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import type { Request, Response } from "express";
|
||||||
|
import mockjs from "mockjs";
|
||||||
|
|
||||||
|
const getTags = (_: Request, res: Response) => {
|
||||||
|
return res.json({
|
||||||
|
data: mockjs.mock({
|
||||||
|
"list|100": [{ name: "@city", "value|1-100": 150, "type|0-2": 1 }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
"GET /api/tags": getTags,
|
||||||
|
};
|
||||||
113
mock/notices.ts
113
mock/notices.ts
@@ -1,117 +1,20 @@
|
|||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from "express";
|
||||||
|
|
||||||
const getNotices = (_req: Request, res: Response) => {
|
const getSampleTag = (_req: Request, res: Response) => {
|
||||||
res.json({
|
res.json({
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
id: '000000001',
|
id: "000000001",
|
||||||
avatar:
|
avatar:
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/MSbDR4FR2MUAAAAAAAAAAAAAFl94AQBr',
|
"https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/MSbDR4FR2MUAAAAAAAAAAAAAFl94AQBr",
|
||||||
title: '你收到了 14 份新周报',
|
title: "你收到了 14 份新周报",
|
||||||
datetime: '2017-08-09',
|
datetime: "2017-08-09",
|
||||||
type: 'notification',
|
type: "notification",
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000002',
|
|
||||||
avatar:
|
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/hX-PTavYIq4AAAAAAAAAAAAAFl94AQBr',
|
|
||||||
title: '你推荐的 曲妮妮 已通过第三轮面试',
|
|
||||||
datetime: '2017-08-08',
|
|
||||||
type: 'notification',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000003',
|
|
||||||
avatar:
|
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/jHX5R5l3QjQAAAAAAAAAAAAAFl94AQBr',
|
|
||||||
title: '这种模板可以区分多种通知类型',
|
|
||||||
datetime: '2017-08-07',
|
|
||||||
read: true,
|
|
||||||
type: 'notification',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000004',
|
|
||||||
avatar:
|
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Wr4mQqx6jfwAAAAAAAAAAAAAFl94AQBr',
|
|
||||||
title: '左侧图标用于区分不同的类型',
|
|
||||||
datetime: '2017-08-07',
|
|
||||||
type: 'notification',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000005',
|
|
||||||
avatar:
|
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Mzj_TbcWUj4AAAAAAAAAAAAAFl94AQBr',
|
|
||||||
title: '内容不要超过两行字,超出时自动截断',
|
|
||||||
datetime: '2017-08-07',
|
|
||||||
type: 'notification',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000006',
|
|
||||||
avatar:
|
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/eXLzRbPqQE4AAAAAAAAAAAAAFl94AQBr',
|
|
||||||
title: '曲丽丽 评论了你',
|
|
||||||
description: '描述信息描述信息描述信息',
|
|
||||||
datetime: '2017-08-07',
|
|
||||||
type: 'message',
|
|
||||||
clickClose: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000007',
|
|
||||||
avatar:
|
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/w5mRQY2AmEEAAAAAAAAAAAAAFl94AQBr',
|
|
||||||
title: '朱偏右 回复了你',
|
|
||||||
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
|
|
||||||
datetime: '2017-08-07',
|
|
||||||
type: 'message',
|
|
||||||
clickClose: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000008',
|
|
||||||
avatar:
|
|
||||||
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/wPadR5M9918AAAAAAAAAAAAAFl94AQBr',
|
|
||||||
title: '标题',
|
|
||||||
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
|
|
||||||
datetime: '2017-08-07',
|
|
||||||
type: 'message',
|
|
||||||
clickClose: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000009',
|
|
||||||
title: '任务名称',
|
|
||||||
description: '任务需要在 2017-01-12 20:00 前启动',
|
|
||||||
extra: '未开始',
|
|
||||||
status: 'todo',
|
|
||||||
type: 'event',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000010',
|
|
||||||
title: '第三方紧急代码变更',
|
|
||||||
description:
|
|
||||||
'冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
|
|
||||||
extra: '马上到期',
|
|
||||||
status: 'urgent',
|
|
||||||
type: 'event',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000011',
|
|
||||||
title: '信息安全考试',
|
|
||||||
description: '指派竹尔于 2017-01-09 前完成更新并发布',
|
|
||||||
extra: '已耗时 8 天',
|
|
||||||
status: 'doing',
|
|
||||||
type: 'event',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '000000012',
|
|
||||||
title: 'ABCD 版本发布',
|
|
||||||
description:
|
|
||||||
'冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
|
|
||||||
extra: '进行中',
|
|
||||||
status: 'processing',
|
|
||||||
type: 'event',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'GET /api/notices': getNotices,
|
"GET /api/notices": getSampleTag,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { ProFormColumnsType } from '@ant-design/pro-components';
|
import type { ProFormColumnsType } from '@ant-design/pro-components';
|
||||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
import { Button, type ColProps, Drawer, Space, Typography } from 'antd';
|
import { Button, type ColProps, Drawer, Space, Typography } from 'antd';
|
||||||
|
import type { FormInstance } from 'antd/lib';
|
||||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||||
|
|
||||||
interface ConfigurableDrawerFormProps {
|
interface ConfigurableDrawerFormProps {
|
||||||
@@ -38,7 +39,7 @@ const ConfigurableDrawerForm = forwardRef<
|
|||||||
const [formData, setFormData] = React.useState(initialValues || {});
|
const [formData, setFormData] = React.useState(initialValues || {});
|
||||||
const [loading, setLoading] = React.useState<boolean>(false);
|
const [loading, setLoading] = React.useState<boolean>(false);
|
||||||
// 添加表单实例引用
|
// 添加表单实例引用
|
||||||
const formRef = React.useRef<any>(null);
|
const formRef = React.useRef<FormInstance>(null);
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
open: (data) => {
|
open: (data) => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type ParamsType,
|
type ParamsType,
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { Button, Space } from 'antd';
|
import { Button, Space, Table } from 'antd';
|
||||||
import React, { forwardRef, useCallback, useMemo, useState } from 'react';
|
import React, { forwardRef, useCallback, useMemo, useState } from 'react';
|
||||||
import { formatPaginationTotal } from '@/utils/antd/tableHelpers';
|
import { formatPaginationTotal } from '@/utils/antd/tableHelpers';
|
||||||
import type { BaseRecord, EnhancedProTableProps } from './types';
|
import type { BaseRecord, EnhancedProTableProps } from './types';
|
||||||
@@ -33,7 +33,7 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
// onExport,
|
// onExport,
|
||||||
// customToolbarRender,
|
// customToolbarRender,
|
||||||
customActionRender,
|
customActionRender,
|
||||||
rowKey,
|
rowKey = 'id',
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -42,84 +42,48 @@ function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
|||||||
// 行选择配置
|
// 行选择配置
|
||||||
const rowSelection = useMemo(() => {
|
const rowSelection = useMemo(() => {
|
||||||
if (!showSelection) return undefined;
|
if (!showSelection) return undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
|
selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
|
||||||
onChange: (keys: React.Key[], rows: T[]) => {
|
onChange: (keys: React.Key[], rows: T[]) => {
|
||||||
setSelectedRowKeys(keys);
|
setSelectedRowKeys(keys);
|
||||||
setSelectedRows(rows);
|
setSelectedRows(rows);
|
||||||
},
|
},
|
||||||
getCheckboxProps: (record: T) => ({
|
getCheckboxProps: (record: T) => ({
|
||||||
name: record.id?.toString(),
|
name: record[rowKey]?.toString(),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}, [showSelection, selectedRowKeys]);
|
}, [showSelection, selectedRowKeys]);
|
||||||
|
|
||||||
// 表格提醒渲染
|
const toolBarRender = useCallback(() => {
|
||||||
const tableAlertRender = useCallback(
|
const toolbarElements =
|
||||||
({ selectedRowKeys, onCleanSelected }: any) => {
|
toolbarActions?.map((action) => {
|
||||||
if (!showSelection || selectedRowKeys.length === 0) return false;
|
return (
|
||||||
|
<Button
|
||||||
return (
|
key={action.key}
|
||||||
<Space size={24}>
|
type={action.type}
|
||||||
<span>
|
danger={action.danger}
|
||||||
已选 {selectedRowKeys.length} 项
|
disabled={action.disabled}
|
||||||
<a style={{ marginLeft: 8 }} onClick={onCleanSelected}>
|
icon={<PlusOutlined />}
|
||||||
取消选择
|
onClick={() => action.onClick(selectedRowKeys, selectedRows)}
|
||||||
</a>
|
>
|
||||||
</span>
|
{action.label}
|
||||||
</Space>
|
</Button>
|
||||||
);
|
);
|
||||||
},
|
}) || [];
|
||||||
[showSelection],
|
return toolbarElements;
|
||||||
);
|
}, [toolbarActions]);
|
||||||
|
|
||||||
const toolBarRender = useCallback(() =>
|
|
||||||
// action: ActionType | undefined,
|
|
||||||
// rows: {
|
|
||||||
// selectedRowKeys?: (string | number)[] | undefined;
|
|
||||||
// selectedRows?: T[] | undefined;
|
|
||||||
// }
|
|
||||||
{
|
|
||||||
const toolbarElements =
|
|
||||||
toolbarActions?.map((action) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={action.key}
|
|
||||||
type={action.type}
|
|
||||||
danger={action.danger}
|
|
||||||
disabled={action.disabled}
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => action.onClick(selectedRowKeys, selectedRows)}
|
|
||||||
>
|
|
||||||
{action.label}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}) || [];
|
|
||||||
// return [
|
|
||||||
// <Button
|
|
||||||
// key="button"
|
|
||||||
// icon={<PlusOutlined />}
|
|
||||||
// onClick={}
|
|
||||||
// type="primary"
|
|
||||||
// >
|
|
||||||
// 新建
|
|
||||||
// </Button>,
|
|
||||||
// ];
|
|
||||||
return toolbarElements;
|
|
||||||
}, [toolbarActions]);
|
|
||||||
return (
|
return (
|
||||||
<ProTable<T, U>
|
<ProTable<T, U>
|
||||||
{...restProps}
|
{...restProps}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
actionRef={ref}
|
actionRef={ref}
|
||||||
request={request}
|
request={request}
|
||||||
rowKey={rowKey || 'id'}
|
rowKey={rowKey}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
toolBarRender={toolBarRender}
|
toolBarRender={toolBarRender}
|
||||||
manualRequest={false}
|
manualRequest={false}
|
||||||
showSorterTooltip
|
showSorterTooltip
|
||||||
tableAlertRender={tableAlertRender}
|
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{ x: 'max-content' }}
|
||||||
search={{
|
search={{
|
||||||
labelWidth: 'auto',
|
labelWidth: 'auto',
|
||||||
|
|||||||
8
src/components/Upload/UploadCard/index.module.less
Normal file
8
src/components/Upload/UploadCard/index.module.less
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.uploader-card {
|
||||||
|
background-color: #fff;
|
||||||
|
:global {
|
||||||
|
.ant-upload-drag {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
src/components/Upload/UploadCard/index.tsx
Normal file
176
src/components/Upload/UploadCard/index.tsx
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
|
FileTextOutlined,
|
||||||
|
PlayCircleOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { message, Upload } from 'antd';
|
||||||
|
import type { RcFile, UploadFile, UploadProps } from 'antd/lib/upload';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { createSample, getSample } from '@/services/ai/sample';
|
||||||
|
import styles from './index.module.less';
|
||||||
|
|
||||||
|
const { Dragger } = Upload;
|
||||||
|
interface AudioUploaderProps {
|
||||||
|
value?: UploadFile[] | UploadFile | null;
|
||||||
|
onChange?: (file: UploadFile[] | UploadFile | null) => void;
|
||||||
|
maxSize?: number;
|
||||||
|
accept?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
maxCount?: number;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AudioUploader: React.FC<AudioUploaderProps> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
maxSize = 10,
|
||||||
|
accept = '.mp3,.wav,.flac,.aac,.ogg',
|
||||||
|
disabled = false,
|
||||||
|
maxCount = 10,
|
||||||
|
placeholder = '点击或拖拽音频文件到此区域上传',
|
||||||
|
}) => {
|
||||||
|
const [fileList, setFileList] = useState<UploadFile[]>(() => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value;
|
||||||
|
} else if (value) {
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
const [uploading, setUploading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const beforeUpload = (file: RcFile): boolean => {
|
||||||
|
const isAudio = file.type.startsWith('audio/');
|
||||||
|
if (!isAudio) {
|
||||||
|
message.error('只能上传音频文件!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件大小
|
||||||
|
const isLtMaxSize = file.size / 1024 / 1024 < maxSize;
|
||||||
|
if (!isLtMaxSize) {
|
||||||
|
message.error(`音频文件大小不能超过 ${maxSize}MB!`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件数量限制
|
||||||
|
if (fileList.length >= maxCount) {
|
||||||
|
message.error(`最多只能上传 ${maxCount} 个文件!`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 实际的后端接口上传
|
||||||
|
const uploadToServer = async (file: File) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
// formData.append("type", "audio"); // 可以添加额外参数
|
||||||
|
|
||||||
|
const response = await createSample(formData);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
const customRequest: UploadProps['customRequest'] = async (options) => {
|
||||||
|
const { file, onSuccess, onError, onProgress } = options;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setUploading(true);
|
||||||
|
|
||||||
|
// 模拟进度更新
|
||||||
|
onProgress?.({ percent: 10 });
|
||||||
|
|
||||||
|
// 调用后端接口
|
||||||
|
const result = await uploadToServer(file as File);
|
||||||
|
|
||||||
|
onProgress?.({ percent: 100 });
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
// 构造返回数据
|
||||||
|
const responseData = {
|
||||||
|
url: result.data.url,
|
||||||
|
name: result.data.filename || (file as File).name,
|
||||||
|
uid: (file as any).uid,
|
||||||
|
status: 'done' as const,
|
||||||
|
response: result.data,
|
||||||
|
};
|
||||||
|
|
||||||
|
onSuccess?.(responseData);
|
||||||
|
message.success('音频上传成功!');
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || '上传失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Upload error:', error);
|
||||||
|
onError?.(error as Error);
|
||||||
|
message.error(`上传失败: ${(error as Error).message}`);
|
||||||
|
} finally {
|
||||||
|
setUploading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange: UploadProps['onChange'] = ({
|
||||||
|
fileList: newFileList,
|
||||||
|
file,
|
||||||
|
}) => {
|
||||||
|
setFileList(newFileList);
|
||||||
|
onChange?.(newFileList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemove = (file: UploadFile): boolean => {
|
||||||
|
const newFileList = fileList.filter((item) => item.uid !== file.uid);
|
||||||
|
setFileList(newFileList);
|
||||||
|
onChange?.(newFileList);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePreview = (file: UploadFile): void => {
|
||||||
|
const audioUrl = file.url || (file.response as any)?.url;
|
||||||
|
if (audioUrl) {
|
||||||
|
const audio = new Audio(audioUrl);
|
||||||
|
audio.play().catch(() => {
|
||||||
|
message.error('音频播放失败');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadProps: UploadProps = {
|
||||||
|
name: 'file',
|
||||||
|
multiple: true,
|
||||||
|
fileList,
|
||||||
|
accept,
|
||||||
|
disabled: disabled || uploading,
|
||||||
|
beforeUpload,
|
||||||
|
customRequest,
|
||||||
|
// onChange: handleChange,
|
||||||
|
onRemove: handleRemove,
|
||||||
|
onPreview: handlePreview,
|
||||||
|
showUploadList: {
|
||||||
|
showPreviewIcon: true,
|
||||||
|
showRemoveIcon: true,
|
||||||
|
previewIcon: <PlayCircleOutlined />,
|
||||||
|
removeIcon: <DeleteOutlined />,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles['uploader-card']}>
|
||||||
|
<Dragger {...uploadProps}>
|
||||||
|
<p className="">
|
||||||
|
<FileTextOutlined style={{ fontSize: 36 }} />
|
||||||
|
</p>
|
||||||
|
<p className="ant-upload-text">{placeholder}</p>
|
||||||
|
<p className="ant-upload-hint">
|
||||||
|
支持格式:{accept},单次最多同时上传 {maxCount} 个文件
|
||||||
|
</p>
|
||||||
|
</Dragger>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AudioUploader;
|
||||||
136
src/pages/ai/sample-tag/config.tsx
Normal file
136
src/pages/ai/sample-tag/config.tsx
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import type {
|
||||||
|
ProColumns,
|
||||||
|
ProFormColumnsType,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import TagEditor from '@/components/TagEditor';
|
||||||
|
import TinyMCEEditor from '@/components/Tinymce';
|
||||||
|
export const baseTenantColumns: ProColumns<API.CategoryDO>[] = [
|
||||||
|
{
|
||||||
|
title: '样本名称',
|
||||||
|
dataIndex: 'sample_name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '注释',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标签',
|
||||||
|
hideInTable: true,
|
||||||
|
dataIndex: 'tag_name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '样本格式',
|
||||||
|
hideInTable: true,
|
||||||
|
dataIndex: 'sample_mine_type',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const formColumns = (data: {
|
||||||
|
type: string;
|
||||||
|
grade: number;
|
||||||
|
}): ProFormColumnsType[] => [
|
||||||
|
{
|
||||||
|
title: '类目',
|
||||||
|
dataIndex: 'grade',
|
||||||
|
valueType: 'radio',
|
||||||
|
fieldProps: {
|
||||||
|
value: data.grade || 1,
|
||||||
|
options: [
|
||||||
|
{ label: '一级类目', value: 1 },
|
||||||
|
{ label: '二级类目', value: 2 },
|
||||||
|
{ label: '三级类目', value: 3 },
|
||||||
|
],
|
||||||
|
disabled: data.type === 'create',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类目名称',
|
||||||
|
dataIndex: 'username',
|
||||||
|
formItemProps: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入用户名',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '排序权重',
|
||||||
|
dataIndex: 'sort',
|
||||||
|
valueType: 'digit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类目描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
valueType: 'textarea',
|
||||||
|
renderFormItem: () => {
|
||||||
|
return <TinyMCEEditor />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '关联父级',
|
||||||
|
dataIndex: 'parentId',
|
||||||
|
valueType: 'select',
|
||||||
|
hideInForm: data.grade - 1 === 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类目icon',
|
||||||
|
dataIndex: 'icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类目标签',
|
||||||
|
dataIndex: 'tages',
|
||||||
|
renderFormItem: () => {
|
||||||
|
return (
|
||||||
|
<TagEditor
|
||||||
|
placeholder="输入标签名称"
|
||||||
|
maxCount={10}
|
||||||
|
tagProps={{
|
||||||
|
color: 'blue',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类目状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
hideInForm: data.type === 'create',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类目ID',
|
||||||
|
dataIndex: 'categoryId',
|
||||||
|
hideInForm: data.type === 'create',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
hideInForm: data.type === 'create',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建人',
|
||||||
|
dataIndex: 'creator',
|
||||||
|
hideInForm: data.type === 'create',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '更新时间',
|
||||||
|
dataIndex: 'updateTime',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
hideInForm: data.type === 'create',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '更新人',
|
||||||
|
dataIndex: 'updator',
|
||||||
|
hideInForm: data.type === 'create',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// {
|
||||||
|
// title: "模板内容",
|
||||||
|
// dataIndex: "content",
|
||||||
|
// valueType: "textarea",
|
||||||
|
// },
|
||||||
81
src/pages/ai/sample-tag/detail.tsx
Normal file
81
src/pages/ai/sample-tag/detail.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
ProForm,
|
||||||
|
ProFormCascader,
|
||||||
|
ProFormCheckbox,
|
||||||
|
ProFormColorPicker,
|
||||||
|
ProFormDigit,
|
||||||
|
ProFormDigitRange,
|
||||||
|
ProFormGroup,
|
||||||
|
ProFormRadio,
|
||||||
|
ProFormSelect,
|
||||||
|
ProFormSlider,
|
||||||
|
ProFormSwitch,
|
||||||
|
ProFormText,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { Switch } from 'antd';
|
||||||
|
import type { FormInstance } from 'antd/lib';
|
||||||
|
import Mock from 'mockjs';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import TagEditor from '@/components/TagEditor';
|
||||||
|
|
||||||
|
export const waitTime = (time: number = 100) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(true);
|
||||||
|
}, time);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const SampleTagDetail = () => {
|
||||||
|
const [readonly, setReadonly] = useState(false);
|
||||||
|
const formRef = useRef<FormInstance>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* <Switch
|
||||||
|
style={{
|
||||||
|
marginBlockEnd: 16,
|
||||||
|
}}
|
||||||
|
checked={readonly}
|
||||||
|
checkedChildren="编辑"
|
||||||
|
unCheckedChildren="只读"
|
||||||
|
onChange={setReadonly}
|
||||||
|
/> */}
|
||||||
|
<ProForm
|
||||||
|
readonly={readonly}
|
||||||
|
name="validate_other"
|
||||||
|
formRef={formRef}
|
||||||
|
initialValues={{
|
||||||
|
sample_name: '1111',
|
||||||
|
}}
|
||||||
|
onValuesChange={(_, values) => {
|
||||||
|
console.log(values);
|
||||||
|
}}
|
||||||
|
onFinish={async (value) => console.log(value)}
|
||||||
|
>
|
||||||
|
<ProFormGroup title="预览">{/* <audio></audio> */}</ProFormGroup>
|
||||||
|
<ProFormGroup title="基本信息">
|
||||||
|
<ProFormText
|
||||||
|
width="md"
|
||||||
|
name="sample_name"
|
||||||
|
placeholder="请输入样本名称"
|
||||||
|
rules={[{ required: true, message: '样本名称不能为空' }]}
|
||||||
|
/>
|
||||||
|
<ProFormText width="md" name="remark" placeholder="请输入注释" />
|
||||||
|
</ProFormGroup>
|
||||||
|
<ProFormGroup
|
||||||
|
title="标签"
|
||||||
|
style={{
|
||||||
|
gap: '0 32px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProForm.Item name="tags">
|
||||||
|
<TagEditor placeholder="输入标签名称" maxCount={10} />
|
||||||
|
</ProForm.Item>
|
||||||
|
</ProFormGroup>
|
||||||
|
</ProForm>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SampleTagDetail;
|
||||||
15
src/pages/ai/sample-tag/index.module.less
Normal file
15
src/pages/ai/sample-tag/index.module.less
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.tag-content {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
:global {
|
||||||
|
.ant-pro-table {
|
||||||
|
flex: 1 auto;
|
||||||
|
}
|
||||||
|
.detail {
|
||||||
|
border-left: 1px solid #e8e8e8;
|
||||||
|
width: 400px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/pages/ai/sample-tag/index.tsx
Normal file
61
src/pages/ai/sample-tag/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import type { ActionType } from '@ant-design/pro-components';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import EnhancedProTable from '@/components/EnhancedProTable';
|
||||||
|
import type { ToolbarAction } from '@/components/EnhancedProTable/types';
|
||||||
|
import UploadCard from '@/components/Upload/UploadCard';
|
||||||
|
import { baseTenantColumns } from './config';
|
||||||
|
import SampleTagDetail from './detail';
|
||||||
|
import styles from './index.module.less';
|
||||||
|
|
||||||
|
const SampleTag: React.FC = () => {
|
||||||
|
const tableRef = useRef<ActionType>(null);
|
||||||
|
// const [detail, setDetail] = useState<any>(null);
|
||||||
|
// const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
|
const handleAll = () => {
|
||||||
|
// console.log(tableRef.current.getSelectedRowKeys());
|
||||||
|
};
|
||||||
|
const toolbarActions: ToolbarAction[] = [
|
||||||
|
{
|
||||||
|
key: 'add',
|
||||||
|
label: '批量编辑',
|
||||||
|
type: 'primary',
|
||||||
|
|
||||||
|
onClick: handleAll,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const onFetch = async (_params: API.getProductCategoryCategoryListParams) => {
|
||||||
|
// const data = await getCategoryList({
|
||||||
|
// ...params,
|
||||||
|
// });
|
||||||
|
const data: any = [
|
||||||
|
{ sample_name: 111, id: 1, remark: 222 },
|
||||||
|
{ sample_name: 22, id: 2, remark: 33 },
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<UploadCard />
|
||||||
|
<div className={styles['tag-content']}>
|
||||||
|
<EnhancedProTable<API.CategoryDO>
|
||||||
|
ref={tableRef}
|
||||||
|
columns={baseTenantColumns}
|
||||||
|
request={onFetch}
|
||||||
|
toolbarActions={toolbarActions}
|
||||||
|
headerTitle="样本列表"
|
||||||
|
showIndex={false}
|
||||||
|
showSelection={true}
|
||||||
|
/>
|
||||||
|
<div className="detail">
|
||||||
|
<SampleTagDetail />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SampleTag;
|
||||||
161
src/services/ai/sample/index.ts
Normal file
161
src/services/ai/sample/index.ts
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import { request } from "@umijs/max";
|
||||||
|
|
||||||
|
export interface SampleVo {
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
createTime?: string[];
|
||||||
|
/**
|
||||||
|
* 页码,从 1 开始", example = "1
|
||||||
|
*/
|
||||||
|
remark?: string;
|
||||||
|
/**
|
||||||
|
* 样本文件id
|
||||||
|
*/
|
||||||
|
sampleFileId?: number;
|
||||||
|
/**
|
||||||
|
* 样本格式
|
||||||
|
*/
|
||||||
|
sampleMineType?: string;
|
||||||
|
/**
|
||||||
|
* 样本名称
|
||||||
|
*/
|
||||||
|
sampleName?: string;
|
||||||
|
/**
|
||||||
|
* 样本大小
|
||||||
|
*/
|
||||||
|
sampleSize?: string;
|
||||||
|
/**
|
||||||
|
* 样本时长
|
||||||
|
*/
|
||||||
|
sampleTime?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回数据
|
||||||
|
*
|
||||||
|
* PageResultAiSampleRespVO
|
||||||
|
*/
|
||||||
|
export interface PageResultAiSampleRespVO {
|
||||||
|
/**
|
||||||
|
* 数据
|
||||||
|
*/
|
||||||
|
list?: AiSampleRespVO[];
|
||||||
|
/**
|
||||||
|
* 总量
|
||||||
|
*/
|
||||||
|
total?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* com.tashow.cloud.ai.controller.admin.aisample.vo.AiSampleRespVO
|
||||||
|
*
|
||||||
|
* AiSampleRespVO
|
||||||
|
*/
|
||||||
|
export interface AiSampleRespVO {
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
createTime?: string;
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
id?: number;
|
||||||
|
/**
|
||||||
|
* 样本注释
|
||||||
|
*/
|
||||||
|
remark?: string;
|
||||||
|
/**
|
||||||
|
* 样本文件id
|
||||||
|
*/
|
||||||
|
sampleFileId?: number;
|
||||||
|
/**
|
||||||
|
* 样本格式
|
||||||
|
*/
|
||||||
|
sampleMineType?: string;
|
||||||
|
/**
|
||||||
|
* 样本名称
|
||||||
|
*/
|
||||||
|
sampleName?: string;
|
||||||
|
/**
|
||||||
|
* 样本大小
|
||||||
|
*/
|
||||||
|
sampleSize?: string;
|
||||||
|
/**
|
||||||
|
* 样本时长
|
||||||
|
*/
|
||||||
|
sampleTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SampleReqVo extends PageParam {
|
||||||
|
name?: string;
|
||||||
|
status?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得样本库分页
|
||||||
|
export const getSamplePage = async (params: SampleReqVo) => {
|
||||||
|
return request("/ai/sample/get", {
|
||||||
|
method: "GET",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getSample = async (id: number) => {
|
||||||
|
return request("/ai/sample/get", {
|
||||||
|
method: "GET",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询部门列表
|
||||||
|
// export const getDeptPage = async (params: DeptReq): Promise<Dept[]> => {
|
||||||
|
// return await request.get({ url: "/system/dept/list", params });
|
||||||
|
// };
|
||||||
|
|
||||||
|
export const getDeptPage = (params: SampleReqVo) => {
|
||||||
|
return request("/system/dept/list", {
|
||||||
|
method: "GET",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查询部门详情
|
||||||
|
// export const getDept = async (id: number) => {
|
||||||
|
// return await request.get({ url: "/system/dept/get?id=" + id });
|
||||||
|
// };
|
||||||
|
|
||||||
|
export const getDept = (id: number) => {
|
||||||
|
return request("/system/dept/get", {
|
||||||
|
method: "GET",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建样本库
|
||||||
|
export const createSample = (formData: FormData) => {
|
||||||
|
return request("/ai/sample/create", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
},
|
||||||
|
data: formData,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateSample = (params: SampleReqVo) => {
|
||||||
|
return request("/ai/sample/update", {
|
||||||
|
method: "PUT",
|
||||||
|
data: params,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除部门
|
||||||
|
// export const deleteDept = async (id: number) => {
|
||||||
|
// return await request.delete({ url: "/system/dept/delete?id=" + id });
|
||||||
|
// };
|
||||||
|
|
||||||
|
export const deleteSample = (id: number) => {
|
||||||
|
return request("/ai/sample/delete", {
|
||||||
|
method: "DELETE",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user