refactor(layout): 重构布局组件并优化代码格式

-调整了多个组件中的导入顺序和结构
- 优化了部分代码的格式和缩进
- 移除了未使用的变量和导入
- 添加了一些必要的注释
main
Tuzki 5 months ago
parent 163301e4a6
commit e8edfab07f
  1. 2
      web/client/api/index.ts
  2. 1442
      web/components/IconSelect/IconSelect.tsx
  3. 41
      web/components/chat/chat-content/agent-messages.tsx
  4. 4
      web/components/flow/canvas-modal/flow-template-modal.tsx
  5. 5
      web/components/knowledge/arguments-modal.tsx
  6. 80
      web/components/layout/side-bar.tsx
  7. 4
      web/hooks/useUserMenu.ts
  8. 37
      web/new-components/layout/Construct.tsx
  9. 8
      web/new-components/layout/SystemConstruct.tsx
  10. 4
      web/new-components/layout/UserBar.tsx
  11. 56
      web/next.config.js
  12. 16
      web/pages/_app.tsx
  13. 15
      web/pages/construct/database.tsx
  14. 8
      web/pages/evaluation/index.tsx
  15. 2
      web/pages/login.tsx
  16. 10
      web/pages/system/menu/index.tsx
  17. 10
      web/pages/system/organization/index.tsx
  18. 6
      web/pages/system/role/index.tsx
  19. 4
      web/pages/system/user/index.tsx

