Răsfoiți Sursa

feat: 完成主题切换,语言切换的本地存储

MTrun 3 ani în urmă
părinte
comite
557ddd6ee5

+ 3 - 1
package.json

@@ -8,12 +8,13 @@
     "lint": "eslint \"{src}/**/*.{vue,ts,tsx}\" --fix --ext"
   },
   "dependencies": {
-    "@vicons/ionicons5": "^0.11.0",
+    "@vicons/ionicons5": "~0.11.0",
     "axios": "^0.23.0",
     "mockjs": "^1.1.0",
     "naive-ui": "^2.19.9",
     "pinia": "^2.0.6",
     "vue": "^3.2.16",
+    "vue-i18n": "^9.2.0-beta.23",
     "vue-router": "4.0.12"
   },
   "devDependencies": {
@@ -23,6 +24,7 @@
     "@vitejs/plugin-vue": "^1.9.3",
     "@vitejs/plugin-vue-jsx": "^1.2.0",
     "@vue/compiler-sfc": "^3.2.20",
+    "@vueuse/core": "^7.3.0",
     "default-passive-events": "^2.0.0",
     "eslint": "^8.4.1",
     "eslint-config-prettier": "^8.3.0",

+ 106 - 2
pnpm-lock.yaml

@@ -4,10 +4,11 @@ specifiers:
   '@types/node': ^16.11.1
   '@typescript-eslint/eslint-plugin': ^5.6.0
   '@typescript-eslint/parser': ^5.6.0
-  '@vicons/ionicons5': ^0.11.0
+  '@vicons/ionicons5': ~0.11.0
   '@vitejs/plugin-vue': ^1.9.3
   '@vitejs/plugin-vue-jsx': ^1.2.0
   '@vue/compiler-sfc': ^3.2.20
+  '@vueuse/core': ^7.3.0
   axios: ^0.23.0
   default-passive-events: ^2.0.0
   eslint: ^8.4.1
@@ -28,6 +29,7 @@ specifiers:
   vite-plugin-mock: ^2.9.6
   vite-plugin-style-import: ^1.2.1
   vue: ^3.2.16
+  vue-i18n: ^9.2.0-beta.23
   vue-router: 4.0.12
   vue-tsc: ^0.28.7
 
@@ -38,6 +40,7 @@ dependencies:
   naive-ui: rg.cnpmjs.org/naive-ui/2.21.5_vue@3.2.24
   pinia: rg.cnpmjs.org/pinia/2.0.6_typescript@4.5.2+vue@3.2.24
   vue: rg.cnpmjs.org/vue/3.2.24
+  vue-i18n: rg.cnpmjs.org/vue-i18n/9.2.0-beta.23_vue@3.2.24
   vue-router: rg.cnpmjs.org/vue-router/4.0.12_vue@3.2.24
 
 devDependencies:
@@ -47,6 +50,7 @@ devDependencies:
   '@vitejs/plugin-vue': rg.cnpmjs.org/@vitejs/plugin-vue/1.10.2_vite@2.7.1
   '@vitejs/plugin-vue-jsx': rg.cnpmjs.org/@vitejs/plugin-vue-jsx/1.3.1
   '@vue/compiler-sfc': rg.cnpmjs.org/@vue/compiler-sfc/3.2.24
+  '@vueuse/core': rg.cnpmjs.org/@vueuse/core/7.3.0_vue@3.2.24
   default-passive-events: rg.cnpmjs.org/default-passive-events/2.0.0
   eslint: rg.cnpmjs.org/eslint/8.4.1
   eslint-config-prettier: rg.cnpmjs.org/eslint-config-prettier/8.3.0_eslint@8.4.1
@@ -510,6 +514,54 @@ packages:
     version: 1.2.1
     dev: true
 
+  rg.cnpmjs.org/@intlify/core-base/9.2.0-beta.23:
+    resolution: {integrity: sha512-sNet9/RpU/qydW1bOwVICYzGIC/SWyV+1gakcSP12XD1tGEiaoI1Ln+6nHh4dICfegR/5XyaoJ7NC4/ukH7/Ew==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@intlify/core-base/download/@intlify/core-base-9.2.0-beta.23.tgz}
+    name: '@intlify/core-base'
+    version: 9.2.0-beta.23
+    engines: {node: '>= 12'}
+    dependencies:
+      '@intlify/devtools-if': rg.cnpmjs.org/@intlify/devtools-if/9.2.0-beta.23
+      '@intlify/message-compiler': rg.cnpmjs.org/@intlify/message-compiler/9.2.0-beta.23
+      '@intlify/shared': rg.cnpmjs.org/@intlify/shared/9.2.0-beta.23
+      '@intlify/vue-devtools': rg.cnpmjs.org/@intlify/vue-devtools/9.2.0-beta.23
+    dev: false
+
+  rg.cnpmjs.org/@intlify/devtools-if/9.2.0-beta.23:
+    resolution: {integrity: sha512-f2iY2LFRHTwPnBpT0R/kG8CwZbUWiSccMzfXYLwhjJC1irCcFCXmVtL9Mkz5gc0Elqvl+zOWxk5g9rwhah5bBQ==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@intlify/devtools-if/download/@intlify/devtools-if-9.2.0-beta.23.tgz}
+    name: '@intlify/devtools-if'
+    version: 9.2.0-beta.23
+    engines: {node: '>= 12'}
+    dependencies:
+      '@intlify/shared': rg.cnpmjs.org/@intlify/shared/9.2.0-beta.23
+    dev: false
+
+  rg.cnpmjs.org/@intlify/message-compiler/9.2.0-beta.23:
+    resolution: {integrity: sha512-qmGN8k5yGGdZ5St8yg8U4Tg2K9Sc6h3BhWCdJKAqQVs5jnfZG+nMtsLVgnJUWkDvhjzyg7/rEOhHm2uJcu4vjw==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@intlify/message-compiler/download/@intlify/message-compiler-9.2.0-beta.23.tgz}
+    name: '@intlify/message-compiler'
+    version: 9.2.0-beta.23
+    engines: {node: '>= 12'}
+    dependencies:
+      '@intlify/shared': rg.cnpmjs.org/@intlify/shared/9.2.0-beta.23
+      source-map: rg.cnpmjs.org/source-map/0.6.1
+    dev: false
+
+  rg.cnpmjs.org/@intlify/shared/9.2.0-beta.23:
+    resolution: {integrity: sha512-3aELL2KTp1MWKGm2gIUKSagthgKzcK5hpQEFzOwkJ1SAthpTXR7BHeWGEaD+Lj+Pbiz3U8cspvp8s2lFWVbYxg==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@intlify/shared/download/@intlify/shared-9.2.0-beta.23.tgz}
+    name: '@intlify/shared'
+    version: 9.2.0-beta.23
+    engines: {node: '>= 12'}
+    dev: false
+
+  rg.cnpmjs.org/@intlify/vue-devtools/9.2.0-beta.23:
+    resolution: {integrity: sha512-5uGvrtUQhiyEqrMpDYh1FAU5uZviLaiEy1HAs+ypX46EdaDvyEYYpWvhezTWZ7hsDBsbsKJ9ICjgTvZxm6PDcw==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@intlify/vue-devtools/download/@intlify/vue-devtools-9.2.0-beta.23.tgz}
+    name: '@intlify/vue-devtools'
+    version: 9.2.0-beta.23
+    engines: {node: '>= 12'}
+    dependencies:
+      '@intlify/core-base': rg.cnpmjs.org/@intlify/core-base/9.2.0-beta.23
+      '@intlify/shared': rg.cnpmjs.org/@intlify/shared/9.2.0-beta.23
+    dev: false
+
   rg.cnpmjs.org/@jest/types/27.4.2:
     resolution: {integrity: sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@jest/types/download/@jest/types-27.4.2.tgz}
     name: '@jest/types'
@@ -1044,6 +1096,43 @@ packages:
     name: '@vue/shared'
     version: 3.2.24
 
+  rg.cnpmjs.org/@vueuse/core/7.3.0_vue@3.2.24:
+    resolution: {integrity: sha512-gPJyMMAquva9Qwqz63qGQT122m5hWI8Kuy8kfPV/JLQU7m01CXooyv8FIrX9TV8OxVcHBTPXPJHY0oyUiFoNgw==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@vueuse/core/download/@vueuse/core-7.3.0.tgz}
+    id: rg.cnpmjs.org/@vueuse/core/7.3.0
+    name: '@vueuse/core'
+    version: 7.3.0
+    peerDependencies:
+      '@vue/composition-api': ^1.1.0
+      vue: ^2.6.0 || ^3.2.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      vue:
+        optional: true
+    dependencies:
+      '@vueuse/shared': rg.cnpmjs.org/@vueuse/shared/7.3.0_vue@3.2.24
+      vue: rg.cnpmjs.org/vue/3.2.24
+      vue-demi: rg.cnpmjs.org/vue-demi/0.12.1_vue@3.2.24
+    dev: true
+
+  rg.cnpmjs.org/@vueuse/shared/7.3.0_vue@3.2.24:
+    resolution: {integrity: sha512-vOAeI84tIXKVkzm8s/Mxbrzhj0QN6NyVc/sC6LrW0AjVNdvpD8sB1dZiDn9yh8T77WJmloCEt4zZVIppeq7I+w==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/@vueuse/shared/download/@vueuse/shared-7.3.0.tgz}
+    id: rg.cnpmjs.org/@vueuse/shared/7.3.0
+    name: '@vueuse/shared'
+    version: 7.3.0
+    peerDependencies:
+      '@vue/composition-api': ^1.1.0
+      vue: ^2.6.0 || ^3.2.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      vue:
+        optional: true
+    dependencies:
+      vue: rg.cnpmjs.org/vue/3.2.24
+      vue-demi: rg.cnpmjs.org/vue-demi/0.12.1_vue@3.2.24
+    dev: true
+
   rg.cnpmjs.org/acorn-jsx/5.3.2_acorn@8.6.0:
     resolution: {integrity: sha1-ftW7VZCLOy8bxVxq8WU7rafweTc=, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/acorn-jsx/download/acorn-jsx-5.3.2.tgz}
     id: rg.cnpmjs.org/acorn-jsx/5.3.2
@@ -4260,7 +4349,6 @@ packages:
         optional: true
     dependencies:
       vue: rg.cnpmjs.org/vue/3.2.24
-    dev: false
 
   rg.cnpmjs.org/vue-eslint-parser/8.0.1_eslint@8.4.1:
     resolution: {integrity: sha1-JeCLIKQUVRUx8+GfmZkC4ez0XxM=, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/vue-eslint-parser/download/vue-eslint-parser-8.0.1.tgz}
@@ -4283,6 +4371,22 @@ packages:
       - supports-color
     dev: true
 
+  rg.cnpmjs.org/vue-i18n/9.2.0-beta.23_vue@3.2.24:
+    resolution: {integrity: sha512-9zpylFVjhMDiNnSpa8pFf/lXiALKzxDKEo9QrSV906cN0m6jtyjvjCWw6dRx/7Q4ZJuwXYg0wi/UtUkgu0wkQw==, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/vue-i18n/download/vue-i18n-9.2.0-beta.23.tgz}
+    id: rg.cnpmjs.org/vue-i18n/9.2.0-beta.23
+    name: vue-i18n
+    version: 9.2.0-beta.23
+    engines: {node: '>= 12'}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      '@intlify/core-base': rg.cnpmjs.org/@intlify/core-base/9.2.0-beta.23
+      '@intlify/shared': rg.cnpmjs.org/@intlify/shared/9.2.0-beta.23
+      '@intlify/vue-devtools': rg.cnpmjs.org/@intlify/vue-devtools/9.2.0-beta.23
+      '@vue/devtools-api': rg.cnpmjs.org/@vue/devtools-api/6.0.0-beta.20.1
+      vue: rg.cnpmjs.org/vue/3.2.24
+    dev: false
+
   rg.cnpmjs.org/vue-router/4.0.12_vue@3.2.24:
     resolution: {integrity: sha1-jceSzd9bsavMOQj5BkE23n4TxGA=, registry: http://r.cnpmjs.org/, tarball: https://rg.cnpmjs.org/vue-router/download/vue-router-4.0.12.tgz}
     id: rg.cnpmjs.org/vue-router/4.0.12

+ 3 - 0
src/components/LangSelect/index.ts

@@ -0,0 +1,3 @@
+import LangSelect from './index.vue';
+
+export { LangSelect };

+ 28 - 0
src/components/LangSelect/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <n-dropdown
+    trigger="hover"
+    @select="handleSelect"
+    :show-arrow="true"
+    :options="options"
+  >
+    <n-button quaternary>
+      <n-icon size="20" :depth="1">
+        <LanguageIcon />
+      </n-icon>
+    </n-button>
+  </n-dropdown>
+</template>
+
+<script lang="ts" setup>
+import { useLangStore } from '@/store/modules/langStore/langStore'
+import { Language as LanguageIcon } from '@vicons/ionicons5'
+import { langList } from '@/settings/designSetting'
+import { LangEnum } from '@/enums/styleEnum'
+
+const langStore = useLangStore()
+const options = langList
+
+const handleSelect = (key: LangEnum) => {
+  langStore.changeLang(key)
+}
+</script>

+ 6 - 8
src/components/ThemeSelect/index.vue

@@ -1,12 +1,10 @@
 <template>
-  <div>
-    <n-button quaternary @click="changeTheme">
-      <n-icon size="20" :depth="1">
-        <MoonIcon v-if="designStore.darkTheme" />
-        <SunnyIcon v-else />
-      </n-icon>
-    </n-button>
-  </div>
+  <n-button quaternary @click="changeTheme">
+    <n-icon size="20" :depth="1">
+      <MoonIcon v-if="designStore.darkTheme" />
+      <SunnyIcon v-else />
+    </n-icon>
+  </n-button>
 </template>
 
 <script lang="ts" setup>

+ 6 - 1
src/enums/styleEnum.ts

@@ -1,4 +1,9 @@
 export enum ThemeEnum {
   dark = 'dark',
   light = 'light'
-}
+}
+
+export enum LangEnum {
+  zh = 'zh',
+  en = 'en'
+}

+ 12 - 0
src/i18n/en/index.ts

@@ -0,0 +1,12 @@
+import login from './login'
+
+const global = {
+  doc_addr: "Doc Address",
+  form_account: "Please enter your account or email",
+  form_password: "Please enter your password"
+}
+
+export default {
+  global: global,
+  login: login
+}

+ 7 - 0
src/i18n/en/login.ts

@@ -0,0 +1,7 @@
+export default {
+  desc: "Log in to GoView",
+  form_auto: "Sign in automatically",
+  form_button: "Login",
+  login_success: "Login success",
+  login_message: "Please complete the letter",
+}

+ 20 - 0
src/i18n/index.ts

@@ -0,0 +1,20 @@
+//语言
+import { lang } from '@/settings/designSetting'
+import { createI18n } from 'vue-i18n' //引入vue-i18n组件
+import { getLocalStorage } from '@/utils/index'
+import { GO_LANG_SELECT } from '@/settings/storageConst'
+import zh from './zh/index'
+import en from './en/index'
+
+const lang_storage = getLocalStorage(GO_LANG_SELECT)
+
+const i18n = createI18n({
+  locale: lang_storage || lang,
+  globalInjection: true,
+  messages: {
+    zh: zh,
+    en: en
+  }
+})
+
+export default i18n

+ 12 - 0
src/i18n/zh/index.ts

@@ -0,0 +1,12 @@
+import login from './login'
+
+const global = {
+  doc_addr: "文档地址",
+  form_account: "请输入账号或邮箱",
+  form_password: "请输入密码",
+}
+
+export default {
+  global: global,
+  login: login
+}

+ 7 - 0
src/i18n/zh/login.ts

@@ -0,0 +1,7 @@
+export default {
+  desc: "登录 GoView",
+  form_auto: "自动登录",
+  form_button: "登录",
+  login_success: "登录成功",
+  login_message: "请填写完整信息",
+}

+ 10 - 4
src/layout/components/Header/index.vue

@@ -2,12 +2,17 @@
   <div class="go-header">
     <header class="go-header-box">
       <div class="li">
-        <slot name="left"></slot>
+        <n-space>
+          <slot name="left"></slot>
+        </n-space>
       </div>
       <div class="ri">
-        <slot name="right">
-          <ThemeSelect />
-        </slot>
+        <n-space>
+          <slot name="right">
+            <LangSelect />
+            <ThemeSelect />
+          </slot>
+        </n-space>
       </div>
     </header>
     <n-divider class="go-header-divider" />
@@ -16,6 +21,7 @@
 
 <script setup lang="ts">
 import { ThemeSelect } from '@/components/ThemeSelect'
+import { LangSelect } from '@/components/LangSelect'
 </script>
 
 <style lang="scss" scoped>

+ 24 - 20
src/main.ts

@@ -1,38 +1,42 @@
-import { createApp } from 'vue';
-import App from './App.vue';
-import router, { setupRouter } from '@/router';
-import { setupStore } from '@/store';
-import { setupNaive, setupDirectives } from '@/plugins';
-import { AppProvider } from '@/components/Application';
+import { createApp } from 'vue'
+import App from './App.vue'
+import router, { setupRouter } from '@/router'
+import i18n from '@/i18n/index'
+import { setupStore } from '@/store'
+import { setupNaive, setupDirectives } from '@/plugins'
+import { AppProvider } from '@/components/Application'
 import { setHtmlTheme } from '@/utils/style'
 
 async function appInit() {
-  const appProvider = createApp(AppProvider);
+  const appProvider = createApp(AppProvider)
 
-  const app = createApp(App);
+  const app = createApp(App)
 
   // 注册全局常用的 naive-ui 组件
-  setupNaive(app);
+  setupNaive(app)
 
   // 注册全局自定义指令,如:v-permission权限指令
-  setupDirectives(app);
+  setupDirectives(app)
 
   // 挂载状态管理
-  setupStore(app);
+  setupStore(app)
 
-  // 处理主题色
-  setHtmlTheme()
-
-  //优先挂载一下 Provider 解决路由守卫,Axios中可使用,Dialog,Message 等之类组件
-  appProvider.mount('#appProvider', true);
+  // 优先挂载一下 Provider 解决路由守卫,Axios中可使用,Dialog,Message 等之类组件
+  appProvider.mount('#appProvider', true)
 
   // 挂载路由
-  await setupRouter(app);
+  await setupRouter(app)
 
   // 路由准备就绪后挂载APP实例
-  await router.isReady();
+  await router.isReady()
+
+  // Store 准备就绪后处理主题色
+  setHtmlTheme()
+
+  // 语言注册
+  app.use(i18n)
 
-  app.mount('#app', true);
+  app.mount('#app', true)
 }
 
-void appInit();
+void appInit()

+ 23 - 8
src/settings/designSetting.ts

@@ -1,4 +1,21 @@
-// app theme preset color
+import { LangEnum } from '@/enums/styleEnum'
+
+// 默认语言
+export const lang = LangEnum.zh
+
+// 语言数组
+export const langList = [
+  {
+    label: '中文',
+    key: LangEnum.zh
+  },
+  {
+    label: 'English',
+    key: LangEnum.en
+  }
+]
+
+// 主体色
 export const appThemeList: string[] = [
   '#2d8cf0',
   '#0960bd',
@@ -17,22 +34,20 @@ export const appThemeList: string[] = [
   '#78DEC7',
   '#1768AC',
   '#FB9300',
-  '#FC5404',
-];
+  '#FC5404'
+]
 
 export const theme = {
-  darkThemeName: 'dark',
-  lightThemeName: 'light',
   //深色主题
   darkTheme: true,
   //系统主题色
   appTheme: '#63e2b7',
   //系统内置主题色列表
-  appThemeList,
-};
+  appThemeList
+}
 
 // 修改边框圆角
 export const borderRadius = '8px'
 
 // 轮播间隔
