mirror of
https://github.com/fatedier/frp.git
synced 2025-04-23 17:11:26 +00:00
Merge 3208c975bb9f7d55e96c326e3f910aafa26b61c2 into 01fed8d1a97dce7d19d520877949f6e19054ee54
This commit is contained in:
commit
657a915b5c
60
assets/frpc/static/index-Cgx39LXC.js
Normal file
60
assets/frpc/static/index-Cgx39LXC.js
Normal file
File diff suppressed because one or more lines are too long
1
assets/frpc/static/index-N4E3zN8T.css
Normal file
1
assets/frpc/static/index-N4E3zN8T.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4,8 +4,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>frp client admin UI</title>
|
||||
<script type="module" crossorigin src="./index-bLBhaJo8.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./index-iuf46MlF.css">
|
||||
<script type="module" crossorigin src="./index-Cgx39LXC.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./index-N4E3zN8T.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
1
assets/frps/static/index-5A9aPAsI.css
Normal file
1
assets/frps/static/index-5A9aPAsI.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
102
assets/frps/static/index-yOnBcT7d.js
Normal file
102
assets/frps/static/index-yOnBcT7d.js
Normal file
File diff suppressed because one or more lines are too long
@ -4,8 +4,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>frps dashboard</title>
|
||||
<script type="module" crossorigin src="./index-82-40HIG.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./index-rzPDshRD.css">
|
||||
<script type="module" crossorigin src="./index-yOnBcT7d.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./index-5A9aPAsI.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
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"
|
||||
|
3
web/frps/components.d.ts
vendored
3
web/frps/components.d.ts
vendored
@ -11,6 +11,9 @@ declare module 'vue' {
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
|
@ -16,6 +16,7 @@
|
||||
"element-plus": "^2.5.3",
|
||||
"humanize-plus": "^1.8.2",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^10.0.5",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -3,37 +3,40 @@
|
||||
<header class="grid-content header-color">
|
||||
<div class="header-content">
|
||||
<div class="brand">
|
||||
<a href="#">frp</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="/"
|
||||
mode="vertical"
|
||||
theme="light"
|
||||
router="false"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item index="/">Overview</el-menu-item>
|
||||
<el-menu default-active="/" mode="vertical" theme="light" router="false" @select="handleSelect">
|
||||
<el-menu-item index="/">{{ t("main.Overview") }}</el-menu-item>
|
||||
<el-sub-menu index="/proxies">
|
||||
<template #title>
|
||||
<span>Proxies</span>
|
||||
<span>{{ t("main.Proxies.title") }}</span>
|
||||
</template>
|
||||
<el-menu-item index="/proxies/tcp">TCP</el-menu-item>
|
||||
<el-menu-item index="/proxies/udp">UDP</el-menu-item>
|
||||
@ -43,7 +46,7 @@
|
||||
<el-menu-item index="/proxies/stcp">STCP</el-menu-item>
|
||||
<el-menu-item index="/proxies/sudp">SUDP</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-menu-item index="">Help</el-menu-item>
|
||||
<el-menu-item index="">{{ t("main.Help") }}</el-menu-item>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
|
||||
@ -61,11 +64,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')
|
||||
@ -118,10 +126,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/frps/src/assets/lang.svg
Normal file
1
web/frps/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 |
79
web/frps/src/assets/locales/en.json
Normal file
79
web/frps/src/assets/locales/en.json
Normal file
@ -0,0 +1,79 @@
|
||||
{
|
||||
"main": {
|
||||
"title": "frp",
|
||||
"Overview": "Overview",
|
||||
"Proxies": {
|
||||
"title": "Proxies"
|
||||
},
|
||||
"Help": "Help",
|
||||
"dark": {
|
||||
"Dark": "Dark",
|
||||
"Light": "Light"
|
||||
}
|
||||
},
|
||||
"OverView": {
|
||||
"Version": "Version",
|
||||
"BindPort": "BindPort",
|
||||
"KPC_Port": "KCP Bind Port",
|
||||
"QUIC_Port": "QUIC Bind Port",
|
||||
"HTTP_Port": "HTTP Port",
|
||||
"HTTPS_Port": "HTTPS Port",
|
||||
"TCPMux": "TCPMux HTTPConnect Port",
|
||||
"Subdomain": "Subdomain Host",
|
||||
"MaxPoolCount": "Max PoolCount",
|
||||
"MaxPortsPerClient": "Max Ports Per Client",
|
||||
"AllowPorts": "Allow Ports",
|
||||
"TLSForce": "TLS Force",
|
||||
"HeartBeatTimeout": "HeartBeat Timeout",
|
||||
"ClientCounts": "Client Counts",
|
||||
"curConns": "Current Connections",
|
||||
"ProxyCounts": "Proxy Counts",
|
||||
"Chart": {
|
||||
"Traffic": {
|
||||
"title": "Network Traffic",
|
||||
"subTitle": "today",
|
||||
"TrafficIn": "Traffic In",
|
||||
"TrafficOut": "Traffic Out"
|
||||
},
|
||||
"Proxies": {
|
||||
"title": "Proxies",
|
||||
"subTitle": "now"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProxiesView": {
|
||||
"Expand": {
|
||||
"Name": "Name",
|
||||
"Type": "Type",
|
||||
"Encryption": "Encryption",
|
||||
"Compression": "Compression",
|
||||
"LastStart": "Last Start",
|
||||
"LastClose": "Last Close",
|
||||
"Domains": "Domains",
|
||||
"SubDomain": "SubDomain",
|
||||
"locations": "locations",
|
||||
"HostRewrite": "HostRewrite",
|
||||
"Multiplexer": "Multiplexer",
|
||||
"RouteByHTTPUser": "RouteByHTTPUser",
|
||||
"Addr": "Addr",
|
||||
"Annotations": "Annotations"
|
||||
},
|
||||
"ClearOffLine": "ClearOfflineProxies",
|
||||
"Refresh": "Refresh",
|
||||
"ClearOffLineInfo": "Are you sure to clear all data of offline proxies?",
|
||||
"name": "Name",
|
||||
"Port": "Port",
|
||||
"Connections": "Connections",
|
||||
"Traffic_In": "Traffic In",
|
||||
"Traffic_Out": "Traffic Out",
|
||||
"ClientVersion": "ClientVersion",
|
||||
"Status": {
|
||||
"title": "Status",
|
||||
"Successinfo": "online"
|
||||
},
|
||||
"Operations": {
|
||||
"title": "Operations",
|
||||
"Traffic": "Traffic"
|
||||
}
|
||||
}
|
||||
}
|
88
web/frps/src/assets/locales/zh.json
Normal file
88
web/frps/src/assets/locales/zh.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"main": {
|
||||
"title": "frp 服务端",
|
||||
"Overview": "总览",
|
||||
"Proxies": {
|
||||
"title": "代理"
|
||||
},
|
||||
"Help": "帮助",
|
||||
"dark": {
|
||||
"Dark": "暗",
|
||||
"Light": "明"
|
||||
}
|
||||
},
|
||||
"OverView": {
|
||||
"Expand": {
|
||||
"Name": "通道名称",
|
||||
"Type": "协议类型",
|
||||
"Encryption": "加密",
|
||||
"Compression": "压缩",
|
||||
"LastStart": "最后一次启用时间",
|
||||
"LastClose": "最后一次关闭时间",
|
||||
"Domains": "域名",
|
||||
"SubDomain": "二级域名后缀",
|
||||
"locations": "URL 路由配置",
|
||||
"HostRewrite": "替换 Host Header",
|
||||
"Multiplexer": "复用器类型",
|
||||
"RouteByHTTPUser": "根据 HTTP Basic Auth user 路由",
|
||||
"Addr": "代理端口",
|
||||
"Annotations": "注释"
|
||||
},
|
||||
"Version": "版本",
|
||||
"BindPort": "frp 绑定端口",
|
||||
"KPC_Port": "KCP 绑定端口",
|
||||
"QUIC_Port": "QUIC 绑定端口",
|
||||
"HTTP_Port": "HTTP 代理监听端口",
|
||||
"HTTPS_Port": "HTTPS 代理监听端口",
|
||||
"TCPMux": "TCPMux HTTPConnect 代理监听的端口",
|
||||
"Subdomain": "二级域名后缀",
|
||||
"MaxPoolCount": "最大连接池数量",
|
||||
"MaxPortsPerClient": "单客户端最大代理数",
|
||||
"AllowPorts": "允许代理的服务端端口",
|
||||
"TLSForce": "TLS协议版本",
|
||||
"HeartBeatTimeout": "心跳超时时间",
|
||||
"ClientCounts": "客户端数量",
|
||||
"curConns": "当前连接数",
|
||||
"ProxyCounts": "代理数量",
|
||||
"Chart": {
|
||||
"Traffic": {
|
||||
"title": "网络流量",
|
||||
"subTitle": "今日",
|
||||
"TrafficIn": "进站流量",
|
||||
"TrafficOut": "出站流量"
|
||||
},
|
||||
"Proxies": {
|
||||
"title": "代理占比",
|
||||
"subTitle": "当前"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProxiesView": {
|
||||
"ClearOffLine": "清理离线链接",
|
||||
"Refresh": "刷新",
|
||||
"ClearOffLineInfo": "确定清理所有离线数据?",
|
||||
"name": "代理名称",
|
||||
"Port": "代理端口",
|
||||
"Connections": "连接数",
|
||||
"Traffic_In": "入站流量",
|
||||
"Traffic_Out": "出站流量",
|
||||
"ClientVersion": "frp客户端版本",
|
||||
"Status": {
|
||||
"title": "状态",
|
||||
"Successinfo": "在线"
|
||||
},
|
||||
"Operations": {
|
||||
"title": "操作",
|
||||
"Traffic": "流量"
|
||||
}
|
||||
},
|
||||
"ProxiesViews": {
|
||||
"name": "通道名称",
|
||||
"type": "协议类型",
|
||||
"local_addr": "本地地址",
|
||||
"plugin": "插件",
|
||||
"remote_addr": "远程地址",
|
||||
"status": "状态",
|
||||
"err": "信息"
|
||||
}
|
||||
}
|
@ -1,85 +1,57 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-page-header
|
||||
:icon="null"
|
||||
style="width: 100%; margin-left: 30px; margin-bottom: 20px"
|
||||
>
|
||||
<el-page-header :icon="null" style="width: 100%; margin-left: 30px; margin-bottom: 20px">
|
||||
<template #title>
|
||||
<span>{{ proxyType }}</span>
|
||||
</template>
|
||||
<template #content> </template>
|
||||
<template #extra>
|
||||
<div class="flex items-center" style="margin-right: 30px">
|
||||
<el-popconfirm
|
||||
title="Are you sure to clear all data of offline proxies?"
|
||||
@confirm="clearOfflineProxies"
|
||||
>
|
||||
<el-popconfirm :title="t('ProxiesView.ClearOffLineInfo')" @confirm="clearOfflineProxies">
|
||||
<template #reference>
|
||||
<el-button>ClearOfflineProxies</el-button>
|
||||
<el-button>{{ t("ProxiesView.ClearOffLine") }}</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-button @click="$emit('refresh')">Refresh</el-button>
|
||||
<el-button @click="$emit('refresh')">{{ t("ProxiesView.Refresh") }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-page-header>
|
||||
|
||||
<el-table
|
||||
:data="proxies"
|
||||
:default-sort="{ prop: 'name', order: 'ascending' }"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
|
||||
<el-table-column type="expand">
|
||||
<template #default="props">
|
||||
<ProxyViewExpand :row="props.row" :proxyType="proxyType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Name" prop="name" sortable> </el-table-column>
|
||||
<el-table-column label="Port" prop="port" sortable> </el-table-column>
|
||||
<el-table-column label="Connections" prop="conns" sortable>
|
||||
<el-table-column :label="t('ProxiesView.name')" prop="name" sortable> </el-table-column>
|
||||
<el-table-column :label="t('ProxiesView.Port')" prop="port" sortable> </el-table-column>
|
||||
<el-table-column :label="t('ProxiesView.Connections')" prop="conns" sortable>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="Traffic In"
|
||||
prop="trafficIn"
|
||||
:formatter="formatTrafficIn"
|
||||
sortable
|
||||
>
|
||||
<el-table-column :label="t('ProxiesView.Traffic_In')" prop="trafficIn" :formatter="formatTrafficIn" sortable>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="Traffic Out"
|
||||
prop="trafficOut"
|
||||
:formatter="formatTrafficOut"
|
||||
sortable
|
||||
>
|
||||
<el-table-column :label="t('ProxiesView.Traffic_Out')" prop="trafficOut" :formatter="formatTrafficOut" sortable>
|
||||
</el-table-column>
|
||||
<el-table-column label="ClientVersion" prop="clientVersion" sortable>
|
||||
<el-table-column :label="t('ProxiesView.ClientVersion')" prop="clientVersion" sortable>
|
||||
</el-table-column>
|
||||
<el-table-column label="Status" prop="status" sortable>
|
||||
<el-table-column :label="t('ProxiesView.Status.title')" prop="status" sortable>
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status === 'online'" type="success">{{
|
||||
scope.row.status
|
||||
t("ProxiesView.Status.Successinfo")
|
||||
}}</el-tag>
|
||||
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Operations">
|
||||
<el-table-column :label="t('ProxiesView.Operations.title')">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
:name="scope.row.name"
|
||||
style="margin-bottom: 10px"
|
||||
@click="dialogVisibleName = scope.row.name; dialogVisible = true"
|
||||
>Traffic
|
||||
<el-button type="primary" :name="scope.row.name" style="margin-bottom: 10px"
|
||||
@click="dialogVisibleName = scope.row.name; dialogVisible = true">{{ t("ProxiesView.Operations.Traffic") }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
destroy-on-close="true"
|
||||
:title="dialogVisibleName"
|
||||
width="700px">
|
||||
<el-dialog v-model="dialogVisible" destroy-on-close="true" :title="dialogVisibleName" width="700px">
|
||||
<Traffic :proxyName="dialogVisibleName" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
@ -91,6 +63,8 @@ import type { BaseProxy } from '../utils/proxy.js'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import ProxyViewExpand from './ProxyViewExpand.vue'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
defineProps<{
|
||||
proxies: BaseProxy[]
|
||||
|
@ -1,77 +1,74 @@
|
||||
<template>
|
||||
<el-form
|
||||
label-position="left"
|
||||
label-width="auto"
|
||||
inline
|
||||
class="proxy-table-expand"
|
||||
>
|
||||
<el-form-item label="Name">
|
||||
<el-form label-position="left" label-width="auto" inline class="proxy-table-expand">
|
||||
<el-form-item :label="t('OverView.Expand.Name')">
|
||||
<span>{{ row.name }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Type">
|
||||
<el-form-item :label="t('OverView.Expand.Type')">
|
||||
<span>{{ row.type }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Encryption">
|
||||
<el-form-item :label="t('OverView.Expand.Encryption')">
|
||||
<span>{{ row.encryption }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Compression">
|
||||
<el-form-item :label="t('OverView.Expand.Compression')">
|
||||
<span>{{ row.compression }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Start">
|
||||
<el-form-item :label="t('OverView.Expand.LastStart')">
|
||||
<span>{{ row.lastStartTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<el-form-item :label="t('OverView.Expand.LastClose')">
|
||||
<span>{{ row.lastCloseTime }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="proxyType === 'http' || proxyType === 'https'">
|
||||
<el-form-item label="Domains">
|
||||
<el-form-item :label="t('OverView.Expand.Domains')">
|
||||
<span>{{ row.customDomains }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="SubDomain">
|
||||
<el-form-item :label="t('OverView.Expand.SubDomain')">
|
||||
<span>{{ row.subdomain }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="locations">
|
||||
<el-form-item :label="t('OverView.Expand.locations')">
|
||||
<span>{{ row.locations }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="HostRewrite">
|
||||
<el-form-item :label="t('OverView.Expand.HostRewrite')">
|
||||
<span>{{ row.hostHeaderRewrite }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else-if="proxyType === 'tcpmux'">
|
||||
<el-form-item label="Multiplexer">
|
||||
<el-form-item :label="t('OverView.Expand.Multiplexer')">
|
||||
<span>{{ row.multiplexer }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="RouteByHTTPUser">
|
||||
<el-form-item :label="t('OverView.Expand.RouteByHTTPUser')">
|
||||
<span>{{ row.routeByHTTPUser }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Domains">
|
||||
<el-form-item :label="t('OverView.Expand.Domains')">
|
||||
<span>{{ row.customDomains }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="SubDomain">
|
||||
<el-form-item :label="t('OverView.Expand.SubDomain')">
|
||||
<span>{{ row.subdomain }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-form-item label="Addr">
|
||||
<el-form-item :label="t('OverView.Expand.Addr')">
|
||||
<span>{{ row.addr }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div v-if="row.annotations && row.annotations.size > 0">
|
||||
<el-divider />
|
||||
<el-text class="title-text" size="large">Annotations</el-text>
|
||||
<ul>
|
||||
<li v-for="item in annotationsArray()">
|
||||
<span class="annotation-key">{{ item.key }}</span>
|
||||
<span>{{ item.value }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<el-divider />
|
||||
<el-text class="title-text" size="large">{{ t("OverView.Expand.Annotations") }}</el-text>
|
||||
<ul>
|
||||
<li v-for="item in annotationsArray()">
|
||||
<span class="annotation-key">{{ item.key }}</span>
|
||||
<span>{{ item.value }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
row: any
|
||||
|
@ -3,73 +3,60 @@
|
||||
<el-row>
|
||||
<el-col :md="12">
|
||||
<div class="source">
|
||||
<el-form
|
||||
label-position="left"
|
||||
label-width="220px"
|
||||
class="server_info"
|
||||
>
|
||||
<el-form-item label="Version">
|
||||
<el-form label-position="left" label-width="220px" class="server_info">
|
||||
<el-form-item :label="t('OverView.Version')">
|
||||
<span>{{ data.version }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="BindPort">
|
||||
<el-form-item :label="t('OverView.BindPort')">
|
||||
<span>{{ data.bindPort }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="KCP Bind Port" v-if="data.kcpBindPort != 0">
|
||||
<el-form-item :label="t('OverView.KPC_Port')" v-if="data.kcpBindPort != 0">
|
||||
<span>{{ data.kcpBindPort }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="QUIC Bind Port" v-if="data.quicBindPort != 0">
|
||||
<el-form-item :label="t('OverView.QUIC_Port')" v-if="data.quicBindPort != 0">
|
||||
<span>{{ data.quicBindPort }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="HTTP Port" v-if="data.vhostHTTPPort != 0">
|
||||
<el-form-item :label="t('OverView.HTTP_Port')" v-if="data.vhostHTTPPort != 0">
|
||||
<span>{{ data.vhostHTTPPort }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="HTTPS Port" v-if="data.vhostHTTPSPort != 0">
|
||||
<el-form-item :label="t('OverView.HTTPS_Port')" v-if="data.vhostHTTPSPort != 0">
|
||||
<span>{{ data.vhostHTTPSPort }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="TCPMux HTTPConnect Port"
|
||||
v-if="data.tcpmuxHTTPConnectPort != 0"
|
||||
>
|
||||
<el-form-item :label="t('OverView.TCPMux')" v-if="data.tcpmuxHTTPConnectPort != 0">
|
||||
<span>{{ data.tcpmuxHTTPConnectPort }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="Subdomain Host"
|
||||
v-if="data.subdomainHost != ''"
|
||||
>
|
||||
<el-form-item :label="t('OverView.Subdomain')" v-if="data.subdomainHost != ''">
|
||||
<LongSpan :content="data.subdomainHost" :length="30"></LongSpan>
|
||||
</el-form-item>
|
||||
<el-form-item label="Max PoolCount">
|
||||
<el-form-item :label="t('OverView.MaxPoolCount')">
|
||||
<span>{{ data.maxPoolCount }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Max Ports Per Client">
|
||||
<el-form-item :label="t('OverView.MaxPortsPerClient')">
|
||||
<span>{{ data.maxPortsPerClient }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Allow Ports" v-if="data.allowPortsStr != ''">
|
||||
<el-form-item :label="t('OverView.AllowPorts')" v-if="data.allowPortsStr != ''">
|
||||
<LongSpan :content="data.allowPortsStr" :length="30"></LongSpan>
|
||||
</el-form-item>
|
||||
<el-form-item label="TLS Force" v-if="data.tlsForce === true">
|
||||
<el-form-item :label="t('OverView.TLSForce')" v-if="data.tlsForce === true">
|
||||
<span>{{ data.tlsForce }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="HeartBeat Timeout">
|
||||
<el-form-item :label="t('OverView.HeartBeatTimeout')">
|
||||
<span>{{ data.heartbeatTimeout }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Client Counts">
|
||||
<el-form-item :label="t('OverView.ClientCounts')">
|
||||
<span>{{ data.clientCounts }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Current Connections">
|
||||
<el-form-item :label="t('OverView.curConns')">
|
||||
<span>{{ data.curConns }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Proxy Counts">
|
||||
<el-form-item :label="t('OverView.ProxyCounts')">
|
||||
<span>{{ data.proxyCounts }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :md="12">
|
||||
<div
|
||||
id="traffic"
|
||||
style="width: 400px; height: 250px; margin-bottom: 30px"
|
||||
></div>
|
||||
<div id="traffic" style="width: 400px; height: 250px; margin-bottom: 30px"></div>
|
||||
<div id="proxies" style="width: 400px; height: 250px"></div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -81,6 +68,8 @@ import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DrawTrafficChart, DrawProxyChart } from '../utils/chart'
|
||||
import LongSpan from './LongSpan.vue'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
let data = ref({
|
||||
version: '',
|
||||
|
@ -1,4 +1,5 @@
|
||||
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'
|
||||
@ -6,9 +7,19 @@ import router from './router'
|
||||
|
||||
import './assets/custom.css'
|
||||
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"
|
||||
@ -2523,6 +2544,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