Jelajahi Sumber

feat:国际化

叶静 1 bulan lalu
induk
melakukan
b8d84f2045

+ 23 - 22
src/config/menuConfig.ts

@@ -1,6 +1,7 @@
 /**
  * 固定导航菜单配置
  * @description 系统的导航菜单固定配置,不再从后端获取
+ * @description 使用 i18n key 支持国际化,name 字段对应 i18n 的 menu.xxx 键
  */
 
 export function getStaticMenuData() {
@@ -12,7 +13,7 @@ export function getStaticMenuData() {
 				id: '1000',
 				parentId: '-1',
 				weight: 1,
-				name: '订单管理',
+				name: 'menu.orderManagement',
 				path: '/order',
 				componentPath: null,
 				meta: {
@@ -21,7 +22,7 @@ export function getStaticMenuData() {
 					isKeepAlive: false,
 					icon: 'iconfont icon-document-record',
 					isAffix: false,
-					title: '订单管理',
+					title: 'menu.orderManagement',
 					isHide: false,
 				},
 				sortOrder: 1,
@@ -32,7 +33,7 @@ export function getStaticMenuData() {
 						id: '1001',
 						parentId: '1000',
 						weight: 0,
-						name: '代收订单',
+						name: 'menu.payOrder',
 						path: '/order/payOrder/index',
 						componentPath: null,
 						meta: {
@@ -41,7 +42,7 @@ export function getStaticMenuData() {
 							isKeepAlive: true,
 							icon: 'iconfont icon-document-record',
 							isAffix: false,
-							title: '代收订单',
+							title: 'menu.payOrder',
 							isHide: false,
 						},
 						sortOrder: 1,
@@ -52,7 +53,7 @@ export function getStaticMenuData() {
 						id: '1002',
 						parentId: '1000',
 						weight: 0,
-						name: '代付订单',
+						name: 'menu.withdrawOrder',
 						path: '/order/withdrawOrder/index',
 						componentPath: null,
 						meta: {
@@ -61,7 +62,7 @@ export function getStaticMenuData() {
 							isKeepAlive: true,
 							icon: 'iconfont icon-document-record',
 							isAffix: false,
-							title: '代付订单',
+							title: 'menu.withdrawOrder',
 							isHide: false,
 						},
 						sortOrder: 2,
@@ -74,7 +75,7 @@ export function getStaticMenuData() {
 				id: '2000',
 				parentId: '-1',
 				weight: 1,
-				name: '支付配置',
+				name: 'menu.paymentConfig',
 				path: '/payment',
 				componentPath: null,
 				meta: {
@@ -83,7 +84,7 @@ export function getStaticMenuData() {
 					isKeepAlive: false,
 					icon: 'iconfont icon-shujuyuanguanli',
 					isAffix: false,
-					title: '支付配置',
+					title: 'menu.paymentConfig',
 					isHide: false,
 				},
 				sortOrder: 2,
@@ -94,7 +95,7 @@ export function getStaticMenuData() {
 						id: '2001',
 						parentId: '2000',
 						weight: 0,
-						name: '支付通道',
+						name: 'menu.paymentChannel',
 						path: '/payment/channel/index',
 						componentPath: null,
 						meta: {
@@ -103,7 +104,7 @@ export function getStaticMenuData() {
 							isKeepAlive: true,
 							icon: 'iconfont icon-shujuyuanguanli',
 							isAffix: false,
-							title: '支付通道',
+							title: 'menu.paymentChannel',
 							isHide: false,
 						},
 						sortOrder: 1,
@@ -116,7 +117,7 @@ export function getStaticMenuData() {
 				id: '3000',
 				parentId: '-1',
 				weight: 1,
-				name: '结算管理',
+				name: 'menu.settlementManagement',
 				path: '/settlement',
 				componentPath: null,
 				meta: {
@@ -125,7 +126,7 @@ export function getStaticMenuData() {
 					isKeepAlive: false,
 					icon: 'iconfont icon-lingpai',
 					isAffix: false,
-					title: '结算管理',
+					title: 'menu.settlementManagement',
 					isHide: false,
 				},
 				sortOrder: 3,
@@ -136,7 +137,7 @@ export function getStaticMenuData() {
 						id: '3001',
 						parentId: '3000',
 						weight: 0,
-						name: '申请结算',
+						name: 'menu.applySettlement',
 						path: '/settlement/apply/index',
 						componentPath: null,
 						meta: {
@@ -145,7 +146,7 @@ export function getStaticMenuData() {
 							isKeepAlive: false,
 							icon: 'iconfont icon-lingpai',
 							isAffix: false,
-							title: '申请结算',
+							title: 'menu.applySettlement',
 							isHide: false,
 						},
 						sortOrder: 1,
@@ -156,7 +157,7 @@ export function getStaticMenuData() {
 						id: '3002',
 						parentId: '3000',
 						weight: 0,
-						name: '结算记录',
+						name: 'menu.settlementRecord',
 						path: '/settlement/record/index',
 						componentPath: null,
 						meta: {
@@ -165,7 +166,7 @@ export function getStaticMenuData() {
 							isKeepAlive: false,
 							icon: 'iconfont icon-lingpai',
 							isAffix: false,
-							title: '结算记录',
+							title: 'menu.settlementRecord',
 							isHide: false,
 						},
 						sortOrder: 2,
@@ -176,7 +177,7 @@ export function getStaticMenuData() {
 						id: '3003',
 						parentId: '3000',
 						weight: 0,
-						name: '资金明细',
+						name: 'menu.fundFlow',
 						path: '/settlement/fundFlow/index',
 						componentPath: null,
 						meta: {
@@ -185,7 +186,7 @@ export function getStaticMenuData() {
 							isKeepAlive: false,
 							icon: 'iconfont icon-lingpai',
 							isAffix: false,
-							title: '资金明细',
+							title: 'menu.fundFlow',
 							isHide: false,
 						},
 						sortOrder: 3,
@@ -198,7 +199,7 @@ export function getStaticMenuData() {
 				id: '4000',
 				parentId: '-1',
 				weight: 1,
-				name: '系统设置',
+				name: 'menu.systemSettings',
 				path: '/settings',
 				componentPath: null,
 				meta: {
@@ -207,7 +208,7 @@ export function getStaticMenuData() {
 					isKeepAlive: false,
 					icon: 'iconfont icon-xitongshezhi',
 					isAffix: false,
-					title: '系统设置',
+					title: 'menu.systemSettings',
 					isHide: false,
 				},
 				sortOrder: 4,
@@ -218,7 +219,7 @@ export function getStaticMenuData() {
 						id: '4001',
 						parentId: '4000',
 						weight: 0,
-						name: '安全中心',
+						name: 'menu.securityCenter',
 						path: '/settings/security/index',
 						componentPath: null,
 						meta: {
@@ -227,7 +228,7 @@ export function getStaticMenuData() {
 							isKeepAlive: false,
 							icon: 'iconfont icon-xitongshezhi',
 							isAffix: false,
-							title: '安全中心',
+							title: 'menu.securityCenter',
 							isHide: false,
 						},
 						sortOrder: 0,

+ 19 - 5
src/i18n/lang/en.ts

@@ -1,4 +1,18 @@
 export default {
+	menu: {
+		home: 'Home',
+		orderManagement: 'Order Management',
+		payOrder: 'Pay Order',
+		withdrawOrder: 'Withdraw Order',
+		paymentConfig: 'Payment Config',
+		paymentChannel: 'Payment Channel',
+		settlementManagement: 'Settlement Management',
+		applySettlement: 'Apply Settlement',
+		settlementRecord: 'Settlement Record',
+		fundFlow: 'Fund Flow',
+		systemSettings: 'System Settings',
+		securityCenter: 'Security Center',
+	},
 	timeGreeting: {
 		dawn: 'Good early morning',
 		morning: 'Good morning',
@@ -127,7 +141,7 @@ export default {
 		fullscreen: 'Fullscreen',
 		closeFullscreen: 'Close Fullscreen',
 		favorite: 'Favorite',
-		favoriteMax: 'The number of shortcut navigation exceeds the upper limit. Please remove some shortcuts'
+		favoriteMax: 'The number of shortcut navigation exceeds the upper limit. Please remove some shortcuts',
 	},
 	notFound: {
 		foundTitle: 'Wrong Address Input, Please Re-enter the Address~',
@@ -215,17 +229,17 @@ export default {
 		confirmTitle: 'Switch Tenant Confirmation',
 		confirmMessage: 'Are you sure to switch to tenant "{name}"? The page will refresh after switching.',
 		switchSuccess: 'Tenant switched successfully, refreshing page...',
-		switchError: 'Failed to switch tenant, please try again'
+		switchError: 'Failed to switch tenant, please try again',
 	},
 	common: {
 		cancel: 'Cancel',
 		confirm: 'Confirm',
 		confirmButtonText: 'Confirm',
-		cancelButtonText: 'Cancel'
+		cancelButtonText: 'Cancel',
 	},
 	message: {
 		box: {
-			title: 'Tips'
-		}
+			title: 'Tips',
+		},
 	},
 };

+ 18 - 4
src/i18n/lang/zh-cn.ts

@@ -1,5 +1,19 @@
 // 定义内容
 export default {
+	menu: {
+		home: '首页',
+		orderManagement: '订单管理',
+		payOrder: '代收订单',
+		withdrawOrder: '代付订单',
+		paymentConfig: '支付配置',
+		paymentChannel: '支付通道',
+		settlementManagement: '结算管理',
+		applySettlement: '申请结算',
+		settlementRecord: '结算记录',
+		fundFlow: '资金明细',
+		systemSettings: '系统设置',
+		securityCenter: '安全中心',
+	},
 	timeGreeting: {
 		dawn: '凌晨好',
 		morning: '早上好',
@@ -120,7 +134,7 @@ export default {
 		fullscreen: '当前页全屏',
 		closeFullscreen: '关闭全屏',
 		favorite: '收藏',
-		favoriteMax: '快捷导航数量超过上限,请移除部分'
+		favoriteMax: '快捷导航数量超过上限,请移除部分',
 	},
 	notFound: {
 		foundTitle: '地址输入错误,请重新输入地址~',
@@ -208,11 +222,11 @@ export default {
 		confirmTitle: '切换租户确认',
 		confirmMessage: '确定要切换到租户 "{name}" 吗?切换后页面将刷新。',
 		switchSuccess: '租户切换成功,即将刷新页面...',
-		switchError: '租户切换失败,请重试'
+		switchError: '租户切换失败,请重试',
 	},
 	message: {
 		box: {
-			title: '提示'
-		}
+			title: '提示',
+		},
 	},
 };

+ 13 - 12
src/layout/navBars/breadcrumb/user.vue

@@ -47,7 +47,10 @@
 		</div>
 		<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
 			<span class="layout-navbars-breadcrumb-user-link">
-				<img :src="userInfos.user.avatar?.startsWith('http') ? userInfos.user.avatar : baseURL + userInfos.user.avatar" class="layout-navbars-breadcrumb-user-link-photo mr5" />
+				<img
+					:src="userInfos.user.avatar?.startsWith('http') ? userInfos.user.avatar : baseURL + userInfos.user.avatar"
+					class="layout-navbars-breadcrumb-user-link-photo mr5"
+				/>
 				{{ userInfos.user.username }}
 				<el-icon class="el-icon--right">
 					<ele-ArrowDown />
@@ -67,12 +70,7 @@
 		<Search ref="searchRef" />
 		<global-websocket uri="/admin/ws/info" v-if="websocketEnable" @rollback="rollback" />
 		<personal-drawer ref="personalDrawerRef"></personal-drawer>
-		<tenant-selector 
-			ref="tenantSelectorRef" 
-			:tenant-list="tenantList" 
-			:loading="tenantLoading"
-			@change="onTenantChange"
-		></tenant-selector>
+		<tenant-selector ref="tenantSelectorRef" :tenant-list="tenantList" :loading="tenantLoading" @change="onTenantChange"></tenant-selector>
 	</div>
 </template>
 
@@ -81,6 +79,7 @@ import { logout } from '/@/api/login';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import screenfull from 'screenfull';
 import { useI18n } from 'vue-i18n';
+import { setLanguage } from '/@/locales/index';
 import { useUserInfo } from '/@/stores/userInfo';
 import { useThemeConfig } from '/@/stores/themeConfig';
 import other from '/@/utils/other';
@@ -110,7 +109,7 @@ interface Tenant {
 }
 
 // 定义变量内容
-const { locale, t } = useI18n();
+const { t } = useI18n();
 const router = useRouter();
 const stores = useUserInfo();
 const storesThemeConfig = useThemeConfig();
@@ -134,7 +133,7 @@ const shouldShowTenantOption = computed(() => {
 const currentTenantName = computed(() => {
 	if (tenantList.value.length <= 1) return null;
 	const currentTenantId = Session.getTenant();
-	const currentTenant = tenantList.value.find(tenant => tenant.id === currentTenantId);
+	const currentTenant = tenantList.value.find((tenant) => tenant.id === currentTenantId);
 	return currentTenant?.name || null;
 });
 
@@ -238,7 +237,9 @@ const onLanguageChange = (lang: string) => {
 	Local.remove('themeConfig');
 	themeConfig.value.globalI18n = lang;
 	Local.set('themeConfig', themeConfig.value);
-	locale.value = lang;
+	// 映射旧格式语言代码到新格式
+	const localeMap: Record<string, string> = { 'zh-cn': 'zh-CN', en: 'en-US' };
+	setLanguage(localeMap[lang] || lang);
 	other.useTitle();
 	initI18nOrSize('globalI18n', 'disabledI18n');
 };
@@ -256,7 +257,7 @@ const initI18nOrSize = (value: string, attr: string) => {
 
 // 获取到消息
 const rollback = (msg: string) => {
-			useMsg().setMsg({ label: 'websocket消息', value: msg, time: formatAxisI18n(new Date(), t) });
+	useMsg().setMsg({ label: 'websocket消息', value: msg, time: formatAxisI18n(new Date(), t) });
 };
 
 // 获取是否显示未读
@@ -295,7 +296,7 @@ onMounted(() => {
 	}
 
 	getIsDot();
-	
+
 	// 加载租户列表
 	loadTenantList();
 });

+ 559 - 0
src/locales/en-US/index.json

@@ -0,0 +1,559 @@
+{
+	"app": {
+		"title": "Merchant System"
+	},
+	"menu": {
+		"home": "Home",
+		"orderManagement": "Order Management",
+		"payOrder": "Pay Order",
+		"withdrawOrder": "Withdraw Order",
+		"paymentConfig": "Payment Config",
+		"paymentChannel": "Payment Channel",
+		"settlementManagement": "Settlement Management",
+		"applySettlement": "Apply Settlement",
+		"settlementRecord": "Settlement Record",
+		"fundFlow": "Fund Flow",
+		"systemSettings": "System Settings",
+		"securityCenter": "Security Center"
+	},
+	"home": {
+		"submitOrder": "Submit Order",
+		"payOrder": "Pay Order",
+		"withdrawOrder": "Withdraw Order",
+		"withdrawSuccess": "Withdraw Success",
+		"paySuccessRate": "Pay Success Rate",
+		"amount": "Amount",
+		"loadStatsFailed": "Failed to load statistics"
+	},
+	"timeGreeting": {
+		"dawn": "Good early morning",
+		"morning": "Good morning",
+		"forenoon": "Good forenoon",
+		"noon": "Good noon",
+		"afternoon": "Good afternoon",
+		"evening": "Good evening",
+		"night": "Good night",
+		"lateNight": "Good late night"
+	},
+	"router": {
+		"home": "Home",
+		"system": "System",
+		"systemMenu": "System Menu",
+		"systemRole": "System Role",
+		"systemUser": "System User",
+		"systemDept": "System Department",
+		"systemDic": "System Dictionary",
+		"limits": "Permissions",
+		"limitsFrontEnd": "Front End",
+		"limitsFrontEndPage": "Front End Page",
+		"limitsFrontEndBtn": "Front End Button",
+		"limitsBackEnd": "Back End",
+		"limitsBackEndEndPage": "Back End Page",
+		"menu": "Menu",
+		"menu1": "Menu 1",
+		"menu11": "Menu 1-1",
+		"menu12": "Menu 1-2",
+		"menu121": "Menu 1-2-1",
+		"menu122": "Menu 1-2-2",
+		"menu13": "Menu 1-3",
+		"menu2": "Menu 2",
+		"funIndex": "Functions",
+		"funTagsView": "Tags View",
+		"funCountup": "Count Up",
+		"funWangEditor": "Wang Editor",
+		"funCropper": "Cropper",
+		"funQrcode": "QR Code",
+		"funEchartsMap": "Echarts Map",
+		"funPrintJs": "PrintJS",
+		"funClipboard": "Copy and Cut",
+		"funGridLayout": "Drag Layout",
+		"funSplitpanes": "Split Pane",
+		"funDragVerify": "Validator",
+		"pagesIndex": "Pages",
+		"pagesFiltering": "Filtering",
+		"pagesFilteringDetails": "Filtering Details",
+		"pagesFilteringDetails1": "Filtering Details 1",
+		"pagesIocnfont": "Icon Font Icon",
+		"pagesElement": "Element Icon",
+		"pagesAwesome": "Awesome Icon",
+		"pagesFormAdapt": "Form Adapt",
+		"pagesTableRules": "Table Rules",
+		"pagesFormI18n": "Form I18n",
+		"pagesFormRules": "Multi-Form Validation",
+		"pagesDynamicForm": "Dynamic Complex Form",
+		"pagesWorkflow": "Workflow",
+		"pagesListAdapt": "List Adapt",
+		"pagesWaterfall": "Waterfall",
+		"pagesSteps": "Steps",
+		"pagesPreview": "Large Preview",
+		"pagesWaves": "Wave Effect",
+		"pagesTree": "Tree Alter Table",
+		"pagesDrag": "Drag Command",
+		"pagesLazyImg": "Image Lazy Loading",
+		"makeIndex": "Make Index",
+		"makeSelector": "Icon Selector",
+		"makeNoticeBar": "Notification Bar",
+		"makeSvgDemo": "Svgicon Demo",
+		"makeTableDemo": "Table Demo",
+		"paramsIndex": "Routing Parameters",
+		"paramsCommon": "General Routing",
+		"paramsDynamic": "Dynamic Routing",
+		"paramsCommonDetails": "General Routing Details",
+		"paramsDynamicDetails": "Dynamic Routing Details",
+		"chartIndex": "Chart Index",
+		"visualizingIndex": "Visualizing Index",
+		"visualizingLinkDemo1": "Visualizing Link Demo 1",
+		"visualizingLinkDemo2": "Visualizing Link Demo 2",
+		"personal": "Personal",
+		"tools": "Tools",
+		"layoutLinkView": "Link View",
+		"layoutIframeViewOne": "Iframe View One",
+		"layoutIframeViewTwo": "Iframe View Two"
+	},
+	"staticRoutes": {
+		"login": "Login",
+		"authredirect": "Auth Redirect",
+		"expire": "Password Expire",
+		"signIn": "Sign In",
+		"notFound": "Not Found",
+		"noPower": "No Power"
+	},
+	"user": {
+		"title0": "Component Size",
+		"title1": "Language Switching",
+		"title2": "Menu Search",
+		"title3": "Layout Configuration",
+		"title4": "News",
+		"title5": "Full Screen On",
+		"title6": "Full Screen Off",
+		"dropdownLarge": "Large",
+		"dropdownDefault": "Default",
+		"dropdownSmall": "Small",
+		"dropdown1": "Home Page",
+		"dropdown2": "Personal Center",
+		"dropdown3": "Tenant Switch",
+		"dropdown4": "Current Tenant",
+		"dropdown5": "Log Out",
+		"searchPlaceholder": "Menu Search: Supports Chinese and Routing Path",
+		"newTitle": "Notice",
+		"newBtn": "All",
+		"newGo": "Go to the Notification Center",
+		"newDesc": "No Notice",
+		"logOutTitle": "Tips",
+		"logOutMessage": "This Operation Will Log Out. Do You Want to Continue?",
+		"logOutConfirm": "Determine",
+		"logOutCancel": "Cancel",
+		"logOutExit": "Exiting"
+	},
+	"tagsView": {
+		"refresh": "Refresh",
+		"close": "Close",
+		"closeOther": "Close Other",
+		"closeAll": "Close All",
+		"fullscreen": "Fullscreen",
+		"closeFullscreen": "Close Fullscreen",
+		"favorite": "Favorite",
+		"favoriteMax": "The number of shortcut navigation exceeds the upper limit. Please remove some shortcuts"
+	},
+	"notFound": {
+		"foundTitle": "Wrong Address Input, Please Re-enter the Address~",
+		"foundMsg": "You Can Check the Web Address First, and Then Re-enter or Give Us Feedback.",
+		"foundBtn": "Back to Home Page"
+	},
+	"noAccess": {
+		"accessTitle": "You Are Not Authorized to Operate~",
+		"accessMsg": "Contact Information: Add QQ Group Discussion 665452019",
+		"accessBtn": "Reauthorization"
+	},
+	"layout": {
+		"configTitle": "Layout Configuration",
+		"oneTitle": "Global Themes",
+		"twoTopTitle": "Top Bar Set Up",
+		"twoMenuTitle": "Menu Set Up",
+		"twoColumnsTitle": "Columns Set Up",
+		"twoTopBar": "Top Bar Background",
+		"twoTopBarColor": "Top Bar Default Font Color",
+		"twoIsTopBarColorGradual": "Top Bar Gradient",
+		"twoMenuBar": "Menu Background",
+		"twoMenuBarColor": "Menu Default Font Color",
+		"twoMenuBarActiveColor": "Menu Highlight Color",
+		"twoIsMenuBarColorGradual": "Menu Gradient",
+		"twoColumnsMenuBar": "Column Menu Background",
+		"twoColumnsMenuBarColor": "Default Font Color Bar Menu",
+		"twoIsColumnsMenuBarColorGradual": "Column Gradient",
+		"twoIsColumnsMenuHoverPreload": "Column Menu Hover Preload",
+		"threeTitle": "Interface Settings",
+		"threeIsCollapse": "Menu Horizontal Collapse",
+		"threeIsUniqueOpened": "Menu Accordion",
+		"threeIsFixedHeader": "Fixed Header",
+		"threeIsClassicSplitMenu": "Classic Layout Split Menu",
+		"threeIsLockScreen": "Open the Lock Screen",
+		"threeLockScreenTime": "Screen Locking (s/s)",
+		"fourTitle": "Interface Display",
+		"fourIsShowLogo": "Sidebar Logo",
+		"fourIsBreadcrumb": "Open Breadcrumb",
+		"fourIsBreadcrumbIcon": "Open Breadcrumb Icon",
+		"fourIsTagsview": "Open Tagsview",
+		"fourIsTagsviewIcon": "Open Tagsview Icon",
+		"fourIsCacheTagsView": "Enable Tagsview Cache",
+		"fourIsSortableTagsView": "Enable Tagsview Drag",
+		"fourIsShareTagsView": "Enable Tagsview Sharing",
+		"fourIsFooter": "Open Footer",
+		"fourIsGrayscale": "Grey Model",
+		"fourIsInvert": "Color Weak Mode",
+		"fourIsDark": "Dark Mode",
+		"fourIsWartermark": "Turn on Watermark",
+		"fourWartermarkText": "Watermark Copy",
+		"fourIsChat": "LLaMA Chat",
+		"fiveTitle": "Other Settings",
+		"fiveTagsStyle": "Tagsview Style",
+		"fiveAnimation": "Page Animation",
+		"fiveColumnsAsideStyle": "Column Style",
+		"fiveColumnsAsideLayout": "Column Layout",
+		"sixTitle": "Layout Switch",
+		"sixDefaults": "One",
+		"sixClassic": "Two",
+		"sixTransverse": "Three",
+		"sixColumns": "Four",
+		"tipText": "Click the Button Below to Copy the Layout Configuration to `/src/stores/themeConfig.ts`. It Has Been Modified In.",
+		"copyText": "Replication Configuration",
+		"resetText": "Restore Default",
+		"copyTextSuccess": "Copy Succeeded!",
+		"copyTextError": "Copy Failed!"
+	},
+	"upgrade": {
+		"title": "New Version",
+		"msg": "The New Version is Available, Please Update It Now! Dont Worry, the Update is Fast!",
+		"desc": "Prompt: Update Will Restore the Default Configuration",
+		"btnOne": "Cruel Refusal",
+		"btnTwo": "Update Now",
+		"btnTwoLoading": "Updating"
+	},
+	"tenantSelector": {
+		"title": "Switch Tenant",
+		"searchPlaceholder": "Search tenant name, domain or website",
+		"noData": "No tenant data available",
+		"current": "Current",
+		"selected": "Selected",
+		"loadError": "Failed to load tenant list",
+		"selectTip": "Please select a tenant to switch",
+		"sameError": "Already in this tenant, no need to switch",
+		"confirmTitle": "Switch Tenant Confirmation",
+		"confirmMessage": "Are you sure to switch to tenant \"{name}\"? The page will refresh after switching.",
+		"switchSuccess": "Tenant switched successfully, refreshing page...",
+		"switchError": "Failed to switch tenant, please try again"
+	},
+	"common": {
+		"queryBtn": "Query",
+		"addBtn": "Add",
+		"editBtn": "Edit",
+		"delBtn": "Delete",
+		"viewBtn": "View",
+		"detailBtn": "Details",
+		"exportBtn": "Export",
+		"expandBtn": "expand/phrase ",
+		"refreshCacheBtn": "Refresh cache",
+		"importBtn": "Import",
+		"importUserTip": "Import user",
+		"queryDeptTip": "Enter department name",
+		"resetBtn": "Reset",
+		"copyBtn": "Copy",
+		"action": "Action",
+		"optSuccessText": "Operation successful",
+		"optConfirmText": "Confirm this operation?",
+		"editSuccessText": "Successfully edited",
+		"addSuccessText": "Successfully added",
+		"delSuccessText": "Successfully deleted",
+		"delConfirmText": "This operation will permanently delete",
+		"confirmButtonText": "Confirm",
+		"cancelButtonText": "Cancel",
+		"download": "Download",
+		"expand": "Expand",
+		"selectAll": "SelectAll",
+		"cancel": "Cancel",
+		"confirm": "Confirm"
+	},
+	"message": {
+		"box": {
+			"title": "System prompt"
+		}
+	},
+	"label": {
+		"one1": "Account login",
+		"two2": "SMS login",
+		"three3": "Social login",
+		"register": "Create account"
+	},
+	"link": {
+		"one3": "Third party login",
+		"two4": "Links"
+	},
+	"password": {
+		"accountPlaceholder1": "Please enter username",
+		"accountPlaceholder2": "Password",
+		"accountPlaceholder3": "Please enter the verification code",
+		"phonePlaceholder4": "Please enter your phone number",
+		"tenantPlaceholder": "Please select tenant",
+		"accountBtnText": "Sign in",
+		"resetBtnText": "Reset",
+		"registerBtnText": "Create account",
+		"readAccept": "I have read and accept",
+		"privacyPolicy": "the Privacy Policy",
+		"oldPassword": "Please enter old password",
+		"newPassword": "Please enter new password",
+		"confirmPassword": "Please confirm new password",
+		"backToLogin": "Back to login",
+		"mobileLogin": "Mobile Login",
+		"createAccount": "Create Account",
+		"imageCodeTip": "Please enter code",
+		"forgetPassword": "Forgot Password",
+		"googleCaptchaPlaceholder": "Google Authenticator code (optional)"
+	},
+	"mobile": {
+		"placeholder1": "Please enter your mobile number",
+		"placeholder2": "Please enter the verification code",
+		"codeText": "Get code",
+		"btnText": "Sign in",
+		"mobileLogin": "Mobile Login",
+		"backToLogin": "Back to login",
+		"createAccount": "Create Account",
+		"sendSuccess": "Verification code sent successfully",
+		"seconds": "seconds to resend",
+		"mobileRequired": "Please enter your mobile number",
+		"codeRequired": "Please enter verification code",
+		"codeLength": "Verification code must be 4 digits",
+		"sendFailed": "Failed to send verification code",
+		"loginSuccess": "Login successful",
+		"loginFailed": "Login failed",
+		"signIn": "Sign In"
+	},
+	"forget": {
+		"newPasswordPlaceholder": "Please enter new password",
+		"confirmPasswordPlaceholder": "Please confirm new password",
+		"passwordEmpty": "Password cannot be empty",
+		"passwordLength": "Password length must be between 6 and 20 characters",
+		"passwordRule": "The two passwords do not match",
+		"passwordScore": "Password strength is too low"
+	},
+	"scan": {
+		"text": "Use your mobile device to scan and quickly log in or create an account",
+		"wechatApp": "Scan with WeChat",
+		"appErrorTip": "The login fails because the application is not configured"
+	},
+	"signInText": "Welcome back!",
+	"browserMsgText": "Tip: For best results, we recommend using Google Chrome, Microsoft Edge (version 80 or higher), or the 360 browser with speed mode enabled.",
+	"expire": {
+		"oldPassword": "Please enter old password",
+		"newPassword": "Please enter new password",
+		"confirmPassword": "Please confirm new password",
+		"passwordRule": "The two passwords do not match",
+		"passwordScore": "Password strength is too low",
+		"resetSuccess": "Password reset successfully"
+	},
+	"tenantSelect": {
+		"select": "Select Tenant",
+		"loadError": "Failed to get tenant list"
+	},
+	"verify": {
+		"imageCode": "Please enter verification code"
+	},
+	"register": {
+		"tenantEmpty": "Please select a tenant",
+		"tenantLoadError": "Failed to load tenant list",
+		"usernameEmpty": "Username cannot be empty",
+		"usernameLength": "Username length must be between 5 and 20 characters",
+		"phoneEmpty": "Phone number cannot be empty",
+		"passwordEmpty": "Password cannot be empty",
+		"passwordLength": "Password length must be between 6 and 20 characters",
+		"passwordStrength": "Password strength is too low",
+		"termsRequired": "Please read and accept the terms"
+	},
+	"divider": {
+		"or": "or"
+	},
+	"socialLogin": {
+		"wechatWork": "WeCom",
+		"dingtalk": "DingTalk"
+	},
+	"personal": {
+		"name": "Personal Info",
+		"basicInfo": "Basic Info",
+		"securityInfo": "Security Info",
+		"merchantNo": "Merchant No",
+		"nickname": "Nickname",
+		"nicknamePlaceholder": "Please enter nickname",
+		"realName": "Name",
+		"realNamePlaceholder": "Please enter name",
+		"phone": "Phone",
+		"phonePlaceholder": "Please enter phone",
+		"email": "Email",
+		"emailPlaceholder": "Please enter email",
+		"oldPassword": "Old Password",
+		"oldPasswordPlaceholder": "Please enter password",
+		"newPassword": "New Password",
+		"newPasswordPlaceholder": "Please enter new password",
+		"confirmPassword": "Confirm Password",
+		"confirmPasswordPlaceholder": "Please repeat password",
+		"changePassword": "Change Password",
+		"updateInfo": "Update Info",
+		"passwordRule": "The two passwords do not match",
+		"passwordScore": "Password strength is too low"
+	},
+	"order": {
+		"mchOrderNo": "Merchant Order No",
+		"mchOrderNoPlaceholder": "Please enter merchant order no",
+		"transactionId": "Platform Order No",
+		"transactionIdPlaceholder": "Please enter platform order no",
+		"merchantName": "Merchant Name",
+		"merchantNamePlaceholder": "Please enter merchant name",
+		"agentName": "Agent Name",
+		"agentNamePlaceholder": "Please enter agent name",
+		"orderStatus": "Order Status",
+		"orderStatusPlaceholder": "Please select order status",
+		"channelName": "Channel Name",
+		"submitAmount": "Submit Amount",
+		"actualAmount": "Actual Amount",
+		"createTime": "Create Time",
+		"callbackLog": "Callback Log",
+		"submitOrderCount": "Submit Orders",
+		"totalOrderAmount": "Total Order Amount",
+		"paidOrderCount": "Paid Orders",
+		"paidTotalAmount": "Paid Total Amount",
+		"statusCreateOrder": "Created",
+		"statusPaySuccess": "Pay Success",
+		"statusPayFail": "Pay Failed",
+		"statusCancelOrder": "Cancelled",
+		"statusPayTimeout": "Pay Timeout",
+		"statusWithdrawSuccess": "Withdraw Success",
+		"statusWithdrawFail": "Withdraw Failed"
+	},
+	"payment": {
+		"channelName": "Channel Name",
+		"channelNamePlaceholder": "Please enter channel name",
+		"paymentMethod": "Payment Method",
+		"paymentMethodPlaceholder": "Please enter payment method",
+		"paymentType": "Payment Type",
+		"paymentTypePlaceholder": "Please select payment type",
+		"typePay": "Payout",
+		"typeHarvest": "Collection",
+		"merchantFeeType": "Merchant Fee Type",
+		"merchantFeeRate": "Merchant Fee Rate",
+		"merchantFeeFixed": "Merchant Fixed Fee",
+		"agentFeeType": "Agent Fee Type",
+		"agentFeeRate": "Agent Fee Rate",
+		"agentFeeFixed": "Agent Fixed Fee",
+		"feeTypePercentage": "Percentage",
+		"feeTypeFixed": "Fixed",
+		"feeTypeMixed": "Mixed",
+		"refresh": "Refresh",
+		"getRateFailed": "Failed to get rate info"
+	},
+	"settlement": {
+		"applySettlement": "Apply Settlement",
+		"availableBalance": "Available Balance",
+		"withdrawAmount": "Withdraw Amount",
+		"withdrawAmountPlaceholder": "Please enter withdraw amount",
+		"withdrawType": "Withdraw Type",
+		"bankCard": "Bank Card",
+		"usdt": "USDT",
+		"bankName": "Bank Name",
+		"bankNamePlaceholder": "Please enter bank name",
+		"bankAccount": "Bank Account",
+		"bankAccountPlaceholder": "Please enter bank account/USDT address",
+		"realName": "Real Name",
+		"realNamePlaceholder": "Please enter real name",
+		"submitApply": "Submit",
+		"applySuccess": "Application submitted successfully",
+		"exceedBalance": "Withdraw amount cannot exceed available balance",
+		"auditStatus": "Audit Status",
+		"auditStatusPlaceholder": "Please select audit status",
+		"statusPending": "Pending",
+		"statusApproved": "Approved",
+		"statusRejected": "Rejected",
+		"rejectReason": "Reject Reason",
+		"applyTime": "Apply Time",
+		"balanceAvailable": "Available Balance",
+		"balanceTotal": "Total Balance",
+		"balanceFrozen": "Frozen Balance",
+		"balanceWithdrawn": "Withdrawn Balance",
+		"orderNo": "Order No",
+		"orderNoPlaceholder": "Please enter order no",
+		"orderType": "Order Type",
+		"orderTypePlaceholder": "Please select order type",
+		"orderTypePay": "Pay Order",
+		"orderTypeWithdraw": "Withdraw Order",
+		"orderCreateTime": "Order Create Time",
+		"payTime": "Pay Time",
+		"startTime": "Start Time",
+		"endTime": "End Time",
+		"beforeBalance": "Before Balance",
+		"changeAmount": "Change Amount",
+		"afterBalance": "After Balance",
+		"businessOrder": "Business Order",
+		"orderAmount": "Order Amount"
+	},
+	"security": {
+		"title": "Security Center",
+		"desc": "Manage your account security settings to protect your information",
+		"paymentPassword": "Payment Password",
+		"paymentPwdSet": "Payment password has been set",
+		"paymentPwdNotSet": "Payment password not set, recommended to set for better security",
+		"enabled": "Enabled",
+		"disabled": "Disabled",
+		"setNow": "Set Now",
+		"modifyPassword": "Modify Password",
+		"whitelist": "Whitelist Settings",
+		"whitelistDesc": "Set allowed IP address whitelist",
+		"setWhitelist": "Set Whitelist",
+		"googleAuth": "Google Authenticator",
+		"googleBound": "Google Authenticator bound",
+		"googleNotBound": "Google Authenticator not bound, recommended to enable for better security",
+		"bindNow": "Bind Now",
+		"unbind": "Unbind",
+		"bindGoogleTitle": "Bind Google Authenticator",
+		"scanQrCode": "Scan QR Code",
+		"inputCode": "Enter Code",
+		"complete": "Complete",
+		"generatingQrCode": "Generating QR code...",
+		"scanTip": "Please scan the QR code with Google Authenticator",
+		"verifyCode": "Verification Code",
+		"verifyCodePlaceholder": "Please enter 6-digit code",
+		"verifyCodeTip": "Open Google Authenticator app and enter the 6-digit code",
+		"confirmBind": "Confirm Bind",
+		"bindSuccess": "Bind Success",
+		"bindSuccessDesc": "You have successfully bound Google Authenticator",
+		"unbindGoogleTitle": "Unbind Google Authenticator",
+		"unbindWarning": "After unbinding, you will not be able to use Google Authenticator for verification",
+		"loginPassword": "Login Password",
+		"loginPasswordPlaceholder": "Please enter login password",
+		"confirmUnbind": "Confirm Unbind",
+		"unbindSuccess": "Unbind Success",
+		"setPaymentPwdTitle": "Set Payment Password",
+		"modifyPaymentPwdTitle": "Modify Payment Password",
+		"firstSetTip": "First time setting payment password, no need to enter old password",
+		"oldPaymentPwd": "Old Password",
+		"oldPaymentPwdPlaceholder": "Please enter old payment password",
+		"newPaymentPwd": "New Password",
+		"newPaymentPwdPlaceholder": "Please enter new payment password",
+		"confirmPaymentPwd": "Confirm Password",
+		"confirmPaymentPwdPlaceholder": "Please enter new payment password again",
+		"passwordNotMatch": "The two passwords do not match",
+		"passwordMinLength": "Password must be at least 6 characters",
+		"setSuccess": "Set successfully",
+		"whitelistTitle": "Whitelist Settings",
+		"whitelistTip": "Enter allowed IP addresses, separated by commas",
+		"ipAddress": "IP Address",
+		"ipPlaceholder": "e.g. 192.168.1.1,192.168.1.2",
+		"ipEmptyTip": "Tip: Leave empty to allow all IPs",
+		"ipFormatError": "Please enter valid IP address format",
+		"logoutTip": "Click OK to logout and redirect to login page",
+		"qrCodeFailed": "Failed to generate QR code",
+		"getQrCodeFailed": "Failed to get QR code",
+		"verifyCodeRequired": "Please enter verification code",
+		"verifyCodeLength": "Verification code must be 6 digits",
+		"loginPwdRequired": "Please enter login password",
+		"oldPwdRequired": "Please enter old payment password",
+		"newPwdRequired": "Please enter new payment password",
+		"confirmPwdRequired": "Please enter new payment password again"
+	}
+}

+ 139 - 0
src/locales/index.ts

@@ -0,0 +1,139 @@
+import { createI18n } from 'vue-i18n';
+import pinia from '/@/stores/index';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { info } from '/@/api/admin/i18n';
+
+// element plus 自带国际化
+import enLocale from 'element-plus/es/locale/lang/en';
+import zhcnLocale from 'element-plus/es/locale/lang/zh-cn';
+
+import zhCN from './zh-CN/index.json';
+import enUS from './en-US/index.json';
+
+// 语言代码映射(兼容旧格式)
+const localeMap: Record<string, 'zh-CN' | 'en-US'> = {
+	'zh-cn': 'zh-CN',
+	'zh-CN': 'zh-CN',
+	en: 'en-US',
+	'en-US': 'en-US',
+};
+
+const messages = {
+	'zh-CN': {
+		name: 'zh-CN',
+		el: zhcnLocale.el,
+		...zhCN,
+	},
+	'en-US': {
+		name: 'en-US',
+		el: enLocale.el,
+		...enUS,
+	},
+};
+
+// 读取 pinia 默认语言
+const stores = useThemeConfig(pinia);
+const { themeConfig } = storeToRefs(stores);
+
+// 获取并标准化语言代码
+const getLocale = (locale: string): 'zh-CN' | 'en-US' => localeMap[locale] || 'zh-CN';
+
+// 从 localStorage 中读取 themeConfig 获取语言设置
+const getStoredLocale = (): string => {
+	try {
+		const storedThemeConfig = localStorage.getItem('themeConfig');
+		if (storedThemeConfig) {
+			const parsed = JSON.parse(storedThemeConfig);
+			return parsed.globalI18n || 'zh-cn';
+		}
+	} catch (e) {
+		// 解析失败,使用默认值
+	}
+	return localStorage.getItem('locale') || themeConfig.value.globalI18n || 'zh-cn';
+};
+
+const defaultLocale = getLocale(getStoredLocale());
+
+// 导出语言国际化
+export const i18n = createI18n({
+	legacy: false,
+	silentTranslationWarn: true,
+	missingWarn: false,
+	silentFallbackWarn: true,
+	fallbackWarn: false,
+	locale: defaultLocale,
+	fallbackLocale: 'zh-CN',
+	messages,
+	globalInjection: true,
+});
+
+// 初始化时设置 data-locale 属性
+if (typeof document !== 'undefined') {
+	document.querySelector('html')?.setAttribute('lang', defaultLocale);
+	document.querySelector('html')?.setAttribute('data-locale', defaultLocale);
+}
+
+// 远程获取i18n(延迟执行,避免 import.meta.env 未定义)
+async function fetchI18n() {
+	try {
+		// 检查环境变量是否可用
+		if (!import.meta.env?.VITE_API_URL) {
+			// VITE_API_URL not available, skipping remote i18n fetch
+			return;
+		}
+		const infoI18n = await info();
+		const messageLocal: Record<string, any> = {};
+		const itemizeLocal: Record<string, any[]> = { 'en-US': [], 'zh-CN': [] };
+		itemizeLocal['zh-CN'].push(...(infoI18n.data?.data?.['zh-cn'] || []));
+		itemizeLocal['en-US'].push(...(infoI18n.data?.data?.en || []));
+
+		for (const key in itemizeLocal) {
+			messageLocal[key] = {
+				name: key,
+				...mergeArrObj(itemizeLocal, key),
+			};
+		}
+
+		i18n.global.mergeLocaleMessage('zh-CN', messageLocal['zh-CN']);
+		i18n.global.mergeLocaleMessage('en-US', messageLocal['en-US']);
+		// 使用存储的语言设置,而不是 pinia 的默认值
+		i18n.global.locale.value = getLocale(getStoredLocale());
+	} catch (error) {
+		// Failed to fetch remote i18n, using local translations only
+	}
+}
+
+// 安全地执行远程 i18n 获取
+if (typeof import.meta.env?.VITE_API_URL === 'string') {
+	fetchI18n();
+}
+
+// 合并数组对象
+function mergeArrObj<T extends Record<string, any[]>>(list: T, key: string) {
+	let obj: Record<string, any> = {};
+	list[key].forEach((i: Record<string, any>) => {
+		obj = Object.assign({}, obj, i);
+	});
+	return obj;
+}
+
+export default i18n;
+
+// 导出语言代码映射函数
+export { getLocale };
+
+// 导出语言切换函数
+export function setLanguage(locale: string) {
+	const normalizedLocale = getLocale(locale);
+	i18n.global.locale.value = normalizedLocale;
+	localStorage.setItem('locale', normalizedLocale);
+	document.querySelector('html')?.setAttribute('lang', normalizedLocale);
+	// 设置 data-locale 属性用于样式适配
+	document.querySelector('html')?.setAttribute('data-locale', normalizedLocale);
+}
+
+// 导出当前语言
+export function getCurrentLanguage() {
+	return i18n.global.locale.value;
+}

+ 547 - 0
src/locales/zh-CN/index.json

@@ -0,0 +1,547 @@
+{
+	"app": {
+		"title": "商户系统"
+	},
+	"menu": {
+		"home": "首页",
+		"orderManagement": "订单管理",
+		"payOrder": "代收订单",
+		"withdrawOrder": "代付订单",
+		"paymentConfig": "支付配置",
+		"paymentChannel": "支付通道",
+		"settlementManagement": "结算管理",
+		"applySettlement": "申请结算",
+		"settlementRecord": "结算记录",
+		"fundFlow": "资金明细",
+		"systemSettings": "系统设置",
+		"securityCenter": "安全中心"
+	},
+	"home": {
+		"submitOrder": "提交订单",
+		"payOrder": "支付订单",
+		"withdrawOrder": "提现订单",
+		"withdrawSuccess": "提现成功",
+		"paySuccessRate": "支付成功率",
+		"amount": "金额",
+		"loadStatsFailed": "加载统计数据失败"
+	},
+	"timeGreeting": {
+		"dawn": "凌晨好",
+		"morning": "早上好",
+		"forenoon": "上午好",
+		"noon": "中午好",
+		"afternoon": "下午好",
+		"evening": "傍晚好",
+		"night": "晚上好",
+		"lateNight": "夜里好"
+	},
+	"router": {
+		"home": "首页",
+		"system": "系统设置",
+		"systemMenu": "菜单管理",
+		"systemRole": "角色管理",
+		"systemUser": "用户管理",
+		"systemDept": "部门管理",
+		"systemDic": "字典管理",
+		"limits": "权限管理",
+		"limitsFrontEnd": "前端控制",
+		"limitsFrontEndPage": "页面权限",
+		"limitsFrontEndBtn": "按钮权限",
+		"limitsBackEnd": "后端控制",
+		"limitsBackEndEndPage": "页面权限",
+		"funIndex": "功能",
+		"funTagsView": "tagsView 操作",
+		"funCountup": "数字滚动",
+		"funWangEditor": "Editor 编辑器",
+		"funCropper": "图片裁剪",
+		"funQrcode": "二维码生成",
+		"funEchartsMap": "地理坐标/地图",
+		"funPrintJs": "页面打印",
+		"funClipboard": "复制剪切",
+		"funGridLayout": "拖拽布局",
+		"funSplitpanes": "窗格拆分器",
+		"funDragVerify": "验证器",
+		"pagesIndex": "页面",
+		"pagesFiltering": "过滤筛选组件",
+		"pagesFilteringDetails": "过滤筛选组件详情",
+		"pagesFilteringDetails1": "过滤筛选组件详情111",
+		"pagesIocnfont": "ali 字体图标",
+		"pagesElement": "ele 字体图标",
+		"pagesAwesome": "awe 字体图标",
+		"pagesFormAdapt": "表单自适应",
+		"pagesTableRules": "表单表格验证",
+		"pagesFormI18n": "表单国际化",
+		"pagesFormRules": "多表单验证",
+		"pagesDynamicForm": "动态复杂表单",
+		"pagesWorkflow": "工作流",
+		"pagesListAdapt": "列表自适应",
+		"pagesWaterfall": "瀑布屏",
+		"pagesSteps": "步骤条",
+		"pagesPreview": "大图预览",
+		"pagesWaves": "波浪效果",
+		"pagesTree": "树形改表格",
+		"pagesDrag": "拖动指令",
+		"pagesLazyImg": "图片懒加载",
+		"makeIndex": "组件封装",
+		"makeSelector": "图标选择器",
+		"makeNoticeBar": "滚动通知栏",
+		"makeSvgDemo": "svgIcon 演示",
+		"makeTableDemo": "表格封装演示",
+		"paramsIndex": "路由参数",
+		"paramsCommon": "普通路由",
+		"paramsDynamic": "动态路由",
+		"paramsCommonDetails": "普通路由详情",
+		"paramsDynamicDetails": "动态路由详情",
+		"chartIndex": "大数据图表",
+		"visualizingIndex": "数据可视化",
+		"visualizingLinkDemo1": "数据可视化演示1",
+		"visualizingLinkDemo2": "数据可视化演示2",
+		"personal": "个人中心",
+		"tools": "工具类集合",
+		"layoutLinkView": "外链",
+		"layoutIframeViewOne": "内嵌 iframe1",
+		"layoutIframeViewTwo": "内嵌 iframe2"
+	},
+	"staticRoutes": {
+		"login": "登录",
+		"authredirect": "回调页",
+		"expire": "密码过期",
+		"signIn": "登录",
+		"notFound": "找不到此页面",
+		"noPower": "没有权限"
+	},
+	"user": {
+		"title0": "组件大小",
+		"title1": "语言切换",
+		"title2": "菜单搜索",
+		"title3": "布局配置",
+		"title4": "消息",
+		"title5": "开全屏",
+		"title6": "关全屏",
+		"dropdownLarge": "大型",
+		"dropdownDefault": "默认",
+		"dropdownSmall": "小型",
+		"dropdown1": "首页",
+		"dropdown2": "个人中心",
+		"dropdown3": "切换租户",
+		"dropdown4": "当前租户",
+		"dropdown5": "退出登录",
+		"searchPlaceholder": "菜单搜索:支持中文、路由路径",
+		"newTitle": "通知",
+		"newBtn": "全部",
+		"newGo": "前往通知中心",
+		"newDesc": "暂无通知",
+		"logOutTitle": "提示",
+		"logOutMessage": "此操作将退出登录, 是否继续?",
+		"logOutConfirm": "确定",
+		"logOutCancel": "取消",
+		"logOutExit": "退出中"
+	},
+	"tagsView": {
+		"refresh": "刷新",
+		"close": "关闭",
+		"closeOther": "关闭其它",
+		"closeAll": "全部关闭",
+		"fullscreen": "当前页全屏",
+		"closeFullscreen": "关闭全屏",
+		"favorite": "收藏",
+		"favoriteMax": "快捷导航数量超过上限,请移除部分"
+	},
+	"notFound": {
+		"foundTitle": "地址输入错误,请重新输入地址~",
+		"foundMsg": "您可以先检查网址,然后重新输入或给我们反馈问题。",
+		"foundBtn": "返回首页"
+	},
+	"noAccess": {
+		"accessTitle": "您未被授权,没有操作权限~",
+		"accessMsg": "联系方式:加QQ群探讨 665452019",
+		"accessBtn": "重新授权"
+	},
+	"layout": {
+		"configTitle": "布局配置",
+		"oneTitle": "全局主题",
+		"twoTopTitle": "顶栏设置",
+		"twoMenuTitle": "菜单设置",
+		"twoColumnsTitle": "分栏设置",
+		"twoTopBar": "顶栏背景",
+		"twoTopBarColor": "顶栏默认字体颜色",
+		"twoIsTopBarColorGradual": "顶栏背景渐变",
+		"twoMenuBar": "菜单背景",
+		"twoMenuBarColor": "菜单默认字体颜色",
+		"twoMenuBarActiveColor": "菜单高亮背景色",
+		"twoIsMenuBarColorGradual": "菜单背景渐变",
+		"twoColumnsMenuBar": "分栏菜单背景",
+		"twoColumnsMenuBarColor": "分栏菜单默认字体颜色",
+		"twoIsColumnsMenuBarColorGradual": "分栏菜单背景渐变",
+		"twoIsColumnsMenuHoverPreload": "分栏菜单鼠标悬停预加载",
+		"threeTitle": "界面设置",
+		"threeIsCollapse": "菜单水平折叠",
+		"threeIsUniqueOpened": "菜单手风琴",
+		"threeIsFixedHeader": "固定 Header",
+		"threeIsClassicSplitMenu": "经典布局顶部菜单",
+		"threeIsLockScreen": "开启锁屏",
+		"threeLockScreenTime": "自动锁屏(s/秒)",
+		"fourTitle": "界面显示",
+		"fourIsShowLogo": "侧边栏 Logo",
+		"fourIsBreadcrumb": "开启 Breadcrumb",
+		"fourIsBreadcrumbIcon": "开启 Breadcrumb 图标",
+		"fourIsTagsview": "开启 Tagsview",
+		"fourIsTagsviewIcon": "开启 Tagsview 图标",
+		"fourIsCacheTagsView": "开启 TagsView 缓存",
+		"fourIsSortableTagsView": "开启 TagsView 拖拽",
+		"fourIsShareTagsView": "开启 TagsView 共用",
+		"fourIsFooter": "开启 Footer",
+		"fourIsGrayscale": "灰色模式",
+		"fourIsInvert": "色弱模式",
+		"fourIsDark": "深色模式",
+		"fourIsWartermark": "开启水印",
+		"fourIsChat": "开启AI助手",
+		"fourWartermarkText": "水印文案",
+		"fiveTitle": "其它设置",
+		"fiveTagsStyle": "Tagsview 风格",
+		"fiveAnimation": "主页面切换动画",
+		"fiveColumnsAsideStyle": "分栏高亮风格",
+		"fiveColumnsAsideLayout": "分栏布局风格",
+		"sixTitle": "布局切换",
+		"sixDefaults": "左侧",
+		"sixClassic": "左顶",
+		"sixTransverse": "横向",
+		"sixColumns": "分栏",
+		"tipText": "点击下方按钮,复制布局配置去 `src/stores/themeConfig.ts` 中修改。",
+		"copyText": "一键复制配置",
+		"resetText": "一键恢复默认",
+		"copyTextSuccess": "复制成功!",
+		"copyTextError": "复制失败!"
+	},
+	"upgrade": {
+		"title": "新版本升级",
+		"msg": "新版本来啦,马上更新尝鲜吧!不用担心,更新很快的哦!",
+		"desc": "提示:更新会还原默认配置",
+		"btnOne": "残忍拒绝",
+		"btnTwo": "马上更新",
+		"btnTwoLoading": "更新中"
+	},
+	"tenantSelector": {
+		"title": "切换租户",
+		"searchPlaceholder": "搜索租户名称、域名或网站名",
+		"noData": "暂无租户数据",
+		"current": "当前租户",
+		"selected": "已选择",
+		"loadError": "加载租户列表失败",
+		"selectTip": "请选择要切换的租户",
+		"sameError": "当前已是该租户,无需切换",
+		"confirmTitle": "切换租户确认",
+		"confirmMessage": "确定要切换到租户 \"{name}\" 吗?切换后页面将刷新。",
+		"switchSuccess": "租户切换成功,即将刷新页面...",
+		"switchError": "租户切换失败,请重试"
+	},
+	"common": {
+		"queryBtn": "查询",
+		"addBtn": "新 增",
+		"editBtn": "修 改",
+		"expandBtn": "展开/折叠",
+		"delBtn": "删除",
+		"viewBtn": "查看",
+		"detailBtn": "详情",
+		"refreshCacheBtn": "刷新缓存",
+		"exportBtn": "导出",
+		"importBtn": "导入",
+		"queryDeptTip": "请输入部门名称",
+		"resetBtn": "重置",
+		"copyBtn": "复制",
+		"action": "操作",
+		"optSuccessText": "操作成功",
+		"editSuccessText": "修改成功",
+		"addSuccessText": "添加成功",
+		"delSuccessText": "删除成功",
+		"delConfirmText": "此操作将永久删除",
+		"optConfirmText": "是否确认本操作",
+		"confirmButtonText": "确 认",
+		"cancelButtonText": "取 消",
+		"download": "下载",
+		"expand": "展开/折叠",
+		"selectAll": "全选/不全选",
+		"cancel": "取消",
+		"confirm": "确认"
+	},
+	"message": {
+		"box": {
+			"title": "系统提示"
+		}
+	},
+	"label": {
+		"one1": "用户名登录",
+		"two2": "手机号登录",
+		"three3": "社交登录",
+		"register": "注册账号"
+	},
+	"link": {
+		"one3": "第三方登录",
+		"two4": "友情链接"
+	},
+	"password": {
+		"accountPlaceholder1": "请输入用户名",
+		"accountPlaceholder2": "请输入密码",
+		"accountPlaceholder3": "请输入验证码",
+		"phonePlaceholder4": "请输入手机号",
+		"tenantPlaceholder": "请选择租户",
+		"accountBtnText": "登 录",
+		"registerBtnText": "注 册",
+		"resetBtnText": "重 置",
+		"readAccept": "我已仔细阅读并接受",
+		"privacyPolicy": "《隐私政策》",
+		"oldPassword": "请输入原密码",
+		"newPassword": "请输入新密码",
+		"confirmPassword": "请确认新密码",
+		"backToLogin": "返回登录",
+		"mobileLogin": "验证码登录",
+		"createAccount": "注册账号",
+		"imageCodeTip": "请输入验证码",
+		"forgetPassword": "找回密码",
+		"googleCaptchaPlaceholder": "谷歌验证码(若没有可以不填)"
+	},
+	"mobile": {
+		"placeholder1": "请输入手机号",
+		"placeholder2": "请输入验证码",
+		"codeText": "获取验证码",
+		"btnText": "登 录",
+		"mobileLogin": "验证码登录",
+		"backToLogin": "返回登录",
+		"createAccount": "注册账号",
+		"sendSuccess": "验证码发送成功",
+		"seconds": "秒后重发",
+		"mobileRequired": "请输入手机号码",
+		"codeRequired": "请输入验证码",
+		"codeLength": "验证码必须是4位数字",
+		"sendFailed": "发送验证码失败",
+		"loginSuccess": "登录成功",
+		"loginFailed": "登录失败",
+		"signIn": "登 录"
+	},
+	"forget": {
+		"newPasswordPlaceholder": "请输入新密码",
+		"confirmPasswordPlaceholder": "请确认新密码",
+		"passwordEmpty": "密码不能为空",
+		"passwordLength": "用户密码长度必须介于 6 和 20 之间",
+		"passwordRule": "两次输入的密码不一致",
+		"passwordScore": "密码强度太低"
+	},
+	"scan": {
+		"text": "打开手机扫一扫,快速登录/注册",
+		"wechatApp": "微信扫码体验移动端",
+		"appErrorTip": "应用未配置,登录失败"
+	},
+	"signInText": "欢迎回来!",
+	"browserMsgText": "* 温馨提示:建议使用谷歌、Microsoft Edge,版本 80 及以上浏览器,360浏览器请使用极速模式",
+	"expire": {
+		"oldPassword": "请输入原密码",
+		"newPassword": "请输入新密码",
+		"confirmPassword": "请确认新密码",
+		"passwordRule": "两次输入的密码不一致",
+		"passwordScore": "密码强度太低",
+		"resetSuccess": "密码重置成功"
+	},
+	"tenantSelect": {
+		"select": "选择租户",
+		"loadError": "获取租户列表失败"
+	},
+	"register": {
+		"tenantEmpty": "请选择租户",
+		"tenantLoadError": "获取租户列表失败",
+		"usernameEmpty": "用户名不能为空",
+		"usernameLength": "用户名称长度必须介于 5 和 20 之间",
+		"phoneEmpty": "手机号不能为空",
+		"passwordEmpty": "密码不能为空",
+		"passwordLength": "用户密码长度必须介于 6 和 20 之间",
+		"passwordStrength": "密码强度太低",
+		"termsRequired": "请阅读并同意条款"
+	},
+	"divider": {
+		"or": "或"
+	},
+	"socialLogin": {
+		"wechatWork": "企微",
+		"dingtalk": "钉钉"
+	},
+	"personal": {
+		"name": "个人信息",
+		"basicInfo": "基本信息",
+		"securityInfo": "安全信息",
+		"merchantNo": "商户号",
+		"nickname": "昵称",
+		"nicknamePlaceholder": "请输入昵称",
+		"realName": "姓名",
+		"realNamePlaceholder": "请输入姓名",
+		"phone": "手机",
+		"phonePlaceholder": "请输入手机",
+		"email": "邮箱",
+		"emailPlaceholder": "请输入邮箱",
+		"oldPassword": "原密码",
+		"oldPasswordPlaceholder": "请输入密码",
+		"newPassword": "新密码",
+		"newPasswordPlaceholder": "请输入新密码",
+		"confirmPassword": "确认密码",
+		"confirmPasswordPlaceholder": "请重复密码",
+		"changePassword": "修改密码",
+		"updateInfo": "更新个人信息",
+		"passwordRule": "两次输入密码不一致",
+		"passwordScore": "密码等级太低"
+	},
+	"order": {
+		"mchOrderNo": "商户订单号",
+		"mchOrderNoPlaceholder": "请输入商户订单号",
+		"transactionId": "平台订单号",
+		"transactionIdPlaceholder": "请输入平台订单号",
+		"merchantName": "商户名称",
+		"merchantNamePlaceholder": "请输入商户名称",
+		"agentName": "代理名称",
+		"agentNamePlaceholder": "请输入代理名称",
+		"orderStatus": "订单状态",
+		"orderStatusPlaceholder": "请选择订单状态",
+		"channelName": "通道名称",
+		"submitAmount": "提交金额",
+		"actualAmount": "实际金额",
+		"createTime": "创建时间",
+		"callbackLog": "回调日志",
+		"submitOrderCount": "提交订单数",
+		"totalOrderAmount": "订单总金额",
+		"paidOrderCount": "已付订单数",
+		"paidTotalAmount": "已付总金额",
+		"statusCreateOrder": "创建订单",
+		"statusPaySuccess": "支付成功",
+		"statusPayFail": "支付失败",
+		"statusCancelOrder": "取消订单",
+		"statusPayTimeout": "支付超时",
+		"statusWithdrawSuccess": "提现成功",
+		"statusWithdrawFail": "提现失败"
+	},
+	"payment": {
+		"channelName": "通道名称",
+		"channelNamePlaceholder": "请输入通道名称",
+		"paymentMethod": "支付方式",
+		"paymentMethodPlaceholder": "请输入支付方式",
+		"paymentType": "支付类别",
+		"paymentTypePlaceholder": "请选择支付类别",
+		"typePay": "代付",
+		"typeHarvest": "代收",
+		"merchantFeeType": "商户手续费类型",
+		"merchantFeeRate": "商户手续费比例",
+		"merchantFeeFixed": "商户手续费固定金额",
+		"agentFeeType": "代理商手续费类型",
+		"agentFeeRate": "代理商手续费比例",
+		"agentFeeFixed": "代理商手续费固定金额",
+		"feeTypePercentage": "百分比",
+		"feeTypeFixed": "固定金额",
+		"feeTypeMixed": "混合",
+		"refresh": "刷新",
+		"getRateFailed": "获取费率信息失败"
+	},
+	"settlement": {
+		"applySettlement": "申请结算",
+		"availableBalance": "可提现金额",
+		"withdrawAmount": "提现金额",
+		"withdrawAmountPlaceholder": "请输入提现金额",
+		"withdrawType": "提现类型",
+		"bankCard": "银行卡",
+		"usdt": "USDT",
+		"bankName": "银行名称",
+		"bankNamePlaceholder": "请输入银行名称",
+		"bankAccount": "银行卡账号",
+		"bankAccountPlaceholder": "请输入银行卡账号/USDT地址",
+		"realName": "真实姓名",
+		"realNamePlaceholder": "请输入真实姓名",
+		"submitApply": "提交申请",
+		"applySuccess": "申请提交成功",
+		"exceedBalance": "提现金额不能超过可提现金额",
+		"auditStatus": "审核状态",
+		"auditStatusPlaceholder": "请选择审核状态",
+		"statusPending": "待审核",
+		"statusApproved": "通过",
+		"statusRejected": "拒绝",
+		"rejectReason": "拒绝原因",
+		"applyTime": "申请时间",
+		"balanceAvailable": "可用余额",
+		"balanceTotal": "总金额",
+		"balanceFrozen": "总冻结金额",
+		"balanceWithdrawn": "总提现金额",
+		"orderNo": "订单号",
+		"orderNoPlaceholder": "请输入订单号",
+		"orderType": "订单类型",
+		"orderTypePlaceholder": "请选择订单类型",
+		"orderTypePay": "支付订单",
+		"orderTypeWithdraw": "提现订单",
+		"orderCreateTime": "订单创建时间",
+		"payTime": "支付时间",
+		"startTime": "开始时间",
+		"endTime": "结束时间",
+		"beforeBalance": "变更前余额",
+		"changeAmount": "变更金额",
+		"afterBalance": "变更后余额",
+		"businessOrder": "业务订单",
+		"orderAmount": "订单金额"
+	},
+	"security": {
+		"title": "安全中心",
+		"desc": "管理您的账号安全设置,保护账户信息",
+		"paymentPassword": "支付密码",
+		"paymentPwdSet": "已设置支付密码",
+		"paymentPwdNotSet": "未设置支付密码,建议设置以提高账号安全性",
+		"enabled": "已开启",
+		"disabled": "未开启",
+		"setNow": "立即设置",
+		"modifyPassword": "修改密码",
+		"whitelist": "白名单设置",
+		"whitelistDesc": "设置允许访问的IP地址白名单",
+		"setWhitelist": "设置白名单",
+		"googleAuth": "Google验证器",
+		"googleBound": "已绑定Google验证器",
+		"googleNotBound": "未绑定Google验证器,建议开启以提高账号安全性",
+		"bindNow": "立即绑定",
+		"unbind": "解除绑定",
+		"bindGoogleTitle": "绑定Google验证器",
+		"scanQrCode": "扫描二维码",
+		"inputCode": "输入验证码",
+		"complete": "完成",
+		"generatingQrCode": "正在生成二维码...",
+		"scanTip": "请使用Google Authenticator扫描二维码",
+		"verifyCode": "验证码",
+		"verifyCodePlaceholder": "请输入6位验证码",
+		"verifyCodeTip": "请打开Google Authenticator应用,输入显示的6位数字验证码",
+		"confirmBind": "确认绑定",
+		"bindSuccess": "绑定成功",
+		"bindSuccessDesc": "您已成功绑定Google验证器",
+		"unbindGoogleTitle": "解除绑定Google验证器",
+		"unbindWarning": "解除绑定后,您将无法使用Google验证器进行二次验证",
+		"loginPassword": "登录密码",
+		"loginPasswordPlaceholder": "请输入登录密码",
+		"confirmUnbind": "确认解绑",
+		"unbindSuccess": "解绑成功",
+		"setPaymentPwdTitle": "设置支付密码",
+		"modifyPaymentPwdTitle": "修改支付密码",
+		"firstSetTip": "首次设置支付密码,无需输入旧密码",
+		"oldPaymentPwd": "旧密码",
+		"oldPaymentPwdPlaceholder": "请输入旧支付密码",
+		"newPaymentPwd": "新密码",
+		"newPaymentPwdPlaceholder": "请输入新支付密码",
+		"confirmPaymentPwd": "确认密码",
+		"confirmPaymentPwdPlaceholder": "请再次输入新支付密码",
+		"passwordNotMatch": "两次输入的密码不一致",
+		"passwordMinLength": "密码长度不能少于6位",
+		"setSuccess": "设置成功",
+		"whitelistTitle": "白名单设置",
+		"whitelistTip": "请输入允许访问的IP地址,多个IP用英文逗号分隔",
+		"ipAddress": "IP地址",
+		"ipPlaceholder": "例如: 192.168.1.1,192.168.1.2",
+		"ipEmptyTip": "提示: 留空表示不限制IP访问",
+		"ipFormatError": "请输入正确的IP地址格式",
+		"logoutTip": "点击确定后将自动注销并跳转到登录页",
+		"qrCodeFailed": "二维码生成失败",
+		"getQrCodeFailed": "获取二维码失败",
+		"verifyCodeRequired": "请输入验证码",
+		"verifyCodeLength": "验证码必须为6位数字",
+		"loginPwdRequired": "请输入登录密码",
+		"oldPwdRequired": "请输入旧支付密码",
+		"newPwdRequired": "请输入新支付密码",
+		"confirmPwdRequired": "请再次输入新支付密码"
+	}
+}

+ 1 - 1
src/main.ts

@@ -4,7 +4,7 @@ import App from './App.vue';
 import router from './router';
 import { directive } from '/@/directive';
 import Components from '/@/components';
-import { i18n } from '/@/i18n';
+import { i18n } from '/@/locales';
 import { properties } from '/@/utils/globalProperties';
 import { initAntiDebug } from './utils/antiDebug';
 

+ 2 - 2
src/router/route.ts

@@ -38,7 +38,7 @@ declare module 'vue-router' {
 export const dynamicRoutes: Array<RouteRecordRaw> = [
 	{
 		path: '/home',
-		name: 'router.home',
+		name: 'menu.home',
 		component: () => import('/@/views/home/index.vue'),
 		meta: {
 			isLink: '',
@@ -51,7 +51,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
 	},
 	{
 		path: '/personal',
-		name: 'router.personal',
+		name: 'personal.name',
 		component: () => import('/@/views/admin/system/user/personal.vue'),
 		meta: {
 			isHide: true,

+ 321 - 322
src/utils/other.ts

@@ -1,30 +1,29 @@
-import {nextTick} from 'vue';
+import { nextTick } from 'vue';
 import router from '/@/router/index';
 import pinia from '/@/stores/index';
-import {storeToRefs} from 'pinia';
-import {useThemeConfig} from '/@/stores/themeConfig';
-import {i18n} from '/@/i18n/index';
-import {Local} from '/@/utils/storage';
-import {verifyUrl} from '/@/utils/toolsValidate';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '/@/stores/themeConfig';
+import { i18n } from '/@/locales/index';
+import { Local } from '/@/utils/storage';
+import { verifyUrl } from '/@/utils/toolsValidate';
 import request from '/@/utils/request';
-import {useMessage} from '/@/hooks/message';
+import { useMessage } from '/@/hooks/message';
 import * as CryptoJS from 'crypto-js';
-import {sm4} from 'sm-crypto'
-import {validateNull} from './validate';
-
+import { sm4 } from 'sm-crypto';
+import { validateNull } from './validate';
 
 /**
  * 设置浏览器标题国际化
  * @method const title = useTitle(); ==> title()
  */
 export function useTitle() {
-    const stores = useThemeConfig(pinia);
-    const {themeConfig} = storeToRefs(stores);
-    nextTick(() => {
-        let globalTitle: string = themeConfig.value.globalTitle;
-        let webTitle = setTagsViewNameI18n(router.currentRoute.value);
-        document.title = `${webTitle} - ${globalTitle}` || globalTitle;
-    });
+	const stores = useThemeConfig(pinia);
+	const { themeConfig } = storeToRefs(stores);
+	nextTick(() => {
+		let globalTitle: string = themeConfig.value.globalTitle;
+		let webTitle = setTagsViewNameI18n(router.currentRoute.value);
+		document.title = `${webTitle} - ${globalTitle}` || globalTitle;
+	});
 }
 
 /**
@@ -33,24 +32,24 @@ export function useTitle() {
  * @returns 返回当前 tagsViewName 名称
  */
 export function setTagsViewNameI18n(item: any) {
-    let tagsViewName: string = '';
-    const {query, params} = item;
-    //修复tagsViewName匹配到其他含下列单词的路由
-    const pattern = /^\{("(zh-cn|en|zh-tw)":"[^,]+",?){1,3}}$/;
-    if (query?.tagsViewName || params?.tagsViewName) {
-        if (pattern.test(query?.tagsViewName) || pattern.test(params?.tagsViewName)) {
-            // 国际化
-            const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
-            tagsViewName = urlTagsParams[i18n.global.locale.value];
-        } else {
-            // 非国际化
-            tagsViewName = query?.tagsViewName || params?.tagsViewName;
-        }
-    } else {
-        // 非自定义 tagsView 名称
-        tagsViewName = i18n.global.t(item.name);
-    }
-    return tagsViewName;
+	let tagsViewName: string = '';
+	const { query, params } = item;
+	//修复tagsViewName匹配到其他含下列单词的路由
+	const pattern = /^\{("(zh-cn|en|zh-tw)":"[^,]+",?){1,3}}$/;
+	if (query?.tagsViewName || params?.tagsViewName) {
+		if (pattern.test(query?.tagsViewName) || pattern.test(params?.tagsViewName)) {
+			// 国际化
+			const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
+			tagsViewName = urlTagsParams[i18n.global.locale.value];
+		} else {
+			// 非国际化
+			tagsViewName = query?.tagsViewName || params?.tagsViewName;
+		}
+	} else {
+		// 非自定义 tagsView 名称
+		tagsViewName = i18n.global.t(item.name);
+	}
+	return tagsViewName;
 }
 
 /**
@@ -60,21 +59,21 @@ export function setTagsViewNameI18n(item: any) {
  * @description data-xxx 属性用于存储页面或应用程序的私有自定义数据
  */
 export const lazyImg = (el: string, arr: EmptyArrayType) => {
-    const io = new IntersectionObserver((res) => {
-        res.forEach((v: any) => {
-            if (v.isIntersecting) {
-                const {img, key} = v.target.dataset;
-                v.target.src = img;
-                v.target.onload = () => {
-                    io.unobserve(v.target);
-                    arr[key]['loading'] = false;
-                };
-            }
-        });
-    });
-    nextTick(() => {
-        document.querySelectorAll(el).forEach((img) => io.observe(img));
-    });
+	const io = new IntersectionObserver((res) => {
+		res.forEach((v: any) => {
+			if (v.isIntersecting) {
+				const { img, key } = v.target.dataset;
+				v.target.src = img;
+				v.target.onload = () => {
+					io.unobserve(v.target);
+					arr[key]['loading'] = false;
+				};
+			}
+		});
+	});
+	nextTick(() => {
+		document.querySelectorAll(el).forEach((img) => io.observe(img));
+	});
 };
 
 /**
@@ -82,9 +81,9 @@ export const lazyImg = (el: string, arr: EmptyArrayType) => {
  * @returns 返回 `window.localStorage` 中读取的缓存值 `globalComponentSize`
  */
 export const globalComponentSize = (): string => {
-    const stores = useThemeConfig(pinia);
-    const {themeConfig} = storeToRefs(stores);
-    return Local.get('themeConfig')?.globalComponentSize || themeConfig.value?.globalComponentSize;
+	const stores = useThemeConfig(pinia);
+	const { themeConfig } = storeToRefs(stores);
+	return Local.get('themeConfig')?.globalComponentSize || themeConfig.value?.globalComponentSize;
 };
 
 /**
@@ -93,35 +92,35 @@ export const globalComponentSize = (): string => {
  * @returns 克隆后的对象
  */
 export function deepClone(obj: EmptyObjectType) {
-    let newObj: EmptyObjectType;
-    try {
-        newObj = obj.push ? [] : {};
-    } catch (error) {
-        newObj = {};
-    }
-    for (let attr in obj) {
-        if (obj[attr] && typeof obj[attr] === 'object') {
-            newObj[attr] = deepClone(obj[attr]);
-        } else {
-            newObj[attr] = obj[attr];
-        }
-    }
-    return newObj;
+	let newObj: EmptyObjectType;
+	try {
+		newObj = obj.push ? [] : {};
+	} catch (error) {
+		newObj = {};
+	}
+	for (let attr in obj) {
+		if (obj[attr] && typeof obj[attr] === 'object') {
+			newObj[attr] = deepClone(obj[attr]);
+		} else {
+			newObj[attr] = obj[attr];
+		}
+	}
+	return newObj;
 }
 
 /**
  * 判断是否是移动端
  */
 export function isMobile() {
-    if (
-        navigator.userAgent.match(
-            /('phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone')/i
-        )
-    ) {
-        return true;
-    } else {
-        return false;
-    }
+	if (
+		navigator.userAgent.match(
+			/('phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone')/i
+		)
+	) {
+		return true;
+	} else {
+		return false;
+	}
 }
 
 /**
@@ -131,18 +130,18 @@ export function isMobile() {
  * @returns 删除空值后的数组对象
  */
 export function handleEmpty(list: EmptyArrayType) {
-    const arr = [] as any[];
-    for (const i in list) {
-        const d = [] as any[];
-        for (const j in list[i]) {
-            d.push(list[i][j]);
-        }
-        const leng = d.filter((item) => item === '').length;
-        if (leng !== d.length) {
-            arr.push(list[i]);
-        }
-    }
-    return arr;
+	const arr = [] as any[];
+	for (const i in list) {
+		const d = [] as any[];
+		for (const j in list[i]) {
+			d.push(list[i][j]);
+		}
+		const leng = d.filter((item) => item === '').length;
+		if (leng !== d.length) {
+			arr.push(list[i]);
+		}
+	}
+	return arr;
 }
 
 /**
@@ -150,55 +149,55 @@ export function handleEmpty(list: EmptyArrayType) {
  * @param val 当前点击项菜单
  */
 export function handleOpenLink(val: RouteItem) {
-    router.push(val.path);
-    if (verifyUrl(<string>val.meta?.isLink)) window.open(val.meta?.isLink);
-    else window.open(`${val.meta?.isLink}`);
+	router.push(val.path);
+	if (verifyUrl(<string>val.meta?.isLink)) window.open(val.meta?.isLink);
+	else window.open(`${val.meta?.isLink}`);
 }
 
 /**
  * 打开小窗口
  */
 export const openWindow = (url: string, title: string, w: number, h: number) => {
-    // @ts-ignore
-    const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
-    // @ts-ignore
-    const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
-
-    const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
-    const height = window.innerHeight
-        ? window.innerHeight
-        : document.documentElement.clientHeight
-            ? document.documentElement.clientHeight
-            : screen.height;
-
-    const left = width / 2 - w / 2 + dualScreenLeft;
-    const top = height / 2 - h / 2 + dualScreenTop;
-    return window.open(
-        url,
-        title,
-        'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' +
-        w +
-        ', height=' +
-        h +
-        ', top=' +
-        top +
-        ', left=' +
-        left
-    );
+	// @ts-ignore
+	const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
+	// @ts-ignore
+	const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
+
+	const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
+	const height = window.innerHeight
+		? window.innerHeight
+		: document.documentElement.clientHeight
+		? document.documentElement.clientHeight
+		: screen.height;
+
+	const left = width / 2 - w / 2 + dualScreenLeft;
+	const top = height / 2 - h / 2 + dualScreenTop;
+	return window.open(
+		url,
+		title,
+		'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' +
+			w +
+			', height=' +
+			h +
+			', top=' +
+			top +
+			', left=' +
+			left
+	);
 };
 
 /**
  *加密处理
  */
 export function encryption(src: string, keyWord: string) {
-    const key = CryptoJS.enc.Utf8.parse(keyWord);
-    // 加密
-    var encrypted = CryptoJS.AES.encrypt(src, key, {
-        iv: key,
-        mode: CryptoJS.mode.CFB,
-        padding: CryptoJS.pad.NoPadding,
-    });
-    return encrypted.toString();
+	const key = CryptoJS.enc.Utf8.parse(keyWord);
+	// 加密
+	var encrypted = CryptoJS.AES.encrypt(src, key, {
+		iv: key,
+		mode: CryptoJS.mode.CFB,
+		padding: CryptoJS.pad.NoPadding,
+	});
+	return encrypted.toString();
 }
 
 /**
@@ -207,22 +206,22 @@ export function encryption(src: string, keyWord: string) {
  * @returns 明文
  */
 export function decryption(src: string, keyWord: string) {
-    const key = CryptoJS.enc.Utf8.parse(keyWord);
-    // 解密逻辑
-    var decryptd = CryptoJS.AES.decrypt(src, key, {
-        iv: key,
-        mode: CryptoJS.mode.CFB,
-        padding: CryptoJS.pad.NoPadding,
-    });
-
-    return decryptd.toString(CryptoJS.enc.Utf8);
+	const key = CryptoJS.enc.Utf8.parse(keyWord);
+	// 解密逻辑
+	var decryptd = CryptoJS.AES.decrypt(src, key, {
+		iv: key,
+		mode: CryptoJS.mode.CFB,
+		padding: CryptoJS.pad.NoPadding,
+	});
+
+	return decryptd.toString(CryptoJS.enc.Utf8);
 }
 
 /**
  * SM4加密处理
  */
 export function sm4Encryption(src: string, keyWord: string) {
-    return sm4.encrypt(src, keyWord);
+	return sm4.encrypt(src, keyWord);
 }
 
 /**
@@ -231,7 +230,7 @@ export function sm4Encryption(src: string, keyWord: string) {
  * @returns 明文
  */
 export function sm4Decryption(src: string, keyWord: string) {
-    return sm4.decrypt(src, keyWord);
+	return sm4.decrypt(src, keyWord);
 }
 
 /**
@@ -240,8 +239,8 @@ export function sm4Decryption(src: string, keyWord: string) {
  * @returns 密文
  */
 export function base64Encrypt(src: string) {
-    const encodedWord = CryptoJS.enc.Utf8.parse(src);
-    return CryptoJS.enc.Base64.stringify(encodedWord);
+	const encodedWord = CryptoJS.enc.Utf8.parse(src);
+	return CryptoJS.enc.Base64.stringify(encodedWord);
 }
 
 /**
@@ -252,14 +251,14 @@ export function base64Encrypt(src: string) {
  * @returns {*}
  */
 export function downBlobFile(url: any, query: any, fileName: string) {
-    return request({
-        url: url,
-        method: 'get',
-        responseType: 'blob',
-        params: query,
-    }).then((response) => {
-        handleBlobFile(response, fileName);
-    });
+	return request({
+		url: url,
+		method: 'get',
+		responseType: 'blob',
+		params: query,
+	}).then((response) => {
+		handleBlobFile(response, fileName);
+	});
 }
 
 /**
@@ -268,26 +267,26 @@ export function downBlobFile(url: any, query: any, fileName: string) {
  * @returns
  */
 export function handleBlobFile(response: any, fileName: string) {
-    // 处理返回的文件流
-    const blob = response;
-    if (blob && blob.size === 0) {
-        useMessage().error('内容为空,无法下载');
-        return;
-    }
-    const link = document.createElement('a');
-
-    // 兼容一下 入参不是 File Blob 类型情况
-    var binaryData = [] as any;
-    binaryData.push(response);
-    link.href = window.URL.createObjectURL(new Blob(binaryData));
-    link.download = fileName;
-    document.body.appendChild(link);
-    link.click();
-    window.setTimeout(function () {
-        // @ts-ignore
-        URL.revokeObjectURL(blob);
-        document.body.removeChild(link);
-    }, 0);
+	// 处理返回的文件流
+	const blob = response;
+	if (blob && blob.size === 0) {
+		useMessage().error('内容为空,无法下载');
+		return;
+	}
+	const link = document.createElement('a');
+
+	// 兼容一下 入参不是 File Blob 类型情况
+	var binaryData = [] as any;
+	binaryData.push(response);
+	link.href = window.URL.createObjectURL(new Blob(binaryData));
+	link.download = fileName;
+	document.body.appendChild(link);
+	link.click();
+	window.setTimeout(function () {
+		// @ts-ignore
+		URL.revokeObjectURL(blob);
+		document.body.removeChild(link);
+	}, 0);
 }
 
 /**
@@ -295,31 +294,31 @@ export function handleBlobFile(response: any, fileName: string) {
  * @return string
  */
 export function generateUUID() {
-    if (typeof crypto === 'object') {
-        if (typeof crypto.randomUUID === 'function') {
-            return crypto.randomUUID();
-        }
-        if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
-            const callback = (c: any) => {
-                const num = Number(c);
-                return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16);
-            };
-            return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback);
-        }
-    }
-    let timestamp = new Date().getTime();
-    let performanceNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
-    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
-        let random = Math.random() * 16;
-        if (timestamp > 0) {
-            random = (timestamp + random) % 16 | 0;
-            timestamp = Math.floor(timestamp / 16);
-        } else {
-            random = (performanceNow + random) % 16 | 0;
-            performanceNow = Math.floor(performanceNow / 16);
-        }
-        return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16);
-    });
+	if (typeof crypto === 'object') {
+		if (typeof crypto.randomUUID === 'function') {
+			return crypto.randomUUID();
+		}
+		if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
+			const callback = (c: any) => {
+				const num = Number(c);
+				return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16);
+			};
+			return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback);
+		}
+	}
+	let timestamp = new Date().getTime();
+	let performanceNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
+	return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+		let random = Math.random() * 16;
+		if (timestamp > 0) {
+			random = (timestamp + random) % 16 | 0;
+			timestamp = Math.floor(timestamp / 16);
+		} else {
+			random = (performanceNow + random) % 16 | 0;
+			performanceNow = Math.floor(performanceNow / 16);
+		}
+		return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16);
+	});
 }
 
 /**
@@ -334,96 +333,96 @@ export function generateUUID() {
  * @method handleOpenLink 打开外部链接
  */
 const other = {
-    useTitle: () => {
-        useTitle();
-    },
-    setTagsViewNameI18n(route: RouteToFrom) {
-        return setTagsViewNameI18n(route);
-    },
-    lazyImg: (el: string, arr: EmptyArrayType) => {
-        lazyImg(el, arr);
-    },
-    globalComponentSize: () => {
-        return globalComponentSize();
-    },
-    deepClone: (obj: EmptyObjectType) => {
-        return deepClone(obj);
-    },
-    isMobile: () => {
-        return isMobile();
-    },
-    handleEmpty: (list: EmptyArrayType) => {
-        return handleEmpty(list);
-    },
-    handleOpenLink: (val: RouteItem) => {
-        handleOpenLink(val);
-    },
-    encryption: (src: string, keyWord: string) => {
-        return encryption(src, keyWord);
-    },
-    decryption: (src: string, keyWord: string) => {
-        return decryption(src, keyWord);
-    },
-    base64Encrypt: (data: any) => {
-        return base64Encrypt(data);
-    },
-    downBlobFile: (url: any, query: any, fileName: string) => {
-        return downBlobFile(url, query, fileName);
-    },
-    toUnderline: (str: string) => {
-        return toUnderline(str);
-    },
-    openWindow: (url: string, title: string, w: number, h: number) => {
-        return openWindow(url, title, w, h);
-    },
-    getQueryString: (url: string, paraName: string) => {
-        return getQueryString(url, paraName);
-    },
-    adaptationUrl: (url?: string) => {
-        return adaptationUrl(url);
-    },
-    resolveAllEunuchNodeId: (json: any[], idArr: any[], temp: any[] = []) => {
-        return resolveAllEunuchNodeId(json, idArr, temp);
-    },
-    getNonDuplicateID: () => {
-        return getNonDuplicateID();
-    },
-
-    addUnit: (value: string | number, unit = 'px') => {
-        return addUnit(value, unit);
-    },
-    validateNull: (value: any) => {
-        return validateNull(value);
-    },
-    getNumberRadixNum: (input: Number) => {
-        return getNumberRadixNum(input);
-    }
+	useTitle: () => {
+		useTitle();
+	},
+	setTagsViewNameI18n(route: RouteToFrom) {
+		return setTagsViewNameI18n(route);
+	},
+	lazyImg: (el: string, arr: EmptyArrayType) => {
+		lazyImg(el, arr);
+	},
+	globalComponentSize: () => {
+		return globalComponentSize();
+	},
+	deepClone: (obj: EmptyObjectType) => {
+		return deepClone(obj);
+	},
+	isMobile: () => {
+		return isMobile();
+	},
+	handleEmpty: (list: EmptyArrayType) => {
+		return handleEmpty(list);
+	},
+	handleOpenLink: (val: RouteItem) => {
+		handleOpenLink(val);
+	},
+	encryption: (src: string, keyWord: string) => {
+		return encryption(src, keyWord);
+	},
+	decryption: (src: string, keyWord: string) => {
+		return decryption(src, keyWord);
+	},
+	base64Encrypt: (data: any) => {
+		return base64Encrypt(data);
+	},
+	downBlobFile: (url: any, query: any, fileName: string) => {
+		return downBlobFile(url, query, fileName);
+	},
+	toUnderline: (str: string) => {
+		return toUnderline(str);
+	},
+	openWindow: (url: string, title: string, w: number, h: number) => {
+		return openWindow(url, title, w, h);
+	},
+	getQueryString: (url: string, paraName: string) => {
+		return getQueryString(url, paraName);
+	},
+	adaptationUrl: (url?: string) => {
+		return adaptationUrl(url);
+	},
+	resolveAllEunuchNodeId: (json: any[], idArr: any[], temp: any[] = []) => {
+		return resolveAllEunuchNodeId(json, idArr, temp);
+	},
+	getNonDuplicateID: () => {
+		return getNonDuplicateID();
+	},
+
+	addUnit: (value: string | number, unit = 'px') => {
+		return addUnit(value, unit);
+	},
+	validateNull: (value: any) => {
+		return validateNull(value);
+	},
+	getNumberRadixNum: (input: Number) => {
+		return getNumberRadixNum(input);
+	},
 };
 
 export function getNumberRadixNum(input: Number) {
-    let strings = input.toString().split(".");
-    if (strings.length <= 1) {
-        return 0;
-    }
-    return strings[1].toString().length;
-};
+	let strings = input.toString().split('.');
+	if (strings.length <= 1) {
+		return 0;
+	}
+	return strings[1].toString().length;
+}
 
 export function getQueryString(url: string, paraName: string) {
-    const arrObj = url.split('?');
-    if (arrObj.length > 1) {
-        const arrPara = arrObj[1].split('&');
-        let arr;
-        for (let i = 0; i < arrPara.length; i++) {
-            arr = arrPara[i].split('=');
-            // eslint-disable-next-line eqeqeq
-            if (arr != null && arr[0] == paraName) {
-                return arr[1];
-            }
-        }
-        return '';
-    } else {
-        return '';
-    }
+	const arrObj = url.split('?');
+	if (arrObj.length > 1) {
+		const arrPara = arrObj[1].split('&');
+		let arr;
+		for (let i = 0; i < arrPara.length; i++) {
+			arr = arrPara[i].split('=');
+			// eslint-disable-next-line eqeqeq
+			if (arr != null && arr[0] == paraName) {
+				return arr[1];
+			}
+		}
+		return '';
+	} else {
+		return '';
+	}
 }
 
 /**
@@ -436,31 +435,31 @@ export function getQueryString(url: string, paraName: string) {
  * @returns {*}
  */
 export function handleTree(data: any, id: any, parentId: any, children: any, rootId: any) {
-    id = id || 'id';
-    parentId = parentId || 'parentId';
-    children = children || 'children';
-    rootId =
-        rootId ||
-        Math.min.apply(
-            Math,
-            data.map((item: any) => {
-                return item[parentId];
-            })
-        ) ||
-        0;
-    //对源数据深度克隆
-    const cloneData = JSON.parse(JSON.stringify(data));
-    //循环所有项
-    const treeData = cloneData.filter((father: any) => {
-        const branchArr = cloneData.filter((child: any) => {
-            //返回每一项的子级数组
-            return father[id] === child[parentId];
-        });
-        branchArr.length > 0 ? (father[children] = branchArr) : '';
-        //返回第一层
-        return father[parentId] === rootId;
-    });
-    return treeData !== '' ? treeData : data;
+	id = id || 'id';
+	parentId = parentId || 'parentId';
+	children = children || 'children';
+	rootId =
+		rootId ||
+		Math.min.apply(
+			Math,
+			data.map((item: any) => {
+				return item[parentId];
+			})
+		) ||
+		0;
+	//对源数据深度克隆
+	const cloneData = JSON.parse(JSON.stringify(data));
+	//循环所有项
+	const treeData = cloneData.filter((father: any) => {
+		const branchArr = cloneData.filter((child: any) => {
+			//返回每一项的子级数组
+			return father[id] === child[parentId];
+		});
+		branchArr.length > 0 ? (father[children] = branchArr) : '';
+		//返回第一层
+		return father[parentId] === rootId;
+	});
+	return treeData !== '' ? treeData : data;
 }
 
 /**
@@ -468,14 +467,14 @@ export function handleTree(data: any, id: any, parentId: any, children: any, roo
  * @returns
  */
 const resolveAllEunuchNodeId = (json: any[], idArr: any[], temp: any[] = []) => {
-    for (const item of json) {
-        if (item.children && item.children.length !== 0) {
-            resolveAllEunuchNodeId(item.children, idArr, temp);
-        } else {
-            temp.push(...idArr.filter((id) => id === item.id));
-        }
-    }
-    return temp;
+	for (const item of json) {
+		if (item.children && item.children.length !== 0) {
+			resolveAllEunuchNodeId(item.children, idArr, temp);
+		} else {
+			temp.push(...idArr.filter((id) => id === item.id));
+		}
+	}
+	return temp;
 };
 
 /**
@@ -484,7 +483,7 @@ const resolveAllEunuchNodeId = (json: any[], idArr: any[], temp: any[] = []) =>
  * @returns 下划线
  */
 export function toUnderline(str: string) {
-    return str.replace(/([A-Z])/g, '_$1').toLowerCase();
+	return str.replace(/([A-Z])/g, '_$1').toLowerCase();
 }
 
 /**
@@ -495,14 +494,14 @@ export function toUnderline(str: string) {
  * @param originUrl 原始路径
  */
 const adaptationUrl = (originUrl?: string) => {
-    // 微服务架构 不做路径转换,为空不做路径转换
-    const isMicro = import.meta.env.VITE_IS_MICRO;
-    if (validateNull(isMicro) || isMicro === 'true') {
-        return originUrl;
-    }
-
-    // 转为 /admin 路由前缀的请求
-    return `/admin/${originUrl?.split('/').splice(2).join('/')}`;
+	// 微服务架构 不做路径转换,为空不做路径转换
+	const isMicro = import.meta.env.VITE_IS_MICRO;
+	if (validateNull(isMicro) || isMicro === 'true') {
+		return originUrl;
+	}
+
+	// 转为 /admin 路由前缀的请求
+	return `/admin/${originUrl?.split('/').splice(2).join('/')}`;
 };
 
 /**
@@ -511,9 +510,9 @@ const adaptationUrl = (originUrl?: string) => {
  * @return { String } id
  */
 const getNonDuplicateID = (length = 8) => {
-    let idStr = Date.now().toString(36);
-    idStr += Math.random().toString(36).substring(3, length);
-    return idStr;
+	let idStr = Date.now().toString(36);
+	idStr += Math.random().toString(36).substring(3, length);
+	return idStr;
 };
 
 /**
@@ -522,7 +521,7 @@ const getNonDuplicateID = (length = 8) => {
  * @param {String} unit 单位 px em rem
  */
 const addUnit = (value: string | number, unit = 'px') => {
-    return !Object.is(Number(value), NaN) ? `${value}${unit}` : value;
+	return !Object.is(Number(value), NaN) ? `${value}${unit}` : value;
 };
 
 // 统一批量导出

+ 37 - 27
src/views/admin/system/user/personal.vue

@@ -1,7 +1,7 @@
 <template>
 	<el-drawer v-model="visible" :title="$t('personal.name')" size="40%">
 		<el-tabs style="height: 200px" class="demo-tabs">
-			<el-tab-pane label="基本信息" v-loading="loading">
+			<el-tab-pane :label="$t('personal.basicInfo')" v-loading="loading">
 				<template #label>
 					<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
 						<path
@@ -10,44 +10,44 @@
 							d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
 						/>
 					</svg>
-					基本信息
+					{{ $t('personal.basicInfo') }}
 				</template>
 				<el-form :model="formData" :rules="ruleForm" label-width="100px" class="mt30" ref="formdataRef">
 					<el-row :gutter="20">
 						<el-col :span="24" class="mb20" v-if="formData?.appId">
-							<el-form-item label="商户号">
+							<el-form-item :label="$t('personal.merchantNo')">
 								<el-input v-model="formData.appId" readonly></el-input>
 							</el-form-item>
 						</el-col>
 						<el-col :span="24" class="mb20" v-if="formData?.nickname">
-							<el-form-item label="昵称" prop="nickname">
-								<el-input v-model="formData.nickname" placeholder="请输入昵称" clearable readonly></el-input>
+							<el-form-item :label="$t('personal.nickname')" prop="nickname">
+								<el-input v-model="formData.nickname" :placeholder="$t('personal.nicknamePlaceholder')" clearable readonly></el-input>
 							</el-form-item>
 						</el-col>
 						<el-col :span="24" class="mb20" v-if="formData?.name">
-							<el-form-item label="姓名" prop="name">
-								<el-input v-model="formData.name" placeholder="请输入姓名" clearable readonly></el-input>
+							<el-form-item :label="$t('personal.realName')" prop="name">
+								<el-input v-model="formData.name" :placeholder="$t('personal.realNamePlaceholder')" clearable readonly></el-input>
 							</el-form-item>
 						</el-col>
 						<el-col :span="24" class="mb20" v-if="formData?.phone">
-							<el-form-item label="手机" prop="phone">
-								<el-input v-model="formData.phone" placeholder="请输入手机" clearable readonly></el-input>
+							<el-form-item :label="$t('personal.phone')" prop="phone">
+								<el-input v-model="formData.phone" :placeholder="$t('personal.phonePlaceholder')" clearable readonly></el-input>
 							</el-form-item>
 						</el-col>
 						<el-col :span="24" class="mb20" v-if="formData?.email">
-							<el-form-item label="邮箱" prop="email">
-								<el-input v-model="formData.email" placeholder="请输入邮箱" clearable readonly></el-input>
+							<el-form-item :label="$t('personal.email')" prop="email">
+								<el-input v-model="formData.email" :placeholder="$t('personal.emailPlaceholder')" clearable readonly></el-input>
 							</el-form-item>
 						</el-col>
 						<!-- <el-col :span="24" class="mb20">
 							<el-form-item>
-								<el-button type="primary" @click="handleSaveUser"> 更新个人信息 </el-button>
+								<el-button type="primary" @click="handleSaveUser"> {{ $t('personal.updateInfo') }} </el-button>
 							</el-form-item>
 						</el-col> -->
 					</el-row>
 				</el-form>
 			</el-tab-pane>
-			<el-tab-pane label="安全信息">
+			<el-tab-pane :label="$t('personal.securityInfo')">
 				<template #label>
 					<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
 						<path
@@ -56,13 +56,18 @@
 							d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z"
 						/>
 					</svg>
-					安全信息
+					{{ $t('personal.securityInfo') }}
 				</template>
 				<el-form :model="passwordFormData" :rules="passwordRuleForm" label-width="100px" class="mt30" ref="passwordFormdataRef">
 					<el-row :gutter="20">
 						<el-col :span="24" class="mb20">
-							<el-form-item label="原密码" prop="password">
-								<el-input v-model="passwordFormData.password" :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" clearable>
+							<el-form-item :label="$t('personal.oldPassword')" prop="password">
+								<el-input
+									v-model="passwordFormData.password"
+									:type="showPassword ? 'text' : 'password'"
+									:placeholder="$t('personal.oldPasswordPlaceholder')"
+									clearable
+								>
 									<template #suffix>
 										<i
 											class="iconfont el-input__icon login-content-password"
@@ -75,25 +80,30 @@
 							</el-form-item>
 						</el-col>
 						<el-col :span="24" class="mb20">
-							<el-form-item label="新密码" prop="newpassword1">
+							<el-form-item :label="$t('personal.newPassword')" prop="newpassword1">
 								<strength-meter
 									v-model="passwordFormData.newpassword1"
 									:minlength="6"
 									:maxlength="16"
-									placeholder="请输入新密码"
+									:placeholder="$t('personal.newPasswordPlaceholder')"
 									@score="passwordScore"
 								></strength-meter>
 								<!--									<el-input v-model="passwordFormData.newpassword1" clearable type="password"></el-input>-->
 							</el-form-item>
 						</el-col>
 						<el-col :span="24" class="mb20">
-							<el-form-item label="确认密码" prop="newpassword2">
-								<strength-meter v-model="passwordFormData.newpassword2" :minlength="6" :maxlength="16" placeholder="请重复密码"></strength-meter>
+							<el-form-item :label="$t('personal.confirmPassword')" prop="newpassword2">
+								<strength-meter
+									v-model="passwordFormData.newpassword2"
+									:minlength="6"
+									:maxlength="16"
+									:placeholder="$t('personal.confirmPasswordPlaceholder')"
+								></strength-meter>
 							</el-form-item>
 						</el-col>
 						<el-col :span="24" class="mb20">
 							<el-form-item>
-								<el-button type="primary" @click="handleChangePassword"> 修改密码 </el-button>
+								<el-button type="primary" @click="handleChangePassword"> {{ $t('personal.changePassword') }} </el-button>
 							</el-form-item>
 						</el-col>
 					</el-row>
@@ -165,13 +175,13 @@ const validatorScore = (rule: any, value: any, callback: any) => {
 	}
 };
 
-const passwordRuleForm = reactive({
-	password: [{ required: true, message: '密码不能为空', trigger: 'blur' }],
+const passwordRuleForm = computed(() => ({
+	password: [{ required: true, message: t('forget.passwordEmpty'), trigger: 'blur' }],
 	newpassword1: [
 		{
 			min: 6,
 			max: 20,
-			message: '用户密码长度必须介于 6 和 20 之间',
+			message: t('forget.passwordLength'),
 			trigger: 'blur',
 		},
 		{ validator: validatorScore, trigger: 'blur' },
@@ -180,12 +190,12 @@ const passwordRuleForm = reactive({
 		{
 			min: 6,
 			max: 20,
-			message: '用户密码长度必须介于 6 和 20 之间',
+			message: t('forget.passwordLength'),
 			trigger: 'blur',
 		},
 		{ validator: validatorPassword2, trigger: 'blur' },
 	],
-});
+}));
 
 const score = ref(0);
 
@@ -200,7 +210,7 @@ const handleChangePassword = () => {
 		}
 		password(passwordFormData)
 			.then(() => {
-				useMessage().success('修改成功');
+				useMessage().success(t('common.editSuccessText'));
 				// 需要重新登录
 				// 清除缓存/token等
 				Session.clear();

+ 14 - 11
src/views/home/index.vue

@@ -9,9 +9,9 @@
 							<el-icon :size="28"><DocumentAdd /></el-icon>
 						</div>
 						<div class="stat-info">
-							<div class="stat-title">提交订单</div>
+							<div class="stat-title">{{ $t('home.submitOrder') }}</div>
 							<div class="stat-number">{{ statistics.submitCreateNum || 0 }}</div>
-							<div class="stat-desc">金额: {{ formatAmount(statistics.submitCreateAmount) }}</div>
+							<div class="stat-desc">{{ $t('home.amount') }}: {{ formatAmount(statistics.submitCreateAmount) }}</div>
 						</div>
 					</div>
 				</el-card>
@@ -25,9 +25,9 @@
 							<el-icon :size="28"><Wallet /></el-icon>
 						</div>
 						<div class="stat-info">
-							<div class="stat-title">支付订单</div>
+							<div class="stat-title">{{ $t('home.payOrder') }}</div>
 							<div class="stat-number">{{ statistics.payOrderNum || 0 }}</div>
-							<div class="stat-desc">金额: {{ formatAmount(statistics.payOrderAmount) }}</div>
+							<div class="stat-desc">{{ $t('home.amount') }}: {{ formatAmount(statistics.payOrderAmount) }}</div>
 						</div>
 					</div>
 				</el-card>
@@ -41,9 +41,9 @@
 							<el-icon :size="28"><Money /></el-icon>
 						</div>
 						<div class="stat-info">
-							<div class="stat-title">提现订单</div>
+							<div class="stat-title">{{ $t('home.withdrawOrder') }}</div>
 							<div class="stat-number">{{ statistics.withdrawOrderNum || 0 }}</div>
-							<div class="stat-desc">金额: {{ formatAmount(statistics.withdrawOrderAmount) }}</div>
+							<div class="stat-desc">{{ $t('home.amount') }}: {{ formatAmount(statistics.withdrawOrderAmount) }}</div>
 						</div>
 					</div>
 				</el-card>
@@ -57,9 +57,9 @@
 							<el-icon :size="28"><CircleCheck /></el-icon>
 						</div>
 						<div class="stat-info">
-							<div class="stat-title">提现成功</div>
+							<div class="stat-title">{{ $t('home.withdrawSuccess') }}</div>
 							<div class="stat-number">{{ statistics.withdrawOrderSuccessNum || 0 }}</div>
-							<div class="stat-desc">金额: {{ formatAmount(statistics.withdrawOrderSuccessAmount) }}</div>
+							<div class="stat-desc">{{ $t('home.amount') }}: {{ formatAmount(statistics.withdrawOrderSuccessAmount) }}</div>
 						</div>
 					</div>
 				</el-card>
@@ -75,7 +75,7 @@
 							<el-icon :size="40"><TrendCharts /></el-icon>
 						</div>
 						<div class="rate-info">
-							<div class="rate-title">支付成功率</div>
+							<div class="rate-title">{{ $t('home.paySuccessRate') }}</div>
 							<div class="rate-number">{{ formatRate(statistics.paySuccessRate) }}</div>
 						</div>
 						<div class="rate-progress">
@@ -93,6 +93,9 @@ import { ref, onMounted } from 'vue';
 import { DocumentAdd, Wallet, Money, CircleCheck, TrendCharts } from '@element-plus/icons-vue';
 import { getIndexStatistics } from '/@/api/statistics/index';
 import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 统计数据
 const statistics = ref({
@@ -129,10 +132,10 @@ const loadStatistics = async () => {
 		if (res.code === 0) {
 			statistics.value = res.data;
 		} else {
-			ElMessage.error(res.msg || '加载统计数据失败');
+			ElMessage.error(res.msg || t('home.loadStatsFailed'));
 		}
 	} catch (error) {
-		ElMessage.error('加载统计数据失败');
+		ElMessage.error(t('home.loadStatsFailed'));
 	} finally {
 		loading.value = false;
 	}

+ 5 - 5
src/views/login/component/control.vue

@@ -37,13 +37,14 @@
 import { useThemeConfig } from '/@/stores/themeConfig';
 import { useI18n } from 'vue-i18n';
 import { Local } from '/@/utils/storage';
+import { setLanguage } from '/@/locales/index';
 import other from '/@/utils/other';
 import { useDark } from '@vueuse/core';
 
 // 定义变量内容
 const storesThemeConfig = useThemeConfig();
 const { themeConfig } = storeToRefs(storesThemeConfig);
-const { locale } = useI18n();
+useI18n();
 
 // 语言切换状态
 const state = reactive({
@@ -59,7 +60,7 @@ const onLanguageChange = (lang: string) => {
 	Local.remove('themeConfig');
 	themeConfig.value.globalI18n = lang;
 	Local.set('themeConfig', themeConfig.value);
-	locale.value = lang;
+	setLanguage(lang); // 使用正确的语言切换函数
 	state.disabledI18n = lang;
 	other.useTitle();
 };
@@ -104,12 +105,11 @@ const initLanguage = () => {
 	if (Local.get('themeConfig')) {
 		state.disabledI18n = Local.get('themeConfig').globalI18n;
 		// 确保 i18n locale 与存储的语言保持一致
-
-		locale.value = state.disabledI18n;
+		setLanguage(state.disabledI18n);
 	} else {
 		// 如果没有存储的主题配置,设置默认语言
 		state.disabledI18n = 'zh-cn';
-		locale.value = 'zh-cn';
+		setLanguage('zh-cn');
 		// 保存默认配置
 		themeConfig.value.globalI18n = 'zh-cn';
 		Local.set('themeConfig', themeConfig.value);

+ 1 - 1
src/views/login/component/password.vue

@@ -43,7 +43,7 @@
 			<el-input
 				text
 				maxlength="6"
-				placeholder="谷歌验证码(若没有可以不填)"
+				:placeholder="$t('password.googleCaptchaPlaceholder')"
 				v-model="state.ruleForm.googleCaptcha"
 				clearable
 				autocomplete="off"

+ 47 - 39
src/views/order/payOrder/index.vue

@@ -3,30 +3,35 @@
 		<div class="layout-padding-auto layout-padding-view">
 			<el-row class="ml10" v-show="showSearch">
 				<el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
-					<el-form-item label="商户订单号" prop="mchOrderNo">
-						<el-input placeholder="请输入商户订单号" style="max-width: 180px" v-model="state.queryForm.mchOrderNo" clearable />
+					<el-form-item :label="$t('order.mchOrderNo')" prop="mchOrderNo">
+						<el-input :placeholder="$t('order.mchOrderNoPlaceholder')" style="max-width: 180px" v-model="state.queryForm.mchOrderNo" clearable />
 					</el-form-item>
-					<el-form-item label="平台订单号" prop="transactionId">
-						<el-input placeholder="请输入平台订单号" style="max-width: 180px" v-model="state.queryForm.transactionId" clearable />
+					<el-form-item :label="$t('order.transactionId')" prop="transactionId">
+						<el-input
+							:placeholder="$t('order.transactionIdPlaceholder')"
+							style="max-width: 180px"
+							v-model="state.queryForm.transactionId"
+							clearable
+						/>
 					</el-form-item>
-					<el-form-item label="商户名称" prop="merchantName">
-						<el-input placeholder="请输入商户名称" style="max-width: 180px" v-model="state.queryForm.merchantName" clearable />
+					<el-form-item :label="$t('order.merchantName')" prop="merchantName">
+						<el-input :placeholder="$t('order.merchantNamePlaceholder')" style="max-width: 180px" v-model="state.queryForm.merchantName" clearable />
 					</el-form-item>
-					<el-form-item label="代理名称" prop="agentName">
-						<el-input placeholder="请输入代理名称" style="max-width: 180px" v-model="state.queryForm.agentName" clearable />
+					<el-form-item :label="$t('order.agentName')" prop="agentName">
+						<el-input :placeholder="$t('order.agentNamePlaceholder')" style="max-width: 180px" v-model="state.queryForm.agentName" clearable />
 					</el-form-item>
-					<el-form-item label="订单状态" prop="orderStatus">
-						<el-select v-model="state.queryForm.orderStatus" placeholder="请选择订单状态" style="max-width: 180px" clearable>
-							<el-option label="创建订单" value="CREATE_ORDER" />
-							<el-option label="支付成功" value="PAY_SUCCESS" />
-							<el-option label="支付失败" value="PAY_FAIL" />
-							<el-option label="取消订单" value="CANCEL_ORDER" />
-							<el-option label="支付超时" value="PAY_TIMEOUT" />
+					<el-form-item :label="$t('order.orderStatus')" prop="orderStatus">
+						<el-select v-model="state.queryForm.orderStatus" :placeholder="$t('order.orderStatusPlaceholder')" style="max-width: 180px" clearable>
+							<el-option :label="$t('order.statusCreateOrder')" value="CREATE_ORDER" />
+							<el-option :label="$t('order.statusPaySuccess')" value="PAY_SUCCESS" />
+							<el-option :label="$t('order.statusPayFail')" value="PAY_FAIL" />
+							<el-option :label="$t('order.statusCancelOrder')" value="CANCEL_ORDER" />
+							<el-option :label="$t('order.statusPayTimeout')" value="PAY_TIMEOUT" />
 						</el-select>
 					</el-form-item>
 					<el-form-item>
-						<el-button @click="getDataList" icon="search" type="primary">查询</el-button>
-						<el-button @click="resetQuery" icon="Refresh">重置</el-button>
+						<el-button @click="getDataList" icon="search" type="primary">{{ $t('common.queryBtn') }}</el-button>
+						<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
 					</el-form-item>
 				</el-form>
 			</el-row>
@@ -46,7 +51,7 @@
 				<el-col :span="6">
 					<el-card shadow="hover">
 						<div class="statistic-item">
-							<div class="statistic-label">提交订单数</div>
+							<div class="statistic-label">{{ $t('order.submitOrderCount') }}</div>
 							<div class="statistic-value">{{ statistics.numberOrders || 0 }}</div>
 						</div>
 					</el-card>
@@ -54,7 +59,7 @@
 				<el-col :span="6">
 					<el-card shadow="hover">
 						<div class="statistic-item">
-							<div class="statistic-label">订单总金额</div>
+							<div class="statistic-label">{{ $t('order.totalOrderAmount') }}</div>
 							<div class="statistic-value">¥{{ (statistics.totalOrderAmount || 0).toFixed(2) }}</div>
 						</div>
 					</el-card>
@@ -62,7 +67,7 @@
 				<el-col :span="6">
 					<el-card shadow="hover">
 						<div class="statistic-item">
-							<div class="statistic-label">已付订单数</div>
+							<div class="statistic-label">{{ $t('order.paidOrderCount') }}</div>
 							<div class="statistic-value success">{{ statistics.paidOrders || 0 }}</div>
 						</div>
 					</el-card>
@@ -70,7 +75,7 @@
 				<el-col :span="6">
 					<el-card shadow="hover">
 						<div class="statistic-item">
-							<div class="statistic-label">已付总金额</div>
+							<div class="statistic-label">{{ $t('order.paidTotalAmount') }}</div>
 							<div class="statistic-value success">¥{{ (statistics.totalAmountPaid || 0).toFixed(2) }}</div>
 						</div>
 					</el-card>
@@ -88,58 +93,58 @@
 				:header-cell-style="tableStyle.headerCellStyle"
 			>
 				<el-table-column align="center" type="selection" width="40" />
-				<el-table-column label="商户订单号" prop="mchOrderNo" show-overflow-tooltip>
+				<el-table-column :label="$t('order.mchOrderNo')" prop="mchOrderNo" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.mchOrderNo || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="平台订单号" prop="transactionId" show-overflow-tooltip>
+				<el-table-column :label="$t('order.transactionId')" prop="transactionId" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.transactionId || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="商户名称" prop="merchantName" show-overflow-tooltip>
+				<el-table-column :label="$t('order.merchantName')" prop="merchantName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.merchantName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="代理名称" prop="agentName" show-overflow-tooltip>
+				<el-table-column :label="$t('order.agentName')" prop="agentName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.agentName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="通道名称" prop="paymentChannelName" show-overflow-tooltip>
+				<el-table-column :label="$t('order.channelName')" prop="paymentChannelName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.paymentChannelName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="提交金额" prop="amount" show-overflow-tooltip>
+				<el-table-column :label="$t('order.submitAmount')" prop="amount" show-overflow-tooltip>
 					<template #default="scope">
 						<!-- 橙色 -->
 						<span style="color: #f56c6c; font-weight: bold">¥{{ scope.row.amount ? scope.row.amount.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="实际金额" prop="noticeAmount" show-overflow-tooltip>
+				<el-table-column :label="$t('order.actualAmount')" prop="noticeAmount" show-overflow-tooltip>
 					<template #default="scope">
 						<span style="color: #f56c6c; font-weight: bold">¥{{ scope.row.noticeAmount ? scope.row.noticeAmount.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="订单状态" prop="orderStatus" show-overflow-tooltip>
+				<el-table-column :label="$t('order.orderStatus')" prop="orderStatus" show-overflow-tooltip>
 					<template #default="scope">
 						<el-tag :type="orderStatusMap[scope.row.orderStatus]?.type || 'info'" size="small">
 							{{ orderStatusMap[scope.row.orderStatus]?.label || scope.row.orderStatus }}
 						</el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column label="创建时间" prop="createTime" show-overflow-tooltip width="180">
+				<el-table-column :label="$t('order.createTime')" prop="createTime" show-overflow-tooltip width="180">
 					<template #default="scope">
 						{{ scope.row.createTime || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="操作">
+				<el-table-column :label="$t('common.action')">
 					<template #default="scope">
 						<!-- <el-button icon="View" @click="handleView(scope.row)" text type="primary" size="small">查看</el-button> -->
-						<el-button icon="Document" @click="handleNotifyLog(scope.row)" text type="warning" size="small">回调日志</el-button>
+						<el-button icon="Document" @click="handleNotifyLog(scope.row)" text type="warning" size="small">{{ $t('order.callbackLog') }}</el-button>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -155,18 +160,21 @@
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { fetchPayOrderList, fetchPayOrderStatistics } from '/@/api/order';
 import { useMessage } from '/@/hooks/message';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 引入组件
 const NotifyLogDialog = defineAsyncComponent(() => import('./notifyLog.vue'));
 
 // 订单状态映射
-const orderStatusMap: Record<string, { label: string; type: any }> = {
-	CREATE_ORDER: { label: '创建订单', type: 'info' },
-	PAY_SUCCESS: { label: '支付成功', type: 'success' },
-	PAY_FAIL: { label: '支付失败', type: 'danger' },
-	CANCEL_ORDER: { label: '取消订单', type: 'warning' },
-	PAY_TIMEOUT: { label: '支付超时', type: 'info' },
-};
+const orderStatusMap = computed(() => ({
+	CREATE_ORDER: { label: t('order.statusCreateOrder'), type: 'info' },
+	PAY_SUCCESS: { label: t('order.statusPaySuccess'), type: 'success' },
+	PAY_FAIL: { label: t('order.statusPayFail'), type: 'danger' },
+	CANCEL_ORDER: { label: t('order.statusCancelOrder'), type: 'warning' },
+	PAY_TIMEOUT: { label: t('order.statusPayTimeout'), type: 'info' },
+}));
 
 // 定义变量内容
 const notifyLogDialogRef = ref();

+ 39 - 31
src/views/order/withdrawOrder/index.vue

@@ -3,28 +3,33 @@
 		<div class="layout-padding-auto layout-padding-view">
 			<el-row class="ml10" v-show="showSearch">
 				<el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
-					<el-form-item label="商户订单号" prop="mchOrderNo">
-						<el-input placeholder="请输入商户订单号" style="max-width: 180px" v-model="state.queryForm.mchOrderNo" clearable />
+					<el-form-item :label="$t('order.mchOrderNo')" prop="mchOrderNo">
+						<el-input :placeholder="$t('order.mchOrderNoPlaceholder')" style="max-width: 180px" v-model="state.queryForm.mchOrderNo" clearable />
 					</el-form-item>
-					<el-form-item label="平台订单号" prop="transactionId">
-						<el-input placeholder="请输入平台订单号" style="max-width: 180px" v-model="state.queryForm.transactionId" clearable />
+					<el-form-item :label="$t('order.transactionId')" prop="transactionId">
+						<el-input
+							:placeholder="$t('order.transactionIdPlaceholder')"
+							style="max-width: 180px"
+							v-model="state.queryForm.transactionId"
+							clearable
+						/>
 					</el-form-item>
-					<el-form-item label="商户名称" prop="merchantName">
-						<el-input placeholder="请输入商户名称" style="max-width: 180px" v-model="state.queryForm.merchantName" clearable />
+					<el-form-item :label="$t('order.merchantName')" prop="merchantName">
+						<el-input :placeholder="$t('order.merchantNamePlaceholder')" style="max-width: 180px" v-model="state.queryForm.merchantName" clearable />
 					</el-form-item>
-					<el-form-item label="代理名称" prop="agentName">
-						<el-input placeholder="请输入代理名称" style="max-width: 180px" v-model="state.queryForm.agentName" clearable />
+					<el-form-item :label="$t('order.agentName')" prop="agentName">
+						<el-input :placeholder="$t('order.agentNamePlaceholder')" style="max-width: 180px" v-model="state.queryForm.agentName" clearable />
 					</el-form-item>
-					<el-form-item label="订单状态" prop="orderStatus">
-						<el-select v-model="state.queryForm.orderStatus" placeholder="请选择订单状态" style="max-width: 180px" clearable>
-							<el-option label="创建订单" value="CREATE_ORDER" />
-							<el-option label="提现成功" value="WITHDRAW_SUCCESS" />
-							<el-option label="提现失败" value="WITHDRAW_FAIL" />
+					<el-form-item :label="$t('order.orderStatus')" prop="orderStatus">
+						<el-select v-model="state.queryForm.orderStatus" :placeholder="$t('order.orderStatusPlaceholder')" style="max-width: 180px" clearable>
+							<el-option :label="$t('order.statusCreateOrder')" value="CREATE_ORDER" />
+							<el-option :label="$t('order.statusWithdrawSuccess')" value="WITHDRAW_SUCCESS" />
+							<el-option :label="$t('order.statusWithdrawFail')" value="WITHDRAW_FAIL" />
 						</el-select>
 					</el-form-item>
 					<el-form-item>
-						<el-button @click="getDataList" icon="search" type="primary">查询</el-button>
-						<el-button @click="resetQuery" icon="Refresh">重置</el-button>
+						<el-button @click="getDataList" icon="search" type="primary">{{ $t('common.queryBtn') }}</el-button>
+						<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
 					</el-form-item>
 				</el-form>
 			</el-row>
@@ -50,58 +55,58 @@
 				:header-cell-style="tableStyle.headerCellStyle"
 			>
 				<el-table-column align="center" type="selection" width="40" />
-				<el-table-column label="商户订单号" prop="mchOrderNo" show-overflow-tooltip>
+				<el-table-column :label="$t('order.mchOrderNo')" prop="mchOrderNo" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.mchOrderNo || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="平台订单号" prop="transactionId" show-overflow-tooltip>
+				<el-table-column :label="$t('order.transactionId')" prop="transactionId" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.transactionId || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="商户名称" prop="merchantName" show-overflow-tooltip>
+				<el-table-column :label="$t('order.merchantName')" prop="merchantName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.merchantName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="代理名称" prop="agentName" show-overflow-tooltip>
+				<el-table-column :label="$t('order.agentName')" prop="agentName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.agentName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="通道名称" prop="paymentChannelName" show-overflow-tooltip>
+				<el-table-column :label="$t('order.channelName')" prop="paymentChannelName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.paymentChannelName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="提交金额" prop="amount" show-overflow-tooltip>
+				<el-table-column :label="$t('order.submitAmount')" prop="amount" show-overflow-tooltip>
 					<template #default="scope">
 						<!-- 橙色 -->
 						<span style="color: #f56c6c; font-weight: bold">¥{{ scope.row.amount ? scope.row.amount.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="实际金额" prop="noticeAmount" show-overflow-tooltip>
+				<el-table-column :label="$t('order.actualAmount')" prop="noticeAmount" show-overflow-tooltip>
 					<template #default="scope">
 						<span style="color: #f56c6c; font-weight: bold">¥{{ scope.row.noticeAmount ? scope.row.noticeAmount.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="订单状态" prop="orderStatus" show-overflow-tooltip>
+				<el-table-column :label="$t('order.orderStatus')" prop="orderStatus" show-overflow-tooltip>
 					<template #default="scope">
 						<el-tag :type="orderStatusMap[scope.row.orderStatus]?.type || 'info'" size="small">
 							{{ orderStatusMap[scope.row.orderStatus]?.label || scope.row.orderStatus }}
 						</el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column label="创建时间" prop="createTime" show-overflow-tooltip width="180">
+				<el-table-column :label="$t('order.createTime')" prop="createTime" show-overflow-tooltip width="180">
 					<template #default="scope">
 						{{ scope.row.createTime || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="操作">
+				<el-table-column :label="$t('common.action')">
 					<template #default="scope">
 						<!-- <el-button icon="View" @click="handleView(scope.row)" text type="primary" size="small">查看</el-button> -->
-						<el-button icon="Document" @click="handleNotifyLog(scope.row)" text type="warning" size="small">回调日志</el-button>
+						<el-button icon="Document" @click="handleNotifyLog(scope.row)" text type="warning" size="small">{{ $t('order.callbackLog') }}</el-button>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -116,16 +121,19 @@
 <script lang="ts" name="orderWithdrawOrder" setup>
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { fetchWithdrawOrderList } from '/@/api/order';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 引入组件
 const NotifyLogDialog = defineAsyncComponent(() => import('../payOrder/notifyLog.vue'));
 
 // 订单状态映射
-const orderStatusMap: Record<string, { label: string; type: any }> = {
-	CREATE_ORDER: { label: '创建订单', type: 'info' },
-	WITHDRAW_SUCCESS: { label: '提现成功', type: 'success' },
-	WITHDRAW_FAIL: { label: '提现失败', type: 'danger' },
-};
+const orderStatusMap = computed(() => ({
+	CREATE_ORDER: { label: t('order.statusCreateOrder'), type: 'info' },
+	WITHDRAW_SUCCESS: { label: t('order.statusWithdrawSuccess'), type: 'success' },
+	WITHDRAW_FAIL: { label: t('order.statusWithdrawFail'), type: 'danger' },
+}));
 
 // 定义变量内容
 const notifyLogDialogRef = ref();

+ 33 - 30
src/views/payment/channel/index.vue

@@ -3,27 +3,27 @@
 		<div class="layout-padding-auto layout-padding-view">
 			<el-row class="ml10" v-show="showSearch">
 				<el-form :inline="true" :model="queryForm" @keyup.enter="handleSearch" ref="queryRef">
-					<el-form-item label="通道名称" prop="channelName">
-						<el-input placeholder="请输入通道名称" style="max-width: 180px" v-model="queryForm.channelName" clearable />
+					<el-form-item :label="$t('payment.channelName')" prop="channelName">
+						<el-input :placeholder="$t('payment.channelNamePlaceholder')" style="max-width: 180px" v-model="queryForm.channelName" clearable />
 					</el-form-item>
-					<el-form-item label="支付方式" prop="paymentName">
-						<el-input placeholder="请输入支付方式" style="max-width: 180px" v-model="queryForm.paymentName" clearable />
+					<el-form-item :label="$t('payment.paymentMethod')" prop="paymentName">
+						<el-input :placeholder="$t('payment.paymentMethodPlaceholder')" style="max-width: 180px" v-model="queryForm.paymentName" clearable />
 					</el-form-item>
-					<el-form-item label="支付类别" prop="paymentType">
-						<el-select v-model="queryForm.paymentType" placeholder="请选择支付类别" style="max-width: 180px" clearable>
-							<el-option label="代付" value="PAY" />
-							<el-option label="代收" value="HARVEST" />
+					<el-form-item :label="$t('payment.paymentType')" prop="paymentType">
+						<el-select v-model="queryForm.paymentType" :placeholder="$t('payment.paymentTypePlaceholder')" style="max-width: 180px" clearable>
+							<el-option :label="$t('payment.typePay')" value="PAY" />
+							<el-option :label="$t('payment.typeHarvest')" value="HARVEST" />
 						</el-select>
 					</el-form-item>
 					<el-form-item>
-						<el-button @click="handleSearch" icon="search" type="primary">查询</el-button>
-						<el-button @click="resetQuery" icon="Refresh">重置</el-button>
+						<el-button @click="handleSearch" icon="search" type="primary">{{ $t('common.queryBtn') }}</el-button>
+						<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
 					</el-form-item>
 				</el-form>
 			</el-row>
 			<el-row>
 				<div class="mb8" style="width: 100%">
-					<el-button @click="loadData" class="ml10" icon="Refresh" type="primary">刷新</el-button>
+					<el-button @click="loadData" class="ml10" icon="Refresh" type="primary">{{ $t('payment.refresh') }}</el-button>
 					<right-toolbar
 						@queryTable="handleSearch"
 						class="ml10"
@@ -41,49 +41,49 @@
 				:cell-style="tableStyle.cellStyle"
 				:header-cell-style="tableStyle.headerCellStyle"
 			>
-				<el-table-column label="通道名称" prop="channelName" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.channelName')" prop="channelName" show-overflow-tooltip align="center">
 					<template #default="scope">
 						{{ scope.row.channelName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="支付方式" prop="paymentName" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.paymentMethod')" prop="paymentName" show-overflow-tooltip align="center">
 					<template #default="scope">
 						{{ scope.row.paymentName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="支付类别" prop="paymentType" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.paymentType')" prop="paymentType" show-overflow-tooltip align="center">
 					<template #default="scope">
 						<el-tag :type="scope.row.paymentType === 'PAY' ? 'warning' : 'info'">
 							{{ paymentTypeMap[scope.row.paymentType] || scope.row.paymentType }}
 						</el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column label="商户手续费类型" prop="mfeeType" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.merchantFeeType')" prop="mfeeType" show-overflow-tooltip align="center">
 					<template #default="scope">
 						{{ feeTypeMap[scope.row.mfeeType] || scope.row.mfeeType }}
 					</template>
 				</el-table-column>
-				<el-table-column label="商户手续费比例" prop="mfeeRate" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.merchantFeeRate')" prop="mfeeRate" show-overflow-tooltip align="center">
 					<template #default="scope">
 						{{ scope.row.mfeeRate ? `${scope.row.mfeeRate}%` : '0%' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="商户手续费固定金额" prop="mfeeEvery" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.merchantFeeFixed')" prop="mfeeEvery" show-overflow-tooltip align="center">
 					<template #default="scope">
 						<span class="amount-value">¥{{ scope.row.mfeeEvery ? scope.row.mfeeEvery.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="代理商手续费类型" prop="afeeType" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.agentFeeType')" prop="afeeType" show-overflow-tooltip align="center">
 					<template #default="scope">
 						{{ feeTypeMap[scope.row.afeeType] || scope.row.afeeType }}
 					</template>
 				</el-table-column>
-				<el-table-column label="代理商手续费比例" prop="afeeRate" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.agentFeeRate')" prop="afeeRate" show-overflow-tooltip align="center">
 					<template #default="scope">
 						{{ scope.row.afeeRate ? `${scope.row.afeeRate}%` : '0%' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="代理商手续费固定金额" prop="afeeEvery" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('payment.agentFeeFixed')" prop="afeeEvery" show-overflow-tooltip align="center">
 					<template #default="scope">
 						<span class="amount-value">¥{{ scope.row.afeeEvery ? scope.row.afeeEvery.toFixed(2) : '0.00' }}</span>
 					</template>
@@ -96,19 +96,22 @@
 <script lang="ts" name="paymentChannel" setup>
 import { getMerchantRate } from '/@/api/payment/channel';
 import { useMessage } from '/@/hooks/message';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 支付类别映射
-const paymentTypeMap: Record<string, string> = {
-	PAY: '代付',
-	HARVEST: '代收',
-};
+const paymentTypeMap = computed(() => ({
+	PAY: t('payment.typePay'),
+	HARVEST: t('payment.typeHarvest'),
+}));
 
 // 手续费类型映射
-const feeTypeMap: Record<string, string> = {
-	PERCENTAGE: '百分比',
-	FIXED: '固定金额',
-	MIXED: '混合',
-};
+const feeTypeMap = computed(() => ({
+	PERCENTAGE: t('payment.feeTypePercentage'),
+	FIXED: t('payment.feeTypeFixed'),
+	MIXED: t('payment.feeTypeMixed'),
+}));
 
 // 定义变量
 const queryRef = ref();
@@ -171,7 +174,7 @@ const loadData = async () => {
 		const res = await getMerchantRate();
 		tableData.value = res.data || [];
 	} catch (err: any) {
-		useMessage().error(err.msg || '获取费率信息失败');
+		useMessage().error(err.msg || t('payment.getRateFailed'));
 	} finally {
 		loading.value = false;
 	}

+ 101 - 80
src/views/settings/security/index.vue

@@ -3,9 +3,9 @@
 		<div class="security-header">
 			<h2 class="page-title">
 				<el-icon :size="24"><Lock /></el-icon>
-				安全中心
+				{{ $t('security.title') }}
 			</h2>
-			<p class="page-desc">管理您的账号安全设置,保护账户信息</p>
+			<p class="page-desc">{{ $t('security.desc') }}</p>
 		</div>
 
 		<div class="security-grid">
@@ -15,17 +15,17 @@
 					<el-icon :size="40"><Lock /></el-icon>
 				</div>
 				<div class="card-content">
-					<h3 class="card-title">支付密码</h3>
-					<p class="card-desc">{{ isPaymentPwdSet ? '已设置支付密码' : '未设置支付密码,建议设置以提高账号安全性' }}</p>
+					<h3 class="card-title">{{ $t('security.paymentPassword') }}</h3>
+					<p class="card-desc">{{ isPaymentPwdSet ? $t('security.paymentPwdSet') : $t('security.paymentPwdNotSet') }}</p>
 					<div class="card-status">
 						<el-tag :type="isPaymentPwdSet ? 'success' : 'info'" size="small">
-							{{ isPaymentPwdSet ? '已开启' : '未开启' }}
+							{{ isPaymentPwdSet ? $t('security.enabled') : $t('security.disabled') }}
 						</el-tag>
 					</div>
 				</div>
 				<div class="card-action">
-					<el-button v-if="!isPaymentPwdSet" type="primary" @click="handleSetPaymentPwd">立即设置</el-button>
-					<el-button v-else type="primary" plain @click="handleSetPaymentPwd">修改密码</el-button>
+					<el-button v-if="!isPaymentPwdSet" type="primary" @click="handleSetPaymentPwd">{{ $t('security.setNow') }}</el-button>
+					<el-button v-else type="primary" plain @click="handleSetPaymentPwd">{{ $t('security.modifyPassword') }}</el-button>
 				</div>
 			</el-card>
 
@@ -35,11 +35,11 @@
 					<el-icon :size="40"><List /></el-icon>
 				</div>
 				<div class="card-content">
-					<h3 class="card-title">白名单设置</h3>
-					<p class="card-desc">设置允许访问的IP地址白名单</p>
+					<h3 class="card-title">{{ $t('security.whitelist') }}</h3>
+					<p class="card-desc">{{ $t('security.whitelistDesc') }}</p>
 				</div>
 				<div class="card-action">
-					<el-button type="primary" @click="handleSetWhitelist">设置白名单</el-button>
+					<el-button type="primary" @click="handleSetWhitelist">{{ $t('security.setWhitelist') }}</el-button>
 				</div>
 			</el-card>
 
@@ -66,64 +66,64 @@
 					</svg>
 				</div>
 				<div class="card-content">
-					<h3 class="card-title">Google验证器</h3>
-					<p class="card-desc">{{ isGoogleBound ? '已绑定Google验证器' : '未绑定Google验证器,建议开启以提高账号安全性' }}</p>
+					<h3 class="card-title">{{ $t('security.googleAuth') }}</h3>
+					<p class="card-desc">{{ isGoogleBound ? $t('security.googleBound') : $t('security.googleNotBound') }}</p>
 					<div class="card-status">
 						<el-tag :type="isGoogleBound ? 'success' : 'info'" size="small">
-							{{ isGoogleBound ? '已开启' : '未开启' }}
+							{{ isGoogleBound ? $t('security.enabled') : $t('security.disabled') }}
 						</el-tag>
 					</div>
 				</div>
 				<div class="card-action">
-					<el-button v-if="!isGoogleBound" type="primary" @click="handleBindGoogle">立即绑定</el-button>
-					<el-button v-else type="danger" plain @click="handleUnbindGoogle">解除绑定</el-button>
+					<el-button v-if="!isGoogleBound" type="primary" @click="handleBindGoogle">{{ $t('security.bindNow') }}</el-button>
+					<el-button v-else type="danger" plain @click="handleUnbindGoogle">{{ $t('security.unbind') }}</el-button>
 				</div>
 			</el-card>
 		</div>
 
 		<!-- Google验证器绑定对话框 -->
-		<el-dialog v-model="googleDialogVisible" title="绑定Google验证器" width="500px" :close-on-click-modal="false">
+		<el-dialog v-model="googleDialogVisible" :title="$t('security.bindGoogleTitle')" width="500px" :close-on-click-modal="false">
 			<div class="google-bind-content">
 				<el-steps :active="googleBindStep" finish-status="success" align-center>
-					<el-step title="扫描二维码" />
-					<el-step title="输入验证码" />
-					<el-step title="完成" />
+					<el-step :title="$t('security.scanQrCode')" />
+					<el-step :title="$t('security.inputCode')" />
+					<el-step :title="$t('security.complete')" />
 				</el-steps>
 
 				<div v-if="googleBindStep === 0" class="step-content">
 					<div class="qrcode-container">
 						<div v-show="googleAuth.loading" class="loading-box">
 							<el-icon class="is-loading" :size="40"><Loading /></el-icon>
-							<p>正在生成二维码...</p>
+							<p>{{ $t('security.generatingQrCode') }}</p>
 						</div>
 						<div v-show="!googleAuth.loading && googleAuth.qrCode" class="qrcode-box">
 							<canvas ref="qrcodeCanvas"></canvas>
-							<p class="qrcode-tip">请使用Google Authenticator扫描二维码</p>
+							<p class="qrcode-tip">{{ $t('security.scanTip') }}</p>
 						</div>
 					</div>
 					<div class="step-actions">
-						<el-button @click="googleDialogVisible = false">取消</el-button>
-						<el-button type="primary" @click="googleBindStep = 1" :disabled="!googleAuth.qrCode">下一步</el-button>
+						<el-button @click="googleDialogVisible = false">{{ $t('common.cancel') }}</el-button>
+						<el-button type="primary" @click="googleBindStep = 1" :disabled="!googleAuth.qrCode">{{ $t('common.confirm') }}</el-button>
 					</div>
 				</div>
 
 				<div v-if="googleBindStep === 1" class="step-content">
 					<el-form ref="googleFormRef" :model="googleForm" :rules="googleRules" label-width="100px">
-						<el-form-item label="验证码" prop="code">
-							<el-input v-model="googleForm.code" placeholder="请输入6位验证码" maxlength="6" show-word-limit clearable />
+						<el-form-item :label="$t('security.verifyCode')" prop="code">
+							<el-input v-model="googleForm.code" :placeholder="$t('security.verifyCodePlaceholder')" maxlength="6" show-word-limit clearable />
 						</el-form-item>
-						<el-alert title="请打开Google Authenticator应用,输入显示的6位数字验证码" type="info" :closable="false" />
+						<el-alert :title="$t('security.verifyCodeTip')" type="info" :closable="false" />
 					</el-form>
 					<div class="step-actions">
-						<el-button @click="googleBindStep = 0">上一步</el-button>
-						<el-button type="primary" @click="submitGoogleBind" :loading="googleAuth.binding">确认绑定</el-button>
+						<el-button @click="googleBindStep = 0">{{ $t('common.cancel') }}</el-button>
+						<el-button type="primary" @click="submitGoogleBind" :loading="googleAuth.binding">{{ $t('security.confirmBind') }}</el-button>
 					</div>
 				</div>
 
 				<div v-if="googleBindStep === 2" class="step-content">
-					<el-result icon="success" title="绑定成功" sub-title="您已成功绑定Google验证器">
+					<el-result icon="success" :title="$t('security.bindSuccess')" :sub-title="$t('security.bindSuccessDesc')">
 						<template #extra>
-							<el-button type="primary" @click="closeGoogleDialog">完成</el-button>
+							<el-button type="primary" @click="closeGoogleDialog">{{ $t('security.complete') }}</el-button>
 						</template>
 					</el-result>
 				</div>
@@ -131,58 +131,76 @@
 		</el-dialog>
 
 		<!-- Google验证器解绑对话框 -->
-		<el-dialog v-model="unbindDialogVisible" title="解除绑定Google验证器" width="450px" :close-on-click-modal="false">
+		<el-dialog v-model="unbindDialogVisible" :title="$t('security.unbindGoogleTitle')" width="450px" :close-on-click-modal="false">
 			<el-form ref="unbindFormRef" :model="unbindForm" :rules="unbindRules" label-width="80px">
-				<el-alert title="解除绑定后,您将无法使用Google验证器进行二次验证" type="warning" :closable="false" style="margin-bottom: 20px" />
-				<el-form-item label="登录密码" prop="pwd">
-					<el-input v-model="unbindForm.pwd" type="password" placeholder="请输入登录密码" show-password clearable />
+				<el-alert :title="$t('security.unbindWarning')" type="warning" :closable="false" style="margin-bottom: 20px" />
+				<el-form-item :label="$t('security.loginPassword')" prop="pwd">
+					<el-input v-model="unbindForm.pwd" type="password" :placeholder="$t('security.loginPasswordPlaceholder')" show-password clearable />
 				</el-form-item>
 			</el-form>
 			<template #footer>
-				<el-button @click="unbindDialogVisible = false">取消</el-button>
-				<el-button type="danger" @click="submitGoogleUnbind" :loading="googleAuth.unbinding">确认解绑</el-button>
+				<el-button @click="unbindDialogVisible = false">{{ $t('common.cancel') }}</el-button>
+				<el-button type="danger" @click="submitGoogleUnbind" :loading="googleAuth.unbinding">{{ $t('security.confirmUnbind') }}</el-button>
 			</template>
 		</el-dialog>
 
 		<!-- 支付密码设置对话框 -->
 		<el-dialog
 			v-model="paymentPwdDialogVisible"
-			:title="isPaymentPwdSet ? '修改支付密码' : '设置支付密码'"
+			:title="isPaymentPwdSet ? $t('security.modifyPaymentPwdTitle') : $t('security.setPaymentPwdTitle')"
 			width="450px"
 			:close-on-click-modal="false"
 		>
 			<el-form ref="paymentPwdFormRef" :model="paymentPwdForm" :rules="paymentPwdRules" label-width="100px">
-				<el-alert v-if="!isPaymentPwdSet" title="首次设置支付密码,无需输入旧密码" type="info" :closable="false" style="margin-bottom: 20px" />
-				<el-form-item v-if="isPaymentPwdSet" label="旧密码" prop="oldPassword">
-					<el-input v-model="paymentPwdForm.oldPassword" type="password" placeholder="请输入旧支付密码" show-password clearable />
+				<el-alert v-if="!isPaymentPwdSet" :title="$t('security.firstSetTip')" type="info" :closable="false" style="margin-bottom: 20px" />
+				<el-form-item v-if="isPaymentPwdSet" :label="$t('security.oldPaymentPwd')" prop="oldPassword">
+					<el-input
+						v-model="paymentPwdForm.oldPassword"
+						type="password"
+						:placeholder="$t('security.oldPaymentPwdPlaceholder')"
+						show-password
+						clearable
+					/>
 				</el-form-item>
-				<el-form-item label="新密码" prop="newPassword">
-					<el-input v-model="paymentPwdForm.newPassword" type="password" placeholder="请输入新支付密码" show-password clearable />
+				<el-form-item :label="$t('security.newPaymentPwd')" prop="newPassword">
+					<el-input
+						v-model="paymentPwdForm.newPassword"
+						type="password"
+						:placeholder="$t('security.newPaymentPwdPlaceholder')"
+						show-password
+						clearable
+					/>
 				</el-form-item>
-				<el-form-item label="确认密码" prop="confirmPassword">
-					<el-input v-model="paymentPwdForm.confirmPassword" type="password" placeholder="请再次输入新支付密码" show-password clearable />
+				<el-form-item :label="$t('security.confirmPaymentPwd')" prop="confirmPassword">
+					<el-input
+						v-model="paymentPwdForm.confirmPassword"
+						type="password"
+						:placeholder="$t('security.confirmPaymentPwdPlaceholder')"
+						show-password
+						clearable
+					/>
 				</el-form-item>
 			</el-form>
 			<template #footer>
-				<el-button @click="paymentPwdDialogVisible = false">取消</el-button>
-				<el-button type="primary" @click="submitPaymentPwd" :loading="paymentPwdSubmitting">确认</el-button>
+				<el-button @click="paymentPwdDialogVisible = false">{{ $t('common.cancel') }}</el-button>
+				<el-button type="primary" @click="submitPaymentPwd" :loading="paymentPwdSubmitting">{{ $t('common.confirm') }}</el-button>
 			</template>
 		</el-dialog>
 
 		<!-- 白名单设置对话框 -->
-		<el-dialog v-model="whitelistDialogVisible" title="白名单设置" width="550px" :close-on-click-modal="false">
+		<el-dialog v-model="whitelistDialogVisible" :title="$t('security.whitelistTitle')" width="550px" :close-on-click-modal="false">
 			<el-form ref="whitelistFormRef" :model="whitelistForm" :rules="whitelistRules" label-width="80px">
-				<el-alert title="请输入允许访问的IP地址,多个IP用英文逗号分隔" type="info" :closable="false" style="margin-bottom: 20px" />
-				<el-form-item label="IP地址" prop="whitelist">
-					<el-input v-model="whitelistForm.whitelist" type="textarea" :rows="6" placeholder="例如: 192.168.1.1,192.168.1.2" clearable />
+				<el-alert :title="$t('security.whitelistTip')" type="info" :closable="false" style="margin-bottom: 20px" />
+				<el-form-item :label="$t('security.ipAddress')" prop="whitelist">
+					<el-input v-model="whitelistForm.whitelist" type="textarea" :rows="6" :placeholder="$t('security.ipPlaceholder')" clearable />
 				</el-form-item>
 				<el-form-item>
-					<el-text type="info" size="small">提示: 留空表示不限制IP访问</el-text>
+					<el-text type="info" size="small">{{ $t('security.ipEmptyTip') }}</el-text>
 				</el-form-item>
 			</el-form>
 			<template #footer>
-				<el-button @click="whitelistDialogVisible = false">取消</el-button>
-				<el-button type="primary" @click="submitWhitelist" :loading="whitelistSubmitting">确认</el-button>
+				<el-button @click="whitelistDialogVisible = false">{{ $t('common.cancel') }}</el-button>
+				<el-button type="primary" @click="submitWhitelist" :loading="whitelistSubmitting">{{ $t('common.confirm') }}</el-button>
 			</template>
 		</el-dialog>
 	</div>
@@ -196,6 +214,9 @@ import { getGoogleCode, bindingGoogle, unbindingGoogle, getWhitelist, editWhitel
 import { useUserInfo } from '/@/stores/userInfo';
 import { Session } from '/@/utils/storage';
 import QRCode from 'qrcode';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 获取用户信息store
 const userStore = useUserInfo();
@@ -228,13 +249,13 @@ const googleForm = reactive({
 });
 
 // Google绑定表单验证
-const googleRules = {
+const googleRules = computed(() => ({
 	code: [
-		{ required: true, message: '请输入验证码', trigger: 'blur' },
-		{ len: 6, message: '验证码必须为6位数字', trigger: 'blur' },
-		{ pattern: /^\d{6}$/, message: '验证码必须为6位数字', trigger: 'blur' },
+		{ required: true, message: t('security.verifyCodeRequired'), trigger: 'blur' },
+		{ len: 6, message: t('security.verifyCodeLength'), trigger: 'blur' },
+		{ pattern: /^\d{6}$/, message: t('security.verifyCodeLength'), trigger: 'blur' },
 	],
-};
+}));
 
 // Google解绑对话框
 const unbindDialogVisible = ref(false);
@@ -244,9 +265,9 @@ const unbindForm = reactive({
 });
 
 // Google解绑表单验证
-const unbindRules = {
-	pwd: [{ required: true, message: '请输入登录密码', trigger: 'blur' }],
-};
+const unbindRules = computed(() => ({
+	pwd: [{ required: true, message: t('security.loginPwdRequired'), trigger: 'blur' }],
+}));
 
 // 支付密码设置对话框
 const paymentPwdDialogVisible = ref(false);
@@ -261,9 +282,9 @@ const paymentPwdForm = reactive({
 // 支付密码表单验证
 const validateConfirmPassword = (rule: any, value: any, callback: any) => {
 	if (value === '') {
-		callback(new Error('请再次输入新支付密码'));
+		callback(new Error(t('security.confirmPwdRequired')));
 	} else if (value !== paymentPwdForm.newPassword) {
-		callback(new Error('两次输入的密码不一致'));
+		callback(new Error(t('security.passwordNotMatch')));
 	} else {
 		callback();
 	}
@@ -272,13 +293,13 @@ const validateConfirmPassword = (rule: any, value: any, callback: any) => {
 const paymentPwdRules = computed(() => {
 	const rules: any = {
 		newPassword: [
-			{ required: true, message: '请输入新支付密码', trigger: 'blur' },
-			{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
+			{ required: true, message: t('security.newPwdRequired'), trigger: 'blur' },
+			{ min: 6, message: t('security.passwordMinLength'), trigger: 'blur' },
 		],
 		confirmPassword: [{ validator: validateConfirmPassword, trigger: 'blur' }],
 	};
 	if (isPaymentPwdSet.value) {
-		rules.oldPassword = [{ required: true, message: '请输入旧支付密码', trigger: 'blur' }];
+		rules.oldPassword = [{ required: true, message: t('security.oldPwdRequired'), trigger: 'blur' }];
 	}
 	return rules;
 });
@@ -292,16 +313,16 @@ const whitelistForm = reactive({
 });
 
 // 白名单表单验证
-const whitelistRules = {
+const whitelistRules = computed(() => ({
 	whitelist: [
 		{
 			pattern: /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(,\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})*$/,
-			message: '请输入正确的IP地址格式',
+			message: t('security.ipFormatError'),
 			trigger: 'blur',
 			required: false,
 		},
 	],
-};
+}));
 
 // 获取Google验证器二维码
 const getGoogleQrCode = async () => {
@@ -325,17 +346,17 @@ const getGoogleQrCode = async () => {
 						errorCorrectionLevel: 'M',
 					});
 				} catch (err) {
-					ElMessage.error('二维码生成失败');
+					ElMessage.error(t('security.qrCodeFailed'));
 				}
 			} else {
-				ElMessage.error('二维码生成失败');
+				ElMessage.error(t('security.qrCodeFailed'));
 			}
 			return; // 提前返回,不执行 finally
 		} else {
-			ElMessage.error(res.msg || '获取二维码失败');
+			ElMessage.error(res.msg || t('security.getQrCodeFailed'));
 		}
 	} catch (error) {
-		ElMessage.error('获取二维码失败');
+		ElMessage.error(t('security.getQrCodeFailed'));
 	} finally {
 		googleAuth.loading = false;
 	}
@@ -361,8 +382,8 @@ const submitGoogleBind = async () => {
 				if (res.code === 0) {
 					googleBindStep.value = 2;
 					// 使用确认框提示用户
-					await ElMessageBox.alert('绑定成功,点击确定后将自动注销并跳转到登录页', '绑定成功', {
-						confirmButtonText: '确定',
+					await ElMessageBox.alert(`${t('security.bindSuccess')}, ${t('security.logoutTip')}`, t('security.bindSuccess'), {
+						confirmButtonText: t('common.confirm'),
 						type: 'success',
 						showClose: false,
 						callback: () => {
@@ -404,8 +425,8 @@ const submitGoogleUnbind = async () => {
 				if (res.code === 0) {
 					unbindDialogVisible.value = false;
 					// 使用确认框提示用户
-					await ElMessageBox.alert('解绑成功,点击确定后将自动注销并跳转到登录页', '解绑成功', {
-						confirmButtonText: '确定',
+					await ElMessageBox.alert(`${t('security.unbindSuccess')}, ${t('security.logoutTip')}`, t('security.unbindSuccess'), {
+						confirmButtonText: t('common.confirm'),
 						type: 'success',
 						showClose: false,
 						callback: () => {
@@ -442,8 +463,8 @@ const submitPaymentPwd = async () => {
 				if (res.code === 0) {
 					paymentPwdDialogVisible.value = false;
 					// 使用确认框提示用户
-					await ElMessageBox.alert('设置成功,点击确定后将自动注销并跳转到登录页', '设置成功', {
-						confirmButtonText: '确定',
+					await ElMessageBox.alert(`${t('security.setSuccess')}, ${t('security.logoutTip')}`, t('security.setSuccess'), {
+						confirmButtonText: t('common.confirm'),
 						type: 'success',
 						showClose: false,
 						callback: () => {
@@ -484,7 +505,7 @@ const submitWhitelist = async () => {
 			try {
 				const res = await editWhitelist(whitelistForm.whitelist.trim());
 				if (res.code === 0) {
-					ElMessage.success('设置成功');
+					ElMessage.success(t('security.setSuccess'));
 					whitelistDialogVisible.value = false;
 					whitelistData.value = whitelistForm.whitelist.trim();
 				}

+ 35 - 24
src/views/settlement/apply/index.vue

@@ -6,40 +6,48 @@
 					<el-card shadow="hover" class="apply-card">
 						<template #header>
 							<div class="card-header">
-								<span class="card-title">申请结算</span>
+								<span class="card-title">{{ $t('settlement.applySettlement') }}</span>
 								<span class="available-balance"
-									>可提现金额:<span class="balance-amount">¥{{ formatAmount(availableBalance) }}</span></span
+									>{{ $t('settlement.availableBalance') }}:<span class="balance-amount">¥{{ formatAmount(availableBalance) }}</span></span
 								>
 							</div>
 						</template>
 
 						<el-form :model="formData" :rules="rules" ref="formRef" label-width="120px">
-							<el-form-item label="提现金额" prop="amount">
-								<el-input-number v-model="formData.amount" :min="0" :controls="false" placeholder="请输入提现金额" style="width: 100%" />
+							<el-form-item :label="$t('settlement.withdrawAmount')" prop="amount">
+								<el-input-number
+									v-model="formData.amount"
+									:min="0"
+									:controls="false"
+									:placeholder="$t('settlement.withdrawAmountPlaceholder')"
+									style="width: 100%"
+								/>
 							</el-form-item>
 
-							<el-form-item label="提现类型" prop="type">
+							<el-form-item :label="$t('settlement.withdrawType')" prop="type">
 								<el-radio-group v-model="formData.type">
-									<el-radio :label="1">银行卡</el-radio>
-									<el-radio :label="2">USDT</el-radio>
+									<el-radio :label="1">{{ $t('settlement.bankCard') }}</el-radio>
+									<el-radio :label="2">{{ $t('settlement.usdt') }}</el-radio>
 								</el-radio-group>
 							</el-form-item>
 
-							<el-form-item label="银行名称" prop="bankName" v-if="formData.type === 1">
-								<el-input v-model="formData.bankName" placeholder="请输入银行名称" clearable />
+							<el-form-item :label="$t('settlement.bankName')" prop="bankName" v-if="formData.type === 1">
+								<el-input v-model="formData.bankName" :placeholder="$t('settlement.bankNamePlaceholder')" clearable />
 							</el-form-item>
 
-							<el-form-item label="银行卡账号" prop="bankAccount">
-								<el-input v-model="formData.bankAccount" placeholder="请输入银行卡账号/USDT地址" clearable />
+							<el-form-item :label="$t('settlement.bankAccount')" prop="bankAccount">
+								<el-input v-model="formData.bankAccount" :placeholder="$t('settlement.bankAccountPlaceholder')" clearable />
 							</el-form-item>
 
-							<el-form-item label="真实姓名" prop="realName">
-								<el-input v-model="formData.realName" placeholder="请输入真实姓名" clearable />
+							<el-form-item :label="$t('settlement.realName')" prop="realName">
+								<el-input v-model="formData.realName" :placeholder="$t('settlement.realNamePlaceholder')" clearable />
 							</el-form-item>
 
 							<el-form-item>
-								<el-button type="primary" @click="handleSubmit" :loading="loading" size="large" style="width: 120px"> 提交申请 </el-button>
-								<el-button @click="handleReset" size="large" style="width: 120px">重置</el-button>
+								<el-button type="primary" @click="handleSubmit" :loading="loading" size="large" style="width: 120px">
+									{{ $t('settlement.submitApply') }}
+								</el-button>
+								<el-button @click="handleReset" size="large" style="width: 120px">{{ $t('common.resetBtn') }}</el-button>
 							</el-form-item>
 						</el-form>
 					</el-card>
@@ -52,6 +60,9 @@
 <script lang="ts" name="settlementApply" setup>
 import { applyWithdraw, getMerchantBalance } from '/@/api/settlement';
 import { useMessage } from '/@/hooks/message';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 定义变量
 const formRef = ref();
@@ -66,13 +77,13 @@ const formData = ref({
 });
 
 // 表单验证规则
-const rules = {
-	amount: [{ required: true, message: '请输入提现金额', trigger: 'blur' }],
-	type: [{ required: true, message: '请选择提现类型', trigger: 'change' }],
-	bankName: [{ required: true, message: '请输入银行名称', trigger: 'blur' }],
-	bankAccount: [{ required: true, message: '请输入银行卡账号/USDT地址', trigger: 'blur' }],
-	realName: [{ required: true, message: '请输入真实姓名', trigger: 'blur' }],
-};
+const rules = computed(() => ({
+	amount: [{ required: true, message: t('settlement.withdrawAmountPlaceholder'), trigger: 'blur' }],
+	type: [{ required: true, message: t('settlement.withdrawType'), trigger: 'change' }],
+	bankName: [{ required: true, message: t('settlement.bankNamePlaceholder'), trigger: 'blur' }],
+	bankAccount: [{ required: true, message: t('settlement.bankAccountPlaceholder'), trigger: 'blur' }],
+	realName: [{ required: true, message: t('settlement.realNamePlaceholder'), trigger: 'blur' }],
+}));
 
 // 提交申请
 const handleSubmit = async () => {
@@ -81,7 +92,7 @@ const handleSubmit = async () => {
 
 		// 验证提现金额不能超过可提现金额
 		if (formData.value.amount && formData.value.amount > availableBalance.value) {
-			useMessage().error(`提现金额不能超过可提现金额 ¥${formatAmount(availableBalance.value)}`);
+			useMessage().error(`${t('settlement.exceedBalance')} ¥${formatAmount(availableBalance.value)}`);
 			return;
 		}
 
@@ -100,7 +111,7 @@ const handleSubmit = async () => {
 		}
 
 		await applyWithdraw(submitData);
-		useMessage().success('申请提交成功');
+		useMessage().success(t('settlement.applySuccess'));
 		handleReset();
 	} catch (err: any) {
 		if (err.msg) {

+ 41 - 33
src/views/settlement/fundFlow/index.vue

@@ -16,40 +16,45 @@
 			<!-- 搜索表单 -->
 			<el-row class="ml10" v-show="showSearch">
 				<el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
-					<el-form-item label="订单号" prop="query">
-						<el-input placeholder="请输入订单号" style="max-width: 180px" v-model="state.queryForm.query" clearable />
+					<el-form-item :label="$t('settlement.orderNo')" prop="query">
+						<el-input :placeholder="$t('settlement.orderNoPlaceholder')" style="max-width: 180px" v-model="state.queryForm.query" clearable />
 					</el-form-item>
-					<el-form-item label="订单类型" prop="orderFlowType">
-						<el-select v-model="state.queryForm.orderFlowType" placeholder="请选择订单类型" style="max-width: 180px" clearable>
-							<el-option label="支付订单" value="PAY" />
-							<el-option label="提现订单" value="WITHDRAW" />
+					<el-form-item :label="$t('settlement.orderType')" prop="orderFlowType">
+						<el-select
+							v-model="state.queryForm.orderFlowType"
+							:placeholder="$t('settlement.orderTypePlaceholder')"
+							style="max-width: 180px"
+							clearable
+						>
+							<el-option :label="$t('settlement.orderTypePay')" value="PAY" />
+							<el-option :label="$t('settlement.orderTypeWithdraw')" value="WITHDRAW" />
 						</el-select>
 					</el-form-item>
-					<el-form-item label="订单创建时间" prop="orderCreateTime">
+					<el-form-item :label="$t('settlement.orderCreateTime')" prop="orderCreateTime">
 						<el-date-picker
 							v-model="state.queryForm.orderCreateTime"
 							type="datetimerange"
-							range-separator=""
-							start-placeholder="开始时间"
-							end-placeholder="结束时间"
+							range-separator="-"
+							:start-placeholder="$t('settlement.startTime')"
+							:end-placeholder="$t('settlement.endTime')"
 							style="max-width: 360px"
 							clearable
 						/>
 					</el-form-item>
-					<el-form-item label="支付时间" prop="payTime">
+					<el-form-item :label="$t('settlement.payTime')" prop="payTime">
 						<el-date-picker
 							v-model="state.queryForm.payTime"
 							type="datetimerange"
-							range-separator=""
-							start-placeholder="开始时间"
-							end-placeholder="结束时间"
+							range-separator="-"
+							:start-placeholder="$t('settlement.startTime')"
+							:end-placeholder="$t('settlement.endTime')"
 							style="max-width: 360px"
 							clearable
 						/>
 					</el-form-item>
 					<el-form-item>
-						<el-button @click="getDataList" icon="search" type="primary">查询</el-button>
-						<el-button @click="resetQuery" icon="Refresh">重置</el-button>
+						<el-button @click="getDataList" icon="search" type="primary">{{ $t('common.queryBtn') }}</el-button>
+						<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
 					</el-form-item>
 				</el-form>
 			</el-row>
@@ -76,36 +81,36 @@
 				:header-cell-style="tableStyle.headerCellStyle"
 			>
 				<el-table-column align="center" type="selection" width="40" />
-				<el-table-column label="变更前余额" prop="beforeAmount" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.beforeBalance')" prop="beforeAmount" show-overflow-tooltip>
 					<template #default="scope">
 						<span>¥{{ scope.row.beforeAmount ? scope.row.beforeAmount.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="变更金额" prop="operationAmount" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.changeAmount')" prop="operationAmount" show-overflow-tooltip>
 					<template #default="scope">
 						<span :class="scope.row.amountType === 'ADD' ? 'amount-add' : 'amount-sub'">
 							{{ scope.row.amountType === 'ADD' ? '+' : '-' }} ¥{{ scope.row.operationAmount ? scope.row.operationAmount.toFixed(2) : '0.00' }}
 						</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="变更后余额" prop="afterAmount" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.afterBalance')" prop="afterAmount" show-overflow-tooltip>
 					<template #default="scope">
 						<span>¥{{ scope.row.afterAmount ? scope.row.afterAmount.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="订单类型" prop="orderType" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('settlement.orderType')" prop="orderType" show-overflow-tooltip align="center">
 					<template #default="scope">
 						<el-tag :type="orderTypeMap[scope.row.orderType]?.type || 'info'" size="small">
 							{{ orderTypeMap[scope.row.orderType]?.label || '--' }}
 						</el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column label="业务订单" prop="orderNo" show-overflow-tooltip width="200">
+				<el-table-column :label="$t('settlement.businessOrder')" prop="orderNo" show-overflow-tooltip width="200">
 					<template #default="scope">
 						{{ scope.row.orderNo || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="订单金额" prop="orderAmount" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.orderAmount')" prop="orderAmount" show-overflow-tooltip>
 					<template #default="scope">
 						<span class="amount-value">¥{{ scope.row.orderAmount ? scope.row.orderAmount.toFixed(2) : '0.00' }}</span>
 					</template>
@@ -115,7 +120,7 @@
 						<span class="amount-value">¥{{ calculateFee(scope.row) }}</span>
 					</template>
 				</el-table-column> -->
-				<el-table-column label="创建时间" prop="payTime" show-overflow-tooltip width="180">
+				<el-table-column :label="$t('order.createTime')" prop="payTime" show-overflow-tooltip width="180">
 					<template #default="scope">
 						{{ scope.row.payTime || scope.row.orderCreateTime || '--' }}
 					</template>
@@ -129,14 +134,17 @@
 <script lang="ts" name="settlementFundFlow" setup>
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { fetchFundFlowList, getMerchantBalance } from '/@/api/settlement';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 余额卡片配置
-const balanceCards = [
-	{ key: 'totalUseAmount', label: '可用余额' },
-	{ key: 'totalAmount', label: '总金额' },
-	{ key: 'freezeAmount', label: '总冻结金额' },
-	{ key: 'withdrawnAmount', label: '总提现金额' },
-];
+const balanceCards = computed(() => [
+	{ key: 'totalUseAmount', label: t('settlement.balanceAvailable') },
+	{ key: 'totalAmount', label: t('settlement.balanceTotal') },
+	{ key: 'freezeAmount', label: t('settlement.balanceFrozen') },
+	{ key: 'withdrawnAmount', label: t('settlement.balanceWithdrawn') },
+]);
 
 // 余额数据
 const balanceData = ref({
@@ -148,10 +156,10 @@ const balanceData = ref({
 });
 
 // 订单类型映射
-const orderTypeMap: Record<string, { label: string; type: any }> = {
-	PAY: { label: '支付订单', type: 'success' },
-	WITHDRAW: { label: '提现订单', type: 'warning' },
-};
+const orderTypeMap = computed(() => ({
+	PAY: { label: t('settlement.orderTypePay'), type: 'success' },
+	WITHDRAW: { label: t('settlement.orderTypeWithdraw'), type: 'warning' },
+}));
 
 // 定义变量内容
 const queryRef = ref();

+ 43 - 30
src/views/settlement/record/index.vue

@@ -3,22 +3,32 @@
 		<div class="layout-padding-auto layout-padding-view">
 			<el-row class="ml10" v-show="showSearch">
 				<el-form :inline="true" :model="state.queryForm" @keyup.enter="getDataList" ref="queryRef">
-					<el-form-item label="真实姓名" prop="realName">
-						<el-input placeholder="请输入真实姓名" style="max-width: 180px" v-model="state.queryForm.realName" clearable />
+					<el-form-item :label="$t('settlement.realName')" prop="realName">
+						<el-input :placeholder="$t('settlement.realNamePlaceholder')" style="max-width: 180px" v-model="state.queryForm.realName" clearable />
 					</el-form-item>
-					<el-form-item label="银行卡账号" prop="bankAccount">
-						<el-input placeholder="请输入银行卡账号" style="max-width: 180px" v-model="state.queryForm.bankAccount" clearable />
+					<el-form-item :label="$t('settlement.bankAccount')" prop="bankAccount">
+						<el-input
+							:placeholder="$t('settlement.bankAccountPlaceholder')"
+							style="max-width: 180px"
+							v-model="state.queryForm.bankAccount"
+							clearable
+						/>
 					</el-form-item>
-					<el-form-item label="审核状态" prop="auditStatus">
-						<el-select v-model="state.queryForm.auditStatus" placeholder="请选择审核状态" style="max-width: 180px" clearable>
-							<el-option label="待审核" :value="0" />
-							<el-option label="通过" :value="1" />
-							<el-option label="拒绝" :value="2" />
+					<el-form-item :label="$t('settlement.auditStatus')" prop="auditStatus">
+						<el-select
+							v-model="state.queryForm.auditStatus"
+							:placeholder="$t('settlement.auditStatusPlaceholder')"
+							style="max-width: 180px"
+							clearable
+						>
+							<el-option :label="$t('settlement.statusPending')" :value="0" />
+							<el-option :label="$t('settlement.statusApproved')" :value="1" />
+							<el-option :label="$t('settlement.statusRejected')" :value="2" />
 						</el-select>
 					</el-form-item>
 					<el-form-item>
-						<el-button @click="getDataList" icon="search" type="primary">查询</el-button>
-						<el-button @click="resetQuery" icon="Refresh">重置</el-button>
+						<el-button @click="getDataList" icon="search" type="primary">{{ $t('common.queryBtn') }}</el-button>
+						<el-button @click="resetQuery" icon="Refresh">{{ $t('common.resetBtn') }}</el-button>
 					</el-form-item>
 				</el-form>
 			</el-row>
@@ -44,54 +54,54 @@
 				:header-cell-style="tableStyle.headerCellStyle"
 			>
 				<el-table-column align="center" type="selection" width="40" />
-				<el-table-column label="商户名称" prop="merchantName" show-overflow-tooltip>
+				<el-table-column :label="$t('order.merchantName')" prop="merchantName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.merchantName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="提现金额" prop="amount" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.withdrawAmount')" prop="amount" show-overflow-tooltip>
 					<template #default="scope">
 						<span class="amount-value">¥{{ scope.row.amount ? scope.row.amount.toFixed(2) : '0.00' }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="提现类型" prop="type" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('settlement.withdrawType')" prop="type" show-overflow-tooltip align="center">
 					<template #default="scope">
 						{{ typeMap[scope.row.type] || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="银行名称" prop="bankName" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.bankName')" prop="bankName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.bankName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="银行卡账号" prop="bankAccount" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.bankAccount')" prop="bankAccount" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.bankAccount || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="真实姓名" prop="realName" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.realName')" prop="realName" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.realName || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="审核状态" prop="auditStatus" show-overflow-tooltip align="center">
+				<el-table-column :label="$t('settlement.auditStatus')" prop="auditStatus" show-overflow-tooltip align="center">
 					<template #default="scope">
 						<el-tag :type="auditStatusMap[scope.row.auditStatus]?.type || 'info'" size="small">
 							{{ auditStatusMap[scope.row.auditStatus]?.label || '--' }}
 						</el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column label="拒绝原因" prop="failReason" show-overflow-tooltip>
+				<el-table-column :label="$t('settlement.rejectReason')" prop="failReason" show-overflow-tooltip>
 					<template #default="scope">
 						{{ scope.row.failReason || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="申请时间" prop="applyTime" show-overflow-tooltip width="180">
+				<el-table-column :label="$t('settlement.applyTime')" prop="applyTime" show-overflow-tooltip width="180">
 					<template #default="scope">
 						{{ scope.row.applyTime || '--' }}
 					</template>
 				</el-table-column>
-				<el-table-column label="创建时间" prop="createdTime" show-overflow-tooltip width="180">
+				<el-table-column :label="$t('order.createTime')" prop="createdTime" show-overflow-tooltip width="180">
 					<template #default="scope">
 						{{ scope.row.createdTime || '--' }}
 					</template>
@@ -105,19 +115,22 @@
 <script lang="ts" name="settlementRecord" setup>
 import { BasicTableProps, useTable } from '/@/hooks/table';
 import { fetchWithdrawList } from '/@/api/settlement';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 // 提现类型映射
-const typeMap: Record<number, string> = {
-	1: '银行卡',
-	2: 'USDT',
-};
+const typeMap = computed(() => ({
+	1: t('settlement.bankCard'),
+	2: t('settlement.usdt'),
+}));
 
 // 审核状态映射
-const auditStatusMap: Record<number, { label: string; type: any }> = {
-	0: { label: '待审核', type: 'info' },
-	1: { label: '通过', type: 'success' },
-	2: { label: '拒绝', type: 'danger' },
-};
+const auditStatusMap = computed(() => ({
+	0: { label: t('settlement.statusPending'), type: 'info' },
+	1: { label: t('settlement.statusApproved'), type: 'success' },
+	2: { label: t('settlement.statusRejected'), type: 'danger' },
+}));
 
 // 定义变量内容
 const queryRef = ref();