fix(auth): 完善权限控制与强制改密流程

This commit is contained in:
xy
2026-09-17 16:33:09 +08:00
parent 81922d774f
commit 61b4b19f55
22 changed files with 618 additions and 59 deletions
@@ -0,0 +1,197 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useLoginStore } from '@/stores/login'
const router = useRouter()
const loginStore = useLoginStore()
const currentPassword = ref('')
const newPassword = ref('')
const confirmPassword = ref('')
const errorMessage = ref('')
const isSubmitting = ref(false)
function validatePassword() {
if (!currentPassword.value || !newPassword.value || !confirmPassword.value) {
return '请完整填写当前密码、新密码和确认密码'
}
if (newPassword.value.length < 12 || newPassword.value.length > 128) {
return '新密码长度必须为 12 至 128 位'
}
if (
!/[A-Z]/.test(newPassword.value) ||
!/[a-z]/.test(newPassword.value) ||
!/\d/.test(newPassword.value) ||
!/[^A-Za-z0-9]/.test(newPassword.value)
) {
return '新密码必须包含大写字母、小写字母、数字和特殊字符'
}
if (newPassword.value !== confirmPassword.value) {
return '两次输入的新密码不一致'
}
if (newPassword.value === currentPassword.value) {
return '新密码不能与当前密码相同'
}
return ''
}
async function handleSubmit() {
const validationMessage = validatePassword()
if (validationMessage) {
errorMessage.value = validationMessage
return
}
errorMessage.value = ''
isSubmitting.value = true
try {
await loginStore.changePassword({
currentPassword: currentPassword.value,
newPassword: newPassword.value,
})
await router.replace({ name: 'login', query: { passwordChanged: '1' } })
} catch (error: unknown) {
errorMessage.value =
error instanceof Error && error.message ? error.message : '密码修改失败,请稍后重试'
} finally {
isSubmitting.value = false
}
}
async function handleLogout() {
try {
await loginStore.logout()
} catch {
loginStore.clearSession()
}
await router.replace({ name: 'login' })
}
</script>
<template>
<main class="password-page">
<form class="password-card" novalidate @submit.prevent="handleSubmit">
<div class="password-brand">医签通</div>
<h1>首次登录,请修改密码</h1>
<p class="password-description">为保护患者信息,设置新密码后需要重新登录。</p>
<label>
<span>当前密码</span>
<input v-model="currentPassword" type="password" autocomplete="current-password" />
</label>
<label>
<span>新密码</span>
<input v-model="newPassword" type="password" autocomplete="new-password" />
</label>
<label>
<span>确认新密码</span>
<input v-model="confirmPassword" type="password" autocomplete="new-password" />
</label>
<p class="password-policy">12–128 位,须同时包含大小写字母、数字和特殊字符。</p>
<p v-if="errorMessage" class="password-error" role="alert">{{ errorMessage }}</p>
<button class="password-submit" type="submit" :disabled="isSubmitting">
{{ isSubmitting ? '修改中…' : '修改密码并重新登录' }}
</button>
<button class="password-logout" type="button" :disabled="isSubmitting" @click="handleLogout">
退出当前账号
</button>
</form>
</main>
</template>
<style scoped>
.password-page {
position: fixed;
inset: 0;
display: grid;
place-items: center;
background: var(--login-gradient);
}
.password-card {
width: min(420px, calc(100% - 32px));
padding: 32px 36px;
background: #fff;
border-radius: 14px;
box-shadow: var(--sh-login);
}
.password-brand {
color: var(--brand);
font-size: 15px;
font-weight: 800;
text-align: center;
}
h1 {
margin: 8px 0;
color: var(--ink);
font-size: 22px;
text-align: center;
}
.password-description,
.password-policy {
color: var(--mut);
font-size: 12px;
line-height: 1.6;
text-align: center;
}
label {
display: block;
margin-top: 14px;
}
label span {
display: block;
margin-bottom: 6px;
color: var(--mut);
font-size: 12px;
}
input {
width: 100%;
padding: 9px 10px;
border: 1px solid var(--line);
border-radius: 6px;
outline: none;
}
input:focus {
border-color: var(--brand);
}
.password-error {
color: var(--err);
font-size: 12px;
}
.password-submit,
.password-logout {
width: 100%;
padding: 10px;
border-radius: 7px;
}
.password-submit {
color: #fff;
font-weight: 700;
background: var(--brand);
}
.password-logout {
margin-top: 8px;
color: var(--mut);
background: transparent;
}
button:disabled {
cursor: wait;
opacity: 0.65;
}
</style>