Files
hjc-web/app/components/HjcUserBar.vue
T
weicw1996 81ef9317c7 feat(tender): 忘记密码与修改密码页(10 套模板通用)
密码找回(材料审核制,见 ADR 0008)
- app/pages/forgot-password.vue:两个页签——「提交申请」(企业名称 + 纳税人识别号 + 新密码 + 确认
  + 授权委托书,复用 POST /api/tender/upload,并照 HjcEnterpriseForm 做 5M 校验)与「查询进度」
  (双要素查询,展示状态与时间、驳回原因)。提交后自动把条件带到查询页签。文案纪律:
  「已通过」≠「已重置」。

修改密码(登录态,双因子)
- app/pages/change-password.vue:旧密码 + 短信验证码(60s 倒计时,照 register.vue)+ 新密码 +
  确认;成功后清本机凭据并显示「去登录」面板。

BFF 与封装
- server/api/tender/password/{apply.post,status.get,sms.post,change.put}.ts:四条都照
  register.post.ts 的透传范式(modulesApiBase + TenantId(header 与 query 都带)+ 原样返回
  ApiResult,HTTP 恒 200)。前两条**匿名**(与 sms/upload 一致,不读 cookie);后两条需登录态,
  从 cookie hjc_token 或 Authorization 头取 token 拼 Bearer。
- app/composables/useHjcPassword.ts:四条请求单独成一支,不塞进 useHjcAuth——找回的两条是匿名的、
  不做 401 跳转;修改密码的两条经 handleAuthCode 处理登录态。

接线
- app/pages/login.vue:加「忘记密码?」入口。
- app/components/HjcUserBar.vue:已登录菜单加「修改密码」。

落点说明:新页面直接放 app/pages/*.vue——静态段路由优先于 app/pages/[slug].vue 的动态段,故不必
动 useTemplate.ts / templates/index.ts,与 login.vue、register.vue 完全同款做法,10 套模板自动可用。

验证:针对本次文件 npx eslint 0 error;pnpm run build 成功且四条新 BFF 都出现在产物里。
未验:未起 mp-java 做端到端(见 .scratch/hjc-password/issues/06)。
2026-09-17 02:35:45 +08:00

41 lines
1.7 KiB
Vue

<template>
<div class="flex items-center gap-4 text-sm">
<template v-if="loggedIn">
<span class="text-gray-500">{{ displayName }}</span>
<NuxtLink to="/tender/orders" class="text-blue-600 hover:underline">我的订单</NuxtLink>
<NuxtLink to="/tender/qualification" class="text-blue-600 hover:underline">企业资质</NuxtLink>
<NuxtLink to="/change-password" class="text-blue-600 hover:underline">修改密码</NuxtLink>
<button class="text-gray-500 hover:text-red-600" @click="doLogout">退出</button>
</template>
<template v-else>
<NuxtLink to="/login" class="text-blue-600 hover:underline">登录</NuxtLink>
<NuxtLink to="/register" class="text-blue-600 hover:underline">企业注册</NuxtLink>
</template>
</div>
</template>
<script setup lang="ts">
const { isLoggedIn, user, logout } = useHjcAuth()
/**
* 写成 computed 而不是 `ref(isLoggedIn())`:后者只在组件挂载那一刻求值一次,
* 之后别处清掉凭据(如 401 处理、另一个页签退出)本组件不会跟着变。
*/
const loggedIn = computed(() => isLoggedIn())
/**
* 显示企业名称,取 `username` 而非 `nickname`。
*
* 核心实例建号时会把 `nickname` **无条件覆盖**成打码手机号(`155****2748`,见
* `.scratch/hjc-auth/spec.md` 的 Q16 = G-a),拿它当企业名显示是错的;
* 而 `username` 就是「企业名称(登录账号)」,注册后不可变更,也是 `hjc_enterprise.name` 的值。
*/
const displayName = computed(() => user.value?.username || user.value?.nickname || '企业')
async function doLogout() {
// logout() 会先通知后端(无服务端会话),再清本地凭据
await logout()
return navigateTo('/tender')
}
</script>