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

This commit is contained in:
MTrun
2021-12-17 11:55:42 +08:00
parent 7f67f482da
commit 557ddd6ee5
27 changed files with 446 additions and 102 deletions
+3
View File
@@ -0,0 +1,3 @@
import LangSelect from './index.vue';
export { LangSelect };
+28
View File
@@ -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
View File
@@ -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
View File
@@ -1,4 +1,9 @@
export enum ThemeEnum {
dark = 'dark',
light = 'light'
}
}
export enum LangEnum {
zh = 'zh',
en = 'en'
}
+12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,12 @@
import login from './login'
const global = {
doc_addr: "文档地址",
form_account: "请输入账号或邮箱",
form_password: "请输入密码",
}
export default {
global: global,
login: login
}
+7
View File
@@ -0,0 +1,7 @@
export default {
desc: "登录 GoView",
form_auto: "自动登录",
form_button: "登录",
login_success: "登录成功",
login_message: "请填写完整信息",
}
+10 -4
View File
@@ -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
View File
@@ -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中可使用,DialogMessage 等之类组件
appProvider.mount('#appProvider', true);
// 优先挂载一下 Provider 解决路由守卫,Axios中可使用,DialogMessage 等之类组件
appProvider.mount('#appProvider', true)
// 挂载路由
await setupRouter(app);
await setupRouter(app)
// 路由准备就绪后挂载APP实例
await router.isReady();
await router.isReady()
app.mount('#app', true);
// Store 准备就绪后处理主题色
setHtmlTheme()
// 语言注册
app.use(i18n)
app.mount('#app', true)
}
void appInit();
void appInit()
+23 -8
View File
@@ -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
View File
@@ -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
View File
@@ -1,6 +1,10 @@
import { ThemeEnum } from '@/enums/styleEnum'
export interface DesignStateType {
//深色主题
// 是否是深色主题
darkTheme: boolean;
// 主题名称
themeName: ThemeEnum;
//系统风格
appTheme: string;
//系统内置风格
+27 -14
View File
@@ -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
View File
@@ -0,0 +1,5 @@
import { LangEnum } from '@/enums/styleEnum'
export interface LangStateType {
// 当前语言
lang: LangEnum
}
+26
View File
@@ -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)
}
}
})
-2
View File
@@ -1,2 +0,0 @@
export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token
export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
+2
View File
@@ -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;
}
View File
+61 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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')}`)
}
})
}