优化了登录页
parent
72000d5660
commit
ce9ccd70ef
|
|
@ -249,7 +249,7 @@ async def get_document_activity(
|
|||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""获取指定日期的文档操作日志"""
|
||||
"""获取指定日期的文档操作日志(按项目+文件+操作类型聚合)"""
|
||||
# 解析日期
|
||||
try:
|
||||
target_date = datetime.strptime(date_str, "%Y-%m-%d").date()
|
||||
|
|
@ -284,14 +284,51 @@ async def get_document_activity(
|
|||
|
||||
logs = result.scalars().all()
|
||||
|
||||
# 构建返回数据,包含项目信息
|
||||
logs_data = []
|
||||
# 操作类型中文映射
|
||||
operation_map = {
|
||||
OperationType.CREATE_FILE: "创建",
|
||||
OperationType.SAVE_FILE: "保存",
|
||||
OperationType.DELETE_FILE: "删除",
|
||||
OperationType.RENAME_FILE: "重命名",
|
||||
OperationType.MOVE_FILE: "移动",
|
||||
}
|
||||
|
||||
# 聚合日志:按 (project_id, file_path, operation_type) 分组
|
||||
aggregated = {}
|
||||
|
||||
for log in logs:
|
||||
# 解析 detail 字段获取文件路径和项目ID
|
||||
detail = json.loads(log.detail) if log.detail else {}
|
||||
project_id = detail.get('project_id')
|
||||
file_path = detail.get('path') or detail.get('file_path') or detail.get('old_path')
|
||||
|
||||
# 创建聚合键
|
||||
key = (project_id, file_path, log.operation_type)
|
||||
|
||||
if key not in aggregated:
|
||||
aggregated[key] = {
|
||||
'project_id': project_id,
|
||||
'file_path': file_path,
|
||||
'operation_type': log.operation_type,
|
||||
'count': 0,
|
||||
'first_time': log.created_at,
|
||||
'last_time': log.created_at,
|
||||
}
|
||||
|
||||
aggregated[key]['count'] += 1
|
||||
# 更新最早和最晚时间
|
||||
if log.created_at < aggregated[key]['first_time']:
|
||||
aggregated[key]['first_time'] = log.created_at
|
||||
if log.created_at > aggregated[key]['last_time']:
|
||||
aggregated[key]['last_time'] = log.created_at
|
||||
|
||||
# 构建返回数据,包含项目信息
|
||||
logs_data = []
|
||||
for key, agg in aggregated.items():
|
||||
project_id = agg['project_id']
|
||||
file_path = agg['file_path']
|
||||
operation_type = agg['operation_type']
|
||||
|
||||
# 获取项目信息
|
||||
project_name = None
|
||||
project_storage_key = None
|
||||
|
|
@ -306,29 +343,33 @@ async def get_document_activity(
|
|||
|
||||
# 检查文件是否存在(仅针对非删除操作)
|
||||
file_exists = False
|
||||
if project_storage_key and file_path and log.operation_type != OperationType.DELETE_FILE:
|
||||
if project_storage_key and file_path and operation_type != OperationType.DELETE_FILE:
|
||||
full_path = os.path.join(settings.PROJECTS_PATH, project_storage_key, file_path)
|
||||
file_exists = os.path.exists(full_path) and os.path.isfile(full_path)
|
||||
|
||||
# 操作类型中文映射
|
||||
operation_map = {
|
||||
OperationType.CREATE_FILE: "创建文件",
|
||||
OperationType.SAVE_FILE: "保存文件",
|
||||
OperationType.DELETE_FILE: "删除文件",
|
||||
OperationType.RENAME_FILE: "重命名文件",
|
||||
OperationType.MOVE_FILE: "移动文件",
|
||||
}
|
||||
# 生成描述文本
|
||||
operation_text = operation_map.get(operation_type, operation_type)
|
||||
if agg['count'] > 1:
|
||||
description = f"{operation_text} {agg['count']} 次"
|
||||
else:
|
||||
description = operation_text
|
||||
|
||||
logs_data.append({
|
||||
"id": log.id,
|
||||
"operation_type": operation_map.get(log.operation_type, log.operation_type),
|
||||
"id": f"{project_id}_{file_path}_{operation_type}", # 唯一ID
|
||||
"operation_type": description,
|
||||
"operation_count": agg['count'],
|
||||
"project_id": project_id,
|
||||
"project_name": project_name or "未知项目",
|
||||
"file_path": file_path or "未知文件",
|
||||
"file_exists": file_exists,
|
||||
"created_at": log.created_at.isoformat() if log.created_at else None,
|
||||
"detail": detail,
|
||||
"first_time": agg['first_time'].isoformat() if agg['first_time'] else None,
|
||||
"last_time": agg['last_time'].isoformat() if agg['last_time'] else None,
|
||||
"created_at": agg['last_time'].isoformat() if agg['last_time'] else None, # 用最后操作时间排序
|
||||
})
|
||||
|
||||
# 按最后操作时间降序排序
|
||||
logs_data.sort(key=lambda x: x['last_time'] if x['last_time'] else '', reverse=True)
|
||||
|
||||
return success_response(data={"logs": logs_data})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# NEX Docus Frontend
|
||||
# NexDocus Frontend
|
||||
|
||||
NEX Docus 前端项目 - 基于 React + Vite + Ant Design 构建。
|
||||
NexDocus 前端项目 - 基于 React + Vite + Ant Design 构建。
|
||||
|
||||
## 技术栈
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>NEX Docus - 文档管理平台</title>
|
||||
<title>NexDocus - 文档管理平台</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ function AppHeader({ collapsed, onToggle }) {
|
|||
<div className="header-left">
|
||||
{/* Logo 区域 */}
|
||||
<div className="header-logo">
|
||||
<h2 style={{ margin: 0, color: '#1677ff', fontWeight: 'bold' }}>NEX Docus</h2>
|
||||
<h2 style={{ margin: 0, color: '#1677ff', fontWeight: 'bold' }}>NexDocus</h2>
|
||||
</div>
|
||||
|
||||
{/* 折叠按钮 */}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,269 @@
|
|||
.login-container {
|
||||
min-height: 100vh;
|
||||
.login-page {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* 左侧介绍区域 */
|
||||
.login-left {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #e6f0ff 0%, #f0f5ff 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 60px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 400px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
.login-left-content {
|
||||
max-width: 560px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
/* Logo */
|
||||
.logo-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #1890ff;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 介绍内容 */
|
||||
.intro-section {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
color: #1f1f1f;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
.intro-title .highlight {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.intro-desc {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-top: 8px;
|
||||
/* 底部信息 */
|
||||
.footer-info {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.footer-links span {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.footer-links span::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #1890ff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 右侧登录区域 */
|
||||
.login-right {
|
||||
flex: 0 0 520px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.login-form-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.login-form-container .ant-tabs-nav {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.login-form-container .ant-tabs-tab {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.form-wrapper {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.form-subtitle {
|
||||
font-size: 14px;
|
||||
color: #8c8c8c;
|
||||
margin-bottom: 32px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.login-form-container .ant-form-item-label > label {
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.login-form-container .ant-input-affix-wrapper,
|
||||
.login-form-container .ant-input {
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #f0f0f0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.login-form-container .ant-input-affix-wrapper:hover,
|
||||
.login-form-container .ant-input:hover {
|
||||
background: #fff;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
|
||||
.login-form-container .ant-input-affix-wrapper-focused,
|
||||
.login-form-container .ant-input-affix-wrapper:focus,
|
||||
.login-form-container .ant-input:focus {
|
||||
background: #fff;
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1);
|
||||
}
|
||||
|
||||
.login-form-container .ant-btn-primary {
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.login-form-container .ant-btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4);
|
||||
}
|
||||
|
||||
/* 测试账号信息 */
|
||||
.test-account-info {
|
||||
margin-top: 24px;
|
||||
padding: 16px;
|
||||
background: #f0f5ff;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #1890ff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.test-account-info p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
.test-account-info strong {
|
||||
color: #1890ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.login-left {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.login-right {
|
||||
flex: 0 0 480px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.login-page {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.login-left {
|
||||
flex: none;
|
||||
min-height: 40vh;
|
||||
padding: 40px 24px;
|
||||
}
|
||||
|
||||
.login-left-content {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.footer-info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-right {
|
||||
flex: 1;
|
||||
padding: 40px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.login-right {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
|
||||
.login-form-container {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Form, Input, Button, Card, Tabs, Checkbox } from 'antd'
|
||||
import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons'
|
||||
import { Form, Input, Button, Tabs } from 'antd'
|
||||
import { UserOutlined, LockOutlined, MailOutlined, ArrowRightOutlined } from '@ant-design/icons'
|
||||
import { login, register } from '@/api/auth'
|
||||
import { getUserMenus } from '@/api/menu'
|
||||
import useUserStore from '@/stores/userStore'
|
||||
|
|
@ -11,33 +11,16 @@ import './Login.css'
|
|||
function Login() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [activeTab, setActiveTab] = useState('login')
|
||||
const [rememberMe, setRememberMe] = useState(false)
|
||||
const [loginForm] = Form.useForm()
|
||||
const navigate = useNavigate()
|
||||
const { setUser, setToken } = useUserStore()
|
||||
|
||||
// 组件加载时检查是否有保存的用户名
|
||||
useEffect(() => {
|
||||
const savedUsername = localStorage.getItem('remembered_username')
|
||||
if (savedUsername) {
|
||||
loginForm.setFieldsValue({ username: savedUsername })
|
||||
setRememberMe(true)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleLogin = async (values) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await login(values)
|
||||
Toast.success('登录成功')
|
||||
|
||||
// 处理"记住用户"
|
||||
if (rememberMe) {
|
||||
localStorage.setItem('remembered_username', values.username)
|
||||
} else {
|
||||
localStorage.removeItem('remembered_username')
|
||||
}
|
||||
|
||||
// 保存 token 和用户信息
|
||||
localStorage.setItem('access_token', res.data.access_token)
|
||||
localStorage.setItem('user_info', JSON.stringify(res.data.user))
|
||||
|
|
@ -92,147 +75,199 @@ function Login() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="login-container">
|
||||
<Card className="login-card">
|
||||
<div className="login-header">
|
||||
<h1>NEX Docus</h1>
|
||||
<p>团队协作文档管理平台</p>
|
||||
<div className="login-page">
|
||||
{/* 左侧介绍区域 */}
|
||||
<div className="login-left">
|
||||
<div className="login-left-content">
|
||||
<div className="logo-section">
|
||||
<div className="logo-icon">
|
||||
<img src="/favicon.svg" alt="NexDocus" width="42" height="42" />
|
||||
</div>
|
||||
<span className="logo-text">NexDocus</span>
|
||||
</div>
|
||||
|
||||
<div className="intro-section">
|
||||
<h1 className="intro-title">
|
||||
团队协作
|
||||
<br />
|
||||
<span className="highlight">文档管理平台</span>
|
||||
</h1>
|
||||
<p className="intro-desc">
|
||||
全流程文档共享,提升团队协作效率的新一代解决方案。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="footer-info">
|
||||
<div className="footer-links">
|
||||
<span>团队知识共享</span>
|
||||
<span>大模型能力加持</span>
|
||||
</div>
|
||||
<p className="copyright">© 2024 NexDocus. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs activeKey={activeTab} onChange={setActiveTab} centered>
|
||||
<Tabs.TabPane tab="登录" key="login">
|
||||
<Form
|
||||
form={loginForm}
|
||||
name="login"
|
||||
onFinish={handleLogin}
|
||||
autoComplete="off"
|
||||
size="large"
|
||||
>
|
||||
<Form.Item
|
||||
name="username"
|
||||
rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined />}
|
||||
placeholder="用户名"
|
||||
/>
|
||||
</Form.Item>
|
||||
{/* 右侧登录区域 */}
|
||||
<div className="login-right">
|
||||
<div className="login-form-container">
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={setActiveTab}
|
||||
items={[
|
||||
{
|
||||
key: 'login',
|
||||
label: '账号登录',
|
||||
children: (
|
||||
<div className="form-wrapper">
|
||||
<p className="form-subtitle">欢迎回来,请输入您的登录凭证。</p>
|
||||
|
||||
<Form.Item
|
||||
name="password"
|
||||
rules={[{ required: true, message: '请输入密码' }]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined />}
|
||||
placeholder="密码"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form
|
||||
form={loginForm}
|
||||
name="login"
|
||||
onFinish={handleLogin}
|
||||
autoComplete="off"
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item
|
||||
label="用户名"
|
||||
name="username"
|
||||
rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="请输入用户名"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Checkbox
|
||||
checked={rememberMe}
|
||||
onChange={(e) => setRememberMe(e.target.checked)}
|
||||
>
|
||||
记住用户
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="密码"
|
||||
name="password"
|
||||
rules={[{ required: true, message: '请输入密码' }]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="请输入密码"
|
||||
size="large"
|
||||
visibilityToggle
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
block
|
||||
>
|
||||
登录
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Tabs.TabPane>
|
||||
<Form.Item style={{ marginBottom: '12px' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
block
|
||||
size="large"
|
||||
icon={<ArrowRightOutlined />}
|
||||
iconPosition="end"
|
||||
>
|
||||
立即登录
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'register',
|
||||
label: '注册账号',
|
||||
children: (
|
||||
<div className="form-wrapper">
|
||||
<p className="form-subtitle">创建您的账号,开始使用平台。</p>
|
||||
|
||||
<Tabs.TabPane tab="注册" key="register">
|
||||
<Form
|
||||
name="register"
|
||||
onFinish={handleRegister}
|
||||
autoComplete="off"
|
||||
size="large"
|
||||
>
|
||||
<Form.Item
|
||||
name="username"
|
||||
rules={[
|
||||
{ required: true, message: '请输入用户名' },
|
||||
{ min: 3, message: '用户名至少3个字符' },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined />}
|
||||
placeholder="用户名"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form
|
||||
name="register"
|
||||
onFinish={handleRegister}
|
||||
autoComplete="off"
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item
|
||||
label="用户名"
|
||||
name="username"
|
||||
rules={[
|
||||
{ required: true, message: '请输入用户名' },
|
||||
{ min: 3, message: '用户名至少3个字符' },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="用户名"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="email"
|
||||
rules={[
|
||||
{ type: 'email', message: '请输入有效的邮箱地址' },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
prefix={<MailOutlined />}
|
||||
placeholder="邮箱(选填)"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="邮箱"
|
||||
name="email"
|
||||
rules={[
|
||||
{ type: 'email', message: '请输入有效的邮箱地址' },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
prefix={<MailOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="邮箱(选填)"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="password"
|
||||
rules={[
|
||||
{ required: true, message: '请输入密码' },
|
||||
{ min: 6, message: '密码至少6个字符' },
|
||||
]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined />}
|
||||
placeholder="密码"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="密码"
|
||||
name="password"
|
||||
rules={[
|
||||
{ required: true, message: '请输入密码' },
|
||||
{ min: 6, message: '密码至少6个字符' },
|
||||
]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="密码"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="confirm"
|
||||
dependencies={['password']}
|
||||
rules={[
|
||||
{ required: true, message: '请确认密码' },
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (!value || getFieldValue('password') === value) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject(new Error('两次输入的密码不一致'))
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined />}
|
||||
placeholder="确认密码"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="确认密码"
|
||||
name="confirm"
|
||||
dependencies={['password']}
|
||||
rules={[
|
||||
{ required: true, message: '请确认密码' },
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (!value || getFieldValue('password') === value) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject(new Error('两次输入的密码不一致'))
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="确认密码"
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
block
|
||||
>
|
||||
注册
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
|
||||
<div className="login-footer">
|
||||
<p>重置密码请联系管理员</p>
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
block
|
||||
size="large"
|
||||
>
|
||||
注册
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue