前端样式重新设计,数据面版网系颜色一致
This commit is contained in:
parent
fc7ea534b7
commit
0ea1a453db
|
|
@ -137,10 +137,7 @@ export default function BaseSettingPage() {
|
|||
</FixedHeader>
|
||||
|
||||
<div
|
||||
className="flex flex-col overflow-auto p-4 no-scrollbar"
|
||||
style={{
|
||||
height: "calc(100vh - 48px - 49px)"
|
||||
}}
|
||||
className="flex flex-col p-4 bg-[#eff3f4] h-auto"
|
||||
>
|
||||
<Card bordered={false}>
|
||||
<Typography>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import DeptEditor from "@web/src/components/models/department/dept-editor";
|
||||
|
||||
export default function DepartmentAdminPage() {
|
||||
return <div className=" flex-grow bg-white rounded-xl">
|
||||
return <div className=" flex-grow bg-white ">
|
||||
<DeptEditor></DeptEditor>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import StaffEditor from "@web/src/components/models/staff/staff-editor"
|
||||
export default function StaffPage() {
|
||||
return (
|
||||
<div className=" bg-white rounded-xl flex-grow">
|
||||
<div className=" bg-white flex-grow">
|
||||
<StaffEditor></StaffEditor>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -150,92 +150,36 @@ const DashboardPage = () => {
|
|||
};
|
||||
};
|
||||
|
||||
// 准备网系类型故障时间分布数据
|
||||
const prepareNetworkFaultsByTimeData = () => {
|
||||
if (!devices || !networkTypeTerms) return { xAxis: [], series: [] };
|
||||
// 生成多样化颜色函数(为不同用途添加偏移)
|
||||
const generateDiverseColors = (count: number, offset: number = 0) => {
|
||||
const colors = [];
|
||||
const hueStep = 360 / count;
|
||||
|
||||
const startDate = dateRange[0];
|
||||
const endDate = dateRange[1];
|
||||
const diffDays = endDate.diff(startDate, 'day');
|
||||
|
||||
// 时间间隔逻辑
|
||||
const intervals = [];
|
||||
let format = '';
|
||||
|
||||
if (diffDays <= 14) {
|
||||
format = 'MM-DD';
|
||||
for (let i = 0; i <= diffDays; i++) {
|
||||
intervals.push(startDate.add(i, 'day'));
|
||||
}
|
||||
} else if (diffDays <= 90) {
|
||||
format = 'MM-DD';
|
||||
const weeks = Math.ceil(diffDays / 7);
|
||||
for (let i = 0; i < weeks; i++) {
|
||||
const weekStart = startDate.add(i * 7, 'day');
|
||||
intervals.push(weekStart);
|
||||
}
|
||||
} else {
|
||||
format = 'YYYY-MM';
|
||||
const months = Math.ceil(diffDays / 30);
|
||||
for (let i = 0; i < months; i++) {
|
||||
const monthStart = startDate.add(i, 'month').startOf('month');
|
||||
intervals.push(monthStart);
|
||||
}
|
||||
for (let i = 0; i < count; i++) {
|
||||
const hue = ((i * hueStep) + offset) % 360;
|
||||
// 使用固定的饱和度和亮度,确保颜色鲜明且不相近
|
||||
const color = `hsl(${hue}, 80%, 66%)`;
|
||||
colors.push(color);
|
||||
}
|
||||
|
||||
const timeLabels = intervals.map(date => {
|
||||
if (diffDays <= 14) {
|
||||
return date.format(format);
|
||||
} else if (diffDays <= 90) {
|
||||
return `${date.format(format)}至${date.add(6, 'day').format(format)}`;
|
||||
} else {
|
||||
return date.format(format);
|
||||
}
|
||||
});
|
||||
|
||||
// 统计每个网系类型在每个时间段的故障数
|
||||
const networkNames = networkTypeTerms.map(type => type.name);
|
||||
const seriesData = networkNames.map(name => ({
|
||||
name,
|
||||
type: 'bar',
|
||||
data: new Array(intervals.length).fill(0)
|
||||
}));
|
||||
|
||||
devices.forEach(device => {
|
||||
const deviceTerms = getDeviceTerms(device);
|
||||
const networkName = deviceTerms.networkType;
|
||||
const networkIndex = networkNames.indexOf(networkName);
|
||||
|
||||
if (networkIndex !== -1) {
|
||||
const createDate = dayjs(device.createdAt);
|
||||
|
||||
for (let i = 0; i < intervals.length; i++) {
|
||||
if (diffDays <= 14) {
|
||||
if (createDate.format('YYYY-MM-DD') === intervals[i].format('YYYY-MM-DD')) {
|
||||
seriesData[networkIndex].data[i]++;
|
||||
}
|
||||
} else if (diffDays <= 90) {
|
||||
const weekEnd = intervals[i].add(6, 'day').endOf('day');
|
||||
if (createDate.isAfter(intervals[i]) && createDate.isBefore(weekEnd)) {
|
||||
seriesData[networkIndex].data[i]++;
|
||||
}
|
||||
} else {
|
||||
const monthEnd = intervals[i].endOf('month');
|
||||
if (createDate.isAfter(intervals[i]) && createDate.isBefore(monthEnd)) {
|
||||
seriesData[networkIndex].data[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
xAxis: timeLabels,
|
||||
series: seriesData
|
||||
};
|
||||
return colors;
|
||||
};
|
||||
|
||||
// 准备三层分类统计数据
|
||||
// 为网系类型生成一致的颜色映射(保持不变,用于网系类型图表间的一致性)
|
||||
const getNetworkTypeColors = () => {
|
||||
if (!networkTypeTerms || networkTypeTerms.length === 0) return {};
|
||||
|
||||
const colors = generateDiverseColors(networkTypeTerms.length, 0); // 网系类型从0度开始
|
||||
const colorMap = {};
|
||||
|
||||
networkTypeTerms.forEach((type, index) => {
|
||||
colorMap[type.name] = colors[index];
|
||||
});
|
||||
|
||||
return colorMap;
|
||||
};
|
||||
|
||||
// 修改准备三层分类统计数据函数
|
||||
const prepareHierarchicalFaultData = () => {
|
||||
if (!devices || !networkTypeTerms || networkTypeTerms.length === 0) {
|
||||
return { names: [], values: [] };
|
||||
|
|
@ -243,6 +187,7 @@ const DashboardPage = () => {
|
|||
|
||||
const hierarchicalCounts = {};
|
||||
const totalDevices = devices.length;
|
||||
const networkColors = getNetworkTypeColors(); // 使用一致的网系类型颜色
|
||||
|
||||
// 初始化网系类型计数
|
||||
networkTypeTerms.forEach(type => {
|
||||
|
|
@ -261,13 +206,16 @@ const DashboardPage = () => {
|
|||
const names = Object.keys(hierarchicalCounts);
|
||||
const values = names.map(name => ({
|
||||
value: totalDevices ? ((hierarchicalCounts[name] / totalDevices) * 100).toFixed(2) : 0,
|
||||
name
|
||||
name,
|
||||
itemStyle: {
|
||||
color: networkColors[name]
|
||||
}
|
||||
}));
|
||||
|
||||
return { names, values };
|
||||
};
|
||||
|
||||
// 修改系统类型分布数据(基于选中的网系类型,支持交互)
|
||||
// 修改系统类型分布数据(使用不同的颜色偏移)
|
||||
const prepareGroupedSystemTypeDistribution = () => {
|
||||
if (!devices || !systemTypeTerms || !networkTypeTerms ||
|
||||
systemTypeTerms.length === 0 || networkTypeTerms.length === 0) {
|
||||
|
|
@ -325,7 +273,7 @@ const DashboardPage = () => {
|
|||
}
|
||||
});
|
||||
|
||||
// 构建分组数据
|
||||
// 构建分组数据 - 为系统类型使用不同的颜色偏移
|
||||
const values: Array<{
|
||||
value: number;
|
||||
name: string;
|
||||
|
|
@ -333,18 +281,23 @@ const DashboardPage = () => {
|
|||
itemStyle: { color: string };
|
||||
}> = [];
|
||||
|
||||
// 为每个网系定义色系
|
||||
const networkColors: Record<string, string[]> = {
|
||||
'安全网络': ['#FF6B6B', '#FF8E8E', '#FFB1B1', '#FFD4D4'],
|
||||
'办公网络': ['#4ECDC4', '#7ED7D2', '#AEE2E0', '#DFEEED'],
|
||||
'生产网络': ['#45B7D1', '#6BC5D9', '#91D3E1', '#B7E1E9'],
|
||||
'未分类': ['#96CEB4', '#B2D8C3', '#CDE2D2', '#E8EDE1']
|
||||
};
|
||||
|
||||
// 收集所有有数据的系统类型
|
||||
const allSystemTypes = [];
|
||||
Object.entries(networkGroups).forEach(([networkName, systemTypeDisplayNames]) => {
|
||||
const colors = networkColors[networkName] || ['#DDA0DD', '#E6B3E6', '#F0C6F0', '#F9E9F9'];
|
||||
systemTypeDisplayNames.forEach((displayName) => {
|
||||
const count = systemTypeCounts[displayName];
|
||||
if (count > 0) {
|
||||
allSystemTypes.push(displayName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
systemTypeDisplayNames.forEach((displayName, index) => {
|
||||
// 为系统类型生成多样化颜色(使用120度偏移,避免与网系类型颜色重叠)
|
||||
const systemColors = generateDiverseColors(allSystemTypes.length, 120);
|
||||
|
||||
let colorIndex = 0;
|
||||
Object.entries(networkGroups).forEach(([networkName, systemTypeDisplayNames]) => {
|
||||
systemTypeDisplayNames.forEach((displayName) => {
|
||||
const count = systemTypeCounts[displayName];
|
||||
if (count > 0) {
|
||||
values.push({
|
||||
|
|
@ -352,9 +305,10 @@ const DashboardPage = () => {
|
|||
name: displayName,
|
||||
networkType: networkName,
|
||||
itemStyle: {
|
||||
color: colors[index % colors.length]
|
||||
color: systemColors[colorIndex]
|
||||
}
|
||||
});
|
||||
colorIndex++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -362,7 +316,95 @@ const DashboardPage = () => {
|
|||
return { groups: networkGroups, values };
|
||||
};
|
||||
|
||||
// 新增:故障类型分布数据(基于选中的系统类型)
|
||||
// 修改网系类型故障时间分布数据,使用一致的颜色
|
||||
const prepareNetworkFaultsByTimeData = () => {
|
||||
if (!devices || !networkTypeTerms) return { xAxis: [], series: [] };
|
||||
|
||||
const startDate = dateRange[0];
|
||||
const endDate = dateRange[1];
|
||||
const diffDays = endDate.diff(startDate, 'day');
|
||||
|
||||
// 时间间隔逻辑
|
||||
const intervals = [];
|
||||
let format = '';
|
||||
|
||||
if (diffDays <= 14) {
|
||||
format = 'MM-DD';
|
||||
for (let i = 0; i <= diffDays; i++) {
|
||||
intervals.push(startDate.add(i, 'day'));
|
||||
}
|
||||
} else if (diffDays <= 90) {
|
||||
format = 'MM-DD';
|
||||
const weeks = Math.ceil(diffDays / 7);
|
||||
for (let i = 0; i < weeks; i++) {
|
||||
const weekStart = startDate.add(i * 7, 'day');
|
||||
intervals.push(weekStart);
|
||||
}
|
||||
} else {
|
||||
format = 'YYYY-MM';
|
||||
const months = Math.ceil(diffDays / 30);
|
||||
for (let i = 0; i < months; i++) {
|
||||
const monthStart = startDate.add(i, 'month').startOf('month');
|
||||
intervals.push(monthStart);
|
||||
}
|
||||
}
|
||||
|
||||
const timeLabels = intervals.map(date => {
|
||||
if (diffDays <= 14) {
|
||||
return date.format(format);
|
||||
} else if (diffDays <= 90) {
|
||||
return `${date.format(format)}至${date.add(6, 'day').format(format)}`;
|
||||
} else {
|
||||
return date.format(format);
|
||||
}
|
||||
});
|
||||
|
||||
// 统计每个网系类型在每个时间段的故障数
|
||||
const networkNames = networkTypeTerms.map(type => type.name);
|
||||
const networkColors = getNetworkTypeColors(); // 使用与饼图一致的网系类型颜色
|
||||
|
||||
const seriesData = networkNames.map(name => ({
|
||||
name,
|
||||
type: 'bar',
|
||||
data: new Array(intervals.length).fill(0),
|
||||
color: networkColors[name] // 使用一致的网系类型颜色
|
||||
}));
|
||||
|
||||
devices.forEach(device => {
|
||||
const deviceTerms = getDeviceTerms(device);
|
||||
const networkName = deviceTerms.networkType;
|
||||
const networkIndex = networkNames.indexOf(networkName);
|
||||
|
||||
if (networkIndex !== -1) {
|
||||
const createDate = dayjs(device.createdAt);
|
||||
|
||||
for (let i = 0; i < intervals.length; i++) {
|
||||
if (diffDays <= 14) {
|
||||
if (createDate.format('YYYY-MM-DD') === intervals[i].format('YYYY-MM-DD')) {
|
||||
seriesData[networkIndex].data[i]++;
|
||||
}
|
||||
} else if (diffDays <= 90) {
|
||||
const weekEnd = intervals[i].add(6, 'day').endOf('day');
|
||||
if (createDate.isAfter(intervals[i]) && createDate.isBefore(weekEnd)) {
|
||||
seriesData[networkIndex].data[i]++;
|
||||
}
|
||||
} else {
|
||||
const monthEnd = intervals[i].endOf('month');
|
||||
if (createDate.isAfter(intervals[i]) && createDate.isBefore(monthEnd)) {
|
||||
seriesData[networkIndex].data[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
xAxis: timeLabels,
|
||||
series: seriesData
|
||||
};
|
||||
};
|
||||
|
||||
// 修改故障类型分布数据,使用另一个颜色偏移
|
||||
const prepareDeviceTypeDistribution = () => {
|
||||
if (!devices || !deviceTypeTerms || !systemTypeTerms || !selectedSystemType)
|
||||
return { names: [], values: [] };
|
||||
|
|
@ -388,12 +430,20 @@ const DashboardPage = () => {
|
|||
});
|
||||
|
||||
const names = Object.keys(deviceCounts);
|
||||
const values = names.map(name => ({
|
||||
value: deviceCounts[name],
|
||||
name
|
||||
})).filter(item => item.value > 0);
|
||||
const validNames = names.filter(name => deviceCounts[name] > 0);
|
||||
|
||||
return { names, values };
|
||||
// 为故障类型使用240度偏移,与网系类型和系统类型都不重叠
|
||||
const deviceColors = generateDiverseColors(validNames.length, 240);
|
||||
|
||||
const values = validNames.map((name, index) => ({
|
||||
value: deviceCounts[name],
|
||||
name,
|
||||
itemStyle: {
|
||||
color: deviceColors[index]
|
||||
}
|
||||
}));
|
||||
|
||||
return { names: validNames, values };
|
||||
};
|
||||
|
||||
// 准备故障处置完成率数据
|
||||
|
|
@ -781,8 +831,8 @@ const DashboardPage = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 min-h-screen bg-gray-50">
|
||||
<h1 className="text-2xl font-bold mb-6">故障数据可视化面板</h1>
|
||||
<div className="p-4 min-h-screen bg-[#eff3f4]">
|
||||
{/* <h1 className="text-2xl font-bold mb-6">故障数据可视化面板</h1> */}
|
||||
|
||||
<Row className="mb-4">
|
||||
<Col span={24}>
|
||||
|
|
@ -810,7 +860,7 @@ const DashboardPage = () => {
|
|||
<Card className="shadow-sm hover:shadow-md transition-shadow duration-300">
|
||||
<ReactECharts
|
||||
option={getFaultCompletionRateOption()}
|
||||
style={{ height: '350px' }}
|
||||
style={{ height: '300px' }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
@ -818,7 +868,7 @@ const DashboardPage = () => {
|
|||
<Card className="shadow-sm hover:shadow-md transition-shadow duration-300">
|
||||
<ReactECharts
|
||||
option={getHierarchicalFaultOption()}
|
||||
style={{ height: '350px' }}
|
||||
style={{ height: '300px' }}
|
||||
onEvents={{
|
||||
click: handleNetworkTypeClick
|
||||
}}
|
||||
|
|
@ -829,7 +879,7 @@ const DashboardPage = () => {
|
|||
<Card className="shadow-sm hover:shadow-md transition-shadow duration-300">
|
||||
<ReactECharts
|
||||
option={getGroupedSystemTypeOption()}
|
||||
style={{ height: '350px' }}
|
||||
style={{ height: '300px' }}
|
||||
onEvents={{
|
||||
click: handleSystemTypeClick
|
||||
}}
|
||||
|
|
@ -840,7 +890,7 @@ const DashboardPage = () => {
|
|||
<Card className="shadow-sm hover:shadow-md transition-shadow duration-300">
|
||||
<ReactECharts
|
||||
option={getDeviceTypeOption()}
|
||||
style={{ height: '350px' }}
|
||||
style={{ height: '300px' }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
@ -850,7 +900,7 @@ const DashboardPage = () => {
|
|||
<Card className="shadow-sm hover:shadow-md transition-shadow duration-300">
|
||||
<ReactECharts
|
||||
option={getNetworkFaultsByTimeOption()}
|
||||
style={{ height: '450px' }}
|
||||
style={{ height: '420px' }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ export default function DeviceModal() {
|
|||
onCancel={handleCancel}
|
||||
width={1000}
|
||||
style={{ top: 20 }}
|
||||
bodyStyle={{ maxHeight: "calc(100vh - 200px)", overflowY: "auto" }}
|
||||
bodyStyle={{ maxHeight: "calc(100vh - 200px)", overflowY: "auto",overflowX: "hidden" }}
|
||||
>
|
||||
<Form form={form} initialValues={formValue} layout="vertical">
|
||||
<Row gutter={16}>
|
||||
|
|
@ -236,6 +236,7 @@ export default function DeviceModal() {
|
|||
value={selectedDeviceType}
|
||||
onChange={setSelectedDeviceType}
|
||||
systemTypeId={selectedSystemType}
|
||||
disabled={!selectedSystemType} // 没有选择系统类型时禁用
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import {
|
|||
import DepartmentSelect from "@web/src/components/models/department/department-select";
|
||||
import SystemTypeSelect from "@web/src/app/main/devicepage/select/System-select";
|
||||
import DeviceTypeSelect from "@web/src/app/main/devicepage/select/Device-select";
|
||||
import NetworkTypeSelect from "@web/src/app/main/devicepage/select/Network-type-select";
|
||||
import dayjs from "dayjs";
|
||||
import FixTypeSelect from "./select/Fix-select";
|
||||
import { api } from "@nice/client";
|
||||
|
|
@ -580,7 +581,7 @@ export default function DeviceMessage() {
|
|||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 p-4">
|
||||
<div className="min-h-screen bg-[#eff3f4] p-4">
|
||||
{/* 页面标题区域 */}
|
||||
<div className="mb-6">
|
||||
<Card className="shadow-sm border border-gray-200">
|
||||
|
|
@ -640,7 +641,7 @@ export default function DeviceMessage() {
|
|||
<label className="text-sm font-medium text-gray-600">
|
||||
网系类型
|
||||
</label>
|
||||
<Select
|
||||
<NetworkTypeSelect
|
||||
value={selectedNetworkType}
|
||||
onChange={(value) => {
|
||||
setSelectedNetworkType(value);
|
||||
|
|
@ -649,14 +650,7 @@ export default function DeviceMessage() {
|
|||
}}
|
||||
placeholder="选择网系类型"
|
||||
className="w-full"
|
||||
allowClear
|
||||
>
|
||||
{networkTypeTerms?.map(term => (
|
||||
<Select.Option key={term.id} value={term.id}>
|
||||
{term.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={8} lg={6}>
|
||||
|
|
|
|||
|
|
@ -663,7 +663,7 @@ export default function DeviceManager({ title }: TermManagerProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 min-h-screen bg-gray-50">
|
||||
<div className="p-6 h-auto bg-gray-50">
|
||||
{/* 主要内容卡片 */}
|
||||
<Card className="shadow-sm" bodyStyle={{ padding: 0 }}>
|
||||
{/* 统计信息区域 */}
|
||||
|
|
|
|||
|
|
@ -8,26 +8,26 @@ import {
|
|||
|
||||
export function MainFooter() {
|
||||
return (
|
||||
<footer className="bg-gradient-to-b from-slate-800 to-slate-900 relative z-10 text-secondary-200">
|
||||
<div className="container mx-auto px-4 py-6">
|
||||
<footer className="bg-white shadow-md border-t border-gray-100 relative z-10">
|
||||
<div className="container mx-auto px-4 py-2">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{/* 开发组织信息 */}
|
||||
<div className="text-center md:text-left space-y-2">
|
||||
<h3 className="text-white font-semibold text-sm flex items-center justify-center md:justify-start">
|
||||
<h3 className="text-[#0b3c7c] font-semibold text-sm flex items-center justify-center md:justify-start">
|
||||
软件与数据小组
|
||||
</h3>
|
||||
<p className="text-gray-400 text-xs italic">提供技术支持</p>
|
||||
<p className="text-gray-500 text-xs italic">提供技术支持</p>
|
||||
</div>
|
||||
|
||||
{/* 联系方式 */}
|
||||
<div className="text-center space-y-2">
|
||||
<div className="flex items-center justify-center space-x-2">
|
||||
<PhoneOutlined className="text-gray-400" />
|
||||
<span className="text-gray-300 text-xs">628532</span>
|
||||
<PhoneOutlined className="text-[#0b3c7c]" />
|
||||
<span className="text-gray-600 text-xs">628532</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-center space-x-2">
|
||||
<MailOutlined className="text-gray-400" />
|
||||
<span className="text-gray-300 text-xs">
|
||||
<MailOutlined className="text-[#0b3c7c]" />
|
||||
<span className="text-gray-600 text-xs">
|
||||
ruanjian1@tx3l.nb.kj
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -38,14 +38,14 @@ export function MainFooter() {
|
|||
<div className="flex items-center justify-center md:justify-end space-x-4">
|
||||
<a
|
||||
href="https://27.57.72.21"
|
||||
className="text-gray-400 hover:text-white transition-colors"
|
||||
className="text-[#0b3c7c] hover:text-blue-600 transition-colors"
|
||||
title="访问门户网站"
|
||||
>
|
||||
<HomeOutlined className="text-lg" />
|
||||
</a>
|
||||
<a
|
||||
href="https://27.57.72.14"
|
||||
className="text-gray-400 hover:text-white transition-colors"
|
||||
className="text-[#0b3c7c] hover:text-blue-600 transition-colors"
|
||||
title="访问烽火青云"
|
||||
>
|
||||
<CloudOutlined className="text-lg" />
|
||||
|
|
|
|||
|
|
@ -25,10 +25,8 @@ export function MainHeader() {
|
|||
];
|
||||
|
||||
return (
|
||||
<div className="select-none w-full flex items-center justify-between bg-white shadow-md border-b border-gray-100 fixed z-30 py-2 px-4 md:px-6 h-16">
|
||||
<div className="flex items-center">
|
||||
<span className="text-xl font-bold text-primary">故障收录检索系统</span>
|
||||
</div>
|
||||
<div className="select-none w-full flex items-center justify-end bg-[#eff3f4]shadow-md py-2 px-4 md:px-6 h-auto">
|
||||
|
||||
|
||||
{isAuthenticated && user ? (
|
||||
<Dropdown
|
||||
|
|
@ -36,8 +34,9 @@ export function MainHeader() {
|
|||
items,
|
||||
onClick: ({ key }) => key === "logout" && handleLogout(),
|
||||
}}
|
||||
placement="bottomRight"
|
||||
placement="bottom"
|
||||
arrow
|
||||
|
||||
>
|
||||
<div className="flex items-center cursor-pointer transition-all hover:bg-gray-100 rounded-full py-1 px-3">
|
||||
<Avatar
|
||||
|
|
|
|||
|
|
@ -1,24 +1,38 @@
|
|||
import { Layout } from "antd";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { MainHeader } from "./MainHeader";
|
||||
import { MainFooter } from "./MainFooter";
|
||||
import { MainProvider } from "./MainProvider";
|
||||
import NavigationMenu from "./NavigationMenu";
|
||||
import { UserInfoPanel } from "./UserInfoPanel";
|
||||
import { MainProvider } from "./MainProvider";
|
||||
import { MainFooter } from "./MainFooter";
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
export function MainLayout() {
|
||||
return (
|
||||
<MainProvider>
|
||||
<div className="min-h-screen bg-gray-100">
|
||||
<MainHeader />
|
||||
<div className="flex pt-16">
|
||||
<NavigationMenu />
|
||||
<Content className="flex-grow bg-gray-50">
|
||||
<Outlet />
|
||||
</Content>
|
||||
<div className="bg-gray-100 flex min-h-screen">
|
||||
{/* 左侧固定区域 */}
|
||||
<div className="bg-[#fafafa] border-r-[1px] border-[#e8e8e8] flex flex-col w-[230px] justify-between h-screen fixed left-0 top-0 z-10">
|
||||
<div>
|
||||
<div className="flex items-center justify-center my-3">
|
||||
<span className="text-2xl font-bold text-[#1e1e1f]">故障收录检索系统</span>
|
||||
</div>
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
<UserInfoPanel />
|
||||
</div>
|
||||
|
||||
{/* 右侧内容区域,margin-left等于左侧宽度 */}
|
||||
<div className="flex flex-col h-screen flex-1 ml-[230px]">
|
||||
{/* 上方内容区,可滚动 */}
|
||||
<div className="flex-1 bg-[#eff3f4] border-l-[1px] border-[#e8e8e8]">
|
||||
<Outlet />
|
||||
</div>
|
||||
{/* 下方固定页脚 */}
|
||||
{/* <div className="flex-shrink-0">
|
||||
<MainFooter />
|
||||
</div> */}
|
||||
</div>
|
||||
<MainFooter />
|
||||
</div>
|
||||
</MainProvider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const NavigationMenu: React.FC = () => {
|
|||
}, [selectedKeys]);
|
||||
|
||||
return (
|
||||
<div className="w-[200px] h-full bg-#fff">
|
||||
<div className="w-full h-full">
|
||||
<div
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
|
|
@ -102,12 +102,15 @@ const NavigationMenu: React.FC = () => {
|
|||
{/* 此处为版权标识,严禁删改
|
||||
<img src={logo} className="w-[124px] h-[40px]"/> */}
|
||||
</div>
|
||||
<div className="w-[200px] h-[calc(100%-74px)] overflow-y-auto overflow-x-hidden">
|
||||
<div className="w-full h-full overflow-y-auto overflow-x-hidden ">
|
||||
<Menu
|
||||
onClick={onClick}
|
||||
|
||||
style={{
|
||||
width: 200,
|
||||
background: "#ffffff",
|
||||
width: 230,
|
||||
background:"#f9fafb",
|
||||
border: "none",
|
||||
// color: "#ffffff",
|
||||
}}
|
||||
selectedKeys={selectedKeys}
|
||||
openKeys={openKeys}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
// apps/web/src/app/main/layout/UserInfoPanel.tsx
|
||||
import { Avatar, Button, Dropdown } from "antd";
|
||||
import { UserOutlined, LogoutOutlined } from "@ant-design/icons";
|
||||
import { useAuth } from "@web/src/providers/auth-provider";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export function UserInfoPanel() {
|
||||
const { isAuthenticated, user, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: "logout",
|
||||
icon: <LogoutOutlined />,
|
||||
label: "确认退出",
|
||||
danger: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-start mb-14 px-4">
|
||||
{isAuthenticated && user ? (
|
||||
<>
|
||||
{/* 左侧:头像 */}
|
||||
<Avatar
|
||||
size={40}
|
||||
src={user.avatar}
|
||||
icon={<UserOutlined />}
|
||||
className="mr-1 bg-[#2563eb] text-white"
|
||||
/>
|
||||
|
||||
{/* 右侧:用户名和已登录按钮 */}
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-medium text-[#1e1e1f] text-center ">
|
||||
{user.showname || user.username}
|
||||
</div>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items,
|
||||
onClick: ({ key }) => key === "logout" && logout(),
|
||||
}}
|
||||
placement="bottom"
|
||||
arrow
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<LogoutOutlined />}
|
||||
className="text-gray-500 hover:text-[#2563eb] p-0 h-auto"
|
||||
>
|
||||
退出
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
shape="round"
|
||||
icon={<UserOutlined />}
|
||||
onClick={() => navigate("/login")}
|
||||
>
|
||||
登录
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -159,4 +159,76 @@
|
|||
.custom-table .ant-table-tbody>tr:last-child>td {
|
||||
border-bottom: none;
|
||||
/* 去除最后一行的底部边框 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* 强制保持父菜单正常透明度 */
|
||||
|
||||
/* 父菜单项默认状态 - 只修改颜色 */
|
||||
/* .ant-menu.ant-menu-inline > .ant-menu-item,
|
||||
.ant-menu.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title {
|
||||
color: #000000 !important;
|
||||
background: transparent !important;
|
||||
opacity: 1 !important;
|
||||
} */
|
||||
|
||||
/* 父菜单项悬浮状态 - 只修改颜色 */
|
||||
/* .ant-menu.ant-menu-inline > .ant-menu-item:hover,
|
||||
.ant-menu.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title:hover {
|
||||
background: rgba(255, 255, 255, 0.1) !important;
|
||||
color: #000000 !important;
|
||||
} */
|
||||
|
||||
/* 父菜单项选中状态 - 只修改颜色 */
|
||||
/* .ant-menu.ant-menu-inline > .ant-menu-item.ant-menu-item-selected {
|
||||
background: #0b3c7c !important;
|
||||
color: #ffffff !important;
|
||||
} */
|
||||
|
||||
/* 父菜单展开/激活状态 - 只修改颜色 */
|
||||
/* .ant-menu.ant-menu-inline > .ant-menu-submenu.ant-menu-submenu-active > .ant-menu-submenu-title,
|
||||
.ant-menu.ant-menu-inline > .ant-menu-submenu.ant-menu-submenu-open > .ant-menu-submenu-title {
|
||||
background: rgba(255, 255, 255, 0.1) !important;
|
||||
color: #000000 !important;
|
||||
} */
|
||||
|
||||
/* 子菜单容器背景 */
|
||||
/* .ant-menu.ant-menu-inline .ant-menu-sub.ant-menu-inline {
|
||||
background: #0b3c7c !important;
|
||||
} */
|
||||
|
||||
/* 子菜单项默认状态 - 只修改颜色 */
|
||||
/* .ant-menu.ant-menu-inline .ant-menu-sub .ant-menu-item {
|
||||
color: #000000 !important;
|
||||
background: transparent !important;
|
||||
} */
|
||||
|
||||
/* 子菜单项悬浮状态 - 只修改颜色 */
|
||||
/* .ant-menu.ant-menu-inline .ant-menu-sub .ant-menu-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1) !important;
|
||||
color: #000000 !important;
|
||||
} */
|
||||
|
||||
/* 子菜单项选中状态 - 只修改颜色 */
|
||||
/* .ant-menu.ant-menu-inline .ant-menu-sub .ant-menu-item.ant-menu-item-selected {
|
||||
background: #0b3c7c !important;
|
||||
color: #ffffff !important;
|
||||
} */
|
||||
|
||||
/* 确保图标颜色与文字一致 */
|
||||
/* .ant-menu .ant-menu-item .anticon,
|
||||
.ant-menu .ant-menu-submenu-title .anticon {
|
||||
color: inherit !important;
|
||||
} */
|
||||
|
||||
/* 移除默认的after伪元素 */
|
||||
/* .ant-menu.ant-menu-inline .ant-menu-item::after,
|
||||
.ant-menu.ant-menu-inline .ant-menu-submenu > .ant-menu-submenu-title::after {
|
||||
display: none !important;
|
||||
} */
|
||||
/* 针对 inline 模式下的子菜单 */
|
||||
.ant-menu.ant-menu-inline .ant-menu-sub.ant-menu-inline {
|
||||
background: #f9fafb !important;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ server {
|
|||
# 监听80端口
|
||||
listen 80;
|
||||
# 服务器域名/IP地址,使用环境变量
|
||||
server_name 192.168.142.194;
|
||||
server_name localhost;
|
||||
|
||||
# 基础性能优化配置
|
||||
# 启用tcp_nopush以优化数据发送
|
||||
|
|
@ -100,7 +100,7 @@ server {
|
|||
# 仅供内部使用
|
||||
internal;
|
||||
# 代理到认证服务
|
||||
proxy_pass http://192.168.142.194:3000/auth/file;
|
||||
proxy_pass http://localhost:3000/auth/file;
|
||||
|
||||
# 请求优化:不传递请求体
|
||||
proxy_pass_request_body off;
|
||||
|
|
|
|||
Loading…
Reference in New Issue