-export const carouselInterval = 5000
+export const carouselInterval = 4000

+ 4 - 0
src/settings/storageConst.ts

@@ -0,0 +1,4 @@
+export const GO_ACCESS_TOKEN = 'GO-ACCESS-TOKEN' // 用户token
+export const GO_CURRENT_USER = 'GO-CURRENT-USER' // 当前用户信息
+export const GO_LANG_SELECT = 'GO-LANG-SELECT' // 当前选择的语言类型
+export const GO_Theme_SELECT = 'GO-Theme-SELECT' // 当前选择的主题

+ 5 - 1
src/store/modules/designStore/designStore.d.ts

@@ -1,6 +1,10 @@
+import { ThemeEnum } from '@/enums/styleEnum'
+
 export interface DesignStateType {
-  //深色主题
+  // 是否是深色主题
   darkTheme: boolean;
+  // 主题名称
+  themeName: ThemeEnum;
   //系统风格
   appTheme: string;
   //系统内置风格

+ 27 - 14
src/store/modules/designStore/designStore.ts

@@ -1,34 +1,47 @@
-import { defineStore } from 'pinia';
-import { store } from '@/store';
-import { theme } from '@/settings/designSetting';
-const { darkTheme, appTheme, appThemeList } = theme;
+import { defineStore } from 'pinia'
+import { store } from '@/store'
+import { theme } from '@/settings/designSetting'
 import { DesignStateType } from './designStore.d'
+import { setLocalStorage, getLocalStorage } from '@/utils/index'
+import { GO_Theme_SELECT } from '@/settings/storageConst'
+import { ThemeEnum } from '@/enums/styleEnum'
+
+const { darkTheme, appTheme, appThemeList } = theme
+const storageThemeName = getLocalStorage(GO_Theme_SELECT)
 
 export const useDesignStore = defineStore({
   id: 'useDesignStore',
   state: (): DesignStateType => ({
-    darkTheme,
+    // 是否暗黑
+    darkTheme: storageThemeName === ThemeEnum.dark,
+    // 主题名称
+    themeName:
+      storageThemeName || (darkTheme && ThemeEnum.dark) || ThemeEnum.light,
+    // 颜色色号
     appTheme,
-    appThemeList,
+    // 颜色列表
+    appThemeList
   }),
   getters: {
-    getDarkTheme(): boolean {
-      return this.darkTheme;
+    getDarkTheme(e): boolean {
+      return this.darkTheme
     },
     getAppTheme(): string {
-      return this.appTheme;
+      return this.appTheme
     },
     getAppThemeList(): string[] {
-      return this.appThemeList;
-    },
+      return this.appThemeList
+    }
   },
   actions: {
-    changeTheme():void {
+    changeTheme(): void {
       this.darkTheme = !this.darkTheme
+      this.themeName = this.darkTheme ? ThemeEnum.dark : ThemeEnum.light
+      setLocalStorage(GO_Theme_SELECT, this.themeName)
     }
   }
-});
+})
 
 export function useDesignSettingWithOut() {
-  return useDesignStore(store);
+  return useDesignStore(store)
 }

