feat(header): modify_password

This commit is contained in:
zhangheng
2026-05-25 13:42:38 +08:00
parent 5c81a990a6
commit 8e75a1629f
3 changed files with 147 additions and 10 deletions

View File

@@ -38,6 +38,15 @@ export const userEditById = (data: User.EditProps, id: number) => {
})
}
// 修改用户密码
export const userModifyPassword = (data: User.ModifyPasswordProps) => {
return httpFetch({
url: BASE_LABEL_API + `/api/v1/label_server/user/modify_password`,
method: "PUT",
body: JSON.stringify(data),
})
}
// 编辑用户组织
export const userUpdateGroup = (data: User.UpdateGroupProps) => {
return httpFetch({
@@ -65,16 +74,7 @@ export const userGroupAdd = (data: Organization.CreateProps) => {
}
// 修改用户密码
export const modifyUserPassword = (data: {
old_password: string
new_password: string
}) => {
return httpFetch({
url: BASE_LABEL_API + "/api/v1/label_server/user/modify_password",
method: "PUT",
body: JSON.stringify(data),
})
}
export const modifyUserPassword = userModifyPassword
/* ****************************** Options ****************************** */
interface Option {

View File

@@ -41,6 +41,11 @@ export namespace User {
work_status?: number
}
export interface ModifyPasswordProps {
new_password: string
old_password: string
}
export interface UpdateGroupProps {
user_id: number
group_uuid: string

View File

@@ -1,13 +1,17 @@
"use client"
import {
Button,
AppShell,
Box,
Burger,
Divider,
Flex,
Group,
Modal,
Menu,
NavLink,
PasswordInput,
ScrollArea,
Space,
Stack,
@@ -20,10 +24,13 @@ import {
import Avvvatars from "avvvatars-react"
import { useDisclosure, useMediaQuery } from "@mantine/hooks"
import { useForm } from "@mantine/form"
import { notifications } from "@mantine/notifications"
import {
IconChevronDown,
IconChevronLeft,
IconChevronRight,
IconLock,
IconLogout,
IconMoon,
IconSun,
@@ -36,7 +43,14 @@ import headerLinkClasses from "./HeaderLink.module.css"
import { useUserStore } from "@/app/store/user"
import { usePathname, useRouter } from "next/navigation"
import { usePermissionStore } from "../label/store/auth"
import { userModifyPassword } from "../label/api/user"
import { deleteCookie } from "../login/api"
import {
settingModalActionsClassName,
settingModalClassNames,
settingModalFormClassName,
settingModalMetaTextClassName,
} from "../setting/PageSurface"
import classes from "./AppLayout.module.css"
import { MenuItem, withoutLayoutRoutes } from "./common"
import { ClientIcon } from "./components/ClientIcon"
@@ -149,6 +163,10 @@ export default function AppLayout({
children: React.ReactNode
}) {
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure()
const [
passwordModalOpened,
{ open: openPasswordModal, close: closePasswordModal },
] = useDisclosure(false)
const { updateIsOpen } = useAppLayoutStore.getState()
const { isOpen } = useAppLayoutStore()
@@ -178,6 +196,67 @@ export default function AppLayout({
const { menuDefaultRouter } = useAppLayoutStore.getState()
const passwordForm = useForm({
initialValues: {
old_password: "",
new_password: "",
confirm_new_password: "",
},
validate: {
old_password: (value) => (value ? null : "请输入当前密码"),
new_password: (value) => (value ? null : "请输入新密码"),
confirm_new_password: (value) => (value ? null : "请再次确认新密码"),
},
validateInputOnChange: true,
validateInputOnBlur: true,
clearInputErrorOnChange: true,
})
const [passwordSubmitting, setPasswordSubmitting] = useState(false)
const handleOpenPasswordModal = () => {
passwordForm.reset()
openPasswordModal()
}
const handleClosePasswordModal = () => {
passwordForm.reset()
closePasswordModal()
}
const handlePasswordSubmit = passwordForm.onSubmit(async (values) => {
if (values.new_password !== values.confirm_new_password) {
passwordForm.setFieldError(
"confirm_new_password",
"两次输入的新密码不一致"
)
return
}
setPasswordSubmitting(true)
try {
await userModifyPassword({
old_password: values.old_password,
new_password: values.new_password,
})
handleClosePasswordModal()
notifications.show({
color: "green",
title: "修改成功",
message: "密码已更新",
})
} catch (error) {
notifications.show({
color: "red",
title: "修改失败",
message:
error instanceof Error ? error.message : "密码修改失败,请重试",
})
} finally {
setPasswordSubmitting(false)
}
})
const headItems = menu.map((item) => (
<a
key={item.url}
@@ -374,6 +453,11 @@ export default function AppLayout({
</Box>
<Menu.Divider />
<Menu.Label></Menu.Label>
<Menu.Item
leftSection={<IconLock size={14} />}
onClick={handleOpenPasswordModal}>
</Menu.Item>
<Menu.Item
leftSection={<IconLogout size={14} />}
onClick={handleLogout}>
@@ -381,6 +465,54 @@ export default function AppLayout({
</Menu.Item>
</Menu.Dropdown>
</Menu>
<Modal
opened={passwordModalOpened}
onClose={handleClosePasswordModal}
title="修改密码"
centered
size={440}
classNames={settingModalClassNames}>
<form onSubmit={handlePasswordSubmit}>
<Stack gap={12} className={settingModalFormClassName}>
<Text className={settingModalMetaTextClassName}>
</Text>
<PasswordInput
withAsterisk
data-autofocus
label="当前密码"
placeholder="请输入当前密码"
{...passwordForm.getInputProps("old_password")}
/>
<PasswordInput
withAsterisk
label="新密码"
placeholder="请输入新密码"
{...passwordForm.getInputProps("new_password")}
/>
<PasswordInput
withAsterisk
label="确认新密码"
placeholder="请再次输入新密码"
{...passwordForm.getInputProps("confirm_new_password")}
/>
<Group
justify="flex-end"
gap={12}
className={settingModalActionsClassName}>
<Button
variant="default"
onClick={handleClosePasswordModal}
type="button">
</Button>
<Button type="submit" loading={passwordSubmitting}>
</Button>
</Group>
</Stack>
</form>
</Modal>
</Flex>
</Flex>
</AppShell.Header>