生产管理系统 - 账户安全页面数据填充

This commit is contained in:
2025-11-10 10:51:27 +08:00
parent caae0492ee
commit bb517741b9
4 changed files with 145 additions and 48 deletions

View File

@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <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
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@@ -1,6 +1,13 @@
/**
* filekorolheader: 账户安全页面 - 用户账户安全设置管理
* 功能:密码修改、安全验证、账户信息展示、登录记录查看
* 路径:/central-config/personal-center/account-security
* 规范遵循crop-x-new/docs/开发项目规范.md使用useReducer状态管理集成真实用户数据
*/
'use client';
import { useReducer } from 'react';
import { useReducer, useEffect } from 'react';
import { useAuthStore } from '@/stores/modules/auth';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -27,6 +34,7 @@ interface SecurityState {
lastLoginIp: string;
};
showPasswordDialog: boolean;
isLoading: boolean;
showOldPassword: boolean;
showNewPassword: boolean;
showConfirmPassword: boolean;
@@ -50,19 +58,22 @@ type SecurityAction =
| { type: 'TOGGLE_NEW_PASSWORD_VISIBILITY' }
| { type: 'TOGGLE_CONFIRM_PASSWORD_VISIBILITY' }
| { 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
const initialState: SecurityState = {
user: {
username: 'admin',
phone: '13800138000',
email: 'admin@smart-agriculture.com',
lastLoginTime: '2024-10-14 09:30:00',
lastLoginDevice: 'Windows PC - Chrome 120.0',
lastLoginIp: '192.168.1.100'
username: '',
phone: '',
email: '',
lastLoginTime: '',
lastLoginDevice: '',
lastLoginIp: ''
},
showPasswordDialog: false,
isLoading: true,
showOldPassword: false,
showNewPassword: false,
showConfirmPassword: false,
@@ -132,6 +143,18 @@ function securityReducer(state: SecurityState, action: SecurityAction): Security
passwordStrength: action.payload
};
case 'UPDATE_USER_DATA':
return {
...state,
user: action.payload
};
case 'SET_LOADING':
return {
...state,
isLoading: action.payload
};
default:
return state;
}
@@ -192,6 +215,50 @@ const getStrengthText = (strength: 'weak' | 'medium' | 'strong') => {
export default function AccountSecurity() {
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 = () => {
dispatch({ type: 'UPDATE_PASSWORD_FORM', payload: { oldPassword: '', newPassword: '', confirmPassword: '' } });
@@ -257,58 +324,81 @@ export default function AccountSecurity() {
return (
<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 */}
<Card className="p-6 bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200">
<div className="flex items-start gap-3">
<Shield className="w-6 h-6 text-blue-600 flex-shrink-0 mt-1" />
<div className="flex-1">
<h2 className="text-green-800 mb-2"></h2>
<p className="text-sm text-muted-foreground mb-3">
</p>
<div className="flex flex-wrap gap-2">
<Badge variant="outline" className="bg-white">
<Lock className="w-3 h-3 mr-1" />
</Badge>
<Badge variant="outline" className="bg-white">
<Key className="w-3 h-3 mr-1" />
</Badge>
<Badge variant="outline" className="bg-white">
<Shield className="w-3 h-3 mr-1" />
</Badge>
{!state.isLoading && (
<Card className="p-6 bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200">
<div className="flex items-start gap-3">
<Shield className="w-6 h-6 text-blue-600 flex-shrink-0 mt-1" />
<div className="flex-1">
<h2 className="text-green-800 mb-2"></h2>
<p className="text-sm text-muted-foreground mb-3">
</p>
<div className="flex flex-wrap gap-2">
<Badge variant="outline" className="bg-white">
<Lock className="w-3 h-3 mr-1" />
</Badge>
<Badge variant="outline" className="bg-white">
<Key className="w-3 h-3 mr-1" />
</Badge>
<Badge variant="outline" className="bg-white">
<Shield className="w-3 h-3 mr-1" />
</Badge>
</div>
</div>
</div>
</div>
</Card>
</Card>
)}
{/* Account Overview */}
<Card className="p-6">
{!state.isLoading && (
<Card className="p-6">
<h3 className="mb-4"></h3>
<div className="grid grid-cols-2 gap-6">
<div className="p-4 bg-muted rounded-lg">
<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 className="p-4 bg-muted rounded-lg">
<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 className="p-4 bg-muted rounded-lg">
<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 className="p-4 bg-muted rounded-lg">
<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>
</Card>
)}
{/* Password Management */}
<Card className="p-6">
{!state.isLoading && (
<Card className="p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="mb-1"></h3>
@@ -336,9 +426,11 @@ export default function AccountSecurity() {
</div>
</div>
</Card>
)}
{/* Security Settings */}
<Card className="p-6">
{!state.isLoading && (
<Card className="p-6">
<h3 className="mb-4"></h3>
<div className="space-y-4">
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
@@ -348,7 +440,9 @@ export default function AccountSecurity() {
</div>
<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>
<Badge className="bg-green-100 text-green-700"></Badge>
@@ -374,16 +468,20 @@ export default function AccountSecurity() {
</div>
<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>
<Badge className="bg-green-100 text-green-700"></Badge>
</div>
</div>
</Card>
)}
{/* Recent Login Records */}
<Card className="p-6">
{!state.isLoading && (
<Card className="p-6">
<h3 className="mb-4"></h3>
<div className="space-y-3">
<div className="flex items-center justify-between p-3 bg-muted rounded-lg">
@@ -392,18 +490,19 @@ export default function AccountSecurity() {
<div>
<div className="text-sm font-medium"></div>
<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 className="text-sm text-muted-foreground">
{state.user.lastLoginTime}
{state.user.lastLoginTime || '首次登录'}
</div>
</div>
</div>
</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 })}>
<DialogContent className="max-w-lg">
<DialogHeader>

View File

@@ -277,7 +277,6 @@ export default function TenantUserManagementPage() {
const loadUsers = useCallback(async (resetPage = false) => {
try {
dispatch({ type: 'SET_LOADING', payload: true });
debugger
const params: UsersQueryParams = {
page: resetPage ? 1 : state.pagination.page,
size: state.pagination.size,

View File

@@ -277,7 +277,6 @@ export default function TenantUserManagementPage() {
const loadUsers = useCallback(async (resetPage = false) => {
try {
dispatch({ type: 'SET_LOADING', payload: true });
debugger
const params: UsersQueryParams = {
page: resetPage ? 1 : state.pagination.page,
size: state.pagination.size,
@@ -327,7 +326,7 @@ export default function TenantUserManagementPage() {
payload: error instanceof Error ? error.message : '加载用户数据失败'
});
}
}, []);
}, [state.pagination.page, state.pagination.size, state.sortBy, state.sortOrder, searchFilters]);
// 搜索处理
const handleSearch = useCallback((filters: Record<string, string>) => {