+ 5 - 0
src/store/modules/langStore/langStore.d.ts

@@ -0,0 +1,5 @@
+import { LangEnum } from '@/enums/styleEnum'
+export interface LangStateType {
+  // 当前语言
+  lang: LangEnum
+}

+ 26 - 0
src/store/modules/langStore/langStore.ts

@@ -0,0 +1,26 @@
+import { defineStore } from 'pinia'
+import { lang } from '@/settings/designSetting'
+import { LangStateType } from './langStore.d'
+import { LangEnum } from '@/enums/styleEnum'
+import i18n from '@/i18n/index'
+import { setLocalStorage } from '@/utils/index'
+import { GO_LANG_SELECT } from '@/settings/storageConst'
+
+export const useLangStore = defineStore({
+  id: 'useLangStore',
+  state: (): LangStateType => ({
+    lang
+  }),
+  getters: {
+    getLang(): LangEnum {
+      return this.lang
+    }
+  },
+  actions: {
+    changeLang(lang: LangEnum): void {
+      this.lang = lang
+      i18n.global.locale = lang
+      setLocalStorage(GO_LANG_SELECT, lang)
+    }
+  }
+})

+ 0 - 2
src/store/mutation-types.ts

@@ -1,2 +0,0 @@
-export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token
-export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息

+ 2 - 0
src/store/types.ts

@@ -1,5 +1,7 @@
 import { DesignStateType } from '@/store/modules/designStore/designStore.d';
+import { LangStateType } from '@/store/modules/langStore/langStore.d';
 
 export interface allStore {
   useDesignStore: DesignStateType;
+  useLangStore: LangStateType;
 }

+ 0 - 0
src/utils/canvas.ts


+ 61 - 9
src/utils/index.ts

@@ -1,5 +1,5 @@
-import { h } from 'vue';
-import { NIcon } from 'naive-ui';
+import { h } from 'vue'
+import { NIcon } from 'naive-ui'
 
 /**
  * * 生成一个用不重复的ID
@@ -7,24 +7,76 @@ import { NIcon } from 'naive-ui';
  */
 export function getUUID(randomLength: number) {
   return Number(
-    Math.random()
-      .toString()
-      .substr(2, randomLength) + Date.now()
-  ).toString(36);
+    Math.random().toString().substr(2, randomLength) + Date.now()
+  ).toString(36)
 }
 
 /**
  * * render 图标
  */
 export const renderIcon = (icon: typeof NIcon) => {
-  return () => h(NIcon, null, { default: () => h(icon) });
+  return () => h(NIcon, null, { default: () => h(icon) })
 }
 
 /**
  * * 处理 vite 中无法使用 require 的问题
- * @param name 
- * @returns 
+ * @param name
+ * @returns url
  */
 export const requireUrl = (path: string, name: string) => {
   return new URL(`${path}/${name}`, import.meta.url).href
 }
