Commit befc0b59 authored by 罗超's avatar 罗超

更新国际化,守卫,加载逻辑

parent 15c0667c
......@@ -7,7 +7,7 @@
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin-allow-popups">
<title>C-end</title>
<title></title>
</head>
<body>
<div id="app"></div>
......
{
"name": "C-end",
"name": "boyuecend",
"version": "0.0.1",
"private": true,
"type": "module",
......
<script setup lang="ts">
import { RouterView, useRoute } from 'vue-router'
import { computed, watch, onMounted, ref } from 'vue'
import { watch, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { ConfigProvider } from '@arco-design/web-vue'
import LanguageSwitcher from './components/common/LanguageSwitcher.vue'
import { configureArcoLocale, globalArcoLocale } from './i18n/arco'
import { useUserStore } from './stores/user'
import { useSystemConfigStore } from './stores/systemConfig'
import { loadThemeFromConfig } from './utils/themeUtils'
const route = useRoute()
const { t, locale } = useI18n()
const useUser = useUserStore()
const systemConfigStore = useSystemConfigStore()
// 监听语言变化,更新 Arco Design 国际化
......@@ -28,7 +25,7 @@ watch(
() => route.meta.title,
(title) => {
if (title && typeof title === 'string') {
document.title = `${t(title)} - ${t('common.systemName')}`
document.title = `${t(title)} - ${systemConfigStore.config?.webSiteName || ''}`
}
},
{ immediate: true }
......
......@@ -22,10 +22,33 @@ const service = axios.create({
// 请求拦截器:自动加 token 或临时令牌
service.interceptors.request.use(
config => {
async config => {
const userStore = useUserStore();
const token = userStore.getUserToken;
const systemConfigStore = useSystemConfigStore();
// 如果是初始化接口本身,直接放行(避免循环等待)
const isInitRequest = config.url?.includes('/tenant-confing-by-domain')
// 等待初始化完成(如果是新用户且正在初始化)
// 对于有缓存的用户(isInitialized === true),不需要等待
if (!isInitRequest && !systemConfigStore.isInitialized) {
// 如果正在初始化,等待完成(最多等待 5 秒,避免无限等待)
if (systemConfigStore.isLoading && systemConfigStore.initPromise) {
try {
await Promise.race([
systemConfigStore.initPromise,
new Promise((_, reject) => setTimeout(() => reject(new Error('Init timeout')), 5000))
])
} catch (error) {
// 初始化失败或超时,但继续请求(可能某些接口不需要 tenantId)
console.warn('System config initialization failed or timeout, continuing request:', error)
}
}
// 如果未初始化且未开始初始化,不等待,直接继续
// tenantId 可能为空,但不会阻塞请求
}
if(systemConfigStore.groupId > 0) {
config.headers['x-user-group'] = systemConfigStore.groupId;
}
......
......@@ -97,7 +97,7 @@ const updatePageTitle = () => {
case '/billing':
titleKey = 'billing.title'
break
case '/account':
case '/personalCenter/accountCenter/account':
titleKey = 'account.title'
break
}
......
......@@ -73,7 +73,7 @@
type="primary"
@click.stop="handleClick(item)"
>
查看更多
{{ $t('common.more') }}
</a-button>
</div>
</transition>
......
......@@ -108,6 +108,7 @@ export default {
chinese: '简体中文',
english: 'English',
systemName: 'C-end',
more:'View More'
},
header: {
destinations: 'Destinations',
......
......@@ -108,6 +108,7 @@ export default {
chinese: '简体中文',
english: 'English',
systemName: 'C-end',
more:'Xem thêm'
},
header: {
destinations: 'Điểm đến',
......
......@@ -108,6 +108,7 @@ export default {
chinese: '简体中文',
english: 'English',
systemName: 'C-end',
more:'查看更多'
},
header: {
destinations: '目的地',
......
......@@ -108,6 +108,7 @@ export default {
chinese: '繁體中文',
english: 'English',
systemName: 'C-end',
more:'检索更多'
},
header: {
destinations: '目的地',
......
......@@ -157,11 +157,11 @@ const handleGoLogin = () => {
}
const handleGoProfile = () => {
goPage('/myOrder')
goPage('/personalCenter/myOrder')
}
const handleGoHome = () => {
goPage('/home')
goPage('/')
}
</script>
......
import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
import { useUserStore } from '@/stores/user'
// 白名单路由(不需要登录即可访问)
const whiteList = ['/login', '/register', '/forgePassword', '/', '/home']
const whiteListRegex = [/\/oa\//]
/**
* 路由权限守卫
* 通过路由 meta.requiresAuth 标识是否需要 token 验证
* 如果 requiresAuth 为 true,则检查用户是否已登录
*/
export const createPermissionGuard = (
to: RouteLocationNormalized,
from: RouteLocationNormalized,
_from: RouteLocationNormalized,
next: NavigationGuardNext,
) => {
const userStore = useUserStore()
// 检查是否是白名单路由
if (whiteList.includes(to.path)) {
next()
return
}
// 检查所有匹配的路由记录(包括父路由和子路由)是否需要认证
const requiresAuth = to.matched.some(record => record.meta.requiresAuth === true)
// 检查是否是白名单正则路由
if (whiteListRegex.some((regex) => regex.test(to.path))) {
next()
if (requiresAuth && !userStore.getUserToken) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
return
}
// 检查用户是否登录
// if (!userStore.getUserToken) {
// next('/login')
// return
// }
next()
}
......@@ -7,69 +7,69 @@ const router = createRouter({
routes: [
{
path: '/',
redirect:'/home',
redirect:'',
component: () => import ('../layouts/HomeLayout.vue'),
children: [
{
path: '/home',
path: '/',
name: 'home',
meta: { title: 'page.home' },
component: () => import ('../views/home/index.vue')
},
{
path: '/personalCenter',
meta: { title: "page.profile" },
meta: { title: "page.profile", requiresAuth: true },
component: () => import ('../views/personalCenter/index.vue'),
children: [{
path: '/myOrder',//我的订单
meta: { title: "page.myOrder" },
path: 'myOrder',//我的订单
meta: { title: "page.myOrder", requiresAuth: true },
component: () => import ('../views/personalCenter/myOrder.vue')
},
{
path: '/systemMessage',//系统消息
meta: { title: "page.systemMessage" },
path: 'systemMessage',//系统消息
meta: { title: "page.systemMessage", requiresAuth: true },
component: () => import ('../views/personalCenter/systemMessage.vue')
},
{
path: '/myCollection',//我的收藏
meta: { title: "page.myCollection" },
path: 'myCollection',//我的收藏
meta: { title: "page.myCollection", requiresAuth: true },
component: () => import ('../views/personalCenter/myCollection.vue')
},
{
path: '/myCoupon',//我的优惠券
meta: { title: "page.coupon" },
path: 'myCoupon',//我的优惠券
meta: { title: "page.coupon", requiresAuth: true },
component: () => import ('../views/personalCenter/myCoupon.vue')
},
{
path: '/accountCenter',//账号中心
meta: { title: "page.accountCenter" },
path: 'accountCenter',//账号中心
meta: { title: "page.accountCenter", requiresAuth: true },
component: () => import ('../views/personalCenter/accountCenter.vue'),
children: [
{
path: '/basicInfor',//基础资料
meta: { title: "page.basicInfor" },
path: 'basicInfor',//基础资料
meta: { title: "page.basicInfor", requiresAuth: true },
component: () => import ('../views/personalCenter/accountPage/basicInfor.vue')
},
{
path: '/account/:reType?',//账户
meta: { title: "page.account" },
path: 'account/:reType?',//账户
meta: { title: "page.account", requiresAuth: true },
component: () => import ('../views/personalCenter/accountPage/account.vue')
},
{
path: '/passengerList',//乘客列表
meta: { title: "page.passengerList" },
path: 'passengerList',//乘客列表
meta: { title: "page.passengerList", requiresAuth: true },
component: () => import ('../views/personalCenter/accountPage/passengerList.vue')
},
{
path: '/mailingAddressList',//邮寄地址列表
meta: { title: "page.mailingAddressList" },
path: 'mailingAddressList',//邮寄地址列表
meta: { title: "page.mailingAddressList", requiresAuth: true },
component: () => import ('../views/personalCenter/accountPage/mailingAddressList.vue')
},
]
},
{
path: '/distributionCenter',//分销中心
meta: { title: "page.distributionCenter" },
meta: { title: "page.distributionCenter", requiresAuth: true },
component: () => import ('../views/personalCenter/distributionCenter.vue')
},
]
......
......@@ -7,6 +7,22 @@ import { ResultMessage } from '@/utils/message'
import router from '@/router'
import ErpUserService from '@/services/ErpUserService'
import { ApiResult } from '@/types/ApiResult'
import SecureLS from 'secure-ls'
import type { StorageLike } from 'pinia-plugin-persistedstate'
const ls = new SecureLS({
isCompression: false,
encryptionSecret: '38c31684-d00d-30dc-82e0-fad9eec46d1c',
})
const st: StorageLike = {
setItem(key: string, value: string) {
ls.set(key, value)
},
getItem(key: string): string | null {
return ls.get(key)
},
}
/**
* 系统配置 Store
......@@ -36,6 +52,9 @@ export const useSystemConfigStore = defineStore('systemConfig', {
groupId:0 as number,
erpAdminUserId:0 as number,
// 初始化 Promise(用于等待初始化完成)
initPromise: null as Promise<void> | null,
}),
getters: {
/**
......@@ -167,74 +186,112 @@ export const useSystemConfigStore = defineStore('systemConfig', {
}
},
/**
* 等待初始化完成
* 如果已经初始化,立即返回;如果正在初始化,等待完成;如果未初始化,返回 resolved Promise
*/
async waitForInitialization(): Promise<void> {
// 如果已经初始化,立即返回
if (this.isInitialized) {
return
}
// 如果正在初始化,等待现有的 Promise
if (this.initPromise) {
return this.initPromise
}
// 如果未初始化且未开始初始化,返回 resolved Promise(调用者需要自己初始化)
return Promise.resolve()
},
/**
* 初始化系统配置
* 根据域名获取租户信息和平台配置
*/
async initialize(domainName?: string) {
// 如果已经初始化过,直接返回
if (this.isInitialized && !domainName) {
async initialize(domainName?: string): Promise<void> {
// 如果已经初始化过且没有指定新域名,直接返回
if (this.tenantId && this.tenantId.length > 0 && domainName) {
await this.queryTenantInfoAsyncAsync(domainName)
return
}
this.isLoading = true
this.loadError = null
// 如果正在初始化,等待现有的 Promise
if (this.initPromise && this.isLoading) {
return this.initPromise
}
// 创建新的初始化 Promise
this.initPromise = (async () => {
this.isLoading = true
this.loadError = null
try {
await this.queryTenantInfoAsyncAsync(domainName)
try {
const data = await SystemConfigService.getTenantInfoByDomainAsync(domainName)
// 保存租户信息
this.tenantId = data.tenantId || null
this.navs = data.navList || []
this.domainName = data.config?.domainName || null
this.distributorId = data.distributorId || 0
// 保存平台配置
this.platformConfig = data.platform || null
this.config = data.config
this.groupId = data.erpGroupId
// 如果有租户ID,加载代理商配置
if (this.tenantId) {
await this.getGroupInfoAsync(this.groupId)
try {
const partnerConfig = await PartnerCenterConfigService.getConfigByTenantIdAsync(this.tenantId)
this.partnerCenterConfig = partnerConfig || {
isEnabled: false
// 如果有租户ID,加载代理商配置
if (this.tenantId) {
await this.getGroupInfoAsync(this.groupId)
try {
const partnerConfig = await PartnerCenterConfigService.getConfigByTenantIdAsync(this.tenantId)
this.partnerCenterConfig = partnerConfig || {
isEnabled: false
}
} catch (error) {
console.warn('Failed to load partner center config:', error)
// 代理商配置加载失败不影响整体初始化,设置默认值
this.partnerCenterConfig = {
isEnabled: false
}
}
} catch (error) {
console.warn('Failed to load partner center config:', error)
// 代理商配置加载失败不影响整体初始化,设置默认值
this.partnerCenterConfig = {
isEnabled: false
}
// 如果有默认语言,应用到 i18n
if (this.platformConfig?.defaultLanguage) {
const savedLocale = localStorage.getItem('locale')
// 只有在用户没有手动设置过语言时,才使用默认语言
if (!savedLocale) {
i18n.global.locale.value = this.platformConfig.defaultLanguage as any
localStorage.setItem('locale', this.platformConfig.defaultLanguage)
}
}
}
// 如果有默认语言,应用到 i18n
if (this.platformConfig?.defaultLanguage) {
const savedLocale = localStorage.getItem('locale')
// 只有在用户没有手动设置过语言时,才使用默认语言
if (!savedLocale) {
i18n.global.locale.value = this.platformConfig.defaultLanguage as any
localStorage.setItem('locale', this.platformConfig.defaultLanguage)
// 更新网站图标
if (this.config?.favicon) {
this.updateFavicon(this.config.favicon)
}
} catch (error) {
this.loadError = error as Error
ResultMessage.Error('Failed to initialize system config:' + error)
router.push('/login')
console.error('Failed to initialize system config:', error)
throw error
} finally {
this.isLoading = false
this.isInitialized = true
}
// 更新网站图标
if (this.config?.favicon) {
this.updateFavicon(this.config.favicon)
}
this.isInitialized = true
} catch (error) {
this.loadError = error as Error
ResultMessage.Error('Failed to initialize system config:' + error)
router.push('/login')
console.error('Failed to initialize system config:', error)
//throw error
} finally {
this.isLoading = false
}
})()
return this.initPromise
},
async queryTenantInfoAsyncAsync(domainName?: string) {
console.log('enter in/queryTenantInfoAsyncAsync')
const data = await SystemConfigService.getTenantInfoByDomainAsync(domainName)
// 保存租户信息
this.tenantId = data.tenantId || null
this.navs = data.navList || []
this.domainName = data.config?.domainName || null
this.distributorId = data.distributorId || 0
// 保存平台配置
this.platformConfig = data.platform || null
this.config = data.config
this.groupId = data.erpGroupId
},
/**
......@@ -243,9 +300,81 @@ export const useSystemConfigStore = defineStore('systemConfig', {
*/
async refresh(domainName?: string) {
this.isInitialized = false
this.initPromise = null
await this.initialize(domainName)
},
/**
* 部分更新配置字段
* 只更新指定的字段,不重新加载全部数据
*/
updateConfig(updates: {
tenantId?: string | null
domainName?: string | null
distributorId?: number | null
navs?: NavItemDto[] | null
platformConfig?: Partial<PlatformConfigDto> | null
partnerCenterConfig?: Partial<PartnerCenterConfigDto> | null
config?: any | null
groupId?: number
erpAdminUserId?: number
}) {
if (updates.tenantId !== undefined) {
this.tenantId = updates.tenantId
}
if (updates.domainName !== undefined) {
this.domainName = updates.domainName
}
if (updates.distributorId !== undefined) {
this.distributorId = updates.distributorId
}
if (updates.navs !== undefined) {
this.navs = updates.navs
}
if (updates.platformConfig !== undefined) {
if (updates.platformConfig === null) {
this.platformConfig = null
} else if (this.platformConfig) {
this.platformConfig = {
...this.platformConfig,
...updates.platformConfig,
}
} else {
this.platformConfig = updates.platformConfig as PlatformConfigDto
}
}
if (updates.partnerCenterConfig !== undefined) {
if (updates.partnerCenterConfig === null) {
this.partnerCenterConfig = null
} else if (this.partnerCenterConfig) {
this.partnerCenterConfig = {
...this.partnerCenterConfig,
...updates.partnerCenterConfig,
}
} else {
this.partnerCenterConfig = updates.partnerCenterConfig as PartnerCenterConfigDto
}
}
if (updates.config !== undefined) {
if (updates.config === null) {
this.config = null
} else if (this.config) {
this.config = {
...this.config,
...updates.config,
}
} else {
this.config = updates.config
}
}
if (updates.groupId !== undefined) {
this.groupId = updates.groupId
}
if (updates.erpAdminUserId !== undefined) {
this.erpAdminUserId = updates.erpAdminUserId
}
},
/**
* 更新平台配置(本地状态)
*/
......@@ -287,6 +416,10 @@ export const useSystemConfigStore = defineStore('systemConfig', {
this.isLoading = false
this.isInitialized = false
this.loadError = null
this.initPromise = null
},
}
},
persist: {
storage: st,
},
})
\ No newline at end of file
......@@ -11,6 +11,13 @@ declare module 'vue-router' {
*/
title?: string
/**
* 是否需要 token 验证
* 如果为 true,访问该路由时需要检查用户是否已登录
* 如果未登录,将重定向到登录页
*/
requiresAuth?: boolean
/**
* 筛选器参数
* 用于存储页面级的筛选配置
......
......@@ -6,7 +6,7 @@
import CryptoJS from 'crypto-js'
import type { ComponentNode } from '@/types/pageBuilder'
import { isTranslatableField } from './i18nFieldMap'
import type { TextToTranslateDto, TranslationItemDto } from '@/types/page-builder/pageTranslation'
import type { TextToTranslateDto, TranslationItemDto } from '@/types/translation/pageTranslation'
/**
* 计算文本的 MD5 哈希值
......@@ -38,13 +38,26 @@ function extractTextsRecursive(
if (Array.isArray(obj)) {
obj.forEach((item, index) => {
const result = extractTextsRecursive(
item,
componentType,
`${fieldPath}[${index}]`,
sourceLanguage
)
texts.push(...result)
const itemPath = `${fieldPath}[${index}]`
// 如果数组元素是字符串,且路径可翻译,直接提取
if (typeof item === 'string' && item.trim() && isTranslatableField(componentType, itemPath)) {
const hash = calculateTextHash(item)
texts.push({
hash,
text: item,
fieldPath: itemPath,
sourceLanguage,
})
} else {
// 否则递归处理
const result = extractTextsRecursive(
item,
componentType,
itemPath,
sourceLanguage
)
texts.push(...result)
}
})
return texts
}
......@@ -142,9 +155,17 @@ export function collectTextHashes(components: ComponentNode[]): string[] {
if (obj === null || obj === undefined) return
if (Array.isArray(obj)) {
obj.forEach((item, index) =>
collectFromObj(item, componentType, `${fieldPath}[${index}]`)
)
obj.forEach((item, index) => {
const itemPath = `${fieldPath}[${index}]`
// 如果数组元素是字符串,且路径可翻译,直接收集哈希
if (typeof item === 'string' && item.trim() && isTranslatableField(componentType, itemPath)) {
const hash = calculateTextHash(item)
hashes.add(hash)
} else {
// 否则递归处理
collectFromObj(item, componentType, itemPath)
}
})
return
}
......@@ -203,8 +224,17 @@ export function collectTranslationsByHash(
// 数组:按索引对齐
if (Array.isArray(sourceNode)) {
sourceNode.forEach((item, index) => {
const itemPath = `${fieldPath}[${index}]`
const tgt = Array.isArray(targetNode) ? targetNode[index] : undefined
traversePair(item, tgt, componentType, `${fieldPath}[${index}]`)
// 如果数组元素是字符串,且路径可翻译,直接收集
if (typeof item === 'string' && item.trim() && isTranslatableField(componentType, itemPath)) {
const hash = calculateTextHash(item)
const text = typeof tgt === 'string' ? tgt : item
result.push({ hash, text, fieldPath: itemPath })
} else {
// 否则递归处理
traversePair(item, tgt, componentType, itemPath)
}
})
return
}
......@@ -259,7 +289,18 @@ export function applyTranslations(
}
if (Array.isArray(obj)) {
return obj.map(item => replaceTextRecursive(item))
return obj.map(item => {
// 如果数组元素是字符串,检查是否需要替换
if (typeof item === 'string' && item.trim()) {
const hash = calculateTextHash(item)
const translationItem = translations[hash]
if (translationItem && translationItem.text) {
return translationItem.text
}
}
// 否则递归处理
return replaceTextRecursive(item)
})
}
if (typeof obj === 'object') {
......
......@@ -117,14 +117,11 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from "vue";
import { useI18n } from "vue-i18n";
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
import { useUserStore } from '@/stores/index'
import { useSystemConfigStore } from '@/stores/index'
import ErpUserService from '@/services/ErpUserService'
import { ApiResult } from '@/types/ApiResult'
import { Message } from '@arco-design/web-vue'
import loginHeader from "./components/header.vue";
import f from '@/assets/images/login/login_f.png'
import G from '@/assets/images/login/login_G.png'
import tel from '@/assets/images/login/login_tel.png'
import line from '@/assets/images/login/login_line.png'
......@@ -139,9 +136,25 @@ const systemConfigStore = useSystemConfigStore()
const loading = ref(true)
const router = useRouter()
const route = useRoute()
const { params } = router.currentRoute.value
const googleButtonContainer = ref(null);
// 获取登录后的重定向路径
// 优先级:1. 路由 query 参数中的 redirect 2. localStorage 中的 forward 3. 默认首页
const getRedirectPath = (): string => {
const queryRedirect = route.query.redirect as string | undefined
if (queryRedirect) {
return queryRedirect
}
const forward = localStorage.getItem('forward')
if (forward) {
localStorage.removeItem('forward')
return forward
}
return '/'
}
const loginMsg = reactive({
tenantId: systemConfigStore.tenantId || null,
......@@ -225,10 +238,8 @@ const useLineLogin = async(code:string) => {
if (response.status == 'SUCCESS') {
userStore.setLoginType(loginMsg.reType || 0)
Message.success(t('login.loginSuccess'))
const forward = localStorage.getItem('forward')
localStorage.removeItem('forward')
router.push({
path: forward ? forward : '/',
path: getRedirectPath(),
})
}else if(response.status == 'ERROR'){
router.push('/login')
......@@ -254,10 +265,8 @@ const useWechatLogin = async(code:string) => {
if (response.status == 'SUCCESS') {
userStore.setLoginType(loginMsg.reType || 0)
Message.success(t('login.loginSuccess'))
const forward = localStorage.getItem('forward')
localStorage.removeItem('forward')
router.push({
path: forward ? forward : '/',
path: getRedirectPath(),
})
}else if(response.status == 'ERROR'){
router.push('/login')
......@@ -360,10 +369,8 @@ const handleSignInSuccess = async (googleUser:any) => {
if (response.status == 'SUCCESS') {
userStore.setLoginType(loginMsg.reType || 0)
Message.success(t('login.loginSuccess'))
const forward = localStorage.getItem('forward')
localStorage.removeItem('forward')
router.push({
path: forward ? forward : '/',
path: getRedirectPath(),
})
}else if(response.status == 'ERROR'){
router.push('/login')
......@@ -409,10 +416,8 @@ const handleLogin = async () => {
if (result.status == 'SUCCESS') {
userStore.setLoginType(loginMsg.reType || 0)
Message.success(t('login.loginSuccess'))
const forward = localStorage.getItem('forward')
localStorage.removeItem('forward')
router.push({
path: forward ? forward : '/',
path: getRedirectPath(),
})
}
} catch (error: any) {
......
<template>
<div class="flex justify-between items-center">
<div class="flex justify-center items-center cursor-pointer"
@click="goHome('/home')">
@click="goHome('/')">
<img v-if="systemConfigStore.config?.logo"
:src="systemConfigStore.config.logo"
:alt="systemConfigStore.config.webSiteName"
......@@ -14,7 +14,7 @@
</svg>
</div>
<div class="flex items-center" v-if="!currentStep||currentStep<3">
<div class="flex items-center mr-[67px] cursor-pointer" @click="goHome('/home')">
<div class="flex items-center mr-[67px] cursor-pointer" @click="goHome('/')">
<img
src="../../../assets/images/login/login-home.png"
class="h-[20px]"
......
......@@ -20,7 +20,7 @@
:loading="loading"
html-type="submit"
class="w-[222px] flex-1 font-medium !h-[46px] !rounded-[13px] !text-[#FFFFFF] !text-[16px]"
@click="goHome('/home')"
@click="goHome('/')"
>
{{ t('login.backToHome') }}
</a-button>
......
......@@ -234,7 +234,7 @@ const goPage = (path:string) => {
router.push(path)
return
}
router.push('/basicInfor')
router.push('/personalCenter/accountCenter/basicInfor')
}
// 验证邮箱是否可用
......@@ -370,7 +370,7 @@ const handleSubmit = async () => {
Message.success(t('login.resetSuccess'))
// 延迟跳转到登录页
if(params&&params.email){
router.push('/basicInfor')
router.push('/personalCenter/accountCenter/basicInfor')
}else{
router.push('/login')
}
......
......@@ -15,11 +15,19 @@
const pageData = ref<any>(null)
const translatedPageData = ref<any>(null)
const { locale } = useI18n()
const { locale, t } = useI18n()
// 按目标语言缓存翻译结果,减少重复请求
const translationsCache = new Map<string, Record<string, any>>()
// 更新页面标题
function updatePageTitle(pageDataValue: any) {
if (pageDataValue?.metadata?.pageTitle) {
const pageTitle = pageDataValue.metadata.pageTitle
document.title = `${pageTitle}`
}
}
async function loadPageData() {
try {
const response = await WebSitePageService.getHomePageAsync({
......@@ -31,6 +39,8 @@
if (response.pageDataList && response.pageDataList.length > 0) {
pageData.value = JSON.parse(response.pageDataList[0].plugData as string)
await applyI18nTranslations()
// 更新页面标题
updatePageTitle(translatedPageData.value || pageData.value)
}
} catch (error) {
console.error('加载首页数据失败:', error)
......@@ -40,6 +50,7 @@
async function applyI18nTranslations() {
if (!pageData.value?.components) {
translatedPageData.value = pageData.value
updatePageTitle(pageData.value)
return
}
const sourceLang = pageData.value.metadata?.sourceLanguage || 'zh-CN'
......@@ -48,6 +59,7 @@
// 同源语言直接用原数据
if (targetLang === sourceLang) {
translatedPageData.value = pageData.value
updatePageTitle(pageData.value)
return
}
......@@ -58,12 +70,14 @@
...pageData.value,
components: applyTranslations(pageData.value.components, cached),
}
updatePageTitle(translatedPageData.value)
return
}
const hashes = collectTextHashes(pageData.value.components)
if (!hashes.length) {
translatedPageData.value = pageData.value
updatePageTitle(pageData.value)
return
}
......@@ -79,9 +93,11 @@
...pageData.value,
components: applyTranslations(pageData.value.components, translations),
}
updatePageTitle(translatedPageData.value)
} catch (err) {
console.error('加载翻译失败:', err)
translatedPageData.value = pageData.value
updatePageTitle(pageData.value)
}
}
......@@ -99,6 +115,17 @@
}
}
)
// 监听翻译后的页面数据变化,更新页面标题
watch(
() => translatedPageData.value,
(newData) => {
if (newData) {
updatePageTitle(newData)
}
},
{ immediate: true }
)
</script>
<style scoped>
......
......@@ -22,10 +22,6 @@
import { ref, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter,useRoute } from 'vue-router'
import account from "./components/accountCenter/account.vue"
import basicInfor from "./components/accountCenter/basicInfor.vue"
import passengerList from "./components/accountCenter/passengerList.vue"
import mailingAddressList from "./components/accountCenter/mailingAddressList.vue"
const { t } = useI18n();
const router = useRouter()
......@@ -38,25 +34,25 @@ const TitleBars = [
{
label: t('personal.basicInfo'),
value: 1,
path: '/basicInfor',
path: '/personalCenter/accountCenter/basicInfor',
key: 'basicInfor'
},
{
label: t('personal.accountInfor'),
value: 2,
path: '/account',
path: '/personalCenter/accountCenter/account',
key: 'account'
},
{
label: t('personal.commonPassenger'),
value: 3,
path: '/passengerList',
path: '/personalCenter/accountCenter/passengerList',
key: 'passengerList'
},
{
label: t('personal.postAddress'),
value: 4,
path: '/mailingAddressList',
path: '/personalCenter/accountCenter/mailingAddressList',
key: 'mailingAddressList'
}
]
......
......@@ -240,7 +240,7 @@ const loginWithLine = () => {
// https://www.oytour.com/#/login/2/3
console.log(openInfo.value,'----------')
// return
const redirectUri = encodeURIComponent(openInfo.value.redirectUri || 'http://localhost:8002/account/3'); // 替换为你的重定向 URI
const redirectUri = encodeURIComponent(openInfo.value.redirectUri || 'http://localhost:8002/personalCenter/accountCenter/account/3'); // 替换为你的重定向 URI
const state = generateState(); // 防止 CSRF 攻击,生成随机的 state 参数
// 构造 LINE 授权 URL
const lineLoginUrl = `https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=${channelId}&redirect_uri=${redirectUri}&state=${state}&scope=openid%20profile`;
......@@ -257,7 +257,7 @@ const useLineBind = async(code:string) => {
Message.success(t('personal.bindWechatSuccess'))
getPersonalInfor()
}else if(response.status == 'ERROR'){
router.push('/account')
router.push('/personalCenter/accountCenter/account')
}
}catch (error: any) {
Message.error(error.message)
......@@ -267,7 +267,7 @@ const useLineBind = async(code:string) => {
}
// 微信授权
const loginWechat = () => {
const redirect_url = openInfo.value.redirectUri || 'https://www.oytour.com/#/login/2' ||'http://localhost:8002/account/2'
const redirect_url = openInfo.value.redirectUri || 'https://www.oytour.com/#/login/2' ||'http://localhost:8002/personalCenter/accountCenter/account/2'
const url = `https://open.weixin.qq.com/connect/qrconnect?appid=${openInfo.value.appId}&redirect_uri=${encodeURIComponent(redirect_url)}&response_type=code&scope=snsapi_login&state=${1}&wechat_redirect=${redirect_url}`;
window.location.href = url;
}
......@@ -280,7 +280,7 @@ const useWechatBind = async(code:string) => {
Message.success(t('personal.bindWechatSuccess'))
getPersonalInfor()
}else if(response.status == 'ERROR'){
router.push('/account')
router.push('/personalCenter/accountCenter/account')
}
}catch (error: any) {
Message.error(error.message)
......
......@@ -11,7 +11,7 @@
<a-form-item field="" label="" class="">
<div class="flex justify-center relative">
<div class="flex justify-center items-center absolute left-[-300px] top-[5px]"
@click="goPage('/account')">
@click="goPage('/personalCenter/accountCenter/account')">
<div class="flex items-center justify-center cursor-pointer">
<icon-left class="font-semibold" size="20" strokeLinejoin="miter" />
<span class="text-base ml-[9px]">{{ t('personal.return') }}</span>
......@@ -438,7 +438,7 @@ const handleSubmit = async () => {
if(params&&params.forward){
goPage(`/${params.forward}`)
}else{
goPage('/account')
goPage('/personalCenter/accountCenter/account')
}
}
} catch (error: any) {
......
......@@ -11,7 +11,7 @@
<a-form-item field="" label="" class="">
<div class="flex justify-center relative">
<div class="flex justify-center items-center absolute left-[-300px] top-[5px]"
@click="currentStep==1?goPage('/account'):handlePrevious()">
@click="currentStep==1?goPage('/personalCenter/accountCenter/account'):handlePrevious()">
<div class="flex items-center justify-center cursor-pointer">
<icon-left class="font-semibold" size="20" strokeLinejoin="miter" />
<span class="text-base ml-[9px]">{{ currentStep==1?t('personal.return'):t('personal.returnStep') }}</span>
......@@ -255,7 +255,7 @@ const goPage = (path:string) => {
router.push(path)
return
}
router.push('/account')
router.push('/personalCenter/accountCenter/account')
}
// 验证邮箱是否可用
......@@ -391,7 +391,7 @@ const handleSubmit = async () => {
Message.success(t('login.resetSuccess'))
// currentStep.value ++
// 延迟跳转
router.push('/account')
router.push('/personalCenter/accountCenter/account')
}
} catch (error: any) {
Message.error(error.message || t('login.resetFailed'))
......
......@@ -12,7 +12,7 @@
<a-form-item field="" label="" class="">
<div class="flex justify-center relative">
<div class="flex justify-center items-center absolute left-[-300px] top-[5px]"
@click="goPage('/account')">
@click="goPage('/personalCenter/accountCenter/account')">
<div class="flex items-center justify-center cursor-pointer">
<icon-left class="font-semibold" size="20" strokeLinejoin="miter" />
<span class="text-base ml-[9px]">{{ t('personal.return') }}</span>
......@@ -371,7 +371,7 @@ const handleSubmit = async () => {
if(response){
Message.success(t('login.resetSuccess'))
// 延迟跳转到登录页
goPage('/account')
goPage('/personalCenter/accountCenter/account')
}
} catch (error: any) {
Message.error(error.message || t('login.resetFailed'))
......
......@@ -16,7 +16,7 @@
</div>
<div class="mt-[13px] text-lg font-medium text-center truncate">{{ userInfo?.name || '' }}</div>
<div class="flex justify-center items-center mt-[10px] cursor-pointer"
@click="goPage('/basicInfor')">
@click="goPage('/personalCenter/accountCenter/basicInfor')">
<span v-if="!userInfo?.IsComplete" class="LeftViewTisp w-[6px] h-[6px] rounded-full"></span>
<span class="LeftViewData ml-[5px] text-sm font-medium text-[#666]">
{{ t('personal.completeProfile') }}
......@@ -109,7 +109,7 @@ const props = defineProps({
},
activeMenu: {
type: String,
default: '/myOrder',
default: '/personalCenter/myOrder',
},
})
......
......@@ -28,7 +28,7 @@ const route = useRoute()
const activeMenu = computed(() => {
const path = route.path
const menu = menuList.value.find(item => path.startsWith(item.path))
if(path=='/account'||path=='/passengerList'||path=='/mailingAddressList'){
if(path=='/personalCenter/accountCenter/account'||path=='/personalCenter/accountCenter/passengerList'||path=='/personalCenter/accountCenter/mailingAddressList'){
return 'basicInfor'
}
return menu?.key || 'myOrder'
......@@ -38,28 +38,28 @@ const activeMenu = computed(() => {
const menuList = ref([
{
name: t('personal.menu.myOrder'),
path: '/myOrder',
path: '/personalCenter/myOrder',
key: 'myOrder',
},
{
name: t('personal.menu.systemMessage'),
path: '/systemMessage',
path: '/personalCenter/systemMessage',
key: 'systemMessage',
},
{
name: t('personal.menu.myCollection'),
path: '/myCollection',
path: '/personalCenter/myCollection',
key: 'myCollection',
},
{
name: t('personal.menu.coupon'),
path: '/myCoupon',
path: '/personalCenter/myCoupon',
key: 'myCoupon',
count: 2,
},
{
name: t('personal.menu.accountCenter'),
path: '/basicInfor',
path: '/personalCenter/accountCenter/basicInfor',
key: 'basicInfor',
},
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment