Skip to content

Commit 54c18bf

Browse files
committed
feat(components): language switch
1 parent d5600ac commit 54c18bf

File tree

15 files changed

+379
-88
lines changed

15 files changed

+379
-88
lines changed

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ Give a ⭐️ if this project helped you!
4848
- [x] 图片拖拽/裁剪
4949
- [x] 支持切换主题色:一键换肤
5050
- [x] 指令权限:v-permisson /全局方法:$permission (参考 tableList.vue)
51+
- [x] 国际化
5152

5253
## 正在完成功能 | The functionality is being completed
5354

5455
- [x] 可拖拽弹窗
55-
- [x] 国际化
5656
- [x] 导航模式切换
5757
- [x] 内容区域控制
5858

@@ -166,6 +166,10 @@ git merge upstream/main
166166

167167
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bd27dbaba4f94db88a98468640f3ca00~tplv-k3u1fbpfcp-watermark.image)
168168

169+
### 国际化
170+
171+
![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d97113bee9994d5891ea4f38f5f6c85b~tplv-k3u1fbpfcp-watermark.image)
172+
169173
### 全屏功能
170174

171175
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/17ee79df049c4536a73177e4ae086650~tplv-k3u1fbpfcp-watermark.image)

src/components/Breadcrumb/index.vue