+
+/**
+ * * 存储本地会话数据
+ * @param k 键名
+ * @param v 键值
+ * @returns RemovableRef
+ */
+export const setLocalStorage = <T>(k: string, v: T) => {
+  try {
+    window.localStorage.setItem(k, JSON.stringify(v))
+  } catch (error) {
+    return false
+  }
+}
+
+/**
+ * * 获取本地会话数据
+ * @returns any
+ */
+export const getLocalStorage: (k: string) => any = (k: string) => {
+  const item = window.localStorage.getItem(k)
+  try {
+    return item ? JSON.parse(item) : item
+  } catch (err) {
+    return item
+  }
+}
+
+/**
+ * * 存储临时会话数据
+ * @param k 键名
+ * @param v 键值
+ * @returns RemovableRef
+ */
+export const setSessionStorage = <T>(k: string, v: T) => {
+  try {
+    window.sessionStorage.setItem(k, JSON.stringify(v))
+  } catch (error) {
+    return false
+  }
+}
+
+/**
+ * * 获取临时会话数据
+ * @returns any
+ */
+export const getSessionStorage: (k: string) => any = (k: string) => {
+  const item = window.sessionStorage.getItem(k)
+  try {
+    return item ? JSON.parse(item) : item
+  } catch (err) {
+    return item
+  }
+}

