|
- {taskStatusLabel(task.status)}
+ {t(taskStatusLabel(task.status))}
|
- {actionLabel(task.type)} |
+ {t(actionLabel(task.type))} |
{task.container_name} |
+
+ {task.type === 'create' ? t(task.stage_detail || (task.status === 'pending' ? '排队等待' : '-')) : '-'}
+ |
{task.created_at} |
{task.error || '-'} |
@@ -932,7 +941,7 @@ function TaskQueueModal({ tasks, onRefresh, onClose }: {
} catch { /* ignore */ }
}}
className="p-1 rounded hover:bg-red-50 text-gray-400 hover:text-red-600 transition-colors"
- title="取消任务"
+ title={t('取消任务')}
>
diff --git a/frontend/src/pages/ImageManagement.tsx b/frontend/src/pages/ImageManagement.tsx
index eaa0bb1..50aabb5 100644
--- a/frontend/src/pages/ImageManagement.tsx
+++ b/frontend/src/pages/ImageManagement.tsx
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useState, type ReactNode } from 'react'
+import { useNavigate } from 'react-router-dom'
import {
Download,
Trash2,
@@ -11,15 +12,18 @@ import {
AlertCircle,
X,
} from 'lucide-react'
-import { getImages, downloadImage, cancelImageDownload, deleteImage, toggleImage, ImageInfo } from '../services/api'
+import { getImages, getStorageInfo, downloadImage, cancelImageDownload, deleteImage, toggleImage, ImageInfo, StorageInfo } from '../services/api'
import { useDialog } from '../components/Dialog'
export default function ImageManagement() {
const dialog = useDialog()
+ const navigate = useNavigate()
const [images, setImages] = useState([])
const [loading, setLoading] = useState(true)
const [actionLoading, setActionLoading] = useState(null)
const [error, setError] = useState('')
+ const [storageInfo, setStorageInfo] = useState(null)
+ const [storageLoading, setStorageLoading] = useState(true)
const fetchImages = useCallback(async () => {
try {
@@ -33,9 +37,22 @@ export default function ImageManagement() {
}
}, [])
+ const fetchStorage = useCallback(async () => {
+ setStorageLoading(true)
+ try {
+ const res = await getStorageInfo()
+ setStorageInfo(res.data.data || null)
+ } catch {
+ setStorageInfo(null)
+ } finally {
+ setStorageLoading(false)
+ }
+ }, [])
+
useEffect(() => {
fetchImages()
- }, [fetchImages])
+ fetchStorage()
+ }, [fetchImages, fetchStorage])
useEffect(() => {
const hasDownloads = images.some((img) => img.downloading)
@@ -101,6 +118,9 @@ export default function ImageManagement() {
const downloadedCount = images.filter((img) => img.downloaded).length
const lxcImages = images.filter((img) => img.type === 'lxc')
const kvmImages = images.filter((img) => img.type === 'kvm')
+ const imageStorageReady = (storageInfo?.pools || []).some((pool) =>
+ pool.enabled !== false && pool.available !== false && (pool.content_types || []).includes('images')
+ )
if (loading) {
return (
@@ -121,7 +141,7 @@ export default function ImageManagement() {
|