mirror of
https://github.com/fatedier/frp.git
synced 2025-04-20 00:24:21 +00:00
frpc多语言设计完成
This commit is contained in:
parent
8593eff752
commit
ff0676a3b1
3
web/frpc/components.d.ts
vendored
3
web/frpc/components.d.ts
vendored
@ -10,6 +10,9 @@ declare module 'vue' {
|
||||
ClientConfigure: typeof import('./src/components/ClientConfigure.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
|
@ -13,6 +13,7 @@
|
||||
"dependencies": {
|
||||
"element-plus": "^2.5.3",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^10.0.5",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -3,36 +3,39 @@
|
||||
<header class="grid-content header-color">
|
||||
<div class="header-content">
|
||||
<div class="brand">
|
||||
<a href="#">frp client</a>
|
||||
<a href="#">{{ t("main.title") }}</a>
|
||||
</div>
|
||||
<div class="dark-switch">
|
||||
<el-switch
|
||||
v-model="darkmodeSwitch"
|
||||
inline-prompt
|
||||
active-text="Dark"
|
||||
inactive-text="Light"
|
||||
@change="toggleDark"
|
||||
style="
|
||||
<div class="right-ability">
|
||||
<div class="lang-switch">
|
||||
<el-dropdown>
|
||||
<img src="./assets/lang.svg" alt="lang">
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :disabled="locale == 'en' ? true : false"
|
||||
@click="switchLanguage('en')">English</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="locale == 'zh' ? true : false"
|
||||
@click="switchLanguage('zh')">简体中文</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="dark-switch">
|
||||
<el-switch v-model="darkmodeSwitch" inline-prompt :active-text="t('main.dark.Dark')"
|
||||
:inactive-text="t('main.dark.Light')" @change="toggleDark" style="
|
||||
--el-switch-on-color: #444452;
|
||||
--el-switch-off-color: #589ef8;
|
||||
"
|
||||
/>
|
||||
" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
<el-row>
|
||||
<el-col id="side-nav" :xs="24" :md="4">
|
||||
<el-menu
|
||||
default-active="1"
|
||||
mode="vertical"
|
||||
theme="light"
|
||||
router="false"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item index="/">Overview</el-menu-item>
|
||||
<el-menu-item index="/configure">Configure</el-menu-item>
|
||||
<el-menu-item index="">Help</el-menu-item>
|
||||
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect">
|
||||
<el-menu-item index="/">{{ t("main.Overview") }}</el-menu-item>
|
||||
<el-menu-item index="/configure">{{ t("main.Configure") }}</el-menu-item>
|
||||
<el-menu-item index="">{{ t("main.Help") }}</el-menu-item>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
|
||||
@ -50,11 +53,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
const isDark = useDark()
|
||||
const darkmodeSwitch = ref(isDark)
|
||||
const toggleDark = useToggle(isDark)
|
||||
|
||||
const switchLanguage = (lang: string) => {
|
||||
locale.value = lang;
|
||||
localStorage.setItem('i18n', lang);
|
||||
}
|
||||
const handleSelect = (key: string) => {
|
||||
if (key == '') {
|
||||
window.open('https://github.com/fatedier/frp')
|
||||
@ -107,10 +115,20 @@ html.dark .header-color {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.dark-switch {
|
||||
.right-ability {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.lang-switch {
|
||||
width: 30px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.lang-switch img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
|
1
web/frpc/src/assets/lang.svg
Normal file
1
web/frpc/src/assets/lang.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1732765556989" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1552" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M864 64a96 96 0 0 1 96 96v704a96 96 0 0 1-96 96H160a96 96 0 0 1-96-96V160a96 96 0 0 1 96-96h704z m0 64H160a32 32 0 0 0-32 32v704a32 32 0 0 0 32 32h704a32 32 0 0 0 32-32V160a32 32 0 0 0-32-32z m-322.4 256c0-31.456 40.64-44.032 58.4-18.08l133.6 195.168V384a32 32 0 0 1 64 0v280.48c0 31.456-40.64 44.032-58.4 18.08l-133.6-195.168v177.088a32 32 0 1 1-64 0z" fill="#ffffff" p-id="1553"></path><path d="M448 352a32 32 0 0 1 0 64H288v80h160a32 32 0 0 1 31.776 28.256L480 528a32 32 0 0 1-32 32H288v72.48h160a32 32 0 1 1 0 64H256a32 32 0 0 1-32-32V384a32 32 0 0 1 32-32z" fill="#ffffff" p-id="1554"></path></svg>
|
After Width: | Height: | Size: 936 B |
25
web/frpc/src/assets/locales/en.json
Normal file
25
web/frpc/src/assets/locales/en.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"main": {
|
||||
"title": "frp client",
|
||||
"Overview": "Overview",
|
||||
"Configure": "Configure",
|
||||
"Help": "Help",
|
||||
"dark": {
|
||||
"Dark": "Dark",
|
||||
"Light": "Light"
|
||||
}
|
||||
},
|
||||
"OverView": {
|
||||
"name": "name",
|
||||
"type": "type",
|
||||
"local_addr": "local address",
|
||||
"plugin": "plugin",
|
||||
"remote_addr": "remote address",
|
||||
"status": "status",
|
||||
"err": "info"
|
||||
},
|
||||
"Configure": {
|
||||
"Refresh": "Refresh",
|
||||
"Upload": "Upload"
|
||||
}
|
||||
}
|
25
web/frpc/src/assets/locales/zh.json
Normal file
25
web/frpc/src/assets/locales/zh.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"main": {
|
||||
"title": "frp 客户端",
|
||||
"Overview": "总览",
|
||||
"Configure": "配置",
|
||||
"Help": "帮助",
|
||||
"dark": {
|
||||
"Dark": "暗",
|
||||
"Light": "明"
|
||||
}
|
||||
},
|
||||
"OverView": {
|
||||
"name": "通道名称",
|
||||
"type": "协议类型",
|
||||
"local_addr": "本地地址",
|
||||
"plugin": "插件",
|
||||
"remote_addr": "远程地址",
|
||||
"status": "状态",
|
||||
"err": "信息"
|
||||
},
|
||||
"Configure": {
|
||||
"Refresh": "读取配置",
|
||||
"Upload": "保存配置"
|
||||
}
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row id="head">
|
||||
<el-button type="primary" @click="fetchData">Refresh</el-button>
|
||||
<el-button type="primary" @click="uploadConfig">Upload</el-button>
|
||||
<el-button type="primary" @click="fetchData">{{ t("Configure.Refresh") }}</el-button>
|
||||
<el-button type="primary" @click="uploadConfig">{{ t("Configure.Upload") }}</el-button>
|
||||
</el-row>
|
||||
<el-input
|
||||
type="textarea"
|
||||
autosize
|
||||
v-model="textarea"
|
||||
placeholder="frpc configrue file, can not be empty..."
|
||||
></el-input>
|
||||
<el-input type="textarea" autosize v-model="textarea"
|
||||
placeholder="frpc configrue file, can not be empty..."></el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
let textarea = ref('')
|
||||
|
||||
|
@ -3,47 +3,14 @@
|
||||
<el-row>
|
||||
<el-col :md="24">
|
||||
<div>
|
||||
<el-table
|
||||
:data="status"
|
||||
stripe
|
||||
style="width: 100%"
|
||||
:default-sort="{ prop: 'type', order: 'ascending' }"
|
||||
>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="name"
|
||||
sortable
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="type"
|
||||
label="type"
|
||||
width="150"
|
||||
sortable
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="local_addr"
|
||||
label="local address"
|
||||
width="200"
|
||||
sortable
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="plugin"
|
||||
label="plugin"
|
||||
width="200"
|
||||
sortable
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="remote_addr"
|
||||
label="remote address"
|
||||
sortable
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
label="status"
|
||||
width="150"
|
||||
sortable
|
||||
></el-table-column>
|
||||
<el-table-column prop="err" label="info"></el-table-column>
|
||||
<el-table :data="status" stripe style="width: 100%" :default-sort="{ prop: 'type', order: 'ascending' }">
|
||||
<el-table-column prop="name" :label="t('OverView.name')" sortable></el-table-column>
|
||||
<el-table-column prop="type" :label="t('OverView.type')" width="150" sortable></el-table-column>
|
||||
<el-table-column prop="local_addr" :label="t('OverView.local_addr')" width="200" sortable></el-table-column>
|
||||
<el-table-column prop="plugin" :label="t('OverView.plugin')" width="200" sortable></el-table-column>
|
||||
<el-table-column prop="remote_addr" :label="t('OverView.remote_addr')" sortable></el-table-column>
|
||||
<el-table-column prop="status" :label="t('OverView.status')" width="150" sortable></el-table-column>
|
||||
<el-table-column prop="err" :label="t('OverView.err')"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
@ -54,6 +21,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
let status = ref<any[]>([])
|
||||
|
||||
|
@ -1,13 +1,24 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import 'element-plus/dist/index.css'
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import './assets/dark.css'
|
||||
import en from './assets/locales/en.json';
|
||||
import zh from './assets/locales/zh.json';
|
||||
const storedLocale = localStorage.getItem('i18n') || 'en';
|
||||
const i18n = createI18n({
|
||||
locale: storedLocale, // 默认语言
|
||||
messages: {
|
||||
en,
|
||||
zh,
|
||||
},
|
||||
});
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
app.use(i18n);
|
||||
|
||||
app.mount('#app')
|
||||
|
@ -205,6 +205,27 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
|
||||
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
|
||||
|
||||
"@intlify/core-base@10.0.5":
|
||||
version "10.0.5"
|
||||
resolved "https://registry.npmmirror.com/@intlify/core-base/-/core-base-10.0.5.tgz#c4d992381f8c3a50c79faf67be3404b399c3be28"
|
||||
integrity sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==
|
||||
dependencies:
|
||||
"@intlify/message-compiler" "10.0.5"
|
||||
"@intlify/shared" "10.0.5"
|
||||
|
||||
"@intlify/message-compiler@10.0.5":
|
||||
version "10.0.5"
|
||||
resolved "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-10.0.5.tgz#4eeace9f4560020d5e5d77f32bed7755e71d8efd"
|
||||
integrity sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==
|
||||
dependencies:
|
||||
"@intlify/shared" "10.0.5"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@intlify/shared@10.0.5":
|
||||
version "10.0.5"
|
||||
resolved "https://registry.npmmirror.com/@intlify/shared/-/shared-10.0.5.tgz#1b46ca8b541f03508fe28da8f34e4bb85506d6bc"
|
||||
integrity sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.15":
|
||||
version "1.4.15"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
|
||||
@ -2500,6 +2521,15 @@ vue-eslint-parser@^9.3.1, vue-eslint-parser@^9.4.2:
|
||||
lodash "^4.17.21"
|
||||
semver "^7.3.6"
|
||||
|
||||
vue-i18n@^10.0.5:
|
||||
version "10.0.5"
|
||||
resolved "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-10.0.5.tgz#fdf4e6c7b669e80cfa3a12ed9625e2b46671cdf0"
|
||||
integrity sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==
|
||||
dependencies:
|
||||
"@intlify/core-base" "10.0.5"
|
||||
"@intlify/shared" "10.0.5"
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue-router@^4.2.5:
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.5.tgz#b9e3e08f1bd9ea363fdd173032620bc50cf0e98a"
|
||||
|
Loading…
x
Reference in New Issue
Block a user