@ -1,5 +1,5 @@
import { getUserId } from '@/utils';
import { HEADER_USER_ID_KEY, STORAGE_USERINFO_KEY,USER_MENU } from '@/utils/constants/index';
import { HEADER_USER_ID_KEY, STORAGE_USERINFO_KEY, USER_MENU } from '@/utils/constants/index';
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
// 定义 API 响应数据的类型

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
import { Button } from 'antd';
import { GPTVis } from '@antv/gpt-vis';
import { SoundOutlined } from '@ant-design/icons';
import { GPTVis } from '@antv/gpt-vis';
import { Button } from 'antd';
import { useMemo, useState } from 'react';
import ReferencesContent from './ReferencesContent';
import styles from './common.module.scss';
@ -49,12 +49,12 @@ function AgentMessages({ data }: Props) {
};
fetch('https://api.siliconflow.cn/v1/audio/speech', options)
.then((response) => response.blob())
.then((blob) => {
.then(response => response.blob())
.then(blob => {
const audioUrl = URL.createObjectURL(blob);
setAudioSrc(audioUrl);
})
.catch((err) => console.error(err));
.catch(err => console.error(err));
};
if (!data || !data.length) return null;
@ -85,20 +85,24 @@ function AgentMessages({ data }: Props) {
console.log('cleanMarkdown', cleanMarkdown);
return (
cleanMarkdown !== '' && (
<div className="rounded p-4 pl-0 pt-3 bg-white dark:bg-gray-900">
<div className="whitespace-normal text-sm mb-3">
<p className="text-sm whitespace-normal">{plainText}</p>
<div className='rounded p-4 pl-0 pt-3 bg-white dark:bg-gray-900'>
<div className='whitespace-normal text-sm mb-3'>
<p className='text-sm whitespace-normal'>{plainText}</p>
</div>
{showPlay && (
<div className={`space-x-2 ${item.receiver === '?' ? 'hidden' : ''}`}>
<Button size="small" onClick={handleFetchAndPlay}>
<Button size='small' onClick={handleFetchAndPlay}>
<SoundOutlined />
</Button>
{audioSrc && <audio controls src={audioSrc}> audio </audio>}
{audioSrc && (
<audio controls src={audioSrc}>
audio
</audio>
)}
</div>
)}
{item.resource && item.resource !== 'null' && (
<div className="mt-4">
<div className='mt-4'>
<ReferencesContent references={item.resource} />
</div>
)}
@ -108,26 +112,21 @@ function AgentMessages({ data }: Props) {
};
console.log('data', showPlay);
return (
<div className="space-y-8">
{showPlay &&
data.map((item, index) => (
<AgentMessageItem key={index} item={item} index={index} />
))}
<div className='space-y-8'>
{showPlay && data.map((item, index) => <AgentMessageItem key={index} item={item} index={index} />)}
{!showPlay &&
data.map((item, index) => (
<div key={index}>
<div className="whitespace-normal text-sm mb-3">
<div className='whitespace-normal text-sm mb-3'>
<GPTVis components={markdownComponents} {...markdownPlugins}>
{preprocessLaTeX(item.markdown)}
</GPTVis>
</div>
{item.resource && item.resource !== 'null' && (
<ReferencesContent references={item.resource} />
)}
{item.resource && item.resource !== 'null' && <ReferencesContent references={item.resource} />}
</div>
))}
</div>
);
}
export default AgentMessages;
export default AgentMessages;

@ -2,9 +2,9 @@ import { getFlowTemplates } from '@/client/api';
import CanvasWrapper from '@/pages/construct/flow/canvas/index';
import type { TableProps } from 'antd';
import { Button, Modal, Space, Table } from 'antd';
import cls from 'classnames';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import cls from 'classnames';
import './index.scss';
type Props = {
isFlowTemplateModalOpen: boolean;
@ -88,7 +88,7 @@ export const FlowTemplateModal: React.FC<Props> = ({ isFlowTemplateModalOpen, se
okButtonProps={{ className: 'hidden' }}
>
<Table
className={cls('m-table w-full')}
className={cls('m-table w-full')}
// className='w-full'
// scroll={{ x: 'max-content' }}
dataSource={dataSource}

@ -208,9 +208,8 @@ export default function ArgumentsModal({ space, argumentsShow, setArgumentsShow
}}
size='small'
indicator={{ size: origin => origin - 80, align: 'center' }}
items={items}>
</Tabs>
items={items}
></Tabs>
<div className='mt-3 mb-3'>
<Button htmlType='submit' type='primary' className='mr-6'>
{t('Submit')}

@ -1,8 +1,7 @@
import { ChatContext } from '@/app/chat-context';
import UserBar from '@/new-components/layout/UserBar';
import { DarkSvg, SunnySvg } from '@/components/icons';
import { STORAGE_LANG_KEY, STORAGE_THEME_KEY, STORAGE_USERINFO_KEY } from '@/utils/constants/index';
import Icon, { GlobalOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import { STORAGE_LANG_KEY, STORAGE_THEME_KEY, STORAGE_USERINFO_KEY, USER_MENU } from '@/utils/constants/index';
import { GlobalOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import { Popover, Tooltip } from 'antd';
import { ItemType } from 'antd/es/menu/hooks/useItems';
import cls from 'classnames';
@ -13,7 +12,6 @@ import Link from 'next/link';
import { useRouter } from 'next/router';
import { ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { USER_MENU } from '@/utils/constants/index';
type SettingItem = {
key: string;
@ -43,8 +41,9 @@ type RouteItem = {
// }
function smallMenuItemStyle(active?: boolean) {
return `flex items-center justify-center mx-auto rounded w-14 h-14 text-xl hover:bg-[#F1F5F9] dark:hover:bg-theme-dark transition-colors cursor-pointer ${active ? 'bg-[#F1F5F9] dark:bg-theme-dark' : ''
}`;
return `flex items-center justify-center mx-auto rounded w-14 h-14 text-xl hover:bg-[#F1F5F9] dark:hover:bg-theme-dark transition-colors cursor-pointer ${
active ? 'bg-[#F1F5F9] dark:bg-theme-dark' : ''
}`;
}
function SideBar() {
// const { chatId, scene, isMenuExpand, refreshDialogList, setIsMenuExpand, setAgent, mode, setMode, adminList } =
@ -258,34 +257,35 @@ function SideBar() {
const functions = useMemo(() => {
const items: RouteItem[] = (userMenus || [])
.filter((menu) => menu.visible === 1) // 过滤可见项
.map((menu) => {
const isActive = pathname === menu.path || (menu.path !== '/' && pathname.startsWith(`/${menu.component_name}`));
let iconSrc = '/menus/default.svg'; // 默认图标
if (isActive) {
iconSrc = `/menus/${menu.component_name}_active.svg`; // 动态 active 状态图标
} else {
iconSrc = `/menus/${menu.component_name}.svg`;
}
.filter(menu => menu.visible === 1) // 过滤可见项
.map(menu => {
const isActive =
pathname === menu.path || (menu.path !== '/' && pathname.startsWith(`/${menu.component_name}`));
let iconSrc = '/menus/default.svg'; // 默认图标
if (isActive) {
iconSrc = `/menus/${menu.component_name}_active.svg`; // 动态 active 状态图标
} else {
iconSrc = `/menus/${menu.component_name}.svg`;
}
return {
key: menu.component_name,
name: t(menu.component_name),
path: menu.path,
isActive,
icon: (
<Image
key={`image_${menu.component_name}`}
src={iconSrc}
alt={`${menu.name}_image`}
width={20}
height={20}
/>
),
};
});
return {
key: menu.component_name,
name: t(menu.component_name),
path: menu.path,
isActive,
icon: (
<Image
key={`image_${menu.component_name}`}
src={iconSrc}
alt={`${menu.name}_image`}
width={20}
height={20}
/>
),
};
});
return items;
return items;
// const items: RouteItem[] = [
// {
// key: 'chat',
@ -348,9 +348,9 @@ function SideBar() {
// path: '/system/organization',
// },
// ];
// return items;
}, [t, pathname, hasAdmin,userMenus]);
}, [t, pathname, hasAdmin, userMenus]);
// TODO: unused function
// const dropDownRoutes: ItemType[] = useMemo(() => {
@ -454,9 +454,9 @@ function SideBar() {
return (
<div
className='flex flex-col justify-between pt-4 h-screen bg-bar dark:bg-[#232734] animate-fade animate-duration-300'
// onMouseEnter={() => {
// setIsMenuExpand(true);
// }}
// onMouseEnter={() => {
// setIsMenuExpand(true);
// }}
>
<div>
<Link href='/' className='flex justify-center items-center pb-4'>
@ -489,9 +489,9 @@ function SideBar() {
return (
<div
className='flex flex-col justify-between h-screen px-4 pt-4 bg-bar bg-[#F9FBFF] dark:bg-[#232734] animate-fade animate-duration-300'
// onMouseLeave={() => {
// setIsMenuExpand(false);
// }}
// onMouseLeave={() => {
// setIsMenuExpand(false);
// }}
>
<div>
{/* LOGO */}

@ -1,6 +1,6 @@
// hooks/useUserMenu.ts
import { useState, useEffect } from 'react';
import { USER_MENU } from '@/utils/constants/index';
import { useEffect, useState } from 'react';
export const useUserMenu = () => {
const [userMenus, setUserMenus] = useState<any[]>([]);
@ -23,4 +23,4 @@ export const useUserMenu = () => {
}, []);
return userMenus;
};
};

@ -1,18 +1,11 @@
import { ModelSvg } from '@/components/icons';
import Icon, {
AppstoreOutlined,
ConsoleSqlOutlined,
ForkOutlined,
MessageOutlined,
PartitionOutlined,
} from '@ant-design/icons';
import { USER_MENU } from '@/utils/constants/index';
import Icon, * as Icons from '@ant-design/icons';
import { ConfigProvider, Tabs } from 'antd';
import { t } from 'i18next';
import { useRouter } from 'next/router';
import React from 'react';
import './style.css';
import { USER_MENU } from '@/utils/constants/index';
import * as Icons from '@ant-design/icons';
const renderIcon = (iconName?: string) => {
if (!iconName || iconName === '#' || !Icons[iconName]) return null;
const IconComponent = Icons[iconName as keyof typeof Icons];
@ -20,7 +13,9 @@ const renderIcon = (iconName?: string) => {
};
function ConstructLayout({ children }: { children: React.ReactNode }) {
const data = localStorage.getItem(USER_MENU);
const parsed = data ? JSON.parse(data).menus.filter((item: any) => item.component_name == 'construct')[0].children : [];
const parsed = data
? JSON.parse(data).menus.filter((item: any) => item.component_name == 'construct')[0].children
: [];
console.log('parsed', parsed);
// const items = [
// {
@ -123,23 +118,21 @@ function ConstructLayout({ children }: { children: React.ReactNode }) {
key: item.component_name,
label: t(item.component_name),
children: children,
icon: item.component_name === 'models'
? <Icon component={ModelSvg} />
: renderIcon(item.icon)
icon: item.component_name === 'models' ? <Icon component={ModelSvg} /> : renderIcon(item.icon),
};
})}
onTabClick={key => {
router.push(`/construct/${key}`);
}}
// tabBarExtraContent={
// <Button
// className='border-none text-white bg-button-gradient h-full flex items-center'
// icon={<PlusOutlined className='text-base' />}
// // onClick={handleCreate}
// >
// {t('create_app')}
// </Button>
// }
// tabBarExtraContent={
// <Button
// className='border-none text-white bg-button-gradient h-full flex items-center'
// icon={<PlusOutlined className='text-base' />}
// // onClick={handleCreate}
// >
// {t('create_app')}
// </Button>
// }
/>
</ConfigProvider>
</div>

@ -1,11 +1,11 @@
import { USER_MENU } from '@/utils/constants/index';
import * as Icons from '@ant-design/icons';
import { createFromIconfontCN, MenuFoldOutlined, UsergroupDeleteOutlined, UserOutlined } from '@ant-design/icons';
import { ConfigProvider, Tabs } from 'antd';
import { t } from 'i18next';
import { useRouter } from 'next/router';
import React from 'react';
import { USER_MENU } from '@/utils/constants/index';
import './style.css';
import * as Icons from '@ant-design/icons';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4926951_si989wen79f.js',
@ -711,9 +711,7 @@ function SystemConstructLayout({ children }: { children: React.ReactNode }) {
key: item.component_name,
label: t(item.component_name),
children: children,
icon: item.component_name === 'organization'
? <IconFont type="icon-company" />
: renderIcon(item.icon)
icon: item.component_name === 'organization' ? <IconFont type='icon-company' /> : renderIcon(item.icon),
};
})}
onTabClick={key => {

@ -1,6 +1,6 @@
import { UserInfoResponse } from '@/types/userinfo';
import { STORAGE_USERINFO_KEY,USER_MENU } from '@/utils/constants/index';
import { ExclamationCircleOutlined ,LogoutOutlined} from '@ant-design/icons';
import { STORAGE_USERINFO_KEY, USER_MENU } from '@/utils/constants/index';
import { ExclamationCircleOutlined, LogoutOutlined } from '@ant-design/icons';
import { Avatar, Modal } from 'antd';
import cls from 'classnames';
import { useEffect, useState } from 'react';

@ -32,28 +32,27 @@ const nextConfig = {
],
webpack: (config, { isServer }) => {
config.resolve.fallback = { fs: false };
if (!isServer&& process.env.NODE_ENV === 'production') {
if (!isServer) {
config.plugins.push(
new CopyPlugin({
patterns: [
{
from: path.join(
__dirname,
"node_modules/@oceanbase-odc/monaco-plugin-ob/worker-dist/"
),
to: "static/ob-workers",
},
],
})
new CopyPlugin({
patterns: [
{
from: path.join(
__dirname,
"node_modules/@oceanbase-odc/monaco-plugin-ob/worker-dist/"
),
to: "static/ob-workers",
},
],
})
);
// 添加 monaco-editor-webpack-plugin 插件
config.plugins.push(
new MonacoWebpackPlugin({
// 你可以在这里配置插件的选项,例如:
languages: ["sql"],
features: [],
filename: "static/[name].worker.js",
})
new MonacoWebpackPlugin({
// 你可以在这里配置插件的选项,例如:
languages: ["sql"],
filename: "static/[name].worker.js",
})
);
if (Array.isArray(config.entry)) {
@ -69,19 +68,14 @@ const nextConfig = {
},
};
const dev = process.env.NODE_ENV === 'development';
const withTM = require("next-transpile-modules")(
dev
? [] // 开发时不转译任何模块(可测试性能)
: [
"@berryv/g2-react",
"@antv/g2",
"react-syntax-highlighter",
"@antv/g6",
"@antv/graphin",
"@antv/gpt-vis",
]
);
const withTM = require("next-transpile-modules")([
"@berryv/g2-react",
"@antv/g2",
"react-syntax-highlighter",
"@antv/g6",
"@antv/graphin",
"@antv/gpt-vis",
]);
module.exports = withTM({
...nextConfig,

@ -1,17 +1,17 @@
import { ChatContext, ChatContextProvider } from '@/app/chat-context';
import { getUserMenu } from '@/client/api';
import SideBar from '@/components/layout/side-bar';
import { STORAGE_LANG_KEY, STORAGE_USERINFO_KEY } from '@/utils/constants/index';
import { useRequest } from 'ahooks';
import { App, ConfigProvider, MappingAlgorithm, theme } from 'antd';
import enUS from 'antd/locale/en_US';
import zhCN from 'antd/locale/zh_CN';
import classNames from 'classnames';
import type { AppProps } from 'next/app';
import Head from 'next/head';
import { useRequest } from 'ahooks';
import { useRouter } from 'next/router';
import React, { useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getUserMenu } from '@/client/api';
import '../app/i18n';
import '../nprogress.css';
import '../styles/globals.css';
@ -113,24 +113,24 @@ function LayoutWrapper({ children }: { children: React.ReactNode }) {
// setIsLogin(true);
// }
};
const { pathname } = useRouter();
const { pathname } = useRouter();
const { run: getUserMenuFun } = useRequest(
async () => {
if(pathname.startsWith('/login')) return;
if (pathname.startsWith('/login')) return;
return await getUserMenu(); // 直接传递表单对象
},
{
manual: false,
onSuccess: (res) => {
if(!res) return;
onSuccess: res => {
if (!res) return;
const { data } = res;
console.log('getUserMenu', data);
localStorage.setItem('menu', JSON.stringify(data.data));
// localStorage.setItem('menu', JSON.stringify(menuRes.data.data));
// console.log('menuRes', menuRes);
},
onError: (error) => {
onError: error => {
console.log('error', error);
},
debounceWait: 500,

@ -12,7 +12,6 @@ import cls from 'classnames';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import './app/index.scss';
import { time } from 'console';
type DBItem = DbListResponse[0];
export function isFileDb(dbTypeList: DBOption[], dbType: DBType) {
@ -55,10 +54,16 @@ function Database() {
};
const dbTypeList = useMemo(() => {
console.log(dbSupportList,'dbSupportList');
console.log(dbSupportList, 'dbSupportList');
const supportDbList = dbSupportList.map(item => {
const db_type = item?.name;
return { ...dbMapper[db_type], value: db_type, isFileDb: true, parameters: item.parameters,description:item.description };
return {
...dbMapper[db_type],
value: db_type,
isFileDb: true,
parameters: item.parameters,
description: item.description,
};
}) as DBOption[];
const unSupportDbList = Object.keys(dbMapper)
.filter(item => !supportDbList.some(db => db.value === item))
@ -175,13 +180,13 @@ function Database() {
<div className='flex flex-wrap mx-[-8px] gap-2 md:gap-4'>
{dbTypeList.map(item => {
console.log(dbTypeList,'dbTypeList');
console.log(dbTypeList, 'dbTypeList');
return (
<Badge key={item.value} count={dbListByType[item.value]?.length} className='min-h-fit'>
<GPTCard
className='h-full'
title={item.label}
desc={item.description ?? item.desc?? ''}
desc={item.description ?? item.desc ?? ''}
disabled={item.disabled}
icon={item.icon}
onClick={() => {

@ -37,8 +37,8 @@ import {
message,
} from 'antd';
import { valueType } from 'antd/es/statistic/utils';
import { useMemo, useState } from 'react';
import cls from 'classnames';
import { useMemo, useState } from 'react';
import './index.scss';
const { TextArea } = Input;
@ -585,7 +585,7 @@ const Evaluation = () => {
</Button>
</div>
<Table
className={cls('m-table')}
className={cls('m-table')}
pagination={{
total: dataSetsTotal,
onChange(page) {
@ -612,7 +612,7 @@ const Evaluation = () => {
</Button>
</div>
<Table
className={cls('m-table')}
className={cls('m-table')}
pagination={{
total: evaluationTotal,
onChange(page) {
@ -918,7 +918,7 @@ const Evaluation = () => {
]}
>
<Table
className={cls('m-table')}
className={cls('m-table')}
columns={Object.keys(evaluationShowData?.[0]).map(key => ({
title: key,
dataIndex: key,

@ -24,8 +24,6 @@ const LoginPage: FC = () => {
return;
}
const user = {
token: res.data.data,
user_channel: `CJY`,

@ -7,6 +7,7 @@ import {
updateMenu,
updateMenuStatus,
} from '@/client/api';
import IconSelect from '@/components/IconSelect/IconSelect';
import SystemConstructConstruct from '@/new-components/layout/SystemConstruct';
import { Menu } from '@/types/prompt';
import { useRequest } from 'ahooks';
@ -25,12 +26,11 @@ import {
TreeSelect,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import cls from 'classnames';
import { TFunction } from 'i18next';
import { useEffect, useState } from 'react';
import IconSelect from '@/components/IconSelect/IconSelect';
import { useTranslation } from 'react-i18next';
import styles from './index.module.scss';
import cls from 'classnames';
import './index.scss';
const { Option } = Select;
@ -337,11 +337,7 @@ const Prompt = () => {
<Form.Item label='排序' name='sort' rules={[{ required: true }]}>
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label="菜单图标"
name="icon"
rules={[{ message: '请选择图标' }]}
>
<Form.Item label='菜单图标' name='icon' rules={[{ message: '请选择图标' }]}>
<IconSelect value={selectedIcon} onChange={handleIconChange} />
</Form.Item>
<Form.Item label='父级菜单' name='parent_id' rules={[{ required: true }]}>

@ -18,11 +18,11 @@ import {
TreeSelect,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import cls from 'classnames';
import { TFunction } from 'i18next';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './index.module.scss';
import cls from 'classnames';
import './index.scss';
const { Option } = Select;
@ -103,9 +103,7 @@ const Prompt = () => {
dataIndex: 'status',
// width: '10%',
key: 'status',
render: (status: number) => (
<span >{status == 1? '关闭' : '开启'}</span>
),
render: (status: number) => <span>{status == 1 ? '关闭' : '开启'}</span>,
},
{
title: t('create_time'),
@ -374,8 +372,8 @@ const Prompt = () => {
</Modal>
<Table
className={cls('m-table')}
columns={getColumns(t, handleEditBtn)}
className={cls('m-table')}
columns={getColumns(t, handleEditBtn)}
dataSource={promptList?.dept_list || []}
rowKey={record => record.id}
loading={loadingOrgs}

@ -31,13 +31,13 @@ import {
TreeProps,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import cls from 'classnames';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import { TFunction } from 'i18next';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './index.module.scss';
import cls from 'classnames';
import './index.scss';
dayjs.locale('zh-cn');
const { Option } = Select;
@ -386,7 +386,7 @@ const Prompt = () => {
</div>
</Form>
<Modal
className={cls('a')}
className={cls('a')}
title={modalType === 'add' ? '新增角色' : '编辑角色'}
open={modalVisible}
onCancel={() => {
@ -522,7 +522,7 @@ const Prompt = () => {
</div>
</Modal>
<Table
className={cls('m-table')}
className={cls('m-table')}
columns={getColumns(t, handleEditBtn)}
dataSource={promptList?.roles || []} // 改为使用roles字段
rowKey={record => record.id}

@ -17,17 +17,17 @@ import { IOrganization, IUser, orginzationResponse } from '@/types/prompt';
import { useRequest } from 'ahooks';
import { App, Button, Form, Input, Modal, Popconfirm, Select, Space, Switch, Table, TreeSelect } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import cls from 'classnames';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import { TFunction } from 'i18next';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './index.module.scss';
import './index.scss';
dayjs.locale('zh-cn');
const { Option } = Select;
type LayoutType = Parameters<typeof Form>[0]['layout'];
import cls from 'classnames';
import './index.scss';
const Prompt = () => {
const { message } = App.useApp();

Loading…
Cancel
Save