feat(auth): 用户自助修改密码
- PATCH /auth/me/password(校验原密码 + tokenVersion 吊销其它会话) - 顶栏用户下拉新增「修改密码」 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
23da579b26
commit
b03e9ef62b
2
components.d.ts
vendored
2
components.d.ts
vendored
@ -12,6 +12,7 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ChangePasswordForm: typeof import('./src/components/change-password-form.vue')['default']
|
||||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
@ -70,6 +71,7 @@ declare module 'vue' {
|
||||
|
||||
// For TSX support
|
||||
declare global {
|
||||
const ChangePasswordForm: typeof import('./src/components/change-password-form.vue')['default']
|
||||
const ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||
const ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||
const ElButton: typeof import('element-plus/es')['ElButton']
|
||||
|
||||
@ -21,3 +21,8 @@ export function register(data: {
|
||||
}): Promise<{ id: string; username: string }> {
|
||||
return http.post<{ id: string; username: string }>('/auth/register', data)
|
||||
}
|
||||
|
||||
/** 修改本人密码。 */
|
||||
export function changePassword(oldPassword: string, newPassword: string): Promise<unknown> {
|
||||
return http.patch('/auth/me/password', { oldPassword, newPassword })
|
||||
}
|
||||
|
||||
48
src/components/change-password-form.vue
Normal file
48
src/components/change-password-form.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="model" :rules="rules" label-width="80px">
|
||||
<el-form-item label="原密码" prop="oldPassword">
|
||||
<el-input v-model="model.oldPassword" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input v-model="model.newPassword" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input v-model="model.confirmPassword" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'ChangePasswordForm' })
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const model = reactive({ oldPassword: '', newPassword: '', confirmPassword: '' })
|
||||
|
||||
const rules: FormRules = {
|
||||
oldPassword: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 6, message: '密码长度不能少于 6 位', trigger: 'blur' },
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
if (value !== model.newPassword) callback(new Error('两次输入的密码不一致'))
|
||||
else callback()
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
async function submit(): Promise<{ oldPassword: string; newPassword: string }> {
|
||||
await formRef.value?.validate()
|
||||
return { oldPassword: model.oldPassword, newPassword: model.newPassword }
|
||||
}
|
||||
|
||||
defineExpose({ submit })
|
||||
</script>
|
||||
@ -56,6 +56,7 @@
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="change-password">修改密码</el-dropdown-item>
|
||||
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
@ -143,6 +144,7 @@ const emit = defineEmits<{
|
||||
(e: 'toggle-fullscreen'): void
|
||||
(e: 'open-settings'): void
|
||||
(e: 'navigate', path: string): void
|
||||
(e: 'change-password'): void
|
||||
(e: 'logout'): void
|
||||
}>()
|
||||
|
||||
@ -152,6 +154,7 @@ function onNavSelect(index: string): void {
|
||||
|
||||
function onUserCommand(command: string): void {
|
||||
if (command === 'logout') emit('logout')
|
||||
else if (command === 'change-password') emit('change-password')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
@toggle-fullscreen="toggleFullscreen"
|
||||
@open-settings="settingsOpen = true"
|
||||
@navigate="handleNavigate"
|
||||
@change-password="handleChangePassword"
|
||||
@logout="handleLogout"
|
||||
/>
|
||||
|
||||
@ -69,6 +70,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useAppStore } from '@/stores/modules/app'
|
||||
import { HOME_PATH, useTabsStore } from '@/stores/modules/tabs'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
@ -80,6 +82,9 @@ import type { LayoutSettings } from '@/config/layout'
|
||||
import type { ResourceNode } from '@/types/resource'
|
||||
import type { BreadcrumbItem } from '@/components/ty-breadcrumb.vue'
|
||||
import { applyTheme } from '@/utils/theme'
|
||||
import { showDialog } from '@/utils/dialog'
|
||||
import { changePassword } from '@/api/auth'
|
||||
import ChangePasswordForm from '@/components/change-password-form.vue'
|
||||
|
||||
const app = useAppStore()
|
||||
const tabsStore = useTabsStore()
|
||||
@ -231,6 +236,18 @@ async function handleLogout(): Promise<void> {
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
async function handleChangePassword(): Promise<void> {
|
||||
const result = await showDialog({
|
||||
component: ChangePasswordForm,
|
||||
title: '修改密码',
|
||||
width: '420px',
|
||||
})
|
||||
if (result == null) return
|
||||
await changePassword(result.oldPassword, result.newPassword)
|
||||
ElMessage.success('密码已修改,请重新登录')
|
||||
await handleLogout()
|
||||
}
|
||||
|
||||
/** 把菜单索引(path 或 code)解析为可跳转的叶子路径。 */
|
||||
function resolveTarget(index: string): string | null {
|
||||
const node = findMenu(index)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user