+ 16 - 10
src/utils/page.ts

@@ -1,25 +1,31 @@
-import { ResultEnum } from "@/enums/httpEnum"
-import { ErrorPageNameMap } from "@/enums/pageEnum"
-import router from '@/router';
+import { ResultEnum } from '@/enums/httpEnum'
+import { ErrorPageNameMap } from '@/enums/pageEnum'
+import router from '@/router'
 
 /**
  * * 错误页重定向
- * @param icon 
- * @returns 
+ * @param icon
+ * @returns
  */
 export const redirectErrorPage = (code: ResultEnum) => {
-  if(!code) return false
+  if (!code) return false
   const pageName = ErrorPageNameMap.get(code)
-  if(!pageName) return false
+  if (!pageName) return false
   routerTurnByName(pageName)
 }
 
 /**
  * * 根据名字跳转路由
- * @param pageName 
+ * @param pageName
  */
-export const routerTurnByName = (pageName: string) => {
+export const routerTurnByName = (pageName: string, isReplace?: boolean) => {
+  if (isReplace) {
+    router.replace({
+      name: pageName
+    })
+    return
+  }
   router.push({
     name: pageName
   })
-}
+}

+ 3 - 4
src/utils/style.ts

@@ -1,12 +1,11 @@
 import { useDesignStore } from '@/store/modules/designStore/designStore'
