fix(前端): 文字修改+优化

master
chenyt 2025-08-13 16:34:32 +08:00
parent 46263f8d0d
commit b88b020c3b
6 changed files with 116 additions and 147 deletions

View File

@ -49,7 +49,7 @@ export default defineConfig({
// changeOrigin: true, // changeOrigin: true,
}, },
'/api/files': { '/api/files': {
target: 'http://10.100.51.85:8112', target: 'http://10.100.51.85:8113',
}, },
}, },
}); });

View File

@ -80,60 +80,60 @@ const MainLayout: React.FC = () => {
return ( return (
<ConfigProvider locale={zhCN}> <ConfigProvider locale={zhCN}>
<Layout className="main-layout"> <Layout className="main-layout">
<Sider <Sider
trigger={null} trigger={null}
collapsible collapsible
collapsed={collapsed} collapsed={collapsed}
className="main-sider" className="main-sider"
>
<div className="logo">{!collapsed && <span>VDI</span>}</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={[selectedKey]}
onClick={handleMenuClick}
> >
<Menu.Item key="userList" icon={<AppstoreOutlined />}> <div className="logo">{!collapsed && <span>Nex</span>}</div>
<Menu
</Menu.Item> theme="dark"
<Menu.Item key="terminal" icon={<AppstoreOutlined />}> mode="inline"
selectedKeys={[selectedKey]}
</Menu.Item> onClick={handleMenuClick}
<Menu.Item key="images" icon={<AppstoreOutlined />}>
</Menu.Item>
<Menu.Item key="profile" icon={<UserOutlined />}>
</Menu.Item>
</Menu>
</Sider>
<Layout>
<Header className="main-header">
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
className="trigger"
/>
<div className="header-right">
<span className="welcome-text">{username}</span>
<Dropdown overlay={userMenu} placement="bottomRight">
<Avatar icon={<UserOutlined />} className="user-avatar" />
</Dropdown>
</div>
</Header>
<Content
className="main-content"
style={{ height: 'calc(100vh - 64px)', overflow: 'auto' }}
> >
<Outlet /> <Menu.Item key="terminal" icon={<AppstoreOutlined />}>
</Content>
</Menu.Item>
<Menu.Item key="userList" icon={<AppstoreOutlined />}>
</Menu.Item>
<Menu.Item key="images" icon={<AppstoreOutlined />}>
</Menu.Item>
<Menu.Item key="profile" icon={<UserOutlined />}>
</Menu.Item>
</Menu>
</Sider>
<Layout>
<Header className="main-header">
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
className="trigger"
/>
<div className="header-right">
<span className="welcome-text">{username}</span>
<Dropdown overlay={userMenu} placement="bottomRight">
<Avatar icon={<UserOutlined />} className="user-avatar" />
</Dropdown>
</div>
</Header>
<Content
className="main-content"
style={{ height: 'calc(100vh - 64px)', overflow: 'auto' }}
>
<Outlet />
</Content>
</Layout>
</Layout> </Layout>
</Layout>
</ConfigProvider> </ConfigProvider>
); );
}; };

View File

