更新品牌文案与演示项目名称
- 登录页和侧栏统一使用根目录 logo_square.png,并更新登录系统名称与副标题。 - 更新 Dashboard、项目库和工作区时间轴文案,移除底层时序视频图层说明。 - 演示视频项目显示名改为“演视LC视频序列”,启动时兼容迁移旧 Data_MyVideo_1 名称,恢复出厂设置使用新名。 - 调整侧栏用户管理入口为用户图标,底部当前用户入口为退出图标,并让退出提示不接收鼠标事件。 - 补充前端组件测试、后端演示重置测试和文档说明。
This commit is contained in:
@@ -94,6 +94,7 @@ describe('Dashboard', () => {
|
||||
expect(screen.getByText('已存标注')).toBeInTheDocument();
|
||||
expect(screen.getByText('真实项目.mp4')).toBeInTheDocument();
|
||||
expect(screen.getByText('项目状态: pending')).toBeInTheDocument();
|
||||
expect(screen.getByText('系统全局数据监控')).toBeInTheDocument();
|
||||
expect(screen.queryByText('City_Driving_Dataset_004.mp4')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ export function Dashboard() {
|
||||
{isConnected ? 'WebSocket 已连接' : 'WebSocket 断开'}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-400 text-sm mt-1">系统全局数据吞吐状态与所有接入项目进度实时洞察驾驶舱。</p>
|
||||
<p className="text-gray-400 text-sm mt-1">系统全局数据监控</p>
|
||||
{loadError && <p className="text-red-400 text-xs mt-2">{loadError}</p>}
|
||||
{taskActionMessage && <p className="text-amber-400 text-xs mt-2">{taskActionMessage}</p>}
|
||||
</header>
|
||||
|
||||
@@ -49,6 +49,7 @@ describe('FrameTimeline', () => {
|
||||
|
||||
expect(screen.getAllByText('00:00.10').length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByText('00:00.20').length).toBeGreaterThan(0);
|
||||
expect(screen.queryByText('底层时序视频图层截帧导航轴')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a processing progress bar with red annotation markers and blue propagation segments', () => {
|
||||
|
||||
@@ -534,7 +534,6 @@ export function FrameTimeline({
|
||||
<div className="text-xs font-mono text-cyan-300 mt-1">
|
||||
{formatTime(currentSeconds)} <span className="text-gray-600">/</span> {formatTime(totalSeconds)}
|
||||
</div>
|
||||
<div className="text-[10px] text-gray-500 uppercase tracking-widest mt-1">底层时序视频图层截帧导航轴</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,4 +51,13 @@ describe('Login', () => {
|
||||
expect(screen.getByDisplayValue('admin')).toHaveAttribute('autocomplete', 'username');
|
||||
expect(screen.getByDisplayValue('123456')).toHaveAttribute('autocomplete', 'current-password');
|
||||
});
|
||||
|
||||
it('uses the product logo and updated system title copy', () => {
|
||||
render(<Login />);
|
||||
|
||||
expect(screen.getByAltText('Logo')).toHaveAttribute('src', expect.stringContaining('logo_square'));
|
||||
expect(screen.getByText('多模态影像及视频智能语义分割与标注系统')).toBeInTheDocument();
|
||||
expect(screen.getByText('智能语义分割系统')).toBeInTheDocument();
|
||||
expect(screen.queryByText('欢迎登录协同工作站')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import { BrainCircuit } from 'lucide-react';
|
||||
import { cn } from '../lib/utils';
|
||||
import { useStore } from '../store/useStore';
|
||||
import { login as loginApi } from '../lib/api';
|
||||
@@ -34,10 +33,10 @@ export function Login() {
|
||||
<div className="relative z-10 w-full max-w-md p-8 bg-[#111] border border-white/5 rounded-2xl shadow-2xl scale-in shadow-black/50">
|
||||
<div className="flex flex-col items-center mb-8">
|
||||
<div className="w-16 h-16 bg-white rounded-2xl flex items-center justify-center text-cyan-500 shadow-lg shadow-cyan-500/20 mb-4 overflow-hidden border border-white/10">
|
||||
<BrainCircuit size={32} />
|
||||
<img src="/logo_square.png" alt="Logo" className="h-full w-full object-contain" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-white tracking-wider mb-2">欢迎登录协同工作站</h1>
|
||||
<p className="text-sm text-gray-500">AI 智能切分与多模态数据标注系统</p>
|
||||
<h1 className="text-center text-2xl font-bold text-white tracking-wider mb-2">多模态影像及视频智能语义分割与标注系统</h1>
|
||||
<p className="text-sm text-gray-500">智能语义分割系统</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
|
||||
@@ -46,6 +46,7 @@ describe('ProjectLibrary', () => {
|
||||
fireEvent.click(await screen.findByText('Demo Project'));
|
||||
expect(useStore.getState().currentProject?.id).toBe('p1');
|
||||
expect(onProjectSelect).toHaveBeenCalled();
|
||||
expect(screen.getByText('支持导入视频文件、DICOM序列文件')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the generated frame sequence FPS on project cards instead of source FPS', async () => {
|
||||
|
||||
@@ -576,7 +576,7 @@ export function ProjectLibrary({ onProjectSelect }: ProjectLibraryProps) {
|
||||
<div className="flex justify-between items-end mb-8 border-b border-white/5 pb-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium tracking-tight text-white mb-2">视频与连续帧项目库</h1>
|
||||
<p className="text-gray-400 text-sm">上传源文件、按帧解析配置,并结构化管理多媒体资产实体。</p>
|
||||
<p className="text-gray-400 text-sm">支持导入视频文件、DICOM序列文件</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
|
||||
@@ -19,7 +19,9 @@ describe('Sidebar', () => {
|
||||
|
||||
render(<Sidebar activeModule="dashboard" setActiveModule={setActiveModule} />);
|
||||
|
||||
fireEvent.click(screen.getByTitle('用户管理'));
|
||||
const adminButton = screen.getByTitle('用户管理');
|
||||
expect(adminButton.querySelector('.lucide-circle-user')).toBeInTheDocument();
|
||||
fireEvent.click(adminButton);
|
||||
expect(setActiveModule).toHaveBeenCalledWith('admin');
|
||||
});
|
||||
|
||||
@@ -44,4 +46,15 @@ describe('Sidebar', () => {
|
||||
|
||||
expect(screen.getByAltText('Logo')).toHaveAttribute('src', expect.stringContaining('logo_square'));
|
||||
});
|
||||
|
||||
it('uses a logout icon and prevents the logout tooltip from catching workspace hover', () => {
|
||||
useStore.setState({ currentUser: { id: 1, username: 'admin', role: 'admin' } });
|
||||
|
||||
render(<Sidebar activeModule="dashboard" setActiveModule={vi.fn()} />);
|
||||
|
||||
const logoutButton = screen.getByTitle('当前用户:admin,点击退出');
|
||||
expect(logoutButton.querySelector('.lucide-log-out')).toBeInTheDocument();
|
||||
expect(screen.getByText('admin / 退出')).toHaveClass('pointer-events-none');
|
||||
expect(screen.getByText('admin / 退出')).toHaveClass('invisible');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Home, FolderOpen, Edit3, LayoutTemplate, LogOut, UserCircle, ShieldCheck } from 'lucide-react';
|
||||
import { Home, FolderOpen, Edit3, LayoutTemplate, LogOut, UserCircle } from 'lucide-react';
|
||||
import { cn } from '../lib/utils';
|
||||
import type { ActiveModule } from '../App';
|
||||
import { ModelStatusBadge } from './ModelStatusBadge';
|
||||
@@ -20,13 +20,13 @@ export function Sidebar({ activeModule, setActiveModule }: SidebarProps) {
|
||||
{ id: 'workspace', icon: Edit3, label: '分割工作区' },
|
||||
{ id: 'ai', icon: AiSegmentationIcon, label: 'AI智能分割' },
|
||||
{ id: 'templates', icon: LayoutTemplate, label: '模板库' },
|
||||
...(currentUser?.role === 'admin' ? [{ id: 'admin', icon: ShieldCheck, label: '用户管理' }] : []),
|
||||
...(currentUser?.role === 'admin' ? [{ id: 'admin', icon: UserCircle, label: '用户管理' }] : []),
|
||||
] as const;
|
||||
|
||||
return (
|
||||
<aside className="w-16 flex flex-col items-center py-6 bg-[#0d0d0d] border-r border-white/10 z-50 gap-8">
|
||||
<div className="w-10 h-10 rounded-lg overflow-hidden flex items-center justify-center bg-white">
|
||||
<img src="/logo_square.png" alt="Logo" className="w-full h-full object-cover" />
|
||||
<img src="/logo_square.png" alt="Logo" className="h-full w-full object-contain" />
|
||||
</div>
|
||||
<nav className="flex flex-col gap-6 w-full px-2">
|
||||
{navItems.map((item) => {
|
||||
@@ -60,8 +60,8 @@ export function Sidebar({ activeModule, setActiveModule }: SidebarProps) {
|
||||
onClick={logout}
|
||||
className="group relative flex h-9 w-9 items-center justify-center rounded-lg border border-white/10 bg-white/5 text-gray-400 transition-colors hover:border-red-400/40 hover:bg-red-500/10 hover:text-red-200"
|
||||
>
|
||||
{currentUser ? <UserCircle size={20} /> : <LogOut size={20} />}
|
||||
<span className="absolute left-full ml-2 whitespace-nowrap rounded border border-[#333] bg-[#222] px-2 py-1 text-xs text-gray-200 opacity-0 shadow-xl transition-all group-hover:opacity-100">
|
||||
<LogOut size={20} />
|
||||
<span className="pointer-events-none invisible absolute left-full ml-2 whitespace-nowrap rounded border border-[#333] bg-[#222] px-2 py-1 text-xs text-gray-200 opacity-0 shadow-xl transition-all group-hover:visible group-hover:opacity-100">
|
||||
{currentUser ? `${currentUser.username} / 退出` : '退出登录'}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -113,7 +113,7 @@ describe('UserAdmin', () => {
|
||||
projects: [
|
||||
{
|
||||
id: '7',
|
||||
name: 'Data_MyVideo_1',
|
||||
name: '演视LC视频序列',
|
||||
status: 'pending',
|
||||
frames: 0,
|
||||
fps: '30FPS',
|
||||
@@ -156,7 +156,7 @@ describe('UserAdmin', () => {
|
||||
await waitFor(() => expect(apiMock.resetDemoFactory).toHaveBeenCalledWith('RESET_DEMO_FACTORY'));
|
||||
expect(await screen.findByText('演示环境已恢复出厂设置')).toBeInTheDocument();
|
||||
expect(useStore.getState().projects).toEqual([
|
||||
expect.objectContaining({ name: 'Data_MyVideo_1', source_type: 'video' }),
|
||||
expect.objectContaining({ name: '演视LC视频序列', source_type: 'video' }),
|
||||
expect.objectContaining({ name: '演示DICOM序列', source_type: 'dicom' }),
|
||||
]);
|
||||
expect(useStore.getState().frames).toEqual([]);
|
||||
|
||||
@@ -205,7 +205,7 @@ describe('api client contracts', () => {
|
||||
admin_user: { id: 1, username: 'admin', role: 'admin', is_active: 1 },
|
||||
project: { id: 8, name: '演示DICOM序列', status: 'ready', source_type: 'dicom', frame_count: 300, video_path: 'uploads/8/dicom' },
|
||||
projects: [
|
||||
{ id: 7, name: 'Data_MyVideo_1', status: 'pending', source_type: 'video', frame_count: 0, video_path: 'uploads/7/Data_MyVideo_1.mp4' },
|
||||
{ id: 7, name: '演视LC视频序列', status: 'pending', source_type: 'video', frame_count: 0, video_path: 'uploads/7/Data_MyVideo_1.mp4' },
|
||||
{ id: 8, name: '演示DICOM序列', status: 'ready', source_type: 'dicom', frame_count: 300, video_path: 'uploads/8/dicom' },
|
||||
],
|
||||
deleted_counts: { users: 1 },
|
||||
@@ -216,7 +216,7 @@ describe('api client contracts', () => {
|
||||
admin_user: expect.objectContaining({ username: 'admin' }),
|
||||
project: expect.objectContaining({ id: '8', name: '演示DICOM序列', frames: 300, source_type: 'dicom' }),
|
||||
projects: [
|
||||
expect.objectContaining({ id: '7', name: 'Data_MyVideo_1', frames: 0, source_type: 'video' }),
|
||||
expect.objectContaining({ id: '7', name: '演视LC视频序列', frames: 0, source_type: 'video' }),
|
||||
expect.objectContaining({ id: '8', name: '演示DICOM序列', frames: 300, source_type: 'dicom' }),
|
||||
],
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user