可用版本

This commit is contained in:
戚辰先生 2024-11-30 17:41:04 +08:00
parent b01d9a630a
commit 7efbb63d31
9 changed files with 371 additions and 359 deletions

View File

@ -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({

View File

@ -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}>

View File

@ -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="昵称"