235 lines
6.0 KiB
TypeScript
235 lines
6.0 KiB
TypeScript
|
|
import React, { useState, useEffect } from 'react';
|
|||
|
|
import { Card, Table, Tag, Button, Space, Modal, message, Popconfirm } from 'antd';
|
|||
|
|
import {
|
|||
|
|
PlusOutlined,
|
|||
|
|
EditOutlined,
|
|||
|
|
DeleteOutlined,
|
|||
|
|
EyeOutlined
|
|||
|
|
} from '@ant-design/icons';
|
|||
|
|
import './index.less';
|
|||
|
|
|
|||
|
|
interface ImageItem {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
version: string;
|
|||
|
|
size: string;
|
|||
|
|
status: 'active' | 'inactive' | 'building';
|
|||
|
|
createTime: string;
|
|||
|
|
description: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const ImageList: React.FC = () => {
|
|||
|
|
const [images, setImages] = useState<ImageItem[]>([]);
|
|||
|
|
const [loading, setLoading] = useState(false);
|
|||
|
|
const [selectedImage, setSelectedImage] = useState<ImageItem | null>(null);
|
|||
|
|
const [detailVisible, setDetailVisible] = useState(false);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
loadImages();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
const loadImages = () => {
|
|||
|
|
setLoading(true);
|
|||
|
|
// 模拟数据加载
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const mockData: ImageItem[] = [
|
|||
|
|
{
|
|||
|
|
id: '1',
|
|||
|
|
name: 'Windows 10 专业版',
|
|||
|
|
version: 'v1.0.0',
|
|||
|
|
size: '15.2 GB',
|
|||
|
|
status: 'active',
|
|||
|
|
createTime: '2024-01-15 10:30:00',
|
|||
|
|
description: 'Windows 10 专业版镜像,包含常用办公软件'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: '2',
|
|||
|
|
name: 'Ubuntu 22.04 LTS',
|
|||
|
|
version: 'v2.1.0',
|
|||
|
|
size: '8.5 GB',
|
|||
|
|
status: 'active',
|
|||
|
|
createTime: '2024-01-10 14:20:00',
|
|||
|
|
description: 'Ubuntu 22.04 LTS 服务器版本,适用于开发环境'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: '3',
|
|||
|
|
name: 'CentOS 8',
|
|||
|
|
version: 'v1.5.0',
|
|||
|
|
size: '12.1 GB',
|
|||
|
|
status: 'building',
|
|||
|
|
createTime: '2024-01-20 09:15:00',
|
|||
|
|
description: 'CentOS 8 企业级服务器操作系统'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: '4',
|
|||
|
|
name: 'macOS Monterey',
|
|||
|
|
version: 'v1.2.0',
|
|||
|
|
size: '18.7 GB',
|
|||
|
|
status: 'inactive',
|
|||
|
|
createTime: '2024-01-05 16:45:00',
|
|||
|
|
description: 'macOS Monterey 开发环境镜像'
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
setImages(mockData);
|
|||
|
|
setLoading(false);
|
|||
|
|
}, 1000);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const getStatusTag = (status: string) => {
|
|||
|
|
const statusMap = {
|
|||
|
|
active: { color: 'green', text: '可用' },
|
|||
|
|
inactive: { color: 'red', text: '不可用' },
|
|||
|
|
building: { color: 'orange', text: '构建中' }
|
|||
|
|
};
|
|||
|
|
const config = statusMap[status as keyof typeof statusMap];
|
|||
|
|
return <Tag color={config.color}>{config.text}</Tag>;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleViewDetail = (record: ImageItem) => {
|
|||
|
|
setSelectedImage(record);
|
|||
|
|
setDetailVisible(true);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
const handleEdit = (record: ImageItem) => {
|
|||
|
|
message.info(`编辑镜像:${record.name}`);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleDelete = (record: ImageItem) => {
|
|||
|
|
Modal.confirm({
|
|||
|
|
title: '确认删除',
|
|||
|
|
content: `确定要删除镜像 "${record.name}" 吗?`,
|
|||
|
|
onOk: () => {
|
|||
|
|
setImages(images.filter(img => img.id !== record.id));
|
|||
|
|
message.success('删除成功');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const columns = [
|
|||
|
|
{
|
|||
|
|
title: '镜像名称',
|
|||
|
|
dataIndex: 'name',
|
|||
|
|
key: 'name',
|
|||
|
|
width: 200,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '版本',
|
|||
|
|
dataIndex: 'version',
|
|||
|
|
key: 'version',
|
|||
|
|
width: 100,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '大小',
|
|||
|
|
dataIndex: 'size',
|
|||
|
|
key: 'size',
|
|||
|
|
width: 100,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '状态',
|
|||
|
|
dataIndex: 'status',
|
|||
|
|
key: 'status',
|
|||
|
|
width: 100,
|
|||
|
|
render: (status: string) => getStatusTag(status),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '创建时间',
|
|||
|
|
dataIndex: 'createTime',
|
|||
|
|
key: 'createTime',
|
|||
|
|
width: 180,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
title: '操作',
|
|||
|
|
key: 'action',
|
|||
|
|
width: 200,
|
|||
|
|
render: (_: any, record: ImageItem) => (
|
|||
|
|
<Space size="small">
|
|||
|
|
<Button
|
|||
|
|
type="text"
|
|||
|
|
icon={<EyeOutlined />}
|
|||
|
|
onClick={() => handleViewDetail(record)}
|
|||
|
|
title="查看详情"
|
|||
|
|
/>
|
|||
|
|
<Popconfirm
|
|||
|
|
title="确定要删除这个镜像吗?"
|
|||
|
|
description="删除后无法恢复,请谨慎操作。"
|
|||
|
|
onConfirm={() => handleDelete(record)}
|
|||
|
|
okText="确定"
|
|||
|
|
cancelText="取消"
|
|||
|
|
>
|
|||
|
|
<Button
|
|||
|
|
type="text"
|
|||
|
|
icon={<DeleteOutlined />}
|
|||
|
|
title="删除"
|
|||
|
|
danger
|
|||
|
|
/>
|
|||
|
|
</Popconfirm>
|
|||
|
|
</Space>
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="image-list">
|
|||
|
|
<Card>
|
|||
|
|
<Table
|
|||
|
|
columns={columns}
|
|||
|
|
dataSource={images}
|
|||
|
|
rowKey="id"
|
|||
|
|
loading={loading}
|
|||
|
|
pagination={{
|
|||
|
|
total: images.length,
|
|||
|
|
pageSize: 10,
|
|||
|
|
showSizeChanger: true,
|
|||
|
|
showQuickJumper: true,
|
|||
|
|
showTotal: (total) => `共 ${total} 条记录`,
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
</Card>
|
|||
|
|
|
|||
|
|
<Modal
|
|||
|
|
title="镜像详情"
|
|||
|
|
open={detailVisible}
|
|||
|
|
onCancel={() => setDetailVisible(false)}
|
|||
|
|
footer={[
|
|||
|
|
<Button key="close" onClick={() => setDetailVisible(false)}>
|
|||
|
|
关闭
|
|||
|
|
</Button>
|
|||
|
|
]}
|
|||
|
|
width={600}
|
|||
|
|
>
|
|||
|
|
{selectedImage && (
|
|||
|
|
<div className="image-detail">
|
|||
|
|
<div className="detail-item">
|
|||
|
|
<label>镜像名称:</label>
|
|||
|
|
<span>{selectedImage.name}</span>
|
|||
|
|
</div>
|
|||
|
|
<div className="detail-item">
|
|||
|
|
<label>版本:</label>
|
|||
|
|
<span>{selectedImage.version}</span>
|
|||
|
|
</div>
|
|||
|
|
<div className="detail-item">
|
|||
|
|
<label>大小:</label>
|
|||
|
|
<span>{selectedImage.size}</span>
|
|||
|
|
</div>
|
|||
|
|
<div className="detail-item">
|
|||
|
|
<label>状态:</label>
|
|||
|
|
<span>{getStatusTag(selectedImage.status)}</span>
|
|||
|
|
</div>
|
|||
|
|
<div className="detail-item">
|
|||
|
|
<label>创建时间:</label>
|
|||
|
|
<span>{selectedImage.createTime}</span>
|
|||
|
|
</div>
|
|||
|
|
<div className="detail-item">
|
|||
|
|
<label>描述:</label>
|
|||
|
|
<p>{selectedImage.description}</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</Modal>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default ImageList;
|