2026-10-13 16:28:11 +08:00
|
|
|
|
<!-- 强制修改密码(首登引导 / 管理员重置后的 must_change 语义) -->
|
2026-10-13 14:51:24 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="force-change-password flex-cc w-full h-screen">
|
|
|
|
|
|
<ElCard shadow="never" class="card">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<span class="card-title">首次登录请修改密码</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<ElForm ref="formRef" :model="formData" :rules="rules" label-position="top">
|
2026-10-13 16:28:11 +08:00
|
|
|
|
<ElFormItem prop="oldPassword" label="原密码">
|
|
|
|
|
|
<ElInput
|
|
|
|
|
|
v-model.trim="formData.oldPassword"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
placeholder="请输入原密码"
|
|
|
|
|
|
show-password
|
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</ElFormItem>
|
2026-10-13 14:51:24 +08:00
|
|
|
|
<ElFormItem prop="newPassword" label="新密码">
|
|
|
|
|
|
<ElInput
|
|
|
|
|
|
v-model.trim="formData.newPassword"
|
|
|
|
|
|
type="password"
|
2026-10-13 16:28:11 +08:00
|
|
|
|
placeholder="请输入新密码(不少于 8 位)"
|
2026-10-13 14:51:24 +08:00
|
|
|
|
show-password
|
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</ElFormItem>
|
|
|
|
|
|
<ElFormItem prop="confirmPassword" label="确认新密码">
|
|
|
|
|
|
<ElInput
|
|
|
|
|
|
v-model.trim="formData.confirmPassword"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
placeholder="请再次输入新密码"
|
|
|
|
|
|
show-password
|
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</ElFormItem>
|
|
|
|
|
|
<ElButton type="primary" class="w-full" :loading="loading" @click="handleSubmit">
|
|
|
|
|
|
提交修改
|
|
|
|
|
|
</ElButton>
|
|
|
|
|
|
</ElForm>
|
2026-10-13 16:28:11 +08:00
|
|
|
|
<p class="tip">密码修改成功后方可进入后台;修改后当前会话保持有效,无需重新登录</p>
|
2026-10-13 14:51:24 +08:00
|
|
|
|
</ElCard>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-10-13 16:28:11 +08:00
|
|
|
|
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
|
|
|
|
|
import { useAuthStore } from '@/store/modules/auth'
|
2026-10-13 14:51:24 +08:00
|
|
|
|
|
|
|
|
|
|
defineOptions({ name: 'ForceChangePassword' })
|
|
|
|
|
|
|
2026-10-13 16:28:11 +08:00
|
|
|
|
const authStore = useAuthStore()
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
|
2026-10-13 14:51:24 +08:00
|
|
|
|
const formRef = ref<FormInstance>()
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
const formData = reactive({
|
2026-10-13 16:28:11 +08:00
|
|
|
|
oldPassword: '',
|
2026-10-13 14:51:24 +08:00
|
|
|
|
newPassword: '',
|
|
|
|
|
|
confirmPassword: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const rules: FormRules = {
|
2026-10-13 16:28:11 +08:00
|
|
|
|
oldPassword: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
|
2026-10-13 14:51:24 +08:00
|
|
|
|
newPassword: [
|
|
|
|
|
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
|
|
|
|
|
{ min: 8, message: '密码长度不少于 8 位', trigger: 'blur' }
|
|
|
|
|
|
],
|
|
|
|
|
|
confirmPassword: [
|
|
|
|
|
|
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
|
|
|
|
|
|
{
|
|
|
|
|
|
validator: (_rule, value, callback) => {
|
|
|
|
|
|
if (value !== formData.newPassword) {
|
|
|
|
|
|
callback(new Error('两次输入的密码不一致'))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
callback()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
trigger: 'blur'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-13 16:28:11 +08:00
|
|
|
|
// 提交改密(POST /api/platform/auth/change-password):
|
|
|
|
|
|
// 成功 → must_change 清零 → 进仪表盘;400 原密码不正确 / 401 会话过期
|
|
|
|
|
|
// 由 HTTP client 统一提示
|
2026-10-13 14:51:24 +08:00
|
|
|
|
const handleSubmit = async () => {
|
|
|
|
|
|
if (!formRef.value) return
|
|
|
|
|
|
const valid = await formRef.value.validate().catch(() => false)
|
|
|
|
|
|
if (!valid) return
|
2026-10-13 16:28:11 +08:00
|
|
|
|
|
2026-10-13 14:51:24 +08:00
|
|
|
|
loading.value = true
|
2026-10-13 16:28:11 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await authStore.changePassword({
|
|
|
|
|
|
old_password: formData.oldPassword,
|
|
|
|
|
|
new_password: formData.newPassword
|
|
|
|
|
|
})
|
|
|
|
|
|
ElMessage.success('密码修改成功,即将进入后台')
|
|
|
|
|
|
await router.push('/')
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// 错误提示已由 HTTP client 统一弹出(如 400 原密码不正确)
|
|
|
|
|
|
} finally {
|
2026-10-13 14:51:24 +08:00
|
|
|
|
loading.value = false
|
2026-10-13 16:28:11 +08:00
|
|
|
|
}
|
2026-10-13 14:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.card {
|
|
|
|
|
|
width: 400px;
|
|
|
|
|
|
|
|
|
|
|
|
.card-title {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tip {
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: var(--art-gray-500);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|