mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-05-27 19:19:35 +08:00
优化: 多模块功能修复与体验改进 (#34)
1. 保留策略清理后自动删除空文件夹(新增 StorageDirCleaner 接口) 2. 备份任务删除时清理远端文件但保留备份记录 3. 节点管理修复:本机 IP/版本检测、Heartbeat OS/Arch 修正、新增编辑功能 4. 审计日志规范化:统一格式、丰富详情、节点操作增加审计记录 5. 系统设置移除一键更新操作,仅保留版本检查 6. Rclone 配置项分层展示(必填 + 高级可选折叠) 7. DirectoryPicker 目录选择器样式优化
This commit is contained in:
@@ -3,10 +3,10 @@ import {
|
||||
Table, Button, Space, Tag, Typography, PageHeader, Modal, Input, Message, Badge, Popconfirm, Card, Descriptions, Empty
|
||||
} from '@arco-design/web-react'
|
||||
import {
|
||||
IconPlus, IconDelete, IconDesktop, IconCloudDownload
|
||||
IconPlus, IconDelete, IconDesktop, IconCloudDownload, IconEdit
|
||||
} from '@arco-design/web-react/icon'
|
||||
import type { NodeSummary } from '../../types/nodes'
|
||||
import { listNodes, createNode, deleteNode } from '../../services/nodes'
|
||||
import { listNodes, createNode, deleteNode, updateNode } from '../../services/nodes'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
@@ -17,6 +17,11 @@ export default function NodesPage() {
|
||||
const [newNodeName, setNewNodeName] = useState('')
|
||||
const [newToken, setNewToken] = useState('')
|
||||
|
||||
// 编辑状态
|
||||
const [editVisible, setEditVisible] = useState(false)
|
||||
const [editNode, setEditNode] = useState<NodeSummary | null>(null)
|
||||
const [editName, setEditName] = useState('')
|
||||
|
||||
const fetchNodes = useCallback(async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
@@ -56,6 +61,21 @@ export default function NodesPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleEdit = async () => {
|
||||
if (!editNode || !editName.trim()) {
|
||||
Message.warning('请输入节点名称')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await updateNode(editNode.id, { name: editName.trim() })
|
||||
Message.success('节点更新成功')
|
||||
setEditVisible(false)
|
||||
fetchNodes()
|
||||
} catch {
|
||||
Message.error('更新节点失败')
|
||||
}
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '节点名称',
|
||||
@@ -110,15 +130,22 @@ export default function NodesPage() {
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 80,
|
||||
render: (_: unknown, record: NodeSummary) => {
|
||||
if (record.isLocal) return <Text type="secondary">-</Text>
|
||||
return (
|
||||
<Popconfirm title="确定删除该节点?" onOk={() => handleDelete(record.id)}>
|
||||
<Button type="text" status="danger" icon={<IconDelete />} size="small" />
|
||||
</Popconfirm>
|
||||
)
|
||||
},
|
||||
width: 120,
|
||||
render: (_: unknown, record: NodeSummary) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<IconEdit />}
|
||||
size="small"
|
||||
onClick={() => { setEditNode(record); setEditName(record.name); setEditVisible(true) }}
|
||||
/>
|
||||
{!record.isLocal && (
|
||||
<Popconfirm title="确定删除该节点?" onOk={() => handleDelete(record.id)}>
|
||||
<Button type="text" status="danger" icon={<IconDelete />} size="small" />
|
||||
</Popconfirm>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -145,6 +172,7 @@ export default function NodesPage() {
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 添加节点弹窗 */}
|
||||
<Modal
|
||||
title="添加远程节点"
|
||||
visible={createVisible}
|
||||
@@ -175,6 +203,25 @@ export default function NodesPage() {
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
{/* 编辑节点弹窗 */}
|
||||
<Modal
|
||||
title="编辑节点"
|
||||
visible={editVisible}
|
||||
onCancel={() => setEditVisible(false)}
|
||||
onOk={handleEdit}
|
||||
okText="保存"
|
||||
cancelText="取消"
|
||||
>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Text type="secondary">节点名称</Text>
|
||||
</div>
|
||||
<Input
|
||||
placeholder="输入节点名称"
|
||||
value={editName}
|
||||
onChange={setEditName}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Badge, Button, Card, Descriptions, Grid, Link, Message, PageHeader, Space, Tag, Typography } from '@arco-design/web-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { fetchSystemInfo, checkUpdate, applyUpdate, type SystemInfo, type UpdateCheckResult } from '../../services/system'
|
||||
import { fetchSystemInfo, checkUpdate, type SystemInfo, type UpdateCheckResult } from '../../services/system'
|
||||
import { resolveErrorMessage } from '../../utils/error'
|
||||
import { formatDuration } from '../../utils/format'
|
||||
|
||||
@@ -24,7 +24,6 @@ export function SettingsPage() {
|
||||
const [error, setError] = useState('')
|
||||
const [updateResult, setUpdateResult] = useState<UpdateCheckResult | null>(null)
|
||||
const [checking, setChecking] = useState(false)
|
||||
const [applying, setApplying] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
@@ -53,24 +52,6 @@ export function SettingsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleApplyUpdate() {
|
||||
if (!updateResult?.latestVersion) return
|
||||
setApplying(true)
|
||||
try {
|
||||
const result = await applyUpdate(updateResult.latestVersion)
|
||||
if (result.success) {
|
||||
Message.success('更新已触发,容器即将自动重启...')
|
||||
setTimeout(() => Message.info('请等待 10-30 秒后刷新页面'), 3000)
|
||||
} else {
|
||||
Message.warning(result.message)
|
||||
}
|
||||
} catch (e) {
|
||||
Message.error(resolveErrorMessage(e, '触发更新失败'))
|
||||
} finally {
|
||||
setApplying(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
||||
<PageHeader style={{ paddingBottom: 16 }} title="系统设置" subTitle="运行信息、磁盘状态与版本更新">
|
||||
@@ -124,9 +105,6 @@ export function SettingsPage() {
|
||||
</Card>
|
||||
)}
|
||||
<Space>
|
||||
<Button type="primary" status="success" loading={applying} onClick={handleApplyUpdate}>
|
||||
一键更新(Docker)
|
||||
</Button>
|
||||
{updateResult.downloadUrl && (
|
||||
<Link href={updateResult.downloadUrl} target="_blank">
|
||||
<Button type="outline">下载二进制包</Button>
|
||||
@@ -138,13 +116,6 @@ export function SettingsPage() {
|
||||
</Link>
|
||||
)}
|
||||
</Space>
|
||||
{updateResult.dockerImage && (
|
||||
<Card size="small" title="Docker 更新命令">
|
||||
<Typography.Paragraph copyable code style={{ marginBottom: 0 }}>
|
||||
{`docker pull ${updateResult.dockerImage}:${updateResult.latestVersion} && docker compose up -d`}
|
||||
</Typography.Paragraph>
|
||||
</Card>
|
||||
)}
|
||||
</Space>
|
||||
) : (
|
||||
<Space>
|
||||
|
||||
Reference in New Issue
Block a user