feat(frontend): 完成品牌 SVG 与后台 Phase 1 基础切片
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import { useState } from 'react'
|
||||
import { Alert, App, Button, Form, Input, Typography } from 'antd'
|
||||
import { LockOutlined } from '@ant-design/icons'
|
||||
import { Navigate, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { BrandLogo } from '@/components/BrandLogo'
|
||||
import { getAdminBasePath, getAdminLoginPath } from '@/shared/api/adminClient'
|
||||
import { useAdminSessionStore } from '@/stores/adminSession'
|
||||
|
||||
interface ChangePasswordForm {
|
||||
old_password: string
|
||||
new_password: string
|
||||
confirm_password: string
|
||||
}
|
||||
|
||||
export function AdminChangePasswordPage() {
|
||||
const { message } = App.useApp()
|
||||
const navigate = useNavigate()
|
||||
const admin = useAdminSessionStore(state => state.admin)
|
||||
const logout = useAdminSessionStore(state => state.logout)
|
||||
const changePassword = useAdminSessionStore(state => state.changePassword)
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const basePath = getAdminBasePath()
|
||||
|
||||
if (!admin) return <Navigate to={getAdminLoginPath()} replace />
|
||||
if (!admin.password_must_change) return <Navigate to={basePath} replace />
|
||||
|
||||
const onFinish = async (values: ChangePasswordForm) => {
|
||||
setSubmitting(true)
|
||||
setError('')
|
||||
try {
|
||||
await changePassword(values.old_password, values.new_password)
|
||||
message.success('初始密码已更新')
|
||||
navigate(basePath, { replace: true })
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '密码没有更新,请检查原密码')
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="admin-login-page">
|
||||
<section className="admin-login-brand">
|
||||
<div className="admin-login-brand__inner">
|
||||
<BrandLogo height={56} variant="dark" />
|
||||
<div className="admin-login-brand__eyebrow">FIRST RUN · SECURITY</div>
|
||||
<Typography.Title className="brand-title admin-login-brand__title">
|
||||
先把钥匙
|
||||
<br />
|
||||
换掉。
|
||||
</Typography.Title>
|
||||
<Typography.Paragraph className="admin-login-brand__copy">
|
||||
初始密码只用于第一次进入。修改完成后,这个后台会话才能打开订单、财务和分销操作。
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="admin-login-form-panel">
|
||||
<div className="admin-login-card">
|
||||
<div className="admin-login-card__head">
|
||||
<BrandLogo height={34} />
|
||||
<Typography.Title level={2} className="brand-title">
|
||||
修改初始密码
|
||||
</Typography.Title>
|
||||
<Typography.Text type="secondary">
|
||||
当前账号:{admin.nickname || admin.username}({admin.username})
|
||||
</Typography.Text>
|
||||
</div>
|
||||
|
||||
{error ? <Alert type="error" showIcon message={error} className="login-alert" /> : null}
|
||||
|
||||
<Form<ChangePasswordForm>
|
||||
layout="vertical"
|
||||
size="large"
|
||||
requiredMark={false}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<Form.Item
|
||||
label="原密码"
|
||||
name="old_password"
|
||||
rules={[{ required: true, message: '请输入原密码' }]}
|
||||
>
|
||||
<Input.Password prefix={<LockOutlined />} placeholder="请输入原密码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="新密码"
|
||||
name="new_password"
|
||||
rules={[
|
||||
{ required: true, message: '请输入新密码' },
|
||||
{ min: 8, max: 20, message: '密码长度需为 8–20 位' },
|
||||
]}
|
||||
>
|
||||
<Input.Password prefix={<LockOutlined />} placeholder="8–20 位新密码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="确认新密码"
|
||||
name="confirm_password"
|
||||
dependencies={['new_password']}
|
||||
rules={[
|
||||
{ required: true, message: '请再次输入新密码' },
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (!value || getFieldValue('new_password') === value) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject(new Error('两次输入的新密码不一致'))
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input.Password prefix={<LockOutlined />} placeholder="再次输入新密码" />
|
||||
</Form.Item>
|
||||
|
||||
<Button type="primary" htmlType="submit" block loading={submitting}>
|
||||
更新密码并进入后台
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
<div className="admin-login-card__foot">
|
||||
<span>改密后旧会话会按后端规则刷新</span>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={async () => {
|
||||
await logout()
|
||||
navigate(getAdminLoginPath(), { replace: true })
|
||||
}}
|
||||
>
|
||||
退出登录
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user