feat(ui): 页面标题区分能源BI与资产BI,资产页增加OneOS迁移提示滚动条
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<title>羚牛氢能车辆资产</title>
|
<title>羚牛氢能</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -128,6 +128,10 @@ function AuthGate() {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = pathSet === "energy" ? "羚牛氢能-能源BI" : "羚牛氢能-资产BI";
|
||||||
|
}, [pathSet]);
|
||||||
|
|
||||||
const modules = useMemo<ModuleConfig[]>(() => {
|
const modules = useMemo<ModuleConfig[]>(() => {
|
||||||
if (pathSet === "energy") {
|
if (pathSet === "energy") {
|
||||||
return [HYDROGEN_MODULE, ELECTRIC_MODULE, ETC_MODULE];
|
return [HYDROGEN_MODULE, ELECTRIC_MODULE, ETC_MODULE];
|
||||||
|
|||||||
@@ -14,3 +14,12 @@ body {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes marquee {
|
||||||
|
from { transform: translateX(0); }
|
||||||
|
to { transform: translateX(-50%); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility animate-marquee {
|
||||||
|
animation: marquee 30s linear infinite;
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,42 @@ const TABS = [
|
|||||||
{ id: 'customer', label: '按客户' },
|
{ id: 'customer', label: '按客户' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function MarqueeBanner() {
|
||||||
|
const trackRef = useRef<HTMLDivElement>(null);
|
||||||
|
const innerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [overflow, setOverflow] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const check = () => {
|
||||||
|
if (!trackRef.current || !innerRef.current) return;
|
||||||
|
setOverflow(innerRef.current.scrollWidth > trackRef.current.clientWidth);
|
||||||
|
};
|
||||||
|
check();
|
||||||
|
const ro = new ResizeObserver(check);
|
||||||
|
ro.observe(trackRef.current!);
|
||||||
|
return () => ro.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const text = '⚠️ 因 OneOS 数据迁移未完成,当前状态暂不准确,预计下周完成对接后恢复正常。';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative -mx-6 mb-4 bg-amber-50 border-y border-amber-200">
|
||||||
|
<div ref={trackRef} className="overflow-hidden">
|
||||||
|
<div className={`flex w-max py-2 ${overflow ? 'animate-marquee' : 'w-full justify-center'}`}>
|
||||||
|
<span ref={innerRef} className="inline-block whitespace-nowrap px-6 text-xs text-amber-700 font-medium">
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
{overflow && (
|
||||||
|
<span className="inline-block whitespace-nowrap px-6 text-xs text-amber-700 font-medium">
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function AssetsModule() {
|
export default function AssetsModule() {
|
||||||
const [activeTab, setActiveTab] = useState<'overview' | 'department' | 'region' | 'customer'>('overview');
|
const [activeTab, setActiveTab] = useState<'overview' | 'department' | 'region' | 'customer'>('overview');
|
||||||
const [tabReady, setTabReady] = useState(true);
|
const [tabReady, setTabReady] = useState(true);
|
||||||
@@ -92,7 +128,7 @@ export default function AssetsModule() {
|
|||||||
const [modalWeeklyDetail, setModalWeeklyDetail] = useState<WeeklyDetailItem[]>([]);
|
const [modalWeeklyDetail, setModalWeeklyDetail] = useState<WeeklyDetailItem[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [lastUpdate, setLastUpdate] = useState<string>('');
|
const [lastUpdate] = useState<string>('2026-06-03 23:59:59');
|
||||||
const [modalLoading, setModalLoading] = useState(false);
|
const [modalLoading, setModalLoading] = useState(false);
|
||||||
|
|
||||||
// Dept/Region/Customer data
|
// Dept/Region/Customer data
|
||||||
@@ -162,7 +198,6 @@ export default function AssetsModule() {
|
|||||||
setRegionData(region);
|
setRegionData(region);
|
||||||
setCustomerData(cust);
|
setCustomerData(cust);
|
||||||
setInventoryData(inv);
|
setInventoryData(inv);
|
||||||
setLastUpdate(new Date().toLocaleString('zh-CN'));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError(e instanceof Error ? e.message : '数据加载失败');
|
setError(e instanceof Error ? e.message : '数据加载失败');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -526,7 +561,7 @@ export default function AssetsModule() {
|
|||||||
<div className="sticky top-0 z-40 -mx-6 -mt-6 mb-4 bg-white/95 backdrop-blur-sm border-b border-gray-100/80">
|
<div className="sticky top-0 z-40 -mx-6 -mt-6 mb-4 bg-white/95 backdrop-blur-sm border-b border-gray-100/80">
|
||||||
{/* Title row */}
|
{/* Title row */}
|
||||||
<div className="relative flex items-center justify-center px-4 pt-3 pb-1">
|
<div className="relative flex items-center justify-center px-4 pt-3 pb-1">
|
||||||
<h1 className="hidden sm:block text-base font-semibold text-gray-800 tracking-wide">羚牛氢能车辆资产</h1>
|
<h1 className="hidden sm:block text-base font-semibold text-gray-800 tracking-wide">羚牛氢能-资产BI</h1>
|
||||||
{/* Right: status + theme */}
|
{/* Right: status + theme */}
|
||||||
<div className="absolute right-4 top-1/2 -translate-y-1/2 flex items-center gap-2">
|
<div className="absolute right-4 top-1/2 -translate-y-1/2 flex items-center gap-2">
|
||||||
<div className="hidden sm:flex items-center gap-1 text-[10px] text-gray-400">
|
<div className="hidden sm:flex items-center gap-1 text-[10px] text-gray-400">
|
||||||
@@ -690,12 +725,15 @@ export default function AssetsModule() {
|
|||||||
最后更新: {lastUpdate}
|
最后更新: {lastUpdate}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<span className="w-1 h-1 rounded-full bg-green-400 animate-pulse inline-block" />
|
<span className="w-1 h-1 rounded-full bg-amber-400 inline-block" />
|
||||||
每分钟更新一次
|
OneOS数据源接入中
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* OneOS 迁移提示滚动条 */}
|
||||||
|
<MarqueeBanner />
|
||||||
|
|
||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
|
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ export default function MonitoringView() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5 mt-1">
|
<div className="flex items-center gap-1.5 mt-1">
|
||||||
<span className="flex h-1.5 w-1.5 rounded-full bg-blue-500 animate-pulse"></span>
|
<span className="flex h-1.5 w-1.5 rounded-full bg-blue-500 animate-pulse"></span>
|
||||||
<span className="text-[9px] font-bold text-slate-400 uppercase tracking-tight">实时监控 • 每分钟更新</span>
|
<span className="text-[9px] font-bold text-slate-400 uppercase tracking-tight">数据监控 • 每15分钟更新</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user