fix(lang): 优化语言处理和数据加载逻辑

This commit is contained in:
JiaJun
2026-04-15 10:32:02 +08:00
parent e9d0d46244
commit bc87ef721a
6 changed files with 69 additions and 26 deletions

View File

@@ -30,7 +30,10 @@ function App() {
if (previousLanguage && previousLanguage !== normalizedLanguage) {
await queryClient.cancelQueries()
await queryClient.invalidateQueries({refetchType: 'active'})
await queryClient.invalidateQueries({
refetchType: 'active',
predicate: (query) => query.queryKey[0] !== 'auth-bootstrap',
})
}
previousLanguageRef.current = normalizedLanguage
@@ -120,7 +123,7 @@ function App() {
</div>
}
>
<Routes>
<Routes>
<Route path="/" element={<HomePage/>}/>
<Route path="/goods" element={<GoodsPage/>}/>
<Route path="/record" element={<RecordPage/>}/>

View File

@@ -48,6 +48,7 @@ export function useAddressBook(options?: UseAddressBookOptions) {
const queryClient = useQueryClient()
const sessionId = useUserStore((state) => state.authInfo?.session_id ?? '')
const [addressForm, setAddressForm] = useState<AddAddressForm>(emptyAddressForm)
const [loadAddressesLoading, setLoadAddressesLoading] = useState(false)
const addressListQuery = useQuery({
queryKey: queryKeys.addressList(sessionId),
@@ -100,6 +101,8 @@ export function useAddressBook(options?: UseAddressBookOptions) {
return []
}
setLoadAddressesLoading(true)
try {
const result = await queryClient.fetchQuery({
queryKey: queryKeys.addressList(sessionId),
@@ -112,6 +115,8 @@ export function useAddressBook(options?: UseAddressBookOptions) {
return result
} catch {
return []
} finally {
setLoadAddressesLoading(false)
}
}, [queryClient, sessionId])
@@ -177,7 +182,7 @@ export function useAddressBook(options?: UseAddressBookOptions) {
sessionId,
addresses,
addressOptions,
loading: addressListQuery.isPending,
loading: addressListQuery.isPending || loadAddressesLoading,
addressForm,
isAddressFormValid,
submitLoading: saveAddressMutation.isPending,

View File

@@ -4,6 +4,7 @@ import {type PropsWithChildren, useEffect, useRef, useState} from 'react'
import {login, validateToken} from '@/api/auth.ts'
import {userAssets} from '@/api/user.ts'
import {useTranslation} from 'react-i18next'
import {normalizeLanguage} from '@/lib/i18n'
import {queryKeys} from '@/lib/queryKeys.ts'
import {useUserStore} from '@/store/user.ts'
import type {HostContextMessage} from '@/types'
@@ -70,7 +71,7 @@ export function AuthGuide({children}: PropsWithChildren) {
}
if (typeof language === 'string' && language.trim()) {
setLanguage(language.trim())
setLanguage(normalizeLanguage(language))
}
}

View File

@@ -4,7 +4,7 @@ import {useTranslation} from 'react-i18next'
import {ChevronRight} from 'lucide-react'
import Button from '@/components/button'
import {HOME_CATEGORY_META_MAP} from '@/constant'
import {HOME_CATEGORY_META_MAP, HOME_GOOD_TYPE_ORDER} from '@/constant'
import type {ProductCategory, ProductItem} from '@/types'
type GoodsCategoryListProps = {
@@ -56,12 +56,52 @@ export function GoodsCategoryList({
}: GoodsCategoryListProps) {
const {t} = useTranslation()
const resolvedEmptyText = emptyText ?? t('goods.noGoodsAvailableYet')
if (loading) {
return (
<div className="pb-[24px] mt-[20px]">
<div className="liquid-glass-bg px-[16px] py-[18px] text-[14px] text-white/60">
{t('goods.loading')}
</div>
<div className="pb-[24px]">
{HOME_GOOD_TYPE_ORDER.map((categoryId) => {
const CategoryIcon = HOME_CATEGORY_META_MAP[categoryId].icon
return (
<div key={categoryId} className="mt-[20px]">
<div className="mb-[10px] flex items-center justify-between gap-[12px]">
<div className="flex min-w-0 items-center gap-[10px]">
<div
className="flex h-[36px] w-[36px] items-center justify-center rounded-[12px] bg-[#FA6A00]/15 text-[#FE9F00]">
<CategoryIcon className="h-[18px] w-[18px]" aria-hidden="true"/>
</div>
<div className="truncate text-[15px] font-semibold text-white">
{t(HOME_CATEGORY_META_MAP[categoryId].nameKey)}
</div>
</div>
{showMore ? (
<div className="text-[12px] font-light text-[#FA6A00] underline">
{t('common.more')}
</div>
) : null}
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
{Array.from({length: 4}).map((_, index) => (
<div
key={`${categoryId}-${index}`}
className="liquid-glass-bg flex min-h-[260px] w-full flex-col items-stretch justify-start overflow-hidden"
>
<div className="h-[116px] w-full bg-white/6 sm:h-[128px]"></div>
<div className="flex flex-1 flex-col items-start justify-between gap-[12px] p-[12px] sm:p-[14px]">
<div className="w-full space-y-[8px]">
<div className="h-[18px] w-[72%] rounded-full bg-white/10"></div>
<div className="h-[14px] w-[48%] rounded-full bg-white/8"></div>
</div>
<div className="h-[18px] w-[92px] rounded-full bg-[#FA6A00]/18"></div>
<div className="h-[36px] w-full rounded-[10px] bg-[#FA6A00]/20"></div>
</div>
</div>
))}
</div>
</div>
)
})}
</div>
)
}

View File

@@ -2,6 +2,7 @@ import ky, {HTTPError, type AfterResponseHook, type Input, type KyInstance, type
import {notifyError, resolveToastMessage} from '@/features/notifications'
import i18n from '@/lib/i18n'
import {normalizeLanguage} from '@/lib/i18n'
import {useUserStore} from '@/store/user.ts'
import type {ValidateTokenData} from '@/types/auth.type.ts'
import {objectToFormData} from './tool'
@@ -67,7 +68,7 @@ export const setAccessTokenFormatter = (formatter?: TokenFormatter) => {
accessTokenFormatter = formatter ?? ((token) => `Bearer ${token}`)
}
const getRequestLanguage = () => useUserStore.getState().language?.trim() || 'zh'
const getRequestLanguage = () => normalizeLanguage(useUserStore.getState().language)
const authRefreshClient = ky.create({
baseUrl: API_BASE_URL,

View File

@@ -103,20 +103,6 @@ function getOrderStatus(status?: string | number) {
}
function getOrderCategory(item: OrderItem) {
if (item.type?.trim()) {
const normalizedType = item.type.trim().toUpperCase()
switch (normalizedType) {
case 'BONUS':
return i18n.t('record.categories.bonus')
case 'PHYSICAL':
return i18n.t('record.categories.physical')
case 'WITHDRAW':
return i18n.t('record.categories.withdraw')
default:
return toTitleCase(normalizedType)
}
}
if (item.type_title) {
return item.type_title
}
@@ -126,15 +112,22 @@ function getOrderCategory(item: OrderItem) {
}
if (item.type) {
switch (item.type) {
const normalizedType = item.type.trim().toUpperCase()
switch (normalizedType) {
case 'BONUS':
return i18n.t('record.categories.bonus')
case 'PHYSICAL':
return i18n.t('record.categories.physical')
case 'WITHDRAW':
return i18n.t('record.categories.withdraw')
case '1':
return i18n.t('record.categories.withdraw')
case '2':
return i18n.t('record.categories.physical')
case '3':
return i18n.t('record.categories.bonus')
default:
return toTitleCase(item.type)
return toTitleCase(normalizedType)
}
}