feat(ui): 优化页面标题组件和业务管理页面的视觉设计

重构 PageHeader 组件,移除冗余的 Space 导入,使用内联样式替代 Tailwind 类以提供更灵活的布局控制。同时,为外部应用管理和客户端管理页面引入全新的卡片设计,包括圆角、渐变背景、毛玻璃效果和自定义间距,以提升视觉层次感和现代感。调整按钮样式,统一圆角和高度,增强整体 UI 的一致性。
dev_na
alanpaine 2026-04-16 14:17:12 +08:00
parent 48dd7d1150
commit 0960e625f9
3 changed files with 131 additions and 25 deletions

View File

@ -1,4 +1,4 @@
import { Typography, Space } from 'antd';
import { Typography } from 'antd';
import React from 'react';
const { Title, Text } = Typography;
@ -12,18 +12,28 @@ interface PageHeaderProps {
const PageHeader: React.FC<PageHeaderProps> = ({ title, subtitle, extra, className = '' }) => {
return (
<div className={`page-header flex justify-between items-end mb-6 ${className}`}>
<div>
<Title level={4} className="mb-1" style={{ margin: 0 }}>
<div
className={`page-header ${className}`}
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: 24,
flexWrap: 'wrap',
gap: 16
}}
>
<div style={{ flex: 1, minWidth: 200 }}>
<Title level={4} style={{ margin: '0 0 8px 0', fontWeight: 600 }}>
{title}
</Title>
{subtitle && (
<Text type="secondary" style={{ display: 'block' }}>
<Text type="secondary" style={{ display: 'block', fontSize: 14 }}>
{subtitle}
</Text>
)}
</div>
{extra && <div className="page-header-extra">{extra}</div>}
{extra && <div className="page-header-extra" style={{ display: 'flex', alignItems: 'center' }}>{extra}</div>}
</div>
);
};

View File

