fix(前端): 文字修改+优化
parent
46263f8d0d
commit
b88b020c3b
|
@ -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',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>,
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue