Files
hjc-web/app/pages/tender/orders.vue
T
weicw1996 32098b69ac fix(hjc-web): 修复刷新后登录态丢一半,以及顶栏企业名取错字段
- useHjcAuth:登录用户信息补持久化到 hjc_user cookie。原先用 useState,只活在内存与
  本次 SSR payload 里,刷新后归 null,顶栏企业名退化成占位的「企业」,登录态看着像丢了
  一半;H5 端把用户信息存本地存储,PC 端用同名 cookie 对齐。它**只用于展示**,鉴权一律
  以 JWT 为准(后端不读用户信息)。顺带抽出 setCredentials() 统一写入 token + 展示信息。
- HjcUserBar:loggedIn 改 computed —— 原来 ref(isLoggedIn()) 只在挂载那一刻求值一次,
  之后别处清凭据(401 处理、另一页签退出)本组件不跟着变;企业名改取 username 而非
  nickname —— 核心实例建号时会把 nickname 无条件覆盖成打码手机号,拿它当企业名是错的,
  username 才是「企业名称(登录账号)」。
- login:验证码图 object-cover 改 object-contain,避免被裁掉。
- tender/orders:改用 useRequestFetch()。SSR 期间裸 $fetch 不会把浏览器 Cookie 转发给
  站内 /api 代理,代理会变成匿名调用并返回 code=401,从而被 401 分支误判为「凭据失效」
  ——刷新页面等于把用户登出。
2026-09-14 19:01:31 +08:00

90 lines
3.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="container mx-auto px-4 py-10">
<div class="mb-6 flex items-center justify-between">
<h1 class="text-2xl font-bold text-gray-900">我的订单</h1>
<HjcUserBar />
</div>
<div v-if="!loggedIn" class="py-20 text-center">
<p class="text-gray-500">请先登录后查看订单</p>
<NuxtLink to="/login?redirect=/tender/orders" class="mt-4 inline-block rounded-md bg-blue-600 px-6 py-2 text-white hover:bg-blue-700">去登录</NuxtLink>
</div>
<div v-else-if="error" class="py-20 text-center text-red-500">{{ error }}</div>
<div v-else-if="orders.length" class="space-y-4">
<div v-for="o in orders" :key="o.id" class="rounded-lg border border-gray-200 p-5">
<div class="flex items-start justify-between">
<div>
<p class="font-semibold text-gray-900">{{ o.projectName }}</p>
<p class="mt-1 text-sm text-gray-500">订单号{{ o.orderNo }} · 项目编号{{ o.projectNo }}</p>
</div>
<span class="rounded px-2 py-0.5 text-xs" :class="payStatusClass(o.payStatus)">{{ payStatusText(o.payStatus) }}</span>
</div>
<div class="mt-4 flex items-center justify-between text-sm">
<span class="text-gray-500">{{ fmtTime(o.createTime) }}</span>
<span class="text-blue-600 font-bold">¥{{ formatMoney(o.totalAmount) }}</span>
</div>
</div>
</div>
<div v-else-if="loaded" class="py-20 text-center text-gray-500">暂无订单</div>
<SiteLoading v-else />
</div>
</template>
<script setup lang="ts">
const { isLoggedIn, handleAuthCode } = useHjcAuth()
/**
* 必须用 useRequestFetch 取数,不能用裸 `$fetch`。
*
* 下面这句在 **SSR 期间** 就会执行:裸 `$fetch` 走的是 Nuxt 全局 ofetch,服务端渲染时
* **不会**把浏览器请求里的 Cookie 转发给站内 `/api` 代理(Nuxt 只在 `useRequestFetch`
* / `useFetch` 里做转发)。代理拿不到 `hjc_token` 就等于匿名调用,后端按契约返回
* `code=401``handleAuthCode` 据此判定「凭据失效」→ 在服务端清 Token →
* SSR 响应带上 `Set-Cookie: hjc_token=; Max-Age=0` 并跳登录页。
* 结果就是:刷新「我的订单」= 把自己登出(Cookie 被真正删掉,此后一直是未登录)。
*
* 客户端侧 `useRequestFetch()` 等价于 `globalThis.$fetch`,行为不变。
*/
const requestFetch = useRequestFetch()
const loggedIn = ref(false)
const orders = shallowRef<any[]>([])
const loaded = ref(false)
const error = ref('')
function formatMoney(n?: number | string) {
return Number(n ?? 0).toFixed(2)
}
function fmtTime(s?: string) {
return s ? s.slice(0, 19).replace('T', ' ') : '-'
}
function payStatusText(s?: number) {
return ({ 0: '待支付', 1: '支付成功', 2: '支付失败', 3: '已退款' } as any)[s ?? 0] ?? '-'
}
function payStatusClass(s?: number) {
if (s === 1) return 'bg-green-100 text-green-700'
if (s === 3) return 'bg-gray-100 text-gray-500'
return 'bg-amber-100 text-amber-700'
}
loggedIn.value = isLoggedIn()
if (loggedIn.value) {
// 代理原样透传 ApiResult:按 body.code 判定(HTTP 状态码恒为 200,不可用于判定)
const res: any = await requestFetch('/api/tender/my-orders')
const auth = await handleAuthCode(res, '/tender/orders')
if (auth.handled) {
// 401 已清凭据并跳登录页;403 只提示「没有访问权限」,不清 token
error.value = auth.message || ''
loggedIn.value = isLoggedIn()
} else if (res?.code === 0) {
orders.value = Array.isArray(res.data) ? res.data : []
} else {
error.value = res?.message || '订单加载失败'
}
loaded.value = true
} else {
loaded.value = true
}
</script>