@ -1,6 +1,6 @@
import { App, Button, Card, Col, Drawer, Empty, Form, Input, InputNumber, Popconfirm, Row, Select, Space, Switch, Table, Tabs, Tag, Typography, Upload } from "antd";
import type { ColumnsType } from "antd/es/table";
import { CloudUploadOutlined, DeleteOutlined, DownloadOutlined, EditOutlined, LaptopOutlined, MobileOutlined, PlusOutlined, ReloadOutlined, SearchOutlined, UploadOutlined } from "@ant-design/icons";
import { CheckCircleOutlined, CloudUploadOutlined, DeleteOutlined, DownloadOutlined, EditOutlined, LaptopOutlined, MobileOutlined, PlusOutlined, ReloadOutlined, RocketOutlined, SearchOutlined, UploadOutlined, WindowsOutlined } from "@ant-design/icons";
import { useCallback, useEffect, useMemo, useState } from "react";
import PageHeader from "@/components/shared/PageHeader";
import AppPagination from "@/components/shared/AppPagination";
@ -380,23 +380,71 @@ export default function ClientManagement() {
];
return (
<div style={{ height: "100%", display: "flex", flexDirection: "column", overflow: "hidden" }}>
<div className="app-page">
<PageHeader
title="客户端管理"
subtitle="发布平台由数据字典 client_platform 驱动,并按父子分组展示。"
extra={
<Space>
<Button icon={<ReloadOutlined />} onClick={() => void loadData()} loading={loading || groupLoading || platformLoading}></Button>
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate}></Button>
<Space size={12}>
<Button icon={<ReloadOutlined />} onClick={() => void loadData()} loading={loading || groupLoading || platformLoading} style={{ borderRadius: 8, height: 36, fontWeight: 500 }}></Button>
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate} style={{ borderRadius: 8, height: 36, fontWeight: 500 }}></Button>
</Space>
}
/>
<Row gutter={16} className="mb-4">
<Col span={6}><Card size="small"><Space><CloudUploadOutlined style={{ color: "#1677ff" }} /><div><div></div><Text strong>{stats.total}</Text></div></Space></Card></Col>
<Col span={6}><Card size="small"><Space><LaptopOutlined style={{ color: "#52c41a" }} /><div><div></div><Text strong>{stats.enabled}</Text></div></Space></Card></Col>
<Col span={6}><Card size="small"><Space><MobileOutlined style={{ color: "#722ed1" }} /><div><div></div><Text strong>{stats.latest}</Text></div></Space></Card></Col>
<Col span={6}><Card size="small"><Space><LaptopOutlined style={{ color: "#fa8c16" }} /><div><div></div><Text strong>{stats.groups}</Text></div></Space></Card></Col>
<Row gutter={24} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.total}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(22, 119, 255, 0.1), rgba(54, 207, 201, 0.1))', border: '1px solid rgba(22, 119, 255, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<CloudUploadOutlined style={{ color: "#1677ff", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.enabled}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(82, 196, 26, 0.1), rgba(183, 235, 143, 0.1))', border: '1px solid rgba(82, 196, 26, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<LaptopOutlined style={{ color: "#52c41a", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.latest}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(114, 46, 209, 0.1), rgba(179, 127, 235, 0.1))', border: '1px solid rgba(114, 46, 209, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<MobileOutlined style={{ color: "#722ed1", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.groups}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(250, 140, 22, 0.1), rgba(255, 213, 145, 0.1))', border: '1px solid rgba(250, 140, 22, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<LaptopOutlined style={{ color: "#fa8c16", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
</Row>
<Card className="app-page__filter-card mb-4" styles={{ body: { padding: 16 } }}>

View File

@ -290,23 +290,71 @@ export default function ExternalAppManagement() {
];
return (
<div style={{ height: "100%", display: "flex", flexDirection: "column", overflow: "hidden" }}>
<div className="app-page">
<PageHeader
title="外部应用管理"
subtitle="统一维护首页九宫格与抽屉入口中的原生应用、Web 服务和应用图标资源。"
extra={
<Space>
<Button icon={<ReloadOutlined />} onClick={() => void loadData()} loading={loading}></Button>
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate}></Button>
<Space size={12}>
<Button icon={<ReloadOutlined />} onClick={() => void loadData()} loading={loading} style={{ borderRadius: 8, height: 36, fontWeight: 500 }}></Button>
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate} style={{ borderRadius: 8, height: 36, fontWeight: 500 }}></Button>
</Space>
}
/>
<Row gutter={16} className="mb-4">
<Col span={6}><Card size="small"><Space><AppstoreOutlined style={{ color: "#1677ff" }} /><div><div></div><Text strong>{stats.total}</Text></div></Space></Card></Col>
<Col span={6}><Card size="small"><Space><RobotOutlined style={{ color: "#52c41a" }} /><div><div></div><Text strong>{stats.native}</Text></div></Space></Card></Col>
<Col span={6}><Card size="small"><Space><GlobalOutlined style={{ color: "#722ed1" }} /><div><div>Web </div><Text strong>{stats.web}</Text></div></Space></Card></Col>
<Col span={6}><Card size="small"><Space><PictureOutlined style={{ color: "#fa8c16" }} /><div><div></div><Text strong>{stats.enabled}</Text></div></Space></Card></Col>
<Row gutter={24} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.total}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(22, 119, 255, 0.1), rgba(54, 207, 201, 0.1))', border: '1px solid rgba(22, 119, 255, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<AppstoreOutlined style={{ color: "#1677ff", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.native}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(82, 196, 26, 0.1), rgba(183, 235, 143, 0.1))', border: '1px solid rgba(82, 196, 26, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<RobotOutlined style={{ color: "#52c41a", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}>Web </span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.web}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(114, 46, 209, 0.1), rgba(179, 127, 235, 0.1))', border: '1px solid rgba(114, 46, 209, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<GlobalOutlined style={{ color: "#722ed1", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
<Col span={6}>
<Card bordered={false} style={{ borderRadius: 16, border: '1px solid var(--app-border-color)', background: 'var(--app-bg-surface-strong)', backdropFilter: 'blur(12px)', boxShadow: '0 4px 20px rgba(0,0,0,0.02)' }} styles={{ body: { padding: '20px 24px' } }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ color: 'var(--app-text-secondary)', fontSize: 13, fontWeight: 500 }}></span>
<span style={{ color: 'var(--app-text-main)', fontSize: 32, fontWeight: 800, fontFamily: 'system-ui, -apple-system, sans-serif' }}>{stats.enabled}</span>
</div>
<div style={{ width: 52, height: 52, borderRadius: 16, background: 'linear-gradient(135deg, rgba(250, 140, 22, 0.1), rgba(255, 213, 145, 0.1))', border: '1px solid rgba(250, 140, 22, 0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<PictureOutlined style={{ color: "#fa8c16", fontSize: 24 }} />
</div>
</div>
</Card>
</Col>
</Row>
<Card className="app-page__filter-card mb-4" styles={{ body: { padding: 16 } }}>