chore: 初始化仓库基线(AGENTS.md、git 规范、敏感文件排除)

This commit is contained in:
weijuesen
2026-08-10 22:30:53 +08:00
commit 84abf4454c
358 changed files with 75993 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { App, Dropdown, Form, Input, Modal, Space, theme } from 'antd';
import {
DashboardOutlined,
HomeOutlined,
ControlOutlined,
BellOutlined,
VideoCameraOutlined,
FileTextOutlined,
SettingOutlined,
LogoutOutlined,
UserOutlined,
ThunderboltOutlined,
ApiOutlined,
ControlOutlined as ControlOutlinedIcon,
KeyOutlined,
} from '@ant-design/icons';
import { ProLayout } from '@ant-design/pro-components';
import { useState } from 'react';
import { authService } from '../services/auth';
const menuData = [
{ path: '/dashboard', name: '仪表盘', icon: <DashboardOutlined />, permission: 'dashboard:view' },
{ path: '/houses', name: '蚕房管理', icon: <HomeOutlined />, permission: 'room:read' },
{ path: '/devices', name: '设备管理', icon: <ControlOutlined />, permission: 'device:read' },
{ path: '/energy', name: '能耗管理', icon: <ThunderboltOutlined />, permission: 'energy:view' },
{ path: '/control', name: '设备控制', icon: <ApiOutlined />, permission: 'device:control' },
{ path: '/ir', name: '红外遥控', icon: <ControlOutlinedIcon />, permission: 'device:control' },
{ path: '/thresholds', name: '阈值配置', icon: <SettingOutlined />, permission: 'threshold:read' },
{ path: '/alerts', name: '告警中心', icon: <BellOutlined />, permission: 'alarm:read' },
{ path: '/videos', name: '视频监控', icon: <VideoCameraOutlined />, permission: 'video:read' },
{ path: '/logs', name: '控制日志', icon: <FileTextOutlined />, permission: 'log:read' },
];
// 角色中文名
const roleNames: Record<string, string> = {
admin: '管理员',
operator: '操作员',
viewer: '查看者',
farmer: '养殖员',
};
export const BasicLayout = () => {
const location = useLocation();
const navigate = useNavigate();
const { message } = App.useApp();
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
const [pwdOpen, setPwdOpen] = useState(false);
const [pwdForm] = Form.useForm<{ oldPassword: string; newPassword: string; confirm: string }>();
const [pwdLoading, setPwdLoading] = useState(false);
const handleChangePassword = async () => {
const v = await pwdForm.validateFields();
if (v.newPassword !== v.confirm) {
message.error('两次输入的新密码不一致');
return;
}
setPwdLoading(true);
try {
await authService.changePassword({ oldPassword: v.oldPassword, newPassword: v.newPassword });
message.success('密码修改成功,请重新登录');
setPwdOpen(false);
pwdForm.resetFields();
await authService.logout();
navigate('/login');
} catch (err: any) {
const msg = err?.response?.data?.error || '密码修改失败';
message.error(typeof msg === 'string' ? msg : '密码修改失败');
} finally {
setPwdLoading(false);
}
};
return (
<ProLayout
title="蚕房环境监控"
layout="mix"
navTheme="light"
contentStyle={{ margin: 0, padding: 0 }}
menuItemRender={(item, dom) => (
<div
onClick={() => item.path && navigate(item.path)}
style={{ cursor: 'pointer' }}
>
{dom}
</div>
)}
menuDataRender={() => menuData.filter((m) => authService.hasPermission(m.permission))}
location={{ pathname: location.pathname }}
headerTitleRender={(logo, title) => (
<a onClick={() => navigate('/dashboard')}>
<Space>{logo}{title}</Space>
</a>
)}
avatarProps={{
icon: <UserOutlined />,
size: 'small',
title: (() => {
const user = authService.getUser();
return user ? `${user.username}${roleNames[user.role ?? ''] ?? user.role}` : 'admin';
})(),
render: (_, dom) => (
<Dropdown
menu={{
items: [
{ key: 'changePassword', icon: <KeyOutlined />, label: '修改密码' },
{ type: 'divider' },
{ key: 'logout', icon: <LogoutOutlined />, label: '退出登录' },
],
onClick: async ({ key }) => {
if (key === 'logout') {
await authService.logout();
navigate('/login');
} else if (key === 'changePassword') {
pwdForm.resetFields();
setPwdOpen(true);
}
},
}}
>
{dom}
</Dropdown>
),
}}
>
<div
style={{
margin: 16,
padding: 16,
background: colorBgContainer,
borderRadius: borderRadiusLG,
minHeight: 280,
}}
>
<Outlet />
</div>
<Modal
title="修改密码"
open={pwdOpen}
onCancel={() => setPwdOpen(false)}
onOk={handleChangePassword}
confirmLoading={pwdLoading}
okText="提交"
cancelText="取消"
>
<Form form={pwdForm} layout="vertical">
<Form.Item label="原密码" name="oldPassword" rules={[{ required: true, message: '请输入原密码' }]}>
<Input.Password autoComplete="current-password" placeholder="请输入原密码" />
</Form.Item>
<Form.Item label="新密码" name="newPassword" rules={[{ required: true, message: '请输入新密码' }, { min: 6, message: '至少 6 位' }]}>
<Input.Password autoComplete="new-password" placeholder="至少 6 位" />
</Form.Item>
<Form.Item label="确认新密码" name="confirm" rules={[{ required: true, message: '请再次输入新密码' }]}>
<Input.Password autoComplete="new-password" placeholder="再次输入新密码" />
</Form.Item>
</Form>
</Modal>
</ProLayout>
);
};