refactor(user): 禁用 loginByOpenId 静默登录并优化登录状态控制
- 移除 UserContext 和 useUser 中的 loginByOpenId 调用及相关降级逻辑 - 无本地 token 时保持未登录状态,不再自动触发静默登录 - 清理无用导入 TenantId 和 loginByOpenId - 订单列表页增加登录校验,未登录用户跳转登录页并阻止渲染订单 - 商城首页价格改为 Price 组件,未登录游客显示登录遮罩,单位仅登录后可见
This commit is contained in:
@@ -90,3 +90,29 @@ adapter `/list` 接口忽略分页,一次返回全部收藏;前端 `listShop
|
|||||||
2. **`src/api/shop/shopCart/index.ts`**:`updateCartNum` 发 `{id, cartNum}`;`updateCartChecked` 发 `{id, selected}`
|
2. **`src/api/shop/shopCart/index.ts`**:`updateCartNum` 发 `{id, cartNum}`;`updateCartChecked` 发 `{id, selected}`
|
||||||
3. **`src/contexts/CartContext.tsx`**:`refresh()` 读 `item.cartNum` 和 `item.selected`;`addItem()` 发 `cartNum`;`updateQuantity` 调用 `{id, cartNum}`
|
3. **`src/contexts/CartContext.tsx`**:`refresh()` 读 `item.cartNum` 和 `item.selected`;`addItem()` 发 `cartNum`;`updateQuantity` 调用 `{id, cartNum}`
|
||||||
4. **`src/pages/shop/index.tsx`** + **`src/pages/shop/category.tsx`**:`addToCart` 参数 `num` → `cartNum`
|
4. **`src/pages/shop/index.tsx`** + **`src/pages/shop/category.tsx`**:`addToCart` 参数 `num` → `cartNum`
|
||||||
|
|
||||||
|
## 禁用 loginByOpenId 静默登录(2026-07-14)
|
||||||
|
|
||||||
|
### 改动
|
||||||
|
- **`src/contexts/UserContext.tsx`**:移除"情况3"静默登录块(`Taro.login` + `loginByOpenId`),无 token 时直接保持未登录状态;清理 `loginByOpenId`、`TenantId` 无用导入
|
||||||
|
- **`src/hooks/useUser.ts`**:移除降级方案中的 `loginByOpenId` 调用,仅从 storage 读取;清理 `loginByOpenId`、`TenantId` 无用导入
|
||||||
|
- `src/api/layout/index.ts` 中 `loginByOpenId` 函数定义保留,仅不再被调用
|
||||||
|
|
||||||
|
## 商城分类页价格登录可见(2026-07-14)
|
||||||
|
|
||||||
|
### 改动
|
||||||
|
- **`src/pages/shop/index.tsx`**:价格显示从手写 `Text` 改为 `Price` 组件 + `loginMask` 属性(与首页一致),游客看到"登录后查看价格";VIP 划线价逻辑保留在 `original` prop 中;单位 `unitName` 仅登录后显示
|
||||||
|
|
||||||
|
## 订单列表页未登录可查看订单 Bug 修复(2026-07-14)
|
||||||
|
|
||||||
|
### 问题
|
||||||
|
`pages/order/list` 未登录用户也能看到订单数据。
|
||||||
|
|
||||||
|
### 根因
|
||||||
|
页面缺少登录校验——`useEffect` 直接调用 `loadOrders`,未登录时 `user?.userId` 为 `undefined`,后端未按用户过滤直接返回了订单。
|
||||||
|
|
||||||
|
### 修改
|
||||||
|
- **`src/pages/order/list.tsx`**:采用项目标准模式 A(与 commission.tsx 等页面一致):
|
||||||
|
1. 从 `useUser()` 获取 `isLoggedIn`
|
||||||
|
2. `useEffect` 中加 `if (!isLoggedIn)` 守卫 → `Taro.navigateTo('/passport/login')` + return
|
||||||
|
3. render 中 `if (!isLoggedIn) return null`
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import React, { createContext, useContext, useState, useEffect, type ReactNode } from 'react'
|
import React, { createContext, useContext, useState, useEffect, type ReactNode } from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import type { User } from '@/api/system/user/model'
|
import type { User } from '@/api/system/user/model'
|
||||||
import { getUserInfo, loginByOpenId } from '@/api/layout'
|
import { getUserInfo } from '@/api/layout'
|
||||||
import { saveStorageByLoginUser, clearStorageByLoginUser } from '@/utils/server'
|
import { saveStorageByLoginUser, clearStorageByLoginUser } from '@/utils/server'
|
||||||
import { TenantId } from '@/config/app'
|
|
||||||
|
|
||||||
interface UserContextType {
|
interface UserContextType {
|
||||||
user: User | null
|
user: User | null
|
||||||
@@ -42,36 +41,12 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
return
|
return
|
||||||
} catch {
|
} catch {
|
||||||
// token无效,清除并尝试静默登录
|
// token无效,清除
|
||||||
clearStorageByLoginUser()
|
clearStorageByLoginUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 情况3: 没有本地token,尝试静默登录(老用户自动登录)
|
// 无本地token,保持未登录状态(已禁用静默登录)
|
||||||
try {
|
|
||||||
const loginResult = await new Promise<{ access_token?: string; user?: User }>((resolve, reject) => {
|
|
||||||
Taro.login({
|
|
||||||
success: (res) => {
|
|
||||||
if (res.code) {
|
|
||||||
loginByOpenId({ code: res.code, tenantId: TenantId })
|
|
||||||
.then(resolve)
|
|
||||||
.catch(reject)
|
|
||||||
} else {
|
|
||||||
reject(new Error('no code'))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fail: reject,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// 静默登录成功(老用户),自动完成登录
|
|
||||||
if (loginResult?.access_token && loginResult?.user) {
|
|
||||||
saveStorageByLoginUser(loginResult.access_token, loginResult.user)
|
|
||||||
setUser(loginResult.user)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// 静默登录失败(非老用户或网络问题),保持未登录状态
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { useUserContext } from '@/contexts/UserContext'
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import type { User } from '@/api/system/user/model'
|
import type { User } from '@/api/system/user/model'
|
||||||
import { loginByOpenId } from '@/api/layout'
|
|
||||||
import { TenantId } from '@/config/app'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* useUser - 兼容性 Hook
|
* useUser - 兼容性 Hook
|
||||||
@@ -57,24 +55,8 @@ const useUserFallback = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const init = async () => {
|
loadFromStorage()
|
||||||
const hasLocal = loadFromStorage()
|
|
||||||
if (!hasLocal) {
|
|
||||||
try {
|
|
||||||
const data = await new Promise<any>((resolve, reject) => {
|
|
||||||
Taro.login({
|
|
||||||
success: (res) => loginByOpenId({ code: res.code, tenantId: TenantId }).then(resolve).catch(reject),
|
|
||||||
fail: reject,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (data?.access_token && data?.user) {
|
|
||||||
loginUser(data.access_token, data.user)
|
|
||||||
}
|
|
||||||
} catch { /* ignore */ }
|
|
||||||
}
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
|
||||||
init()
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return { user, isLoggedIn: !!user, loading, loginUser, logoutUser, refreshUser, syncFromStorage }
|
return { user, isLoggedIn: !!user, loading, loginUser, logoutUser, refreshUser, syncFromStorage }
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ interface TabState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const OrderListPage: React.FC = () => {
|
const OrderListPage: React.FC = () => {
|
||||||
const { user } = useUser()
|
const { user, isLoggedIn } = useUser()
|
||||||
const initTab = Taro.getCurrentInstance().router?.params?.tab
|
const initTab = Taro.getCurrentInstance().router?.params?.tab
|
||||||
?? Taro.getStorageSync('order_tab')
|
?? Taro.getStorageSync('order_tab')
|
||||||
// 读取后清除,避免影响下次进入
|
// 读取后清除,避免影响下次进入
|
||||||
@@ -104,10 +104,14 @@ const OrderListPage: React.FC = () => {
|
|||||||
|
|
||||||
// 切换 tab 时加载数据
|
// 切换 tab 时加载数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
Taro.navigateTo({ url: '/passport/login' })
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!tabStates[tabIndex].initialized) {
|
if (!tabStates[tabIndex].initialized) {
|
||||||
loadOrders(tabIndex, 1)
|
loadOrders(tabIndex, 1)
|
||||||
}
|
}
|
||||||
}, [tabIndex])
|
}, [tabIndex, isLoggedIn])
|
||||||
|
|
||||||
const handleLoadMore = () => {
|
const handleLoadMore = () => {
|
||||||
if (!currentState.finished && !currentState.loading) {
|
if (!currentState.finished && !currentState.loading) {
|
||||||
@@ -121,6 +125,10 @@ const OrderListPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='min-h-screen bg-gray-50 flex flex-col'>
|
<View className='min-h-screen bg-gray-50 flex flex-col'>
|
||||||
{/* 自定义 Tabs 标题栏 */}
|
{/* 自定义 Tabs 标题栏 */}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { requireLogin } from '@/utils/login-guard'
|
|||||||
import { useShare } from '@/hooks/useShare'
|
import { useShare } from '@/hooks/useShare'
|
||||||
import EmptyState from '@/components/common/EmptyState'
|
import EmptyState from '@/components/common/EmptyState'
|
||||||
import LoadMore from '@/components/common/LoadMore'
|
import LoadMore from '@/components/common/LoadMore'
|
||||||
|
import Price from '@/components/common/Price'
|
||||||
|
|
||||||
definePageConfig({
|
definePageConfig({
|
||||||
navigationBarTitleText: '商品分类',
|
navigationBarTitleText: '商品分类',
|
||||||
@@ -305,24 +306,21 @@ const ShopPage: React.FC = () => {
|
|||||||
{/* 价格 + 销量 + 加购 */}
|
{/* 价格 + 销量 + 加购 */}
|
||||||
<View className='flex items-end justify-between'>
|
<View className='flex items-end justify-between'>
|
||||||
<View className='flex-1 min-w-0'>
|
<View className='flex-1 min-w-0'>
|
||||||
<Text className='text-sm text-red-500 font-medium'>
|
<Price
|
||||||
¥{getDisplayPrice(item)}
|
price={getDisplayPrice(item)}
|
||||||
</Text>
|
original={
|
||||||
{item.unitName ? (
|
isVipMember() && item.dealerPrice
|
||||||
|
? item.price
|
||||||
|
: item.salePrice && item.salePrice !== item.price
|
||||||
|
? item.salePrice
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
size='small'
|
||||||
|
loginMask
|
||||||
|
/>
|
||||||
|
{item.unitName && !isGuest() ? (
|
||||||
<Text className='text-xs text-gray-400'>/{item.unitName}</Text>
|
<Text className='text-xs text-gray-400'>/{item.unitName}</Text>
|
||||||
) : null}
|
) : null}
|
||||||
{/* VIP 划掉原价 */}
|
|
||||||
{isVipMember() && item.dealerPrice ? (
|
|
||||||
<Text className='text-xs text-gray-400 line-through ml-1'>
|
|
||||||
¥{item.price}
|
|
||||||
</Text>
|
|
||||||
) : item.salePrice && item.salePrice !== item.price ? (
|
|
||||||
isGuest() ? null : (
|
|
||||||
<Text className='text-xs text-gray-400 line-through ml-1'>
|
|
||||||
¥{item.salePrice}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
) : null}
|
|
||||||
{/* 销量 */}
|
{/* 销量 */}
|
||||||
{item.sales !== undefined && item.sales > 0 && (
|
{item.sales !== undefined && item.sales > 0 && (
|
||||||
<Text className='text-xs text-gray-400 block mt-0.5'>
|
<Text className='text-xs text-gray-400 block mt-0.5'>
|
||||||
|
|||||||
Reference in New Issue
Block a user