Ver Fonte

feat: 增加登录页多语言切换

yanglzh há 2 meses atrás
pai
commit
c9b8525534

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

@@ -107,6 +107,11 @@ export default {
 		logOutExit: 'Exiting',
 		logOutSuccess: 'Exit successfully!',
 	},
+	login: {
+		title: 'Login',
+		serverVersion: 'Server Version',
+		frontendVersion: 'Frontend Version',
+	},
 	tagsView: {
 		refresh: 'refresh',
 		close: 'close',

+ 5 - 0
src/i18n/lang/zh-cn.ts

@@ -108,6 +108,11 @@ export default {
 		logOutExit: '退出中',
 		logOutSuccess: '安全退出成功!',
 	},
+	login: {
+		title: '登录',
+		serverVersion: '服务端版本',
+		frontendVersion: '前端版本',
+	},
 	tagsView: {
 		refresh: '刷新',
 		close: '关闭',

+ 5 - 0
src/i18n/lang/zh-tw.ts

@@ -115,6 +115,11 @@ export default {
 		logOutExit: '退出中',
 		logOutSuccess: '安全退出成功!',
 	},
+	login: {
+		title: '登入',
+		serverVersion: '伺服器版本',
+		frontendVersion: '前端版本',
+	},
 	tagsView: {
 		refresh: '重繪',
 		close: '關閉',

+ 0 - 1
src/layout/navBars/breadcrumb/user.vue

@@ -218,7 +218,6 @@ export default defineComponent({
       proxy.$i18n.locale = lang;
       initI18n();
       other.useTitle();
-      console.log('切换语言成功')
     };
     // 设置 element plus 组件的国际化
     const setI18nConfig = (locale: string) => {

+ 2 - 0
src/main.ts

@@ -23,6 +23,8 @@ import copy from '/@/components/copy/index.vue'
 import JsonViewer from "vue3-json-viewer"
 
 const app = createApp(App);
+// 取消 vue warn
+app.config.warnHandler = function () { }
 
 directive(app);
 other.elSvg(app);

+ 4 - 0
src/theme/dark.scss

@@ -67,6 +67,10 @@
 		}
 	}
 
+	.el-table__header-wrapper tr th.el-table-fixed-column--right {
+		background-color: #343435 !important;
+	}
+
 	// 高亮时
 	.el-menu-item.is-active {
 		color: var(--next-color-menu-text-blue) !important;

+ 388 - 299
src/views/login/index.vue

@@ -1,327 +1,416 @@
 <template>
-	<div class="login-container flex-row" v-if="showImg">
-		<el-switch class="switch" v-model="getThemeConfig.isIsDark" size="large" inline-prompt @change="onAddDarkChange" :active-icon="Sunny" :inactive-icon="Moon" style="--el-switch-on-color: #fff; --el-switch-off-color: #151515"></el-switch>
-		<div class="part left">
-			<div class="flex logo">
-				<el-image class="logoimg" :src="sysinfo.systemLogo" />
-				{{ sysinfo.systemName }}
-			</div>
-			<el-image class="img" :src="sysinfo.systemLoginPIC" />
-			<div>
-				<span class="text" v-if="sysinfo.buildTime" style="margin-right: 5px">服务端版本:{{ sysinfo.buildVersion }} </span>
-				<span class="text" v-if="sysinfo.buildTime">{{ dayjs(sysinfo.buildTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
-				<div style="height: 10px;"></div>
-				<span class="text" v-if="versionInfo.version" style="margin-right: 5px">前端版本:{{ versionInfo.version }} </span>
-				<span class="text" v-if="versionInfo.updateTime">{{ versionInfo.updateTime }}</span>
-			</div>
-		</div>
-		<div class="part">
-			<div class="title">登录</div>
-			<Account :showSSO="showSSO" />
-		</div>
-	</div>
+  <div class="login-container flex-row" v-if="showImg">
+    <!-- 多语言切换 -->
+    <el-dropdown class="language-switch" :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
+      <div class="language-switch-icon">
+        <i class="iconfont" :class="disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'" :title="$t('message.user.title1')"></i>
+      </div>
+      <template #dropdown>
+        <el-dropdown-menu>
+          <el-dropdown-item command="zh-cn" :disabled="disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
+          <el-dropdown-item command="en" :disabled="disabledI18n === 'en'">English</el-dropdown-item>
+          <el-dropdown-item command="zh-tw" :disabled="disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
+        </el-dropdown-menu>
+      </template>
+    </el-dropdown>
+    <!-- 深色模式切换 -->
+    <el-switch class="switch" v-model="getThemeConfig.isIsDark" size="large" inline-prompt @change="onAddDarkChange" :active-icon="Sunny" :inactive-icon="Moon" style="--el-switch-on-color: #fff; --el-switch-off-color: #151515"></el-switch>
+    <div class="part left">
+      <div class="flex logo">
+        <el-image class="logoimg" :src="sysinfo.systemLogo" />
+        {{ sysinfo.systemName }}
+      </div>
+      <el-image class="img" :src="sysinfo.systemLoginPIC" />
+      <div>
+        <span class="text" v-if="sysinfo.buildTime" style="margin-right: 5px">{{ $t('message.login.serverVersion') }}:{{ sysinfo.buildVersion }} </span>
+        <span class="text" v-if="sysinfo.buildTime">{{ dayjs(sysinfo.buildTime).format("YYYY-MM-DD HH:mm:ss") }}</span>
+        <div style="height: 10px"></div>
+        <span class="text" v-if="versionInfo.version" style="margin-right: 5px">{{ $t('message.login.frontendVersion') }}:{{ versionInfo.version }} </span>
+        <span class="text" v-if="versionInfo.updateTime">{{ versionInfo.updateTime }}</span>
+      </div>
+    </div>
+    <div class="part">
+      <div class="title">{{ $t('message.login.title') }}</div>
+      <Account :showSSO="showSSO" />
+    </div>
+  </div>
 </template>
 
 <script lang="ts">
-import { toRefs, reactive, computed, defineComponent } from 'vue';
-import Account from '/@/views/login/component/account.vue';
-import { useStore } from '/@/store/index';
-import logoMini from '/imgs/logo.png';
-import { Sunny, Moon } from '@element-plus/icons-vue';
-import dayjs from 'dayjs';
-import api from '/@/api/system';
-import axios from 'axios';
-import { setSystemInfo, setTenantInfo } from '/@/utils/auth';
+import { toRefs, reactive, computed, defineComponent, getCurrentInstance } from "vue";
+import Account from "/@/views/login/component/account.vue";
+import { useStore } from "/@/store/index";
+import logoMini from "/imgs/logo.png";
+import { Sunny, Moon } from "@element-plus/icons-vue";
+import dayjs from "dayjs";
+import api from "/@/api/system";
+import axios from "axios";
+import { setSystemInfo, setTenantInfo } from "/@/utils/auth";
+import { Local } from "/@/utils/storage";
+import other from "/@/utils/other";
 // import PackageJson from '/public/version.json';
 // 定义接口来定义对象的类型
 interface LoginState {
-	tabsActiveName: string;
-	isScan: boolean;
+  tabsActiveName: string;
+  isScan: boolean;
+  disabledI18n: string;
 }
 
 export default defineComponent({
-	name: 'loginIndex',
-	components: {
-		Account,
-	},
-	data: function () {
-		return {
-			Sunny,
-			Moon,
-			dayjs,
-			showImg: false,
-			showSSO: false,
-			sysinfo: {
-				buildVersion: '',
-				systemName: '',
-				buildTime: '',
-				systemCopyright: '',
-				systemLogo: '',
-				systemLoginPIC: '',
-			},
-		};
-	},
-	created() {
-		setTenantInfo();
-	},
-	mounted() {
-		api.sysinfo().then((res: any) => {
-			setSystemInfo(res);
-			this.sysinfo = res
-			const isSSOEnabled = window.atob(res.target).split('|')[4]
-			if (isSSOEnabled == '1') {
-				this.showSSO = true
-			}
-		}).finally(() => this.showImg = true)
-	},
-	setup() {
-		const store = useStore();
-		const state = reactive<LoginState>({
-			tabsActiveName: 'account',
-			isScan: false,
-		});
-		// 获取布局配置信息
-		const getThemeConfig = computed(() => {
-			return store.state.themeConfig.themeConfig;
-		});
-
-		const versionInfo = reactive({
-			version: '',
-			updateTime: '',
-		})
-		// 加载版本信息
-		axios.get('/versionInfo.json').then(res => {
-			versionInfo.version = res.data.version
-			versionInfo.updateTime = res.data.updateTime
-		})
-
-		// 4、界面显示 --> 深色模式
-		const onAddDarkChange = () => {
-			const body = document.documentElement as HTMLElement;
-			if (getThemeConfig.value.isIsDark) {
-				body.setAttribute('data-theme', 'dark');
-				document.querySelector('html')!.className = 'dark'
-			} else {
-				body.setAttribute('data-theme', '');
-				document.querySelector('html')!.className = ''
-			}
-			store.dispatch('themeConfig/setThemeConfig', getThemeConfig.value);
-		};
-
-		return {
-			onAddDarkChange,
-			logoMini,
-			getThemeConfig,
-			versionInfo,
-			...toRefs(state),
-		};
-	},
+  name: "loginIndex",
+  components: {
+    Account,
+  },
+  data: function () {
+    return {
+      Sunny,
+      Moon,
+      dayjs,
+      showImg: false,
+      showSSO: false,
+      sysinfo: {
+        buildVersion: "",
+        systemName: "",
+        buildTime: "",
+        systemCopyright: "",
+        systemLogo: "",
+        systemLoginPIC: "",
+      },
+    };
+  },
+  created() {
+    setTenantInfo();
+  },
+  mounted() {
+    api
+      .sysinfo()
+      .then((res: any) => {
+        setSystemInfo(res);
+        this.sysinfo = res;
+        const isSSOEnabled = window.atob(res.target).split("|")[4];
+        if (isSSOEnabled == "1") {
+          this.showSSO = true;
+        }
+      })
+      .finally(() => (this.showImg = true));
+  },
+  setup() {
+    const store = useStore();
+    const { proxy } = <any>getCurrentInstance();
+    const state = reactive<LoginState>({
+      tabsActiveName: "account",
+      isScan: false,
+      disabledI18n: "zh-cn",
+    });
+    // 获取布局配置信息
+    const getThemeConfig = computed(() => {
+      return store.state.themeConfig.themeConfig;
+    });
+
+    const versionInfo = reactive({
+      version: "",
+      updateTime: "",
+    });
+    // 加载版本信息
+    axios.get("/versionInfo.json").then((res) => {
+      versionInfo.version = res.data.version;
+      versionInfo.updateTime = res.data.updateTime;
+    });
+
+    // 语言切换
+    const onLanguageChange = (lang: string) => {
+      Local.remove("themeConfig");
+      getThemeConfig.value.globalI18n = lang;
+      Local.set("themeConfig", getThemeConfig.value);
+      proxy.$i18n.locale = lang;
+      initI18n();
+      other.useTitle();
+    };
+
+    // 设置 element plus 组件的国际化
+    const setI18nConfig = (locale: string) => {
+      proxy.mittBus.emit("getI18nConfig", proxy.$i18n.messages[locale]);
+    };
+    // 初始化言语国际化
+    const initI18n = () => {
+      switch (Local.get("themeConfig").globalI18n) {
+        case "zh-cn":
+          state.disabledI18n = "zh-cn";
+          setI18nConfig("zh-cn");
+          break;
+        case "en":
+          state.disabledI18n = "en";
+          setI18nConfig("en");
+          break;
+        case "zh-tw":
+          state.disabledI18n = "zh-tw";
+          setI18nConfig("zh-tw");
+          break;
+      }
+    };
+
+    // 4、界面显示 --> 深色模式
+    const onAddDarkChange = () => {
+      const body = document.documentElement as HTMLElement;
+      if (getThemeConfig.value.isIsDark) {
+        body.setAttribute("data-theme", "dark");
+        document.querySelector("html")!.className = "dark";
+      } else {
+        body.setAttribute("data-theme", "");
+        document.querySelector("html")!.className = "";
+      }
+      store.dispatch("themeConfig/setThemeConfig", getThemeConfig.value);
+    };
+
+    // 初始化当前语言状态
+    state.disabledI18n = getThemeConfig.value.globalI18n || "zh-cn";
+
+    return {
+      onAddDarkChange,
+      onLanguageChange,
+      logoMini,
+      getThemeConfig,
+      versionInfo,
+      ...toRefs(state),
+    };
+  },
 });
 </script>
 
 <style scoped lang="scss">
-html[data-theme='dark'] {
-	.login-container {
-		background: #293146;
-	}
-
-	.left {
-		background-image: url(/@/assets/login-bg-dark.svg);
-	}
-
-	.title {
-		color: #aaa;
-	}
+html[data-theme="dark"] {
+  .login-container {
+    background: #293146;
+  }
+
+  .left {
+    background-image: url(/@/assets/login-bg-dark.svg);
+  }
+
+  .title {
+    color: #aaa;
+  }
 }
 
 .flex {
-	display: flex;
-	align-items: center;
+  display: flex;
+  align-items: center;
 }
 
 .text {
-	color: #fff;
+  color: #fff;
+}
+
+.language-switch {
+  position: fixed;
+  right: 80px;
+  top: 20px;
+  z-index: 1000;
+
+  .language-switch-icon {
+    width: 40px;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 50%;
+    background: rgba(255, 255, 255, 0.1);
+    backdrop-filter: blur(10px);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    cursor: pointer;
+    transition: all 0.3s ease;
+
+    &:hover {
+      background: rgba(255, 255, 255, 0.2);
+      transform: scale(1.05);
+    }
+
+    i {
+      font-size: 20px;
+      color: #fff;
+    }
+  }
 }
 
 .switch {
-	position: fixed;
-	right: 20px;
-	top: 20px;
+  position: fixed;
+  right: 20px;
+  top: 20px;
 }
 
 .login-container {
-	width: 100vw;
-	height: 100vh;
-	position: relative;
-	background: #fff;
-
-	.title {
-		font-size: 30px;
-		color: #333;
-		font-weight: bold;
-		letter-spacing: 20px;
-	}
-
-	.logo {
-		font-size: 30px;
-		color: #fff;
-
-		.logoimg {
-			height: 50px;
-			display: block;
-			margin-right: 12px;
-		}
-	}
-
-	.img {
-		max-width: 50%;
-		display: block;
-		max-height: 40vh;
-	}
-
-	.part {
-		flex: 1;
-		display: flex;
-		flex-flow: column nowrap;
-		justify-content: center;
-		align-items: center;
-	}
-
-	.left {
-		height: 100vh;
-		background-image: url(/@/assets/login-bg.svg);
-		background-repeat: no-repeat;
-		background-size: auto 100%;
-		background-position: right center;
-		align-items: flex-start;
-		padding-left: 8%;
-		justify-content: space-around;
-		padding-top: 10vh;
-		padding-bottom: 10vh;
-	}
-
-	.login-icon-group {
-		width: 100%;
-		height: 100%;
-		position: relative;
-
-		.login-icon-group-title {
-			display: flex;
-			align-items: center;
-			justify-content: center;
-			margin: 12px 0;
-
-			img {
-				width: auto;
-				height: 40px;
-			}
-
-			&-text {
-				padding-left: 20px;
-				color: var(--el-color-primary);
-			}
-		}
-
-		&-icon {
-			width: 60%;
-			height: 70%;
-			position: absolute;
-			left: 0;
-			bottom: 0;
-		}
-	}
-
-	.login-content-out {
-		width: 100%;
-		height: 100%;
-		padding-top: calc(50vh - 227px);
-	}
-
-	.login-content {
-		width: 500px;
-		padding: 20px;
-		margin-left: calc(50% - 500px);
-		background-color: rgba(255, 255, 255, 0.8);
-		border: 5px solid var(--el-color-primary-light-8);
-		border-radius: 5px;
-		overflow: hidden;
-		z-index: 1;
-		position: relative;
-
-		.login-content-main {
-			margin: 0 auto;
-			width: 80%;
-
-			.login-content-title {
-				color: var(--el-text-color-primary);
-				font-weight: 500;
-				font-size: 22px;
-				text-align: center;
-				letter-spacing: 4px;
-				margin: 15px 0 30px;
-				white-space: nowrap;
-				z-index: 5;
-				position: relative;
-				transition: all 0.3s ease;
-			}
-		}
-
-		.login-content-main-sacn {
-			position: absolute;
-			top: 0;
-			right: 0;
-			width: 50px;
-			height: 50px;
-			overflow: hidden;
-			cursor: pointer;
-			transition: all ease 0.3s;
-			color: var(--el-text-color-primary);
-
-			&-delta {
-				position: absolute;
-				width: 35px;
-				height: 70px;
-				z-index: 2;
-				top: 2px;
-				right: 21px;
-				background: var(--el-color-white);
-				transform: rotate(-45deg);
-			}
-
-			&:hover {
-				opacity: 1;
-				transition: all ease 0.3s;
-				color: var(--el-color-primary) !important;
-			}
-
-			i {
-				width: 47px;
-				height: 50px;
-				display: inline-block;
-				font-size: 48px;
-				position: absolute;
-				right: 2px;
-				top: -1px;
-			}
-		}
-	}
-
-	.login-footer {
-		position: absolute;
-		bottom: 5px;
-		width: 100%;
-
-		&-content {
-			width: 100%;
-			display: flex;
-
-			&-warp {
-				margin: auto;
-				color: #e0e3e9;
-				text-align: center;
-				animation: error-num 1s ease-in-out;
-			}
-		}
-	}
+  width: 100vw;
+  height: 100vh;
+  position: relative;
+  background: #fff;
+
+  .title {
+    font-size: 30px;
+    color: #333;
+    font-weight: bold;
+    letter-spacing: 20px;
+  }
+
+  .logo {
+    font-size: 30px;
+    color: #fff;
+
+    .logoimg {
+      height: 50px;
+      display: block;
+      margin-right: 12px;
+    }
+  }
+
+  .img {
+    max-width: 50%;
+    display: block;
+    max-height: 40vh;
+  }
+
+  .part {
+    flex: 1;
+    display: flex;
+    flex-flow: column nowrap;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .left {
+    height: 100vh;
+    background-image: url(/@/assets/login-bg.svg);
+    background-repeat: no-repeat;
+    background-size: auto 100%;
+    background-position: right center;
+    align-items: flex-start;
+    padding-left: 8%;
+    justify-content: space-around;
+    padding-top: 10vh;
+    padding-bottom: 10vh;
+  }
+
+  .login-icon-group {
+    width: 100%;
+    height: 100%;
+    position: relative;
+
+    .login-icon-group-title {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin: 12px 0;
+
+      img {
+        width: auto;
+        height: 40px;
+      }
+
+      &-text {
+        padding-left: 20px;
+        color: var(--el-color-primary);
+      }
+    }
+
+    &-icon {
+      width: 60%;
+      height: 70%;
+      position: absolute;
+      left: 0;
+      bottom: 0;
+    }
+  }
+
+  .login-content-out {
+    width: 100%;
+    height: 100%;
+    padding-top: calc(50vh - 227px);
+  }
+
+  .login-content {
+    width: 500px;
+    padding: 20px;
+    margin-left: calc(50% - 500px);
+    background-color: rgba(255, 255, 255, 0.8);
+    border: 5px solid var(--el-color-primary-light-8);
+    border-radius: 5px;
+    overflow: hidden;
+    z-index: 1;
+    position: relative;
+
+    .login-content-main {
+      margin: 0 auto;
+      width: 80%;
+
+      .login-content-title {
+        color: var(--el-text-color-primary);
+        font-weight: 500;
+        font-size: 22px;
+        text-align: center;
+        letter-spacing: 4px;
+        margin: 15px 0 30px;
+        white-space: nowrap;
+        z-index: 5;
+        position: relative;
+        transition: all 0.3s ease;
+      }
+    }
+
+    .login-content-main-sacn {
+      position: absolute;
+      top: 0;
+      right: 0;
+      width: 50px;
+      height: 50px;
+      overflow: hidden;
+      cursor: pointer;
+      transition: all ease 0.3s;
+      color: var(--el-text-color-primary);
+
+      &-delta {
+        position: absolute;
+        width: 35px;
+        height: 70px;
+        z-index: 2;
+        top: 2px;
+        right: 21px;
+        background: var(--el-color-white);
+        transform: rotate(-45deg);
+      }
+
+      &:hover {
+        opacity: 1;
+        transition: all ease 0.3s;
+        color: var(--el-color-primary) !important;
+      }
+
+      i {
+        width: 47px;
+        height: 50px;
+        display: inline-block;
+        font-size: 48px;
+        position: absolute;
+        right: 2px;
+        top: -1px;
+      }
+    }
+  }
+
+  .login-footer {
+    position: absolute;
+    bottom: 5px;
+    width: 100%;
+
+    &-content {
+      width: 100%;
+      display: flex;
+
+      &-warp {
+        margin: auto;
+        color: #e0e3e9;
+        text-align: center;
+        animation: error-num 1s ease-in-out;
+      }
+    }
+  }
 }
 </style>

+ 2 - 2
src/views/modules/iotCard/index/index.vue

@@ -21,7 +21,7 @@
           </el-button>
         </el-form-item>
       </el-form>
-      <el-table :data="tableData" max-height="calc(100vh  - 210px);" v-loading="loading" style="width: 100%">
+      <el-table :data="tableData" max-height="calc(100vh - 210px)" v-loading="loading" style="width: 100%">
         <el-table-column v-col="'accNumber'" fixed="left" min-width="130" :label="$t('message.iotCard.index.table.columns.cardNumber')" prop="accNumber" align="center" />
         <el-table-column v-col="'ICCID'" min-width="180" :label="$t('message.iotCard.index.table.columns.iccid')" prop="iccid" align="center" />
         <el-table-column v-col="'bindDeviceName'" width="120" :label="$t('message.iotCard.index.table.columns.bindDevice')" prop="bindDeviceName" align="center" />
@@ -35,7 +35,7 @@
         <el-table-column v-col="'totalFlow'" width="120" :label="$t('message.iotCard.index.table.columns.totalFlow')" prop="totalFlow" align="center" />
         <el-table-column v-col="'usedFlow'" width="120" :label="$t('message.iotCard.index.table.columns.usedFlow')" prop="usedFlow" align="center" />
         <el-table-column v-col="'leaveFlow'" width="120" :label="$t('message.iotCard.index.table.columns.remainFlow')" prop="leaveFlow" align="center" />
-        <el-table-column v-col="'activationTime'" width="120" :label="$t('message.iotCard.index.table.columns.activationDate')" prop="activationTime" align="center" />
+        <el-table-column v-col="'activationTime'" width="140" :label="$t('message.iotCard.index.table.columns.activationDate')" prop="activationTime" align="center" />
         <el-table-column v-col="'updatedAt'" width="120" :label="$t('message.iotCard.index.table.columns.updateTime')" prop="updatedAt" align="center" />
         <el-table-column v-col="'simStatus'" width="120" :label="$t('message.iotCard.index.table.columns.status')" prop="simStatus" align="center">
           <template #default="scope">{{ formatStatus(scope.row.simStatus) }}</template>