- 后端新增 clearDefault 方法,保存默认地址前清除其他默认状态,避免多默认地址 - 前端地址编辑页保存成功后若为默认,调用 setDefaultAddress 触发后端清理逻辑 - useAddress hook 加入 ensureSingleDefault,前端保证地址数据中只有一个默认地址 - 新增 shop_goods_browse 表及相关实体、Mapper、服务和控制器,实现用户商品浏览记录功能 - 商品详情页接入浏览记录上报,支持登录用户浏览记录服务端同步 - 浏览历史页支持登录用户服务端分页查询,未登录用户使用本地存储兜底 - 浏览历史页支持浏览记录单条删除、清空、上拉加载和下拉刷新功能 - 调整用户中心入口,恢复浏览历史菜单项显示 - 文档和内存文件更新,包括数据库建表 SQL、接口定义和项目编译说明
121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
import { useState, useCallback } from 'react'
|
||
import {
|
||
listShopUserAddress,
|
||
getShopUserAddress,
|
||
addShopUserAddress,
|
||
updateShopUserAddress,
|
||
removeShopUserAddress,
|
||
setDefaultAddress,
|
||
} from '@/api/shop/shopUserAddress'
|
||
import type { ShopUserAddress } from '@/api/shop/shopUserAddress/model'
|
||
|
||
interface UseAddressReturn {
|
||
addresses: ShopUserAddress[]
|
||
loading: boolean
|
||
defaultAddress: ShopUserAddress | null
|
||
loadAddresses: () => Promise<void>
|
||
getAddress: (id: number) => Promise<ShopUserAddress | null>
|
||
addAddress: (data: Partial<ShopUserAddress>) => Promise<boolean>
|
||
updateAddress: (data: ShopUserAddress) => Promise<boolean>
|
||
deleteAddress: (id: number) => Promise<boolean>
|
||
setDefault: (id: number) => Promise<boolean>
|
||
}
|
||
|
||
/**
|
||
* 兜底:保证列表中最多只有一个地址是 isDefault=true。
|
||
* 兼容历史脏数据(之前没有"取消其他默认"逻辑时遗留下来的多个默认)。
|
||
* 保留最早创建(id 最小)的为默认;其余的在前端展示时按非默认处理。
|
||
*/
|
||
function ensureSingleDefault(list: ShopUserAddress[]): ShopUserAddress[] {
|
||
const defaults = list.filter(a => a.isDefault)
|
||
if (defaults.length <= 1) return list
|
||
// 多个默认:保留 createTime 最早的;如果都缺 createTime,则保留 id 最小的
|
||
const sorted = [...defaults].sort((a, b) => {
|
||
const at = a.createTime || ''
|
||
const bt = b.createTime || ''
|
||
if (at && bt) return at < bt ? -1 : at > bt ? 1 : 0
|
||
if (at) return -1
|
||
if (bt) return 1
|
||
return (a.id || 0) - (b.id || 0)
|
||
})
|
||
const keepId = sorted[0].id
|
||
return list.map(a => {
|
||
if (a.isDefault && a.id !== keepId) {
|
||
return {...a, isDefault: false}
|
||
}
|
||
return a
|
||
})
|
||
}
|
||
|
||
export function useAddress(): UseAddressReturn {
|
||
const [addresses, setAddresses] = useState<ShopUserAddress[]>([])
|
||
const [loading, setLoading] = useState(false)
|
||
|
||
const loadAddresses = useCallback(async () => {
|
||
setLoading(true)
|
||
try {
|
||
const list = await listShopUserAddress()
|
||
const raw: ShopUserAddress[] = Array.isArray(list) ? list : (list as any)?.data || []
|
||
setAddresses(ensureSingleDefault(raw))
|
||
} catch (error) {
|
||
console.error('Load addresses error:', error)
|
||
setAddresses([])
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}, [])
|
||
|
||
/** 根据 ID 获取单个地址 */
|
||
const getAddress = useCallback(async (id: number): Promise<ShopUserAddress | null> => {
|
||
try {
|
||
return await getShopUserAddress(id)
|
||
} catch {
|
||
return null
|
||
}
|
||
}, [])
|
||
|
||
const addAddress = useCallback(async (data: Partial<ShopUserAddress>): Promise<boolean> => {
|
||
try {
|
||
await addShopUserAddress(data as any)
|
||
await loadAddresses()
|
||
return true
|
||
} catch {
|
||
return false
|
||
}
|
||
}, [loadAddresses])
|
||
|
||
const updateAddress = useCallback(async (data: ShopUserAddress): Promise<boolean> => {
|
||
try {
|
||
await updateShopUserAddress(data)
|
||
await loadAddresses()
|
||
return true
|
||
} catch {
|
||
return false
|
||
}
|
||
}, [loadAddresses])
|
||
|
||
const deleteAddress = useCallback(async (id: number): Promise<boolean> => {
|
||
try {
|
||
await removeShopUserAddress(id)
|
||
setAddresses(prev => prev.filter(a => a.id !== id))
|
||
return true
|
||
} catch {
|
||
return false
|
||
}
|
||
}, [])
|
||
|
||
const setDefault = useCallback(async (id: number): Promise<boolean> => {
|
||
try {
|
||
await setDefaultAddress(id)
|
||
await loadAddresses()
|
||
return true
|
||
} catch {
|
||
return false
|
||
}
|
||
}, [loadAddresses])
|
||
|
||
const defaultAddress = addresses.find(a => a.isDefault) || addresses[0] || null
|
||
|
||
return { addresses, loading, defaultAddress, loadAddresses, getAddress, addAddress, updateAddress, deleteAddress, setDefault }
|
||
}
|