This commit is contained in:
李志鹏
2026-06-03 13:18:57 +08:00
7 changed files with 92 additions and 94 deletions

View File

@@ -146,7 +146,9 @@ export const getUserLanguage = (): Promise<ApiResponse> => {
} }
// 设置语言 // 设置语言
export const setUserLanguage = (language: string): Promise<ApiResponse> => { export const setUserLanguage = (
language: 'ENGLISH' | 'CHINESE_SIMPLIFIED'
): Promise<ApiResponse> => {
return request({ return request({
url: '/buyer/profile/setLanguage', url: '/buyer/profile/setLanguage',
method: 'post', method: 'post',

View File

@@ -83,7 +83,7 @@ export default {
avatarCrop: { avatarCrop: {
title: 'Crop Avatar', title: 'Crop Avatar',
confirm: 'Confirm', confirm: 'Confirm',
processing: 'Processing...', processing: 'Processing...'
}, },
security: { security: {
title: 'Security', title: 'Security',
@@ -159,8 +159,8 @@ export default {
other: 'Other' other: 'Other'
}, },
languages: { languages: {
english: 'English', ENGLISH: 'English',
chinese: 'Chinese' CHINESE_SIMPLIFIED: 'Chinese Simplified'
}, },
regions: { regions: {
hongKongSar: 'Hong Kong SAR', hongKongSar: 'Hong Kong SAR',
@@ -338,34 +338,41 @@ export default {
AddToCart: 'Add to Cart' AddToCart: 'Add to Cart'
}, },
Home: { Home: {
IndexTitle: 'Were Seeking<br /><span>Fashion Voice</span><br /><span class="small">Worth Featuring.</span>', IndexTitle:
IndexTip: 'Discover collections through the stories behind their creation. A curated space connecting designers, narratives, and fashion commerce.', 'Were Seeking<br /><span>Fashion Voice</span><br /><span class="small">Worth Featuring.</span>',
IndexTip:
'Discover collections through the stories behind their creation. A curated space connecting designers, narratives, and fashion commerce.',
DesignerTitle: 'Designer Community', DesignerTitle: 'Designer Community',
DesignerTip: 'Discover the designers shaping AiDAs creative landscape. <br />Each month, we will showcase a curated selection of their most distinguished works.', DesignerTip:
'Discover the designers shaping AiDAs creative landscape. <br />Each month, we will showcase a curated selection of their most distinguished works.',
SearchBrands: 'Search Brands', SearchBrands: 'Search Brands',
AidaTitle: 'Design with AiDA', AidaTitle: 'Design with AiDA',
AidaTip: 'Each garment on this platform is where designer vision blooms through AiDA. A tool that nurtures your creativity, never overshadows it. Let your ideas flourish.', AidaTip:
'Each garment on this platform is where designer vision blooms through AiDA. A tool that nurtures your creativity, never overshadows it. Let your ideas flourish.',
TryNow: 'Try Now', TryNow: 'Try Now',
DigitalItems: 'Digital Items', DigitalItems: 'Digital Items',
DigitalItemsTip1: 'AiDA captures your boldest thoughts and transforms them into vivid<br/>digital visions—a virtual realm where creativity collides and evolves.', DigitalItemsTip1:
DigitalItemsTip2: 'AiDA accelerates style innovation, shaping daily pieces that keep<br/>your wardrobe in sync with modern fashions rhythm.', 'AiDA captures your boldest thoughts and transforms them into vivid<br/>digital visions—a virtual realm where creativity collides and evolves.',
FooterTip: "Stylish Parade is a commerce platform for designers, serving as AiDA's commercial extension.", DigitalItemsTip2:
FooterAidaTip: "Bloom your Creativity with AiDA!", 'AiDA accelerates style innovation, shaping daily pieces that keep<br/>your wardrobe in sync with modern fashions rhythm.',
Help: "Help", FooterTip:
FAQ: "FAQ", "Stylish Parade is a commerce platform for designers, serving as AiDA's commercial extension.",
MyAccount: "My Account", FooterAidaTip: 'Bloom your Creativity with AiDA!',
MyOrders: "My Orders", Help: 'Help',
PaymentInvoices: "Payment and Invoices", FAQ: 'FAQ',
CopyrightLicense: "Copyright Licence", MyAccount: 'My Account',
Polices: "Policies", MyOrders: 'My Orders',
Legal: "Legal", PaymentInvoices: 'Payment and Invoices',
PrivacyPolicy: "Privacy Policy", CopyrightLicense: 'Copyright Licence',
CookiesSettings: "Cookies Settings", Polices: 'Policies',
PurchaseConditions: "Purchase Conditions", Legal: 'Legal',
Company: "Company", PrivacyPolicy: 'Privacy Policy',
AboutUs: "About Us", CookiesSettings: 'Cookies Settings',
Offices: "Offices", PurchaseConditions: 'Purchase Conditions',
JoinWithUs: "Join with Us", Company: 'Company',
AboutUs: 'About Us',
Offices: 'Offices',
JoinWithUs: 'Join with Us'
}, },
addShoppingCart: { addShoppingCart: {
title: 'Added to your Shopping Cart', title: 'Added to your Shopping Cart',
@@ -394,18 +401,22 @@ export default {
OrderSummary: 'Order Summary', OrderSummary: 'Order Summary',
PaymentDetails: 'Payment Details', PaymentDetails: 'Payment Details',
CreditDebitCard: 'Credit / Debit Card', CreditDebitCard: 'Credit / Debit Card',
AgreementText: 'I agree to the <span onclick="{onTermsClick}">Terms & Conditions</span> and <span onclick="{onPrivacyClick}">Privacy Policy</span>. All digital item sales are final and non-refundable.', AgreementText:
'I agree to the <span onclick="{onTermsClick}">Terms & Conditions</span> and <span onclick="{onPrivacyClick}">Privacy Policy</span>. All digital item sales are final and non-refundable.',
PayWithStripe: 'Pay with Stripe', PayWithStripe: 'Pay with Stripe',
PayWith: 'Pay with', PayWith: 'Pay with',
Cancel: 'Cancel', Cancel: 'Cancel',
IHaveCompletedPayment: 'I Have Completed payment', IHaveCompletedPayment: 'I Have Completed payment',
Back: 'Back', Back: 'Back',
PayTip1: "You'll be redirected to a Stripe popup to log in and confirm. No card details are shared with Stylish Parade — Stripe handles all payment security.", PayTip1:
PayTip2: "Please keep the window open until the payment is completed. If you are to open the payment window, please check your browser settings to see if pop-ups are being blocked. Points may be delayed after successful payment. Please wait 1-3 minutes and click the credits refresh button.", "You'll be redirected to a Stripe popup to log in and confirm. No card details are shared with Stylish Parade — Stripe handles all payment security.",
PurchaseSuccessful: "Purchase Successful", PayTip2:
PurchaseSuccessfulTip: "Your digital items are now available and have been saved in Personal Center → My Wardrobe.", 'Please keep the window open until the payment is completed. If you are to open the payment window, please check your browser settings to see if pop-ups are being blocked. Points may be delayed after successful payment. Please wait 1-3 minutes and click the credits refresh button.',
DownloadAllAssets: "download all Assets", PurchaseSuccessful: 'Purchase Successful',
ExportInvoice: "Export Invoice", PurchaseSuccessfulTip:
ContinueShopping: "Continue Shopping" 'Your digital items are now available and have been saved in Personal Center → My Wardrobe.',
DownloadAllAssets: 'download all Assets',
ExportInvoice: 'Export Invoice',
ContinueShopping: 'Continue Shopping'
} }
} }

View File

@@ -78,7 +78,7 @@ export default {
avatarCrop: { avatarCrop: {
title: '裁剪头像', title: '裁剪头像',
confirm: '确认', confirm: '确认',
processing: '处理中...', processing: '处理中...'
}, },
security: { security: {
title: '安全', title: '安全',
@@ -154,8 +154,8 @@ export default {
other: '其他' other: '其他'
}, },
languages: { languages: {
english: '英文', ENGLISH: '英文',
chinese: '中文' CHINESE_SIMPLIFIED: '简体中文'
}, },
regions: { regions: {
hongKongSar: '中国香港特别行政区', hongKongSar: '中国香港特别行政区',
@@ -393,18 +393,21 @@ export default {
OrderSummary: '订单信息', OrderSummary: '订单信息',
PaymentDetails: '支付详情', PaymentDetails: '支付详情',
CreditDebitCard: '信用卡/借记卡', CreditDebitCard: '信用卡/借记卡',
AgreementText: '我同意 <span onclick="{onTermsClick}">使用条款与条件</span> 和 <span onclick="{onPrivacyClick}">隐私政策</span>。所有数字资产销售均为最终销售,不可退款。', AgreementText:
'我同意 <span onclick="{onTermsClick}">使用条款与条件</span> 和 <span onclick="{onPrivacyClick}">隐私政策</span>。所有数字资产销售均为最终销售,不可退款。',
PayWithStripe: '使用 Stripe 支付', PayWithStripe: '使用 Stripe 支付',
PayWith: '支付', PayWith: '支付',
Cancel: '取消', Cancel: '取消',
IHaveCompletedPayment: '我已完成支付', IHaveCompletedPayment: '我已完成支付',
Back: '返回', Back: '返回',
PayTip1: "您将被重定向到 Stripe 弹窗以登录并确认支付。您的信用卡信息不会被 Stylish Parade 收集。Stripe 处理所有支付安全。", PayTip1:
PayTip2: "请保持窗口打开,直到支付完成。如果您打开支付窗口,请检查浏览器设置以查看是否已阻止弹窗。支付完成后,积分可能会有延迟。请等待 1-3 分钟并点击积分刷新按钮。", '您将被重定向到 Stripe 弹窗以登录并确认支付。您的信用卡信息不会被 Stylish Parade 收集。Stripe 处理所有支付安全。',
PurchaseSuccessful: "购买成功", PayTip2:
PurchaseSuccessfulTip: "您的数字资产已保存在个人中心的我的衣橱中。", '请保持窗口打开,直到支付完成。如果您打开支付窗口,请检查浏览器设置以查看是否已阻止弹窗。支付完成后,积分可能会有延迟。请等待 1-3 分钟并点击积分刷新按钮。',
DownloadAllAssets: "下载所有资产", PurchaseSuccessful: '购买成功',
ExportInvoice: "导出发票", PurchaseSuccessfulTip: '您的数字资产已保存在个人中心的我的衣橱中。',
ContinueShopping: "继续购物" DownloadAllAssets: '下载所有资产',
ExportInvoice: '导出发票',
ContinueShopping: '继续购物'
} }
} }

View File

@@ -175,12 +175,11 @@
const onLanguageClick = () => { const onLanguageClick = () => {
locale.value = locale.value === 'ENGLISH' ? 'CHINESE_SIMPLIFIED' : 'ENGLISH' locale.value = locale.value === 'ENGLISH' ? 'CHINESE_SIMPLIFIED' : 'ENGLISH'
localStorage.setItem('language', locale.value) localStorage.setItem('language', locale.value)
console.log(locale.value)
const localeMap: Record<string, string> = { setUserLanguage(locale.value).then((res) => {
ENGLISH: 'en', userInfoStore.setToken(res)
CHINESE_SIMPLIFIED: 'zh-CN' location.reload()
} })
setUserLanguage(localeMap[locale.value])
} }
</script> </script>

View File

@@ -75,7 +75,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import EmailVerificationDialog from './components/EmailVerificationDialog.vue' import EmailVerificationDialog from './components/EmailVerificationDialog.vue'
import ProfileSection from './components/ProfileSection.vue' import ProfileSection from './components/ProfileSection.vue'
@@ -119,6 +119,10 @@
loadUserProfile loadUserProfile
} = useSettingsForm({ t, locale }) } = useSettingsForm({ t, locale })
// watch(locale, () => {
// loadUserProfile()
// })
onMounted(() => { onMounted(() => {
loadUserProfile() loadUserProfile()
}) })

View File

@@ -12,7 +12,7 @@ export const roleValues = [
'other' 'other'
] as const ] as const
export const languageValues = ['english', 'chinese'] as const export const languageValues = ['ENGLISH', 'CHINESE_SIMPLIFIED'] as const
export type RoleValue = (typeof roleValues)[number] export type RoleValue = (typeof roleValues)[number]

View File

@@ -6,7 +6,8 @@ import {
type UserProfile, type UserProfile,
updateUserProfile, updateUserProfile,
verifyEmailCode, verifyEmailCode,
fetchVerifyCode fetchVerifyCode,
setUserLanguage
} from '@/api/user' } from '@/api/user'
import regionList from '@/utils/area' import regionList from '@/utils/area'
import { import {
@@ -18,6 +19,9 @@ import {
type SettingsData type SettingsData
} from './types' } from './types'
import { validateCase, validateLength, validateSpecial } from '@/views/login/tools' import { validateCase, validateLength, validateSpecial } from '@/views/login/tools'
import {useUserInfoStore} from '@/stores'
const userInfoStore = useUserInfoStore()
type Translate = (key: string, ...args: unknown[]) => string type Translate = (key: string, ...args: unknown[]) => string
@@ -26,24 +30,6 @@ interface UseSettingsFormOptions {
locale: Ref<string> locale: Ref<string>
} }
// 前端 UI 使用的语言值(用于下拉选择)
const languageLocaleMap: Record<LanguageValue, 'ENGLISH' | 'CHINESE_SIMPLIFIED'> = {
english: 'ENGLISH',
chinese: 'CHINESE_SIMPLIFIED'
}
// 后端 API 使用的语言值
const backendLanguageMap: Record<'ENGLISH' | 'CHINESE_SIMPLIFIED', 'en' | 'zh-CN'> = {
ENGLISH: 'en',
CHINESE_SIMPLIFIED: 'zh-CN'
}
// 后端语言值转换为前端语言值
const backendToFrontendLanguage: Record<'en' | 'zh-CN', LanguageValue> = {
en: 'english',
'zh-CN': 'chinese'
}
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const isRoleValue = (value: string): value is RoleValue => const isRoleValue = (value: string): value is RoleValue =>
@@ -75,15 +61,10 @@ const normalizeLanguage = (language: string | null | undefined): LanguageValue =
if (!language) { if (!language) {
return '' as LanguageValue return '' as LanguageValue
} }
// 后端返回 'en' 或 'zh-CN'
const trimmed = language.trim() const trimmed = language.trim()
if (trimmed === 'en') { if (trimmed === 'ENGLISH' || trimmed === 'CHINESE_SIMPLIFIED') {
return 'english' return trimmed as LanguageValue
} else if (trimmed === 'zh-CN') {
return 'chinese'
} }
return '' as LanguageValue return '' as LanguageValue
} }
@@ -182,9 +163,8 @@ export function useSettingsForm({ t, locale }: UseSettingsFormOptions) {
} }
const syncAppLanguage = (language: LanguageValue) => { const syncAppLanguage = (language: LanguageValue) => {
const nextLocale = languageLocaleMap[language] locale.value = language
locale.value = nextLocale localStorage.setItem('language', language)
localStorage.setItem('language', nextLocale)
} }
const loadUserProfile = async () => { const loadUserProfile = async () => {
@@ -334,12 +314,8 @@ export function useSettingsForm({ t, locale }: UseSettingsFormOptions) {
return return
} }
// 前端语言值转换为后端格式 // 前端语言值直接作为后端格式
let backendLanguage = '' const backendLanguage = draftData.value.language || ''
if (draftData.value.language) {
const i18nLocale = languageLocaleMap[draftData.value.language as LanguageValue]
backendLanguage = backendLanguageMap[i18nLocale]
}
const nextData: UserProfile = { const nextData: UserProfile = {
firstName: draftData.value.firstName.trim(), firstName: draftData.value.firstName.trim(),
@@ -370,10 +346,8 @@ export function useSettingsForm({ t, locale }: UseSettingsFormOptions) {
try { try {
await updateUserProfile(nextData) await updateUserProfile(nextData)
// 后端返回的语言值转换为前端格式 // 后端返回的语言值直接作为前端格式
const frontendLanguage = backendLanguage const frontendLanguage = backendLanguage as LanguageValue
? backendToFrontendLanguage[backendLanguage as 'en' | 'zh-CN']
: ''
const settingsData: SettingsData = { const settingsData: SettingsData = {
firstName: nextData.firstName, firstName: nextData.firstName,
@@ -388,7 +362,12 @@ export function useSettingsForm({ t, locale }: UseSettingsFormOptions) {
console.log(nextData) console.log(nextData)
if (frontendLanguage && frontendLanguage !== previousLanguage) { if (frontendLanguage && frontendLanguage !== previousLanguage) {
syncAppLanguage(frontendLanguage as LanguageValue) // syncAppLanguage(frontendLanguage as LanguageValue)
setUserLanguage(frontendLanguage).then((res) => {
// console.log(res)
userInfoStore.setToken(res)
location.reload()
})
} }
draftData.value = cloneSettingsData(sourceData.value) draftData.value = cloneSettingsData(sourceData.value)