优化了样式

main
mula.liu 2026-09-20 18:06:20 +08:00
parent 0d38e1da47
commit 25dfbd9a56
7 changed files with 69 additions and 49 deletions

View File

@ -192,17 +192,9 @@ function AdminShell() {
const avatarUrl = user?.avatar_url ? `/upload/${user.avatar_url}` : undefined;
const displayName = (user?.full_name as string) || (user?.username as string) || '未登录';
// 只保留菜单本身:账号信息已经显示在胶囊上,弹层不再重复
const userMenuContent = (
<div style={{ width: 232 }}>
<div className="adm-user-menu-head">
<Avatar size={38} src={avatarUrl} icon={<UserOutlined />} />
<div className="adm-user-menu-head-meta">
<strong>{displayName}</strong>
<span>{user?.email || t(roleLabelOf(user))}</span>
</div>
</div>
<div className="adm-user-menu-group">{t('个人资料')}</div>
<div className="adm-user-menu-content">
<button
type="button"
className={`adm-user-menu-item ${location.pathname.endsWith('/profile') ? 'is-active' : ''}`}
@ -212,11 +204,11 @@ function AdminShell() {
{t('个人资料')}
</button>
<div className="adm-user-menu-divider" />
<button type="button" className="adm-user-menu-item" onClick={() => go('/')}>
<HomeOutlined />
{t('返回可视化首页')}
</button>
<div className="adm-user-menu-divider" />
<button type="button" className="adm-user-menu-item is-danger" onClick={() => void handleLogout()}>
<LogoutOutlined />
{t('退出登录')}

View File

@ -290,7 +290,7 @@ export function CelestialBodies() {
onFilter: (value, record) => record.type === value,
render: (type: string) => bodyTypeLabels[type] || type,
},
{ title: '描述', dataIndex: 'description', key: 'description', ellipsis: true },
{ title: '描述', dataIndex: 'description', key: 'description', width: 320, ellipsis: true },
{
title: '资源配置',
key: 'resources',

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { Button, Col, Form, Input, InputNumber, Modal, Row, Switch, Tabs, Tag } from 'antd';
import { Button, Col, Form, Input, InputNumber, Modal, Row, Switch, Tabs } from 'antd';
import { RocketOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { DataTable } from '../../components/admin/DataTable';
@ -105,6 +105,17 @@ export function Rockets() {
}
};
/** 与其它列表一致:状态列使用统一的启用/停用开关 */
const handleStatusChange = async (record: RocketConfig, checked: boolean) => {
try {
await request.put(`/rockets/admin/${record.id}`, { is_active: checked });
setData((current) => current.map((item) => (item.id === record.id ? { ...item, is_active: checked } : item)));
toast.success(`${record.name_zh || record.name}${checked ? '启用' : '停用'}`);
} catch {
toast.error('状态更新失败');
}
};
const columns: ColumnsType<RocketConfig> = [
{ title: '排序', dataIndex: 'sort_order', width: 70 },
{ title: '火箭', key: 'name', width: 210, render: (_, record) => <div><div className="adm-cell-strong">{record.name_zh || record.name}</div><div className="adm-cell-sub">{record.name} · {record.code}</div></div> },
@ -114,7 +125,6 @@ export function Rockets() {
{ title: '一级', key: 'stage1', width: 170, render: (_, record) => <span>{record.stage_1.engine_count} / {(record.stage_1.max_thrust_n / 1_000_000).toFixed(2)} MN</span> },
{ title: '二级', key: 'stage2', width: 170, render: (_, record) => <span>{record.stage_2.engine_count} / {(record.stage_2.max_thrust_n / 1_000_000).toFixed(2)} MN</span> },
{ title: '目标轨道', dataIndex: 'target_orbit_km', width: 110, render: (value) => `${value} km` },
{ title: '状态', dataIndex: 'is_active', width: 90, render: (value) => <Tag color={value ? 'green' : 'default'}>{value ? '启用' : '停用'}</Tag> },
];
return (
@ -137,6 +147,8 @@ export function Rockets() {
addText="新增火箭"
onEdit={showEdit}
onDelete={remove}
onStatusChange={handleStatusChange}
statusField="is_active"
deleteConfirmTitle="确认删除该火箭?"
deleteConfirmDescription="删除后前台发射模拟将不再显示该运载器"
/>

View File

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Badge, Button, Form, Tag, Tooltip } from 'antd';
import { Button, Form, Tag, Tooltip } from 'antd';
import { ClockCircleOutlined, PlayCircleOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
@ -119,6 +119,17 @@ export function ScheduledJobs() {
}
};
/** 与其它列表一致:状态列使用统一的启用/停用开关 */
const handleStatusChange = async (record: ScheduledJob, checked: boolean) => {
try {
await request.put(`/scheduled-jobs/${record.id}`, { is_active: checked });
setData((current) => current.map((item) => (item.id === record.id ? { ...item, is_active: checked } : item)));
toast.success(`${record.name}${checked ? '启用' : '禁用'}`);
} catch {
toast.error('状态更新失败');
}
};
const handleModalOk = async () => {
try {
const values = await form.validateFields();
@ -164,7 +175,6 @@ export function ScheduledJobs() {
render: (func, record) => record.job_type === 'predefined' ? <Tag color="cyan">{func}</Tag> : <span className="adm-cell-sub">-</span>,
},
{ title: 'Cron 表达式', dataIndex: 'cron_expression', width: 130, render: (text) => <Tag color="green">{text}</Tag> },
{ title: '状态', dataIndex: 'is_active', width: 80, render: (active) => <Badge status={active ? 'success' : 'default'} text={active ? '启用' : '禁用'} /> },
{
title: '上次执行', width: 200,
render: (_, record) => record.last_run_at ? (
@ -189,6 +199,8 @@ export function ScheduledJobs() {
addText="新增定时任务"
onEdit={handleEdit}
onDelete={handleDelete}
onStatusChange={handleStatusChange}
statusField="is_active"
deleteConfirmTitle="确认删除该定时任务?"
deleteConfirmDescription="删除后该任务不再自动执行"
rowKey="id"

View File

@ -145,6 +145,7 @@ export function StaticData() {
{
title: '数据 (JSON)',
dataIndex: 'data',
width: 320,
ellipsis: true,
render: (text) => <span className="adm-cell-mono">{JSON.stringify(text)}</span>,
},

View File

@ -81,7 +81,7 @@ export function Tasks() {
width: 160,
render: (type: string) => <Tag color="blue">{type}</Tag>,
},
{ title: '描述', dataIndex: 'description', ellipsis: true },
{ title: '描述', dataIndex: 'description', width: 280, ellipsis: true },
{
title: '状态',
dataIndex: 'status',

View File

@ -36,6 +36,9 @@
--adm-chrome-hover: rgba(31, 35, 40, 0.05);
--adm-pill-bg: rgba(31, 35, 40, 0.04);
--adm-pill-hover: rgba(31, 35, 40, 0.08);
--adm-row-hover: rgba(35, 134, 54, 0.06);
/* 固定列必须用不透明色,才能在悬停时继续遮住横向滚动的内容 */
--adm-row-hover-solid: #f2f8f3;
}
/* 深色主题:只切换变量,页面结构与文案不变 */
@ -58,6 +61,8 @@
--adm-chrome-hover: rgba(255, 255, 255, 0.06);
--adm-pill-bg: rgba(255, 255, 255, 0.04);
--adm-pill-hover: rgba(255, 255, 255, 0.1);
--adm-row-hover: rgba(46, 160, 67, 0.14);
--adm-row-hover-solid: #192e27;
}
/* ------------------------------------------------------------ 布局骨架 */
@ -268,42 +273,24 @@
padding: 6px;
}
/* 用户二级菜单Popover */
/*
* Popover
*
* = 236 - 10 dock = 216
* 6px 204
*/
.adm-user-menu .ant-popover-inner {
padding: 8px;
padding: 6px;
border: 1px solid var(--adm-border);
border-radius: var(--adm-radius);
box-shadow: 0 -4px 16px rgba(31, 35, 40, 0.12);
}
.adm-user-menu-head {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 8px 10px;
border-bottom: 1px solid var(--adm-border);
}
.adm-user-menu-head-meta {
.adm-user-menu-content {
display: flex;
flex-direction: column;
min-width: 0;
line-height: 1.35;
}
.adm-user-menu-head-meta strong {
font-size: 13px;
}
.adm-user-menu-head-meta span {
font-size: 11px;
color: var(--adm-text-muted);
word-break: break-all;
}
.adm-user-menu-group {
padding: 8px 8px 2px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.06em;
color: var(--adm-text-muted);
gap: 2px;
width: 204px;
}
.adm-user-menu-item {
@ -576,8 +563,24 @@
font-size: 13px;
}
/*
*
* 1) 使
* 2)
*/
.adm-table .ant-table-tbody > tr:hover > td {
background: rgba(35, 134, 54, 0.04) !important;
background: var(--adm-row-hover) !important;
}
/* 固定列:始终不透明,横向滚动时遮住下方内容 */
.adm-table .ant-table-tbody > tr > td.ant-table-cell-fix-end,
.adm-table .ant-table-tbody > tr > td.ant-table-cell-fix-start {
background: var(--adm-surface) !important;
}
.adm-table .ant-table-tbody > tr:hover > td.ant-table-cell-fix-end,
.adm-table .ant-table-tbody > tr:hover > td.ant-table-cell-fix-start {
background: var(--adm-row-hover-solid) !important;
}
.adm-table .ant-table-pagination {