+11-5
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,31 @@
22
<el-breadcrumb class="app-breadcremb" separator-class="el-icon-arrow-right">
33
<transition-group name="breadcrumb">
44
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
5-
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
6-
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
5+
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title[lang] }}</span>
6+
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title[lang] }}</a>
77
</el-breadcrumb-item>
88
</transition-group>
99
</el-breadcrumb>
1010
</template>
1111
<script lang="ts">
12-
import { defineComponent, onMounted, ref, watch } from 'vue'
12+
import { defineComponent, onMounted, ref, watch, computed } from 'vue'
1313
import { compile } from 'path-to-regexp'
14-
import { useRoute, useRouter } from 'vue-router'
14+
import { RouteLocationMatched, useRoute, useRouter } from 'vue-router'
15+
import { useStore } from '@/store/index'
1516
1617
export default defineComponent({
1718
setup() {
1819
const levelList = ref([] as any)
1920
const route = useRoute()
2021
const router = useRouter()
22+
const store = useStore()
23+
24+
const lang = computed(() => store.getters['settingsModule/getLangState'])
25+
2126
// 获取面包屑
2227
const getBreadcrumb = () => {
2328
// 只展示路由中设置了meta.title的元素;
24-
const matched = route.matched.filter((item) => item.meta && item.meta.title)
29+
const matched = route.matched.filter((item: RouteLocationMatched) => item?.meta?.title)
2530
levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false)
2631
}
2732
@@ -48,6 +53,7 @@ export default defineComponent({
4853
})
4954
return {
5055
levelList,
56+
lang,
5157
handleLink
5258
}
5359
}

src/components/LangSwitch/index.vue

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<template>
2+
<ul id="lang" class="lang-switch">
3+
<li class="header-lang" :class="{ 'is-active': lang === '/zh-CN' }" @click="handleSwitchLang('/zh-CN')">中文</li>
4+
<span>/</span>
5+
<li class="header-lang" :class="{ 'is-active': lang === '/en-US' }" @click="handleSwitchLang('/en-US')">En</li>
6+
</ul>
7+
</template>
8+
<script lang="ts">
9+
import { defineComponent, computed } from 'vue'
10+
import { useStore } from '@/store/index'
11+
import zhLocale from 'element-plus/lib/locale/lang/zh-cn'
12+
import enLocale from 'element-plus/lib/locale/lang/en'
13+
import { use } from 'element-plus/lib/locale'
14+
import { locale } from 'element-plus'
15+
16+
export default defineComponent({
17+
setup() {
18+
const store = useStore()
19+
20+
const lang = computed(() => {
21+
const langState = store.getters['settingsModule/getLangState']
22+
const local = langState === '/zh-CN' ? zhLocale : enLocale
23+
// eslint-disable-next-line no-unused-expressions
24+
import.meta.env.DEV ? locale(local) : use(local)
25+
return langState
26+
})
27+
/**
28+
* @description 语言切换
29+
*/
30+
const handleSwitchLang = (_lang: string) => {
31+
store.dispatch('settingsModule/toToggleLang', { lang: _lang })
32+
}
33+
return {
34+
lang,
35+
handleSwitchLang
36+
}
37+
}
38+
})
39+
</script>
40+
<style lang="stylus" scoped>
41+
.lang-switch {
42+
display: inline-block;
43+
float: right;
44+
height: 100%;
45+
& li {
46+
color: #2c3e50;
47+
display: inline-block;
48+
vertical-align: middle;
49+
padding: 0 10px;
50+
cursor: pointer;
51+
}
52+
.header-download {
53+
opacity: 0.4;
54+
cursor: default;
55+
&.is-available {
56+
opacity: 1;
57+
cursor: pointer;
58+
}
59+
}
60+
span {
61+
opacity: 0.7;
62+
}
63+
.header-lang {
64+
cursor: pointer;
65+
opacity: 0.7;
66+
&.is-active {
67+
opacity: 1;
68+
color: rgba(24, 144, 255, 1);
69+
}
70+
}
71+
}
72+
73+
.lang-switch::after {
74+
display: inline-block;
75+
content: '';
76+
height: 100%;
77+
vertical-align: middle;
78+
.header-lang {
79+
cursor: pointer;
80+
opacity: 0.7;
81+
&.is-active {
82+
opacity: 1;
83+
}
84+
}
85+
}
86+
</style>

src/components/Search/index.vue

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export default defineComponent({
2929
const router = useRouter()
3030
const fuse = ref()
3131
const routes = computed(() => store.state.permissionModule.accessRoutes)
32+
const lang = computed(() => store.getters['settingsModule/getLangState'])
33+
3234
const state = reactive<stateType>({
3335
options: [],
3436
value: ''
@@ -72,7 +74,7 @@ export default defineComponent({
7274
title: [...prefixTitle]
7375
}
7476
if (route?.meta?.title) {
75-
data.title = [...data.title, route.meta.title]
77+
data.title = [...data.title, route.meta.title[lang.value]]
7678
list.push(data)
7779
}
7880
if (route.children) {
@@ -99,6 +101,7 @@ export default defineComponent({
99101
})
100102
101103
return {
104+
lang,
102105
handleChange,
103106
querySearch,
104107
...toRefs(state)

src/layout/components/AppMain.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<section class="app-main">
33
<el-tabs id="Tabs" v-model="currentIndex" type="card" closable @tab-click="clickTab" @tab-remove="removeTab">
4-
<el-tab-pane v-for="item in tabsOption" :key="item.route" :closable="item.route !== '/home'" :label="item.title" :name="item.route">
4+
<el-tab-pane v-for="item in tabsOption" :key="item.route" :closable="item.route !== '/home'" :label="item.title[lang]" :name="item.route">
55
<router-view v-if="$route.meta.keepAlive" v-slot="{ Component }">
66
<transition name="fade" mode="out-in">
77
<component :is="Component" />
@@ -30,8 +30,9 @@ export default defineComponent({
3030
// store 中获取当前路由以及所有的路由对象;
3131
const store = useStore()
3232
const tabsOption = computed(() => store.getters['tabModule/getTabsOption'])
33-
console.log('tabsOption.value is ', tabsOption.value)
3433
const currentIndex = computed(() => store.getters['tabModule/getCurrentIndex'])
34+
const lang = computed(() => store.getters['settingsModule/getLangState'])
35+
3536
const router = useRouter()
3637
// mothods
3738
/**
@@ -61,6 +62,7 @@ export default defineComponent({
6162
}
6263
return {
6364
tabsOption,
65+
lang,
6466
currentIndex,
6567
removeTab,
6668
clickTab

src/layout/components/Navbar.vue

+18-9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<breadcrumb class="breadcrumb-container" />
66
<div class="right-menu">
77
<search></search>
8+
<lang-switch></lang-switch>
89
<div id="Message">
910
<el-dropdown>
1011
<el-badge :value="messageNum" :max="99" class="message-badge" type="danger">
@@ -24,10 +25,10 @@
2425
</el-dropdown>
2526
</div>
2627
<el-button id="fullScreen" class="full-screen">
27-
<el-tooltip content="全屏浏览" effect="dark" placement="left">
28+
<el-tooltip :content="langConfig.header.fullScreen[lang]" effect="dark" placement="left">
2829
<i v-show="fullScreen == false" class="el-icon-full-screen" @click="toShowFullScreen()"></i>
2930
</el-tooltip>
30-
<el-tooltip content="退出全屏" effect="dark" placement="left">
31+
<el-tooltip :content="langConfig.header.exitFullScreen[lang]" effect="dark" placement="left">
3132
<i v-show="fullScreen == true" class="el-icon-bottom-left" @click="toExitFullScreen()"></i>
3233
</el-tooltip>
3334
</el-button>
@@ -39,16 +40,16 @@
3940
<template #dropdown>
4041
<el-dropdown-menu class="user-dropdown">
4142
<router-link to="/">
42-
<el-dropdown-item>首页</el-dropdown-item>
43+
<el-dropdown-item>{{ langConfig.header.user.homePage[lang] }}</el-dropdown-item>
4344
</router-link>
4445
<router-link to="/personal/personalCenter">
45-
<el-dropdown-item>个人中心</el-dropdown-item>
46+
<el-dropdown-item>{{ langConfig.header.user.personalCenter[lang] }}</el-dropdown-item>
4647
</router-link>
4748
<router-link to="/personal/personalSetting">
48-
<el-dropdown-item>个人设置</el-dropdown-item>
49+
<el-dropdown-item>{{ langConfig.header.user.personalSetting[lang] }}</el-dropdown-item>
4950
</router-link>
5051
<el-dropdown-item divided>
51-
<span style="display: block" @click="logout">退出登录</span>
52+
<span style="display: block" @click="logout">{{ langConfig.header.user.logout[lang] }}</span>
5253
</el-dropdown-item>
5354
</el-dropdown-menu>
5455
</template>
@@ -62,17 +63,20 @@ import { defineComponent, computed, ref } from 'vue'
6263
import Hamburger from '@/components/Hamburger/Hamburger.vue'
6364
import Breadcrumb from '@/components/Breadcrumb/index.vue'
6465
import Search from '@/components/Search/index.vue'
66+
import LangSwitch from '@/components/LangSwitch/index.vue'
6567
import avatar from '@/assets/avatar-default.jpg'
6668
import { useRouter } from 'vue-router'
6769
import { toFullScreen, exitFullScreen } from '@/utils/screen'
6870
import { useStore } from '@/store/index'
71+
import { langConfig } from '@/utils/constant/config'
6972
7073
export default defineComponent({
7174
name: 'Navbar',
7275
components: {
7376
Hamburger,
7477
Breadcrumb,
75-
Search
78+
Search,
79+
LangSwitch
7680
},
7781
props: {
7882
primary: {
@@ -84,12 +88,13 @@ export default defineComponent({
8488
const router = useRouter()
8589
const store = useStore()
8690
const opened = computed(() => store.getters['appModule/getSidebarState'])
87-
const nickname = computed(() => JSON.parse(localStorage.getItem('userInfo') as string)?.userName ?? '极客恰恰')
8891
const fullScreen = ref(false)
8992
const messageNum = computed(() => store.getters['messageModule/getMessageNum'])
93+
const lang = computed((): string => store.getters['settingsModule/getLangState'])
94+
const nickname = computed(() => JSON.parse(localStorage.getItem('userInfo') as string)?.userName ?? '极客恰恰')
95+
9096
// methods
9197
const toggleSideBar = () => {
92-
console.log('into toggleSideBar')
9398
store.dispatch('appModule/toggleSideBar')
9499
}
95100
@@ -109,6 +114,7 @@ export default defineComponent({
109114
110115
router.replace('/login')
111116
}
117+
112118
return {
113119
messageNum,
114120
toShowFullScreen,
@@ -117,9 +123,11 @@ export default defineComponent({
117123
exitFullScreen,
118124
fullScreen,
119125
nickname,
126+
lang,
120127
avatar,
121128
toggleSideBar,
122129
opened,
130+
langConfig,
123131
logout
124132
}
125133
}
@@ -166,6 +174,7 @@ export default defineComponent({
166174
&:focus {
167175
outline: none;
168176
}
177+
169178
.message-badge {
170179
.is-fixed {
171180
top: 12px !important;

src/layout/components/Sidebar/sidebarItem.vue

+11-8
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,29 @@
44
<template v-if="hasOneShowingChild(item.children, item)">
55
<el-menu-item :key="item.path" :index="item.path" :route="item.path">
66
<i :class="item.meta.icon"></i>
7-
<template #title>{{ item.meta.title }}</template>
7+
<template #title>{{ item.meta.title[lang] }}</template>
88
</el-menu-item>
99
</template>
1010
<el-submenu v-else ref="subMenu" :index="item.path" popper-append-to-body>
1111
<template #title>
1212
<i :class="item.meta.icon"></i>
13-
<span>{{ item.meta.title }}</span>
13+
<span>{{ item.meta.title[lang] }}</span>
1414
</template>
1515
<!--children 进行递归调用自身组件-->
1616
<sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="child.path" class="nest-menu" />
1717
</el-submenu>
1818
</div>
1919
</template>
2020
<script lang="ts">
21-
import { defineComponent, onMounted, ref, toRef } from 'vue'
22-
// import { isExternal } from '@/utils/validate'
23-
// import path from 'path'
21+
import { defineComponent, onMounted, ref, toRef, computed } from 'vue'
22+
import { useStore } from '@/store/index'
23+
2424
interface childType {
2525
path: string
2626
name?: string
2727
component: Function
2828
meta: {
29-
title: string
29+
title: Object
3030
icon: string
3131
hidden?: boolean
3232
[key: string]: any
@@ -52,9 +52,11 @@ export default defineComponent({
5252
setup(props) {
5353
// 是否只有一个孩子
5454
const onlyOneChild = ref()
55+
const store = useStore()
56+
5557
// 析构获取 props 属性 basePath
5658
const basePath = toRef(props, 'basePath')
57-
59+
const lang = computed(() => store.getters['settingsModule/getLangState'])
5860
onMounted(() => {
5961
// eslint-disable-next-line no-console
6062
console.log('basePath.value', basePath.value)
@@ -103,7 +105,8 @@ export default defineComponent({
103105
104106
return {
105107
onlyOneChild,
106-
hasOneShowingChild
108+
hasOneShowingChild,
109+
lang
107110
// resolvePath,
108111
}
109112
}

0 commit comments

Comments
 (0)