可用版本
This commit is contained in:
parent
b01d9a630a
commit
7efbb63d31
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, {useEffect, useState, useCallback} from 'react';
|
||||||
import {Layout, Menu, Dropdown, Modal, message, Spin, Space, Tooltip} from 'antd';
|
import {Layout, Menu, Dropdown, Modal, message, Spin, Space, Tooltip} from 'antd';
|
||||||
import {useNavigate, useLocation, Outlet} from 'react-router-dom';
|
import {useNavigate, useLocation, Outlet} from 'react-router-dom';
|
||||||
import {useDispatch, useSelector} from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
@ -36,56 +36,58 @@ const BasicLayout: React.FC = () => {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [currentTime, setCurrentTime] = useState(dayjs());
|
const [currentTime, setCurrentTime] = useState(dayjs());
|
||||||
const [weather, setWeather] = useState({temp: '--', weather: '未知', city: '未知'});
|
const [weather, setWeather] = useState({temp: '--', weather: '未知', city: '未知'});
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
// 将天气获取逻辑提取为useCallback
|
||||||
// 每秒更新时间
|
const fetchWeather = useCallback(async () => {
|
||||||
const timer = setInterval(() => {
|
|
||||||
setCurrentTime(dayjs());
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
// 获取天气信息
|
|
||||||
const fetchWeather = async () => {
|
|
||||||
try {
|
try {
|
||||||
const data = await getWeather();
|
const data = await getWeather();
|
||||||
setWeather(data);
|
setWeather(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取天气信息失败:', error);
|
console.error('获取天气信息失败:', error);
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
// 初始化用户数据
|
// 初始化用户数据
|
||||||
|
useEffect(() => {
|
||||||
const initializeUserData = async () => {
|
const initializeUserData = async () => {
|
||||||
if (isInitialized) return;
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// 获取并更新用户信息
|
|
||||||
const userData = await getCurrentUser();
|
const userData = await getCurrentUser();
|
||||||
dispatch(setUserInfo(userData));
|
dispatch(setUserInfo(userData));
|
||||||
|
|
||||||
const menuData = await getCurrentUserMenus();
|
const menuData = await getCurrentUserMenus();
|
||||||
dispatch(setMenus(menuData));
|
dispatch(setMenus(menuData));
|
||||||
|
|
||||||
setIsInitialized(true);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('初始化用户数据失败');
|
message.error('初始化用户数据失败');
|
||||||
|
dispatch(logout());
|
||||||
|
navigate('/login', {replace: true});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchWeather();
|
if (!userInfo) {
|
||||||
initializeUserData();
|
initializeUserData();
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 每半小时更新一次天气
|
// 处理时间和天气更新
|
||||||
|
useEffect(() => {
|
||||||
|
// 每秒更新时间
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
setCurrentTime(dayjs());
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// 初始化天气并设置定时更新
|
||||||
|
fetchWeather();
|
||||||
const weatherTimer = setInterval(fetchWeather, 30 * 60 * 1000);
|
const weatherTimer = setInterval(fetchWeather, 30 * 60 * 1000);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
clearInterval(weatherTimer);
|
clearInterval(weatherTimer);
|
||||||
};
|
};
|
||||||
}, [isInitialized]);
|
}, [fetchWeather]);
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
confirm({
|
confirm({
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Card, Row, Col, Statistic } from 'antd';
|
import { Card, Row, Col, Statistic } from 'antd';
|
||||||
import { UserOutlined, ShoppingCartOutlined, FileOutlined } from '@ant-design/icons';
|
import { UserOutlined, ShoppingCartOutlined, FileOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
const Dashboard: React.FC = () => {
|
const Dashboard: React.FC = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
// 检查这里的数据加载逻辑
|
||||||
|
// 是否有多个useEffect都在加载相同的数据
|
||||||
|
// 或者依赖项是否设置正确
|
||||||
|
}, []); // 检查依赖项
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Table, Button, Modal, Form, Input, Space, message, Switch, TreeSelect } from 'antd';
|
import { Table, Button, Modal, Form, Input, Space, message, Switch, TreeSelect } from 'antd';
|
||||||
import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TeamOutlined } from '@ant-design/icons';
|
import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TeamOutlined } from '@ant-design/icons';
|
||||||
import type { UserResponse } from './types';
|
import type { UserResponse, Role } from './types';
|
||||||
import type { DepartmentDTO } from '../Department/types';
|
import type { DepartmentDTO } from '../Department/types';
|
||||||
import { getUsers, createUser, updateUser, deleteUser, resetPassword } from './service';
|
import { getUsers, createUser, updateUser, deleteUser, resetPassword } from './service';
|
||||||
import { getDepartmentTree } from '../Department/service';
|
import { getDepartmentTree } from '../Department/service';
|
||||||
import { convertToPageParams, convertToPageInfo } from '@/utils/page';
|
import { convertToPageParams, convertToPageInfo } from '@/utils/page';
|
||||||
import { useTableData } from '@/hooks/useTableData';
|
import { useTableData } from '@/hooks/useTableData';
|
||||||
|
import type { FixedType, AlignType } from 'rc-table/lib/interface';
|
||||||
// import RoleModal from './components/RoleModal';
|
// import RoleModal from './components/RoleModal';
|
||||||
|
|
||||||
const UserPage: React.FC = () => {
|
const UserPage: React.FC = () => {
|
||||||
@ -45,10 +46,6 @@ const UserPage: React.FC = () => {
|
|||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [passwordForm] = Form.useForm();
|
const [passwordForm] = Form.useForm();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchUsers();
|
|
||||||
}, [pagination.current, pagination.pageSize]);
|
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
setEditingUser(null);
|
setEditingUser(null);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
@ -61,8 +58,7 @@ const UserPage: React.FC = () => {
|
|||||||
const handleEdit = (record: UserResponse) => {
|
const handleEdit = (record: UserResponse) => {
|
||||||
setEditingUser(record);
|
setEditingUser(record);
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
...record,
|
...record
|
||||||
password: 'UNCHANGED_PASSWORD'
|
|
||||||
});
|
});
|
||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
};
|
};
|
||||||
@ -77,7 +73,7 @@ const UserPage: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const values = await passwordForm.validateFields();
|
const values = await passwordForm.validateFields();
|
||||||
if (editingUser) {
|
if (editingUser) {
|
||||||
await resetPassword(editingUser.id);
|
await resetPassword(editingUser.id, values.password);
|
||||||
message.success('密码重置成功');
|
message.success('密码重置成功');
|
||||||
setResetPasswordModalVisible(false);
|
setResetPasswordModalVisible(false);
|
||||||
}
|
}
|
||||||
@ -92,7 +88,6 @@ const UserPage: React.FC = () => {
|
|||||||
if (editingUser) {
|
if (editingUser) {
|
||||||
await updateUser(editingUser.id, {
|
await updateUser(editingUser.id, {
|
||||||
...values,
|
...values,
|
||||||
password: values.password === 'UNCHANGED_PASSWORD' ? editingUser.password : values.password,
|
|
||||||
version: editingUser.version
|
version: editingUser.version
|
||||||
});
|
});
|
||||||
message.success('更新成功');
|
message.success('更新成功');
|
||||||
@ -121,77 +116,86 @@ const UserPage: React.FC = () => {
|
|||||||
// };
|
// };
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
width: 80,
|
||||||
|
fixed: 'left' as FixedType
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '用户名',
|
title: '用户名',
|
||||||
dataIndex: 'username',
|
dataIndex: 'username',
|
||||||
key: 'username',
|
key: 'username',
|
||||||
width: '12%',
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '昵称',
|
title: '昵称',
|
||||||
dataIndex: 'nickname',
|
dataIndex: 'nickname',
|
||||||
key: 'nickname',
|
key: 'nickname',
|
||||||
width: '12%',
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '邮箱',
|
title: '邮箱',
|
||||||
dataIndex: 'email',
|
dataIndex: 'email',
|
||||||
key: 'email',
|
key: 'email',
|
||||||
width: '15%',
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '手机号',
|
title: '手机号',
|
||||||
dataIndex: 'phone',
|
dataIndex: 'phone',
|
||||||
key: 'phone',
|
key: 'phone',
|
||||||
width: '12%',
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '角色',
|
title: '角色',
|
||||||
dataIndex: 'roles',
|
dataIndex: 'roles',
|
||||||
key: 'roles',
|
key: 'roles',
|
||||||
width: '15%',
|
width: 150,
|
||||||
|
ellipsis: true,
|
||||||
render: (roles: Role[]) => roles?.map(role => role.name).join(', ') || '-'
|
render: (roles: Role[]) => roles?.map(role => role.name).join(', ') || '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
dataIndex: 'createTime',
|
dataIndex: 'createTime',
|
||||||
key: 'createTime',
|
key: 'createTime',
|
||||||
width: '15%',
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '更新时间',
|
||||||
|
dataIndex: 'updateTime',
|
||||||
|
key: 'updateTime',
|
||||||
|
width: 180,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'enabled',
|
dataIndex: 'enabled',
|
||||||
key: 'enabled',
|
key: 'enabled',
|
||||||
width: '8%',
|
width: 80,
|
||||||
|
align: 'center' as AlignType,
|
||||||
render: (enabled: boolean) => (
|
render: (enabled: boolean) => (
|
||||||
<Switch checked={enabled} disabled />
|
<Switch checked={enabled} disabled size="small" />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: '280px',
|
width: 180,
|
||||||
|
fixed: 'right' as FixedType,
|
||||||
render: (_: any, record: UserResponse) => (
|
render: (_: any, record: UserResponse) => (
|
||||||
<Space>
|
<Space size="small">
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
|
size="small"
|
||||||
icon={<EditOutlined />}
|
icon={<EditOutlined />}
|
||||||
onClick={() => handleEdit(record)}
|
onClick={() => handleEdit(record)}
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
{/* 注释掉角色分配按钮
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
icon={<TeamOutlined />}
|
|
||||||
onClick={() => handleAssignRoles(record)}
|
|
||||||
disabled={record.username === 'admin'}
|
|
||||||
>
|
|
||||||
分配角色
|
|
||||||
</Button>
|
|
||||||
*/}
|
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
|
size="small"
|
||||||
icon={<KeyOutlined />}
|
icon={<KeyOutlined />}
|
||||||
onClick={() => handleResetPassword(record)}
|
onClick={() => handleResetPassword(record)}
|
||||||
>
|
>
|
||||||
@ -199,6 +203,7 @@ const UserPage: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
|
size="small"
|
||||||
danger
|
danger
|
||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
onClick={() => handleDelete(record.id)}
|
onClick={() => handleDelete(record.id)}
|
||||||
@ -224,12 +229,25 @@ const UserPage: React.FC = () => {
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={users}
|
dataSource={users}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
|
scroll={{ x: 1500 }}
|
||||||
pagination={{
|
pagination={{
|
||||||
...pagination,
|
...pagination,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
showTotal: (total) => `共 ${total} 条记录`,
|
showTotal: (total) => `共 ${total} 条记录`,
|
||||||
onChange: onPageChange
|
onChange: onPageChange,
|
||||||
|
size: 'small'
|
||||||
|
}}
|
||||||
|
rowSelection={{
|
||||||
|
type: 'checkbox',
|
||||||
|
onChange: (selectedRowKeys, selectedRows) => {
|
||||||
|
console.log('selectedRowKeys:', selectedRowKeys);
|
||||||
|
console.log('selectedRows:', selectedRows);
|
||||||
|
},
|
||||||
|
getCheckboxProps: (record) => ({
|
||||||
|
disabled: record.username === 'admin',
|
||||||
|
}),
|
||||||
|
fixed: true,
|
||||||
}}
|
}}
|
||||||
size="middle"
|
size="middle"
|
||||||
bordered
|
bordered
|
||||||
@ -254,20 +272,6 @@ const UserPage: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<Input placeholder="请输入用户名" disabled={!!editingUser} />
|
<Input placeholder="请输入用户名" disabled={!!editingUser} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{editingUser && (
|
|
||||||
<Form.Item name="password" hidden>
|
|
||||||
<Input type="hidden" />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
{!editingUser && (
|
|
||||||
<Form.Item
|
|
||||||
name="password"
|
|
||||||
label="密码"
|
|
||||||
rules={[{ required: true, message: '请输入密码' }]}
|
|
||||||
>
|
|
||||||
<Input.Password placeholder="请输入密码" />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="nickname"
|
name="nickname"
|
||||||
label="昵称"
|
label="昵称"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user