feat: API层与认证流(client拦截/守卫/登录改密实装/域API骨架/vitest)
- 自建轻量 axios client(src/api/client.ts):Bearer 注入、401 清会话跳登录
(login 请求除外)、409 精确匹配"必须先修改初始密码"跳强制改密(区别于并发
冲突 409)、429/网络错误统一 ElMessage、ApiError 标准化、showErrorMessage
可opt-out——模板 utils/http 的 {code,msg,data} 信封与后端裸 HTTP 语义不符故弃用
- Pinia auth store(src/store/modules/auth.ts):token/role/tenant_slug/
must_change 落地并持久化(all-kefu-auth key),login/logout/changePassword/
会话过期/强制改密跳转(动态 import 断循环)
- 路由守卫:token 事实源判定、must_change 全站门(除改密页)、UserInfo 由登录
快照派生(后端无用户信息端点);user store 残留自愈
- 登录页实装(去除角色调试开关)、强制改密页实装;用户菜单/锁屏对接真实 logout
- 域 API 骨架:tenants/config/conversations/stats/settings(类型+端点常量)
- mask 哨兵助手:__SET__ ↔ 已设置 表单语义
- vitest 25 例(client 11/store 8/mask 6);登录页拖拽验证 reset 空引用修复
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<!-- 强制修改密码(骨架占位页;登录对接与提交逻辑在后续任务实现) -->
|
||||
<!-- 强制修改密码(首登引导 / 管理员重置后的 must_change 语义) -->
|
||||
<template>
|
||||
<div class="force-change-password flex-cc w-full h-screen">
|
||||
<ElCard shadow="never" class="card">
|
||||
@@ -6,11 +6,20 @@
|
||||
<span class="card-title">首次登录请修改密码</span>
|
||||
</template>
|
||||
<ElForm ref="formRef" :model="formData" :rules="rules" label-position="top">
|
||||
<ElFormItem prop="oldPassword" label="原密码">
|
||||
<ElInput
|
||||
v-model.trim="formData.oldPassword"
|
||||
type="password"
|
||||
placeholder="请输入原密码"
|
||||
show-password
|
||||
autocomplete="off"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem prop="newPassword" label="新密码">
|
||||
<ElInput
|
||||
v-model.trim="formData.newPassword"
|
||||
type="password"
|
||||
placeholder="请输入新密码"
|
||||
placeholder="请输入新密码(不少于 8 位)"
|
||||
show-password
|
||||
autocomplete="off"
|
||||
/>
|
||||
@@ -28,25 +37,31 @@
|
||||
提交修改
|
||||
</ElButton>
|
||||
</ElForm>
|
||||
<p class="tip">密码修改成功后方可进入后台(对接后端 API 在后续任务实现)</p>
|
||||
<p class="tip">密码修改成功后方可进入后台;修改后当前会话保持有效,无需重新登录</p>
|
||||
</ElCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
|
||||
defineOptions({ name: 'ForceChangePassword' })
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(false)
|
||||
|
||||
const formData = reactive({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
oldPassword: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 8, message: '密码长度不少于 8 位', trigger: 'blur' }
|
||||
@@ -66,15 +81,27 @@
|
||||
]
|
||||
}
|
||||
|
||||
// 骨架阶段仅做表单校验,不发起请求(Task 3 对接 /api/platform/auth/*)
|
||||
// 提交改密(POST /api/platform/auth/change-password):
|
||||
// 成功 → must_change 清零 → 进仪表盘;400 原密码不正确 / 401 会话过期
|
||||
// 由 HTTP client 统一提示
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
try {
|
||||
await authStore.changePassword({
|
||||
old_password: formData.oldPassword,
|
||||
new_password: formData.newPassword
|
||||
})
|
||||
ElMessage.success('密码修改成功,即将进入后台')
|
||||
await router.push('/')
|
||||
} catch {
|
||||
// 错误提示已由 HTTP client 统一弹出(如 400 原密码不正确)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}, 600)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user