后台-个人设置
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { DownOutlined } from '@ant-design/icons';
|
import { DownOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
import { Button, Dropdown, Popconfirm, Space } from 'antd';
|
import { Button, Dropdown, Popconfirm, Space } from 'antd';
|
||||||
|
import { navigate } from '@/router/routerUtils';
|
||||||
import { useUserStore } from '@/store/UserStore';
|
import { useUserStore } from '@/store/UserStore';
|
||||||
import { GapBox } from '../GapBox';
|
import { GapBox } from '../GapBox';
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ export const HeaderUserInfo: React.FC = () => {
|
|||||||
menu={{
|
menu={{
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: 'user-info',
|
key: 'admin-info',
|
||||||
disabled: true, // 只展示,不可操作
|
disabled: true, // 只展示,不可操作
|
||||||
label: (
|
label: (
|
||||||
<Space size={8} style={{ color: 'rgba(0,0,0,0.88)' }}>
|
<Space size={8} style={{ color: 'rgba(0,0,0,0.88)' }}>
|
||||||
@@ -26,6 +27,15 @@ export const HeaderUserInfo: React.FC = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'profile',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
label: '个人信息',
|
||||||
|
onClick: () => navigate('/profile'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { Checkbox } from 'antd';
|
import { Checkbox } from 'antd';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useCompanyStore } from '@/store/CompanyStore';
|
|
||||||
import { useUserStore } from '@/store/UserStore';
|
import { useUserStore } from '@/store/UserStore';
|
||||||
|
|
||||||
const TabNavSaveCheckBoxPlugin = () => {
|
const TabNavSaveCheckBoxPlugin = () => {
|
||||||
const [value, setValue] = useState(false);
|
const [value, setValue] = useState(false);
|
||||||
const user = useUserStore().user;
|
const user = useUserStore().user;
|
||||||
const company = useCompanyStore().company;
|
const storageKey = `u${user.admin_id}_tabNavSave`;
|
||||||
const storageKey = `u${user.user_id}_c${company.company_id}_tabNavSave`;
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(localStorage.getItem(storageKey) == '1');
|
setValue(localStorage.getItem(storageKey) == '1');
|
||||||
}, []);
|
}, []);
|
||||||
@@ -19,7 +17,8 @@ const TabNavSaveCheckBoxPlugin = () => {
|
|||||||
console.log(e);
|
console.log(e);
|
||||||
setValue(e.target.checked);
|
setValue(e.target.checked);
|
||||||
localStorage.setItem(storageKey, `${e.target.checked ? 1 : 0}`);
|
localStorage.setItem(storageKey, `${e.target.checked ? 1 : 0}`);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
保留标签
|
保留标签
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Menu } from 'antd';
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Colors, DefaultERPName, headerHeight } from '@/configs/config';
|
import { Colors, DefaultERPName, headerHeight } from '@/configs/config';
|
||||||
|
import { routes } from '@/configs/routes';
|
||||||
import { getHash } from '@/router/routerUtils';
|
import { getHash } from '@/router/routerUtils';
|
||||||
import { useCompanyStore } from '@/store/CompanyStore';
|
import { useCompanyStore } from '@/store/CompanyStore';
|
||||||
import { useRefreshStore } from '@/store/RefreshStore';
|
import { useRefreshStore } from '@/store/RefreshStore';
|
||||||
@@ -27,6 +28,29 @@ const TabNavPlugin: React.FC = () => {
|
|||||||
const modeRef = useRef('');
|
const modeRef = useRef('');
|
||||||
const company = useCompanyStore().company;
|
const company = useCompanyStore().company;
|
||||||
|
|
||||||
|
function findRouteTitle(pathname: string, routes: any[], parentPath = ''): string | undefined {
|
||||||
|
for (const route of routes) {
|
||||||
|
const routePath = route.path || '';
|
||||||
|
//const fullPath = routePath.startsWith('/') ? routePath : (parentPath + '/' + routePath).replace(/\/+/g, '/');
|
||||||
|
const fullPath = routePath.startsWith('/') ? routePath : `${parentPath}/${routePath}`.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
// 如果 pathname 完全匹配当前路由,返回 title
|
||||||
|
if (route.title && pathname === fullPath) return route.title;
|
||||||
|
|
||||||
|
// 如果有子路由,递归查找
|
||||||
|
if (route.children?.length) {
|
||||||
|
const title = findRouteTitle(pathname, route.children, fullPath);
|
||||||
|
if (title) return title;
|
||||||
|
|
||||||
|
// 如果 pathname === 父路由 path 且子路由存在 title,返回第一个子路由的 title
|
||||||
|
if (pathname === fullPath && route.children[0]?.title) {
|
||||||
|
return route.children[0].title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const callback = () => {
|
const callback = () => {
|
||||||
const span = document.querySelector('.urlList-active');
|
const span = document.querySelector('.urlList-active');
|
||||||
if (urlListRef.current && span && modeRef.current != 'del') {
|
if (urlListRef.current && span && modeRef.current != 'del') {
|
||||||
@@ -102,6 +126,13 @@ const TabNavPlugin: React.FC = () => {
|
|||||||
const pathname = getHash();
|
const pathname = getHash();
|
||||||
setPathname(pathname);
|
setPathname(pathname);
|
||||||
|
|
||||||
|
const routeTitle = findRouteTitle(pathname, routes);
|
||||||
|
// 决定最终 title
|
||||||
|
const finalTitle =
|
||||||
|
routeTitle || (document.title ? `${document.title}`.replace(` - ${DefaultERPName}`, '') : pathname);
|
||||||
|
|
||||||
|
document.title = `${finalTitle} - ${DefaultERPName}`;
|
||||||
|
|
||||||
let flag = true;
|
let flag = true;
|
||||||
if ((document.title == '404' || pathname == '/temp' || pathname == '/') && !isPhone) {
|
if ((document.title == '404' || pathname == '/temp' || pathname == '/') && !isPhone) {
|
||||||
return;
|
return;
|
||||||
@@ -117,7 +148,7 @@ const TabNavPlugin: React.FC = () => {
|
|||||||
if (flag) {
|
if (flag) {
|
||||||
urlListRef.current.push({
|
urlListRef.current.push({
|
||||||
url: pathname,
|
url: pathname,
|
||||||
title: `${document.title}`.replace(` - ${DefaultERPName}`, ''),
|
title: finalTitle,
|
||||||
search: '',
|
search: '',
|
||||||
});
|
});
|
||||||
setUrlList([...urlListRef.current]);
|
setUrlList([...urlListRef.current]);
|
||||||
|
|||||||
@@ -59,5 +59,13 @@ export const routes: IRouteItem[] = [
|
|||||||
//
|
//
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/profile',
|
||||||
|
Layout: AppLayout,
|
||||||
|
children: [
|
||||||
|
{ path: '/index', Component: lazy(() => import('@/pages/Profile')), title: '个人信息' },
|
||||||
|
//
|
||||||
|
],
|
||||||
|
},
|
||||||
{ path: '*', Layout: ErrorPage, children: [] },
|
{ path: '*', Layout: ErrorPage, children: [] },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const Login = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
document.title = '登录';
|
document.title = `登录 - ${DefaultERPName}`;
|
||||||
|
|
||||||
const login = () => {
|
const login = () => {
|
||||||
request(stringify(userInfo));
|
request(stringify(userInfo));
|
||||||
@@ -48,7 +48,6 @@ const Login = () => {
|
|||||||
>
|
>
|
||||||
<div style={{ marginBottom: 24, fontSize: 20, display: 'flex', justifyContent: 'center' }}>
|
<div style={{ marginBottom: 24, fontSize: 20, display: 'flex', justifyContent: 'center' }}>
|
||||||
{DefaultERPName}
|
{DefaultERPName}
|
||||||
{document.title}
|
|
||||||
</div>
|
</div>
|
||||||
<FormPlugin>
|
<FormPlugin>
|
||||||
<FormItemPlugin allCol={24}>
|
<FormItemPlugin allCol={24}>
|
||||||
|
|||||||
101
src/pages/Profile/components/LoginLog.tsx
Normal file
101
src/pages/Profile/components/LoginLog.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import { stringify } from 'qs';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { FooterPagination } from '@/components/PaginationPlugin';
|
||||||
|
import type { ColumnsTypeUltra } from '@/components/TableColumnsFilterPlugin';
|
||||||
|
import { TablePlugin } from '@/components/TablePlugin';
|
||||||
|
import { statusObj } from '@/configs/adminLogConfig';
|
||||||
|
import type { IAjaxDataBase, IParamsBase } from '@/interfaces/common';
|
||||||
|
import { AdminLogServices } from '@/services/AdminLogServices';
|
||||||
|
import { tableFixedByPhone, toArray } from '@/utils/common';
|
||||||
|
import { useRequest } from '@/utils/useRequest';
|
||||||
|
|
||||||
|
interface IAjaxData extends IAjaxDataBase {
|
||||||
|
data: any[];
|
||||||
|
}
|
||||||
|
type IParams = IParamsBase & {
|
||||||
|
login_dateL?: string;
|
||||||
|
login_dateU?: string;
|
||||||
|
admin_id?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 登录日志页面 */
|
||||||
|
const AdminLoginLogForm: React.FC = () => {
|
||||||
|
const [params] = useState<IParams>({
|
||||||
|
curr_page: 1,
|
||||||
|
page_count: 20,
|
||||||
|
admin_id: Number(localStorage.getItem('admin_id')) || undefined,
|
||||||
|
});
|
||||||
|
const [ajaxData, setAjaxData] = useState<IAjaxData>({ count: 0, data: [] });
|
||||||
|
|
||||||
|
const { loading: listLoading, request: listRequest } = useRequest(AdminLogServices.getAdminLoginLogAjaxList, {
|
||||||
|
onSuccessCodeZero: (res) => {
|
||||||
|
setAjaxData({
|
||||||
|
count: res.count || 0,
|
||||||
|
data: toArray(res.data),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const columns: ColumnsTypeUltra<any> = [
|
||||||
|
{
|
||||||
|
title: '#',
|
||||||
|
width: 50,
|
||||||
|
fixed: tableFixedByPhone('left'),
|
||||||
|
align: 'center',
|
||||||
|
render(_value, _record, index) {
|
||||||
|
return index + 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: '登录账号', dataIndex: 'username', width: 80 },
|
||||||
|
{ title: '登录IP', dataIndex: 'ip', width: 100 },
|
||||||
|
{ title: '登录地址', dataIndex: 'ip_location', width: 100 },
|
||||||
|
{ title: '平台', dataIndex: 'os', width: 80 },
|
||||||
|
{ title: '浏览器', dataIndex: 'browser', width: 80 },
|
||||||
|
{ title: '登录时间', dataIndex: 'login_date', width: 120 },
|
||||||
|
{ title: '状态', dataIndex: 'status', width: 60, render: (value) => statusObj[value] },
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function page(curr: number) {
|
||||||
|
if (!listLoading) {
|
||||||
|
params.curr_page = curr;
|
||||||
|
listRequest(stringify(params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
page(1);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TablePlugin
|
||||||
|
loading={listLoading}
|
||||||
|
onChange={(_p, _f, sort: any) => {
|
||||||
|
page(1);
|
||||||
|
}}
|
||||||
|
dataSource={ajaxData.data}
|
||||||
|
columns={columns}
|
||||||
|
scroll={{ x: true }}
|
||||||
|
rowKey={'login_id'}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FooterPagination
|
||||||
|
current={params.curr_page}
|
||||||
|
pageSize={params.page_count}
|
||||||
|
total={ajaxData.count}
|
||||||
|
onChange={(curr, pageSize) => {
|
||||||
|
params.page_count = pageSize;
|
||||||
|
page(curr);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 登录日志页面 */
|
||||||
|
export default AdminLoginLogForm;
|
||||||
95
src/pages/Profile/components/OperateLog.tsx
Normal file
95
src/pages/Profile/components/OperateLog.tsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { stringify } from 'qs';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { FooterPagination } from '@/components/PaginationPlugin';
|
||||||
|
import type { ColumnsTypeUltra } from '@/components/TableColumnsFilterPlugin';
|
||||||
|
import { TablePlugin } from '@/components/TablePlugin';
|
||||||
|
import { statusObj } from '@/configs/adminLogConfig';
|
||||||
|
import type { IAjaxDataBase, IParamsBase } from '@/interfaces/common';
|
||||||
|
import { AdminLogServices } from '@/services/AdminLogServices';
|
||||||
|
import { tableFixedByPhone, toArray } from '@/utils/common';
|
||||||
|
import { useRequest } from '@/utils/useRequest';
|
||||||
|
|
||||||
|
interface IAjaxData extends IAjaxDataBase {
|
||||||
|
data: any[];
|
||||||
|
}
|
||||||
|
type IParams = IParamsBase & {
|
||||||
|
create_dateL?: string;
|
||||||
|
create_dateU?: string;
|
||||||
|
admin_id?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 操作日志页面 */
|
||||||
|
const AdminSysLogForm: React.FC = () => {
|
||||||
|
const [params] = useState<IParams>({
|
||||||
|
curr_page: 1,
|
||||||
|
page_count: 20,
|
||||||
|
admin_id: Number(localStorage.getItem('admin_id')) || undefined,
|
||||||
|
});
|
||||||
|
const [ajaxData, setAjaxData] = useState<IAjaxData>({ count: 0, data: [] });
|
||||||
|
|
||||||
|
const { loading: listLoading, request: listRequest } = useRequest(AdminLogServices.getAdminSysLogAjaxList, {
|
||||||
|
onSuccessCodeZero: (res) => {
|
||||||
|
setAjaxData({
|
||||||
|
count: res.count || 0,
|
||||||
|
data: toArray(res.data),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const columns: ColumnsTypeUltra<any> = [
|
||||||
|
{
|
||||||
|
title: '#',
|
||||||
|
width: 20,
|
||||||
|
fixed: tableFixedByPhone('left'),
|
||||||
|
align: 'center',
|
||||||
|
render(_value, _record, index) {
|
||||||
|
return index + 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: '操作账号', dataIndex: 'username', width: 50 },
|
||||||
|
{ title: '操作IP', dataIndex: 'ip', width: 100 },
|
||||||
|
{ title: '菜单', dataIndex: 'menu_ch_name', width: 80 },
|
||||||
|
{ title: '权限', dataIndex: 'function_ch_name', width: 60 },
|
||||||
|
{ title: '操作时间', dataIndex: 'create_date', width: 120 },
|
||||||
|
{ title: '状态', dataIndex: 'status', width: 60, render: (value) => statusObj[value] },
|
||||||
|
];
|
||||||
|
|
||||||
|
function page(curr: number) {
|
||||||
|
if (!listLoading) {
|
||||||
|
params.curr_page = curr;
|
||||||
|
listRequest(stringify(params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
page(1);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TablePlugin
|
||||||
|
loading={listLoading}
|
||||||
|
onChange={(_p, _f, sort: any) => {
|
||||||
|
page(1);
|
||||||
|
}}
|
||||||
|
dataSource={ajaxData.data}
|
||||||
|
columns={columns}
|
||||||
|
scroll={{ x: true }}
|
||||||
|
rowKey={'sys_id'}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FooterPagination
|
||||||
|
current={params.curr_page}
|
||||||
|
pageSize={params.page_count}
|
||||||
|
total={ajaxData.count}
|
||||||
|
onChange={(curr, pageSize) => {
|
||||||
|
params.page_count = pageSize;
|
||||||
|
page(curr);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 操作日志页面 */
|
||||||
|
export default AdminSysLogForm;
|
||||||
96
src/pages/Profile/components/ProfileEditForm.tsx
Normal file
96
src/pages/Profile/components/ProfileEditForm.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { Button, Form, Input, notification } from 'antd';
|
||||||
|
import { stringify } from 'qs';
|
||||||
|
import type React from 'react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { AdminServices } from '@/services/AdminServices';
|
||||||
|
import { useUserStore } from '@/store/UserStore';
|
||||||
|
import type { IRef } from '@/utils/type';
|
||||||
|
import { useRequest } from '@/utils/useRequest';
|
||||||
|
|
||||||
|
interface IProps extends IRef {
|
||||||
|
onCallback?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IProfileEditFormType = {
|
||||||
|
show: (data?: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProfileEditForm: React.FC<IProps> = (props) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const userInfo = useUserStore().user;
|
||||||
|
|
||||||
|
// 请求成功回调
|
||||||
|
const success = (res: any) => {
|
||||||
|
if (res.err_code === 0) {
|
||||||
|
notification.success({ message: '保存成功' });
|
||||||
|
props.onCallback?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { loading: editLoading, request: editRequest } = useRequest(AdminServices.profile, { onSuccess: success });
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
editRequest(stringify(values));
|
||||||
|
} catch (error) {
|
||||||
|
console.log('表单验证未通过', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.setFieldsValue({
|
||||||
|
username: userInfo.username,
|
||||||
|
mobile: userInfo.mobile,
|
||||||
|
email: userInfo.email,
|
||||||
|
nickname: userInfo.nickname,
|
||||||
|
});
|
||||||
|
}, [userInfo]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form form={form} labelCol={{ style: { width: window?.dfConfig?.language === 'zh-cn' ? 80 : 120 } }}>
|
||||||
|
<Form.Item label='用户名' name='username'>
|
||||||
|
<Input disabled />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label='昵称'
|
||||||
|
name='nickname'
|
||||||
|
required
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入昵称' },
|
||||||
|
{ min: 2, message: '最少2字符' },
|
||||||
|
{ max: 20, message: '最多20字符' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input allowClear placeholder='请填写昵称' maxLength={20} showCount />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label='手机号'
|
||||||
|
name='mobile'
|
||||||
|
required
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入手机号' },
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input allowClear placeholder='请填写手机号' maxLength={11} showCount />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label='邮箱' name='email' rules={[{ type: 'email', message: '邮箱格式不正确' }]}>
|
||||||
|
<Input allowClear placeholder='请填写邮箱' />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label='密码' name='password' rules={[{ min: 6, max: 18, message: '密码需6~18位' }]}>
|
||||||
|
<Input.Password allowClear placeholder='不修改请留空' />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item style={{ marginBottom: 0, textAlign: 'center' }}>
|
||||||
|
<Button type='primary' size='large' onClick={save} loading={editLoading}>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
69
src/pages/Profile/index.tsx
Normal file
69
src/pages/Profile/index.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { Card, Col, Row, Tabs } from 'antd';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin';
|
||||||
|
import LoginLog from './components/LoginLog';
|
||||||
|
import OperateLog from './components/OperateLog';
|
||||||
|
import { type IProfileEditFormType, ProfileEditForm } from './components/ProfileEditForm';
|
||||||
|
|
||||||
|
/** 管理员个人信息页面 */
|
||||||
|
const ProfileForm: React.FC = () => {
|
||||||
|
const ProfileEditFormRef = useRef<IProfileEditFormType>(null);
|
||||||
|
const [activeTab, setActiveTab] = useState<string>('login');
|
||||||
|
|
||||||
|
const getTabStorageKey = () => {
|
||||||
|
const admin_id = localStorage.getItem('admin_id') || 'guest';
|
||||||
|
return `log_list_active_tab_${admin_id}`;
|
||||||
|
};
|
||||||
|
const onTabChange = (key: string) => {
|
||||||
|
setActiveTab(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 进入页面时恢复上一次 tab */
|
||||||
|
useEffect(() => {
|
||||||
|
const lastTab = localStorage.getItem(getTabStorageKey());
|
||||||
|
if (lastTab) {
|
||||||
|
setActiveTab(lastTab);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row gutter={16}>
|
||||||
|
{/* 左:个人信息编辑 */}
|
||||||
|
<Col span={8}>
|
||||||
|
<Card title='个人信息'>
|
||||||
|
<ProfileEditForm ref={ProfileEditFormRef} />
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
{/* 右:日志 */}
|
||||||
|
<Col span={16}>
|
||||||
|
<Card title='日志记录'>
|
||||||
|
<Tabs
|
||||||
|
type='line'
|
||||||
|
activeKey={activeTab}
|
||||||
|
onChange={onTabChange}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'login',
|
||||||
|
label: '登录日志',
|
||||||
|
children: <LoginLog />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'operate',
|
||||||
|
label: '操作日志',
|
||||||
|
children: <OperateLog />,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Profile = () => (
|
||||||
|
<PageContainerPlugin breadcrumb={['个人中心', '个人信息']}>
|
||||||
|
<ProfileForm />
|
||||||
|
</PageContainerPlugin>
|
||||||
|
);
|
||||||
|
export default Profile;
|
||||||
@@ -7,4 +7,10 @@ export const AdminLogServices = {
|
|||||||
getAdminMenuAjaxList: '/AdminMenu/getAdminMenuAjaxList',
|
getAdminMenuAjaxList: '/AdminMenu/getAdminMenuAjaxList',
|
||||||
/** 权限 */
|
/** 权限 */
|
||||||
getAdminFunctionAjaxList: '/AdminFunction/getAdminFunctionAjaxList',
|
getAdminFunctionAjaxList: '/AdminFunction/getAdminFunctionAjaxList',
|
||||||
|
|
||||||
|
/** AJAX */
|
||||||
|
/** 登录日志 */
|
||||||
|
getAdminLoginLogAjaxList: '/AdminLog/getAdminLoginLogAjaxList',
|
||||||
|
/** 操作日志 */
|
||||||
|
getAdminSysLogAjaxList: '/AdminLog/getAdminSysLogAjaxList',
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export const AdminServices = {
|
|||||||
add: '/Admin/add',
|
add: '/Admin/add',
|
||||||
edit: '/Admin/edit',
|
edit: '/Admin/edit',
|
||||||
del: '/Admin/del',
|
del: '/Admin/del',
|
||||||
|
/** 个人设置 */
|
||||||
|
profile: '/Admin/profile',
|
||||||
/** 登录管理员权限列表 */
|
/** 登录管理员权限列表 */
|
||||||
auth: '/Admin/auth',
|
auth: '/Admin/auth',
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user