-import { theme as themeEnum } from '@/settings/designSetting'
 
 export const setHtmlTheme = (themeName?: string) => {
   const e = window.document.documentElement
   if (themeName) {
-    e.setAttribute("data-theme", themeName);
+    e.setAttribute('data-theme', themeName)
     return
   }
   const designStore = useDesignStore()
-  e.setAttribute("data-theme", designStore.getDarkTheme ? themeEnum.darkThemeName : themeEnum.lightThemeName);
-}
+  e.setAttribute('data-theme', designStore.themeName)
+}

+ 30 - 17
src/views/login/index.vue

@@ -17,6 +17,7 @@
     <Header>
       <template #left></template>
       <template #right>
+        <LangSelect />
         <ThemeSelect />
       </template>
     </Header>
@@ -28,14 +29,14 @@
             :key="i"
             class="go-login-carousel-img"
             :src="getImageUrl(item, 'login')"
-            alt="展示图片"
+            alt="image"
           />
         </n-carousel>
       </div>
       <div class="login-account">
         <div class="login-account-container">
           <n-collapse-transition :appear="true" :show="show">
-            <n-card class="login-account-card" title="登录 GoView">
+            <n-card class="login-account-card" :title="$t('login.desc')">
               <div class="login-account-top">
                 <img
                   class="login-account-top-logo"