@ -76,37 +76,25 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
// 上传镜像时间相关 // 上传镜像时间相关
const [elapsedTime, setElapsedTime] = useState<number>(0); // 上传时间 const [elapsedTime, setElapsedTime] = useState<number>(0); // 上传时间
const timerRef = useRef<NodeJS.Timeout | null>(null); const timerRef = useRef<NodeJS.Timeout | null>(null);
// 上传完成状态
// const [uploadCompleted, _setUploadCompleted] = useState(false); const uploadCompletedRef = useRef(false);
// const uploadCompletedRef = useRef(false); // 手动取消状态
const isManualCancel = useRef(false); // 是否手动取消上传
// const setUploadCompleted = (value: boolean) => {
// uploadCompletedRef.current = value;
// _setUploadCompleted(value);
// };
// 处理页面刷新/关闭 // 处理页面刷新/关闭
// useEffect(() => { useEffect(() => {
// const handleBeforeUnload = (e: BeforeUnloadEvent) => { const handleBeforeUnload = (e: BeforeUnloadEvent) => {
// if (isUploading && !uploadCompletedRef.current) { if (isUploading && !uploadCompletedRef.current) {
// e.preventDefault(); e.preventDefault();
// e.returnValue = '镜像正在上传中,确定要离开吗?'; e.returnValue = '镜像正在上传中,确定要离开吗?';
return e.returnValue;
}
};
// // 使用 sendBeacon 发送取消请求 window.addEventListener('beforeunload', handleBeforeUnload);
// const params = new URLSearchParams(); return () => window.removeEventListener('beforeunload', handleBeforeUnload);
// params.append('file_id', fileId.current); }, [isUploading]);
// const blob = new Blob([params.toString()], {
// type: 'application/x-www-form-urlencoded',
// });
// navigator.sendBeacon('/api/cancel-upload', blob);
// return e.returnValue;
// }
// };
// window.addEventListener('beforeunload', handleBeforeUnload);
// return () => window.removeEventListener('beforeunload', handleBeforeUnload);
// }, [isUploading]);
// 计时器清理(仅组件卸载时执行) // 计时器清理(仅组件卸载时执行)
useEffect(() => { useEffect(() => {
@ -117,30 +105,14 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
}; };
}, []); }, []);
// // 上传取消逻辑(依赖 isUploading 和 uploadCompletedRef
// useEffect(() => {
// return () => {
// if (isUploading && !uploadCompletedRef.current && fileId.current) {
// const params = new URLSearchParams();
// params.append('file_id', fileId.current);
// cancelUploadImagesAPI(params).then((res) => {
// if (res.code === CODE) {
// message.success('上传已取消');
// } else {
// message.error('取消上传失败');
// }
// });
// }
// };
// }, [isUploading]); // 保留原有依赖
// 添加重置状态函数 // 添加重置状态函数
const resetState = () => { const resetState = () => {
// setUploadCompleted(false); // 重置上传完成状态
setUploadProgress(0); setUploadProgress(0);
setIsUploading(false); setIsUploading(false);
setUploadStatus(READY); setUploadStatus(READY);
setUploadMessage(''); setUploadMessage('');
uploadCompletedRef.current = false; // 重置上传完成状态
isManualCancel.current = false; // 重置手动取消状态
completedChunks.current = 0; completedChunks.current = 0;
totalChunks.current = 0; totalChunks.current = 0;
fileId.current = ''; fileId.current = '';
@ -162,13 +134,6 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
} }
}; };
// 当弹窗关闭时重置状态
useEffect(() => {
if (!visible) {
resetState();
}
}, [visible]);
// 4. 上传单个分片 // 4. 上传单个分片
const uploadChunk = async ( const uploadChunk = async (
chunk: Blob, chunk: Blob,
@ -206,8 +171,9 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
// 根据后端返回的状态进行判断 // 根据后端返回的状态进行判断
if (response.success) { if (response.success) {
if (response.status === 'completed') { if (response.status === 'completed') {
uploadCompletedRef.current = true; // 设置上传完成状态
isManualCancel.current = false; // 重置手动取消状态
// 文件上传完成设置进度为100% // 文件上传完成设置进度为100%
// setUploadCompleted(true); // 标记上传完成
setUploadProgress(100); // 这里已经正确设置了100% setUploadProgress(100); // 这里已经正确设置了100%
setIsUploading(false); setIsUploading(false);
setUploadStatus(SUCCESS); setUploadStatus(SUCCESS);
@ -297,8 +263,11 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
hasError = true; // 设置错误标记 hasError = true; // 设置错误标记
setIsUploading(false); setIsUploading(false);
setUploadStatus(ERROR); setUploadStatus(ERROR);
setUploadMessage(result.message || '文件上传失败,请重试'); // 只有当不是用户取消时才显示错误消息
message.error(result.message ||'文件上传失败'); if (!isManualCancel.current) {
setUploadMessage(result.message || '文件上传失败');
message.error(result.message || '文件上传失败');
}
// 中止其他正在进行的上传 // 中止其他正在进行的上传
if (abortController.current) { if (abortController.current) {
@ -326,6 +295,8 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
// 2. 开始上传 // 2. 开始上传
const startUpload = async (file: File) => { const startUpload = async (file: File) => {
try { try {
isManualCancel.current=false;
uploadCompletedRef.current = false;
setIsUploading(true); setIsUploading(true);
setUploadStatus(UPLOADING); setUploadStatus(UPLOADING);
setUploadMessage('正在准备上传...'); setUploadMessage('正在准备上传...');
@ -421,12 +392,17 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
// 取消上传 // 取消上传
const cancelUpload = async () => { const cancelUpload = async () => {
// 先更新 ref 再更新 state // 1. 立即标记状态
// uploadCompletedRef.current = true; isManualCancel.current = true;
// _setUploadCompleted(true); uploadCompletedRef.current = true; // 标记完成(阻止 beforeunload
if (abortController.current) {
abortController.current.abort(); // 2. 清空上传队列(阻止新请求)
abortController.current = null; uploadQueue.current = [];
// 3. 中止所有进行中的请求
const oldController = abortController.current;
abortController.current = new AbortController(); // 新建控制器
if (oldController) {
oldController.abort(); // 中止旧请求
} }
// 如果有正在上传的文件调用后端取消上传API // 如果有正在上传的文件调用后端取消上传API
if (fileId.current) { if (fileId.current) {
@ -458,11 +434,10 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
<Alert <Alert
message="重要提示" message="重要提示"
description={ description={
<div style={{ color: "rgb(237, 41, 31)" }}> <div style={{ color: 'rgb(237, 41, 31)' }}>
<div>1. </div> <div>1. </div>
<div> <div>2. </div>
2. <div>3. </div>
</div>
</div> </div>
} }
type="warning" type="warning"
@ -530,31 +505,25 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
</div> </div>
); );
const handleCancel = () => {
if (isUploading) {
cancelUpload().finally(() => {
resetState(); // 确保取消完成后再重置
onCancel();
});
} else {
resetState();
onCancel();
}
};
return ( return (
<Modal <Modal
title="导入镜像" title="导入镜像"
open={visible} open={visible}
onCancel={() => { onCancel={handleCancel}
if (isUploading) {
cancelUpload();
} else {
// 如果不是上传状态,直接重置状态
resetState();
}
onCancel();
}}
footer={[ footer={[
<Button <Button key="close" onClick={handleCancel}>
key="close"
onClick={() => {
if (isUploading) {
cancelUpload();
} else {
resetState();
}
onCancel();
}}
>
</Button>, </Button>,
]} ]}

View File

@ -195,7 +195,7 @@ const ImageList: React.FC = () => {
key: 'bt_path', key: 'bt_path',
title: 'BT路径', title: 'BT路径',
dataIndex: 'bt_path', dataIndex: 'bt_path',
width: 250, width: 180,
defaultVisible: true, defaultVisible: true,
ellipsis: true, ellipsis: true,
render: (text: string) => text ? <Tooltip title={text} placement="topLeft">{text}</Tooltip>:'--' render: (text: string) => text ? <Tooltip title={text} placement="topLeft">{text}</Tooltip>:'--'
@ -224,7 +224,7 @@ const ImageList: React.FC = () => {
key: 'image_status', key: 'image_status',
title: '镜像状态', title: '镜像状态',
dataIndex: 'image_status', dataIndex: 'image_status',
width: 80, width: 90,
render: (text: number) => (text ? getStatusTag(text) : '--'), render: (text: number) => (text ? getStatusTag(text) : '--'),
defaultVisible: true, defaultVisible: true,
}, },
@ -232,7 +232,7 @@ const ImageList: React.FC = () => {
key: 'create_time', key: 'create_time',
title: '创建时间', title: '创建时间',
dataIndex: 'create_time', dataIndex: 'create_time',
width: 180, width: 160,
render: (text: string) => render: (text: string) =>
text ? ( text ? (
<Tooltip title={dayjs(text).format('YYYY-MM-DD HH:mm:ss')}> <Tooltip title={dayjs(text).format('YYYY-MM-DD HH:mm:ss')}>
@ -247,7 +247,7 @@ const ImageList: React.FC = () => {
{ {
key: 'action', key: 'action',
title: '操作', title: '操作',
width: 100, width: 90,
fixed: 'right' as 'right', fixed: 'right' as 'right',
render: (_: any, record: IMAGES.ImageItem) => ( render: (_: any, record: IMAGES.ImageItem) => (
<Space size="small"> <Space size="small">

View File

@ -42,7 +42,7 @@ const LoginPage: React.FC = () => {
<SafetyCertificateOutlined className="logo-icon" /> <SafetyCertificateOutlined className="logo-icon" />
<h1 className="brand-title"></h1> <h1 className="brand-title"></h1>
</div> </div>
<div className="brand-subtitle">VDI </div> <div className="brand-subtitle">Nex</div>
<div className="brand-description"> <div className="brand-description">
<p></p> <p></p>
<p></p> <p></p>
@ -69,7 +69,7 @@ const LoginPage: React.FC = () => {
<div className="login-form-container"> <div className="login-form-container">
<div className="login-header"> <div className="login-header">
<h2 className="login-title"></h2> <h2 className="login-title"></h2>
<p className="login-subtitle">使VDI</p> <p className="login-subtitle">使Nex</p>
</div> </div>
<Form <Form
@ -113,7 +113,7 @@ const LoginPage: React.FC = () => {
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
<div className="login-tips"> <div className="login-tips">
<p>admin / 123456</p> <p>admin / 123456</p>
</div> </div>

View File

@ -6,7 +6,7 @@ const BASE_URL = '/api/nex/v1';
// 查询镜像列表 // 查询镜像列表
export async function getImagesList(params:any) { export async function getImagesList(params:any) {
// return request<IMAGES.Images_ListInfo>(`${BASE_URL}/queryimagesList`, { // return request<IMAGES.Images_ListInfo>(`${BASE_URL}/queryimagesList`, {
return request<IMAGES.Images_ListInfo>(`${BASE_URL}/image/select/page`, { return request<IMAGES.Images_ListInfo>(`${BASE_URL}/image/select/page`, {
method: 'POST', method: 'POST',
data: params, data: params,
}); });