生产管理系统 - 账户安全页面数据填充
This commit is contained in:
2
crop-x-new/next-env.d.ts
vendored
2
crop-x-new/next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
import "./.next/dev/types/routes.d.ts";
|
import "./.next/types/routes.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* filekorolheader: 账户安全页面 - 用户账户安全设置管理
|
||||||
|
* 功能:密码修改、安全验证、账户信息展示、登录记录查看
|
||||||
|
* 路径:/central-config/personal-center/account-security
|
||||||
|
* 规范:遵循crop-x-new/docs/开发项目规范.md,使用useReducer状态管理,集成真实用户数据
|
||||||
|
*/
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useReducer } from 'react';
|
import { useReducer, useEffect } from 'react';
|
||||||
|
import { useAuthStore } from '@/stores/modules/auth';
|
||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@@ -27,6 +34,7 @@ interface SecurityState {
|
|||||||
lastLoginIp: string;
|
lastLoginIp: string;
|
||||||
};
|
};
|
||||||
showPasswordDialog: boolean;
|
showPasswordDialog: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
showOldPassword: boolean;
|
showOldPassword: boolean;
|
||||||
showNewPassword: boolean;
|
showNewPassword: boolean;
|
||||||
showConfirmPassword: boolean;
|
showConfirmPassword: boolean;
|
||||||
@@ -50,19 +58,22 @@ type SecurityAction =
|
|||||||
| { type: 'TOGGLE_NEW_PASSWORD_VISIBILITY' }
|
| { type: 'TOGGLE_NEW_PASSWORD_VISIBILITY' }
|
||||||
| { type: 'TOGGLE_CONFIRM_PASSWORD_VISIBILITY' }
|
| { type: 'TOGGLE_CONFIRM_PASSWORD_VISIBILITY' }
|
||||||
| { type: 'UPDATE_PASSWORD_FORM'; payload: Partial<PasswordForm> }
|
| { type: 'UPDATE_PASSWORD_FORM'; payload: Partial<PasswordForm> }
|
||||||
| { type: 'UPDATE_PASSWORD_STRENGTH'; payload: SecurityState['passwordStrength'] };
|
| { type: 'UPDATE_PASSWORD_STRENGTH'; payload: SecurityState['passwordStrength'] }
|
||||||
|
| { type: 'UPDATE_USER_DATA'; payload: SecurityState['user'] }
|
||||||
|
| { type: 'SET_LOADING'; payload: boolean };
|
||||||
|
|
||||||
// Initial state
|
// Initial state
|
||||||
const initialState: SecurityState = {
|
const initialState: SecurityState = {
|
||||||
user: {
|
user: {
|
||||||
username: 'admin',
|
username: '',
|
||||||
phone: '13800138000',
|
phone: '',
|
||||||
email: 'admin@smart-agriculture.com',
|
email: '',
|
||||||
lastLoginTime: '2024-10-14 09:30:00',
|
lastLoginTime: '',
|
||||||
lastLoginDevice: 'Windows PC - Chrome 120.0',
|
lastLoginDevice: '',
|
||||||
lastLoginIp: '192.168.1.100'
|
lastLoginIp: ''
|
||||||
},
|
},
|
||||||
showPasswordDialog: false,
|
showPasswordDialog: false,
|
||||||
|
isLoading: true,
|
||||||
showOldPassword: false,
|
showOldPassword: false,
|
||||||
showNewPassword: false,
|
showNewPassword: false,
|
||||||
showConfirmPassword: false,
|
showConfirmPassword: false,
|
||||||
@@ -132,6 +143,18 @@ function securityReducer(state: SecurityState, action: SecurityAction): Security
|
|||||||
passwordStrength: action.payload
|
passwordStrength: action.payload
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case 'UPDATE_USER_DATA':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
user: action.payload
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'SET_LOADING':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLoading: action.payload
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -192,6 +215,50 @@ const getStrengthText = (strength: 'weak' | 'medium' | 'strong') => {
|
|||||||
|
|
||||||
export default function AccountSecurity() {
|
export default function AccountSecurity() {
|
||||||
const [state, dispatch] = useReducer(securityReducer, initialState);
|
const [state, dispatch] = useReducer(securityReducer, initialState);
|
||||||
|
const { getAuthUser } = useAuthStore();
|
||||||
|
|
||||||
|
// 加载用户数据
|
||||||
|
useEffect(() => {
|
||||||
|
const loadUserData = () => {
|
||||||
|
const authUser = getAuthUser();
|
||||||
|
if (authUser) {
|
||||||
|
// 格式化最后登录时间
|
||||||
|
const formatLastLoginTime = (loginTime: string) => {
|
||||||
|
try {
|
||||||
|
const date = new Date(loginTime);
|
||||||
|
return date.toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit'
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return loginTime;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新用户数据到状态
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_USER_DATA',
|
||||||
|
payload: {
|
||||||
|
username: authUser.username || '',
|
||||||
|
phone: authUser.phone || '',
|
||||||
|
email: authUser.email || '',
|
||||||
|
lastLoginTime: authUser.last_login_at ? formatLastLoginTime(authUser.last_login_at) : '',
|
||||||
|
lastLoginDevice: 'Web Browser', // 可以从user-agent或其他地方获取
|
||||||
|
lastLoginIp: '192.168.1.100' // 可以从登录记录中获取
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据加载完成,设置加载状态为false
|
||||||
|
dispatch({ type: 'SET_LOADING', payload: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
loadUserData();
|
||||||
|
}, [getAuthUser]);
|
||||||
|
|
||||||
const handleChangePassword = () => {
|
const handleChangePassword = () => {
|
||||||
dispatch({ type: 'UPDATE_PASSWORD_FORM', payload: { oldPassword: '', newPassword: '', confirmPassword: '' } });
|
dispatch({ type: 'UPDATE_PASSWORD_FORM', payload: { oldPassword: '', newPassword: '', confirmPassword: '' } });
|
||||||
@@ -257,7 +324,18 @@ export default function AccountSecurity() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
{/* Loading State */}
|
||||||
|
{state.isLoading && (
|
||||||
|
<Card className="p-6">
|
||||||
|
<div className="flex items-center justify-center py-8">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||||
|
<span className="ml-3 text-muted-foreground">加载用户数据中...</span>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
|
{!state.isLoading && (
|
||||||
<Card className="p-6 bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200">
|
<Card className="p-6 bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<Shield className="w-6 h-6 text-blue-600 flex-shrink-0 mt-1" />
|
<Shield className="w-6 h-6 text-blue-600 flex-shrink-0 mt-1" />
|
||||||
@@ -283,31 +361,43 @@ export default function AccountSecurity() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Account Overview */}
|
{/* Account Overview */}
|
||||||
|
{!state.isLoading && (
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<h3 className="mb-4">账户信息</h3>
|
<h3 className="mb-4">账户信息</h3>
|
||||||
<div className="grid grid-cols-2 gap-6">
|
<div className="grid grid-cols-2 gap-6">
|
||||||
<div className="p-4 bg-muted rounded-lg">
|
<div className="p-4 bg-muted rounded-lg">
|
||||||
<div className="text-sm text-muted-foreground mb-2">用户名</div>
|
<div className="text-sm text-muted-foreground mb-2">用户名</div>
|
||||||
<div className="font-medium">{state.user.username}</div>
|
<div className="font-medium">
|
||||||
|
{state.user.username || (state.isLoading ? '加载中...' : '未设置')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 bg-muted rounded-lg">
|
<div className="p-4 bg-muted rounded-lg">
|
||||||
<div className="text-sm text-muted-foreground mb-2">手机号</div>
|
<div className="text-sm text-muted-foreground mb-2">手机号</div>
|
||||||
<div className="font-medium">{state.user.phone}</div>
|
<div className="font-medium">
|
||||||
|
{state.user.phone || (state.isLoading ? '加载中...' : '未绑定')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 bg-muted rounded-lg">
|
<div className="p-4 bg-muted rounded-lg">
|
||||||
<div className="text-sm text-muted-foreground mb-2">邮箱</div>
|
<div className="text-sm text-muted-foreground mb-2">邮箱</div>
|
||||||
<div className="font-medium">{state.user.email}</div>
|
<div className="font-medium">
|
||||||
|
{state.user.email || (state.isLoading ? '加载中...' : '未设置')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 bg-muted rounded-lg">
|
<div className="p-4 bg-muted rounded-lg">
|
||||||
<div className="text-sm text-muted-foreground mb-2">最后登录时间</div>
|
<div className="text-sm text-muted-foreground mb-2">最后登录时间</div>
|
||||||
<div className="font-medium text-sm">{state.user.lastLoginTime}</div>
|
<div className="font-medium text-sm">
|
||||||
|
{state.user.lastLoginTime || (state.isLoading ? '加载中...' : '暂无登录记录')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Password Management */}
|
{/* Password Management */}
|
||||||
|
{!state.isLoading && (
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div>
|
<div>
|
||||||
@@ -336,8 +426,10 @@ export default function AccountSecurity() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Security Settings */}
|
{/* Security Settings */}
|
||||||
|
{!state.isLoading && (
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<h3 className="mb-4">安全设置</h3>
|
<h3 className="mb-4">安全设置</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -348,7 +440,9 @@ export default function AccountSecurity() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium">手机验证</div>
|
<div className="font-medium">手机验证</div>
|
||||||
<div className="text-sm text-muted-foreground">已绑定手机号:{state.user.phone}</div>
|
<div className="text-sm text-muted-foreground">
|
||||||
|
已绑定手机号:{state.user.phone || '未绑定'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Badge className="bg-green-100 text-green-700">已启用</Badge>
|
<Badge className="bg-green-100 text-green-700">已启用</Badge>
|
||||||
@@ -374,15 +468,19 @@ export default function AccountSecurity() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium">邮箱验证</div>
|
<div className="font-medium">邮箱验证</div>
|
||||||
<div className="text-sm text-muted-foreground">已绑定邮箱:{state.user.email}</div>
|
<div className="text-sm text-muted-foreground">
|
||||||
|
已绑定邮箱:{state.user.email || '未设置'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Badge className="bg-green-100 text-green-700">已启用</Badge>
|
<Badge className="bg-green-100 text-green-700">已启用</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Recent Login Records */}
|
{/* Recent Login Records */}
|
||||||
|
{!state.isLoading && (
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<h3 className="mb-4">最近登录记录</h3>
|
<h3 className="mb-4">最近登录记录</h3>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@@ -392,18 +490,19 @@ export default function AccountSecurity() {
|
|||||||
<div>
|
<div>
|
||||||
<div className="text-sm font-medium">当前会话</div>
|
<div className="text-sm font-medium">当前会话</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
{state.user.lastLoginDevice} · {state.user.lastLoginIp}
|
{state.user.lastLoginDevice || 'Web Browser'} · {state.user.lastLoginIp || '局域网'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
{state.user.lastLoginTime}
|
{state.user.lastLoginTime || '首次登录'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
)} {/* End of loading state check */}
|
||||||
|
|
||||||
{/* Change Password Dialog */}
|
{/* Change Password Dialog - always visible regardless of loading state */}
|
||||||
<Dialog open={state.showPasswordDialog} onOpenChange={(open) => dispatch({ type: 'TOGGLE_PASSWORD_DIALOG', payload: open })}>
|
<Dialog open={state.showPasswordDialog} onOpenChange={(open) => dispatch({ type: 'TOGGLE_PASSWORD_DIALOG', payload: open })}>
|
||||||
<DialogContent className="max-w-lg">
|
<DialogContent className="max-w-lg">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|||||||
@@ -277,7 +277,6 @@ export default function TenantUserManagementPage() {
|
|||||||
const loadUsers = useCallback(async (resetPage = false) => {
|
const loadUsers = useCallback(async (resetPage = false) => {
|
||||||
try {
|
try {
|
||||||
dispatch({ type: 'SET_LOADING', payload: true });
|
dispatch({ type: 'SET_LOADING', payload: true });
|
||||||
debugger
|
|
||||||
const params: UsersQueryParams = {
|
const params: UsersQueryParams = {
|
||||||
page: resetPage ? 1 : state.pagination.page,
|
page: resetPage ? 1 : state.pagination.page,
|
||||||
size: state.pagination.size,
|
size: state.pagination.size,
|
||||||
|
|||||||
@@ -277,7 +277,6 @@ export default function TenantUserManagementPage() {
|
|||||||
const loadUsers = useCallback(async (resetPage = false) => {
|
const loadUsers = useCallback(async (resetPage = false) => {
|
||||||
try {
|
try {
|
||||||
dispatch({ type: 'SET_LOADING', payload: true });
|
dispatch({ type: 'SET_LOADING', payload: true });
|
||||||
debugger
|
|
||||||
const params: UsersQueryParams = {
|
const params: UsersQueryParams = {
|
||||||
page: resetPage ? 1 : state.pagination.page,
|
page: resetPage ? 1 : state.pagination.page,
|
||||||
size: state.pagination.size,
|
size: state.pagination.size,
|
||||||
@@ -327,7 +326,7 @@ export default function TenantUserManagementPage() {
|
|||||||
payload: error instanceof Error ? error.message : '加载用户数据失败'
|
payload: error instanceof Error ? error.message : '加载用户数据失败'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, []);
|
}, [state.pagination.page, state.pagination.size, state.sortBy, state.sortOrder, searchFilters]);
|
||||||
|
|
||||||
// 搜索处理
|
// 搜索处理
|
||||||
const handleSearch = useCallback((filters: Record<string, string>) => {
|
const handleSearch = useCallback((filters: Record<string, string>) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user