Files
deepdata/frontend/src/pages/Layout/Sidebar.tsx
T
沃巴蒂 ff22ed3a8b feat(deepdata): 引入修改密码、文件预览组件与清洗算子增强,deepflow 改名为 deep-flow
本次同步 09-12 至 09-28 的代码变更,主要内容:

新增功能
- 修改密码:后端新增 ChangePasswordRequest DTO 与UserController/UserService
  对应接口,前端新增 ChangePasswordDialog 并在 Header 接入入口
- 文件预览组件重构:file-preview 目录拆分出 VideoPreview(支持 blobUrl 直传)、
  ImagePreview、AudioPreview,index.tsx 统一调度各类型预览器
- 新增 my_ops 算子模块:markitdown_parser、mineru_pdf_parser、text_quality_evaluator
- 新增 tools/minio-file-server MinIO 文件服务

功能增强
- 数据清洗:cleaning_task_service/scheduler/routes 重构,
  data_juicer_executor 与 wrapper 增强,OperatorTable/LogsTable/TaskDetail 同步调整
- 数据标注:annotation sync.py、project.py 接口与schema 更新
- 数据采集、算子市场、首页与菜单路由相关页面调整
- DataJuicer 算子接入(useOperatorOperations、dj_ops_schema)

结构调整
- deepflow/ 更名为 deep-flow/(419 个文件重命名)
- 新增 helm 部署编排、GitHub Actions 之外的自建镜像构建脚本

未纳入本次提交
- scripts-backup/、runtime/python-executor-backup/、components_backup/(备份目录)
- *.backup*、__pycache__ 产物
- zh_core_web_sm-3.8.0-py3-none-any.whl(46MB 构建产物)
- DataX/(内嵌独立仓库,远程亦未收录其源码)
2026-10-03 22:44:56 +08:00

197 lines
6.4 KiB
TypeScript

import { memo, useEffect, useState } from "react";
import { Button, Drawer, Menu, Popover } from "antd";
import {
SettingOutlined,
} from "@ant-design/icons";
import { ClipboardList, X, ChevronRight, ChevronLeft } from "lucide-react";
import { menuItems } from "@/pages/Layout/Menu.tsx";
import { useLocation, useNavigate } from "react-router";
import TaskUpload from "./TaskUpload";
import SettingsPage from "../SettingsPage/SettingsPage";
import { useAppSelector, useAppDispatch } from "@/store/hooks";
import { showSettings, hideSettings } from "@/store/slices/settingsSlice";
import { useTranslation } from "react-i18next";
const AsiderAndHeaderLayout = () => {
const { t } = useTranslation();
const { pathname } = useLocation();
const navigate = useNavigate();
const [activeItem, setActiveItem] = useState<string>("");
const [sidebarOpen, setSidebarOpen] = useState(true);
const [taskCenterVisible, setTaskCenterVisible] = useState(false);
const settingVisible = useAppSelector((state) => state.settings.visible);
const dispatch = useAppDispatch();
// Initialize active item based on current pathname
const initActiveItem = () => {
for (let index = 0; index < menuItems.length; index++) {
const element = menuItems[index];
if (element.children) {
element.children.forEach((subItem) => {
if (pathname.includes(subItem.id)) {
setActiveItem(subItem.id);
return;
}
});
} else if (pathname.includes(element.id)) {
setActiveItem(element.id);
return;
}
}
console.log(pathname);
};
useEffect(() => {
initActiveItem();
}, [pathname]);
useEffect(() => {
const handleShowTaskPopover = (event: CustomEvent) => {
const { show } = event.detail;
setTaskCenterVisible(show);
};
window.addEventListener(
"show:task-popover",
handleShowTaskPopover as EventListener
);
return () => {
window.removeEventListener(
"show:task-popover",
handleShowTaskPopover as EventListener
);
};
}, []);
return (
<div
className={`${
sidebarOpen ? "w-64" : "w-20"
} bg-white border-r border-gray-200 transition-all duration-300 flex flex-col relative`}
>
{/* Navigation */}
<div className="flex-1">
<Menu
mode="inline"
inlineCollapsed={!sidebarOpen}
items={menuItems.map((item) => ({
key: item.id,
label: t(item.i18Key),
icon: item.icon ? <item.icon className="w-4 h-4" /> : null,
children: item.children
? item.children.map((subItem) => ({
key: subItem.id,
label: t(subItem.i18Key),
icon: subItem.icon ? (
<subItem.icon className="w-4 h-4" />
) : null,
}))
: undefined,
}))}
selectedKeys={[activeItem]}
defaultOpenKeys={["synthesis"]}
onClick={({ key }) => {
setActiveItem(key);
navigate(`/data/${key}`);
}}
/>
</div>
{/* Footer */}
<div className="p-4 border-t border-gray-200">
{sidebarOpen ? (
<div className="space-y-2">
<Popover
forceRender
title={
<div className="flex items-center justify-between gap-2 border-b border-gray-200 pb-2 mb-2">
<h4 className="font-bold">{t("common.taskCenter.title")}</h4>
<X
onClick={() => setTaskCenterVisible(false)}
className="cursor-pointer w-4 h-4 text-gray-500 hover:text-gray-900"
/>
</div>
}
open={taskCenterVisible}
content={<TaskUpload />}
trigger="click"
destroyOnHidden={false}
>
<Button block onClick={() => setTaskCenterVisible(true)}>
{t("common.taskCenter.title")}
</Button>
</Popover>
<Button
block
onClick={() => {
dispatch(showSettings());
}}
>
{t("common.settings.title")}
</Button>
</div>
) : (
<div className="space-y-2">
<div className="relative">
<Popover
forceRender
title={t("common.taskCenter.title")}
open={taskCenterVisible}
content={<TaskUpload />}
trigger="click"
destroyOnHidden={false}
>
<Button
block
onClick={() => setTaskCenterVisible(true)}
icon={<ClipboardList className="w-4 h-4" />}
></Button>
</Popover>
</div>
<Button
block
onClick={() => {
dispatch(showSettings());
}}
>
<SettingOutlined />
</Button>
</div>
)}
</div>
<Drawer
title={t("common.settings.title")}
placement="bottom"
width="100%"
height="100%"
open={settingVisible}
onClose={() => dispatch(hideSettings())}
styles={{ body: { padding: 0 } }}
destroyOnHidden={true}
>
<SettingsPage></SettingsPage>
</Drawer>
{/* 添加遮罩层,点击外部区域时关闭 */}
{taskCenterVisible && (
<div
className="fixed inset-0 z-40"
onClick={() => {
setTaskCenterVisible(false);
}}
/>
)}
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="absolute top-1/2 -right-3 -translate-y-1/2 z-10 flex h-6 w-6 items-center justify-center rounded-full bg-gray-600 text-white shadow-md hover:bg-gray-700 transition-colors"
title={sidebarOpen ? t("common.actions.openSidebar") : t("common.actions.closeSidebar")}
>
{sidebarOpen ? <ChevronLeft className="h-3.5 w-3.5" /> : <ChevronRight className="h-3.5 w-3.5" />}
</button>
</div>
);
};
export default memo(AsiderAndHeaderLayout);