@@ -53,7 +54,7 @@
                 <n-form-item path="username">
                   <n-input
                     v-model:value="formInline.username"
-                    placeholder="请输入用户名"
+                    :placeholder="$t('global.form_account')"
                   >
                     <template #prefix>
                       <n-icon size="18">
@@ -67,7 +68,7 @@
                     v-model:value="formInline.password"
                     type="password"
                     show-password-toggle
-                    placeholder="请输入密码"
+                    :placeholder="$t('global.form_password')"
                   >
                     <template #prefix>
                       <n-icon size="18">
@@ -80,7 +81,7 @@
                   <div class="flex justify-between">
                     <div class="flex-initial">
                       <n-checkbox v-model:checked="autoLogin">
-                        自动登录
+                        {{ $t('login.form_auto') }}
                       </n-checkbox>
                     </div>
                   </div>
@@ -93,7 +94,7 @@
                     :loading="loading"
                     block
                   >
-                    登录
+                    {{ $t('login.form_button') }}
                   </n-button>
                 </n-form-item>
               </n-form>
@@ -104,7 +105,7 @@
     </div>
 
     <div class="go-login-box-footer">
-      <n-a>文档地址: </n-a>
+      <n-a>{{ $t('global.doc_addr') }}: </n-a>
       <n-a italic href="http://www.mtruning.club/">
         http://www.mtruning.club/
       </n-a>
@@ -116,16 +117,20 @@
 import { reactive, ref, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
 import { useMessage } from 'naive-ui'
-import {
-  PersonOutline as PersonOutlineIcon,
-  LockClosedOutline as LockClosedOutlineIcon
-} from '@vicons/ionicons5'
+import { useI18n } from 'vue-i18n'
 import { requireUrl } from '@/utils/index'
+import { routerTurnByName } from '@/utils/page'
 import shuffle from 'lodash/shuffle'
 import { carouselInterval } from '@/settings/designSetting'
 import { useDesignStore } from '@/store/modules/designStore/designStore'
 import { ThemeSelect } from '@/components/ThemeSelect'
+import { LangSelect } from '@/components/LangSelect'
 import { Header } from '@/layout/components/Header'
+import { PageEnum } from '@/enums/pageEnum'
+import {
+  PersonOutline as PersonOutlineIcon,
+  LockClosedOutline as LockClosedOutlineIcon
+} from '@vicons/ionicons5'
 
 interface FormState {
   username: string
@@ -139,6 +144,7 @@ const loading = ref(false)
 const autoLogin = ref(true)
 const show = ref(false)
 const designStore = useDesignStore()
+const { t } = useI18n()
 
 onMounted(() => {
   setTimeout(() => {
@@ -152,8 +158,16 @@ const formInline = reactive({
 })
 
 const rules = {
-  username: { required: true, message: '请输入用户名', trigger: 'blur' },
-  password: { required: true, message: '请输入密码', trigger: 'blur' }
+  username: {
+    required: true,
+    message: t('global.form_account'),
+    trigger: 'blur'
+  },
+  password: {
+    required: true,
+    message: t('global.form_password'),
+    trigger: 'blur'
+  }
 }
 
 // 定时器
@@ -194,11 +208,10 @@ const handleSubmit = (e: Event) => {
     if (!errors) {
       const { username, password } = formInline
       loading.value = true
-
-      message.success('登录成功!')
-      router.replace('/')
+      message.success(`${t('login.login_success')}!`)
+      routerTurnByName(PageEnum.BASE_HOME_NAME, true)
     } else {
-      message.error('请填写完整信息,并且进行验证码校验')
+      message.error(`${t('login.login_message')}!`)
     }
   })
 }

+ 0 - 1
types/config.d.ts

@@ -1 +0,0 @@
-