mirror of
https://github.com/EasyTier/EasyTier.git
synced 2025-10-05 16:56:57 +08:00
Perf/optimize details (#106)
* 🎈 perf: details * 🎈 perf: optimize Style
This commit is contained in:
@@ -13,6 +13,7 @@ enable_vpn_portal: 启用VPN门户
|
|||||||
vpn_portal_listen_port: 监听端口
|
vpn_portal_listen_port: 监听端口
|
||||||
vpn_portal_client_network: 客户端子网
|
vpn_portal_client_network: 客户端子网
|
||||||
advanced_settings: 高级设置
|
advanced_settings: 高级设置
|
||||||
|
basic_settings: 基础设置
|
||||||
listener_urls: 监听地址
|
listener_urls: 监听地址
|
||||||
rpc_port: RPC端口
|
rpc_port: RPC端口
|
||||||
config_network: 配置网络
|
config_network: 配置网络
|
||||||
@@ -43,7 +44,9 @@ peer_count: 已连接
|
|||||||
upload: 上传
|
upload: 上传
|
||||||
download: 下载
|
download: 下载
|
||||||
show_vpn_portal_config: 显示VPN门户配置
|
show_vpn_portal_config: 显示VPN门户配置
|
||||||
|
vpn_portal_config: VPN门户配置
|
||||||
show_event_log: 显示事件日志
|
show_event_log: 显示事件日志
|
||||||
|
event_log: 事件日志
|
||||||
peer_info: 节点信息
|
peer_info: 节点信息
|
||||||
hostname: 主机名
|
hostname: 主机名
|
||||||
route_cost: 路由
|
route_cost: 路由
|
||||||
@@ -54,3 +57,5 @@ loss_rate: 丢包率
|
|||||||
|
|
||||||
run_network: 运行网络
|
run_network: 运行网络
|
||||||
stop_network: 停止网络
|
stop_network: 停止网络
|
||||||
|
network_running: 运行中
|
||||||
|
network_stopped: 已停止
|
||||||
|
@@ -13,6 +13,7 @@ enable_vpn_portal: Enable VPN Portal
|
|||||||
vpn_portal_listen_port: VPN Portal Listen Port
|
vpn_portal_listen_port: VPN Portal Listen Port
|
||||||
vpn_portal_client_network: Client Sub Network
|
vpn_portal_client_network: Client Sub Network
|
||||||
advanced_settings: Advanced Settings
|
advanced_settings: Advanced Settings
|
||||||
|
basic_settings: Basic Settings
|
||||||
listener_urls: Listener URLs
|
listener_urls: Listener URLs
|
||||||
rpc_port: RPC Port
|
rpc_port: RPC Port
|
||||||
config_network: Config Network
|
config_network: Config Network
|
||||||
@@ -43,7 +44,9 @@ peer_count: Connected
|
|||||||
upload: Upload
|
upload: Upload
|
||||||
download: Download
|
download: Download
|
||||||
show_vpn_portal_config: Show VPN Portal Config
|
show_vpn_portal_config: Show VPN Portal Config
|
||||||
|
vpn_portal_config: VPN Portal Config
|
||||||
show_event_log: Show Event Log
|
show_event_log: Show Event Log
|
||||||
|
event_log: Event Log
|
||||||
peer_info: Peer Info
|
peer_info: Peer Info
|
||||||
route_cost: Route Cost
|
route_cost: Route Cost
|
||||||
hostname: Hostname
|
hostname: Hostname
|
||||||
@@ -54,3 +57,5 @@ loss_rate: Loss Rate
|
|||||||
|
|
||||||
run_network: Run Network
|
run_network: Run Network
|
||||||
stop_network: Stop Network
|
stop_network: Stop Network
|
||||||
|
network_running: running
|
||||||
|
network_stopped: stopped
|
||||||
|
@@ -56,8 +56,8 @@ onMounted(async () => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-column h-full">
|
<div class="flex flex-column h-full">
|
||||||
<div class="flex flex-column">
|
<div class="flex flex-column">
|
||||||
<div class="w-10/12 max-w-fit self-center ">
|
<div class="w-7/12 self-center ">
|
||||||
<Panel header="Basic Settings">
|
<Panel :header="$t('basic_settings')">
|
||||||
<div class="flex flex-column gap-y-2">
|
<div class="flex flex-column gap-y-2">
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||||
@@ -107,6 +107,22 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Panel :header="$t('advanced_settings')" toggleable>
|
||||||
|
<div class="flex flex-column gap-y-2">
|
||||||
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
|
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||||
|
<label for="hostname">{{ $t('hostname') }}</label>
|
||||||
|
<InputText
|
||||||
|
id="hostname" v-model="curNetwork.hostname" aria-describedby="hostname-help" :format="true"
|
||||||
|
:placeholder="$t('hostname_placeholder', [osHostname])" @blur="validateHostname"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||||
<div class="flex flex-column gap-2 grow p-fluid">
|
<div class="flex flex-column gap-2 grow p-fluid">
|
||||||
@@ -144,14 +160,8 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Panel>
|
|
||||||
|
|
||||||
<Divider />
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
|
|
||||||
<Panel :header="$t('advanced_settings')" toggleable>
|
|
||||||
<div class="flex flex-column gap-y-2">
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
|
||||||
<div class="flex flex-column gap-2 grow p-fluid">
|
<div class="flex flex-column gap-2 grow p-fluid">
|
||||||
<label for="listener_urls">{{ $t('listener_urls') }}</label>
|
<label for="listener_urls">{{ $t('listener_urls') }}</label>
|
||||||
<Chips
|
<Chips
|
||||||
@@ -170,20 +180,9 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
|
||||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
|
||||||
<label for="hostname">{{ $t('hostname') }}</label>
|
|
||||||
<InputText
|
|
||||||
id="hostname" v-model="curNetwork.hostname" aria-describedby="hostname-help" :format="true"
|
|
||||||
:placeholder="$t('hostname_placeholder', [osHostname])" @blur="validateHostname"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<div class="flex pt-4 justify-content-center">
|
<div class="flex pt-4 justify-content-center">
|
||||||
<Button
|
<Button
|
||||||
:label="$t('run_network')" icon="pi pi-arrow-right" icon-pos="right" :disabled="configInvalid"
|
:label="$t('run_network')" icon="pi pi-arrow-right" icon-pos="right" :disabled="configInvalid"
|
||||||
|
@@ -42,7 +42,7 @@ function resolveObjPath(path: string, obj = globalThis, separator = '.') {
|
|||||||
return properties.reduce((prev, curr) => prev?.[curr], obj)
|
return properties.reduce((prev, curr) => prev?.[curr], obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
function statsCommon(info: any, field: string) {
|
function statsCommon(info: any, field: string): number | undefined {
|
||||||
if (!info.peer)
|
if (!info.peer)
|
||||||
return undefined
|
return undefined
|
||||||
|
|
||||||
@@ -73,8 +73,11 @@ function humanFileSize(bytes: number, si = false, dp = 1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function latencyMs(info: any) {
|
function latencyMs(info: any) {
|
||||||
const lat_us_sum = statsCommon(info, 'stats.latency_us')
|
let lat_us_sum = statsCommon(info, 'stats.latency_us')
|
||||||
return lat_us_sum ? `${lat_us_sum / 1000 / info.peer.conns.length}ms` : ''
|
if (lat_us_sum === undefined)
|
||||||
|
return ''
|
||||||
|
lat_us_sum = lat_us_sum / 1000 / info.peer.conns.length
|
||||||
|
return `${lat_us_sum % 1 > 0 ? Math.round(lat_us_sum) + 1 : Math.round(lat_us_sum)}ms`
|
||||||
}
|
}
|
||||||
|
|
||||||
function txBytes(info: any) {
|
function txBytes(info: any) {
|
||||||
@@ -236,6 +239,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const dialogContent = ref<any>('')
|
const dialogContent = ref<any>('')
|
||||||
|
const dialogHeader = ref('event_log')
|
||||||
|
|
||||||
function showVpnPortalConfig() {
|
function showVpnPortalConfig() {
|
||||||
const my_node_info = myNodeInfo.value
|
const my_node_info = myNodeInfo.value
|
||||||
@@ -244,6 +248,7 @@ function showVpnPortalConfig() {
|
|||||||
|
|
||||||
const url = 'https://www.wireguardconfig.com/qrcode'
|
const url = 'https://www.wireguardconfig.com/qrcode'
|
||||||
dialogContent.value = `${my_node_info.vpn_portal_cfg}\n\n # can generate QR code: ${url}`
|
dialogContent.value = `${my_node_info.vpn_portal_cfg}\n\n # can generate QR code: ${url}`
|
||||||
|
dialogHeader.value = 'vpn_portal_config'
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,13 +258,14 @@ function showEventLogs() {
|
|||||||
return
|
return
|
||||||
|
|
||||||
dialogContent.value = detail.events
|
dialogContent.value = detail.events
|
||||||
|
dialogHeader.value = 'event_log'
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Dialog v-model:visible="dialogVisible" modal header="Dialog" :style="{ width: '70%' }">
|
<Dialog v-model:visible="dialogVisible" modal :header="$t(dialogHeader)" :style="{ width: '70%' }">
|
||||||
<Panel>
|
<Panel>
|
||||||
<ScrollPanel style="width: 100%; height: 400px">
|
<ScrollPanel style="width: 100%; height: 400px">
|
||||||
<pre>{{ dialogContent }}</pre>
|
<pre>{{ dialogContent }}</pre>
|
||||||
@@ -267,7 +273,7 @@ function showEventLogs() {
|
|||||||
</Panel>
|
</Panel>
|
||||||
<Divider />
|
<Divider />
|
||||||
<div class="flex justify-content-end gap-2">
|
<div class="flex justify-content-end gap-2">
|
||||||
<Button type="button" label="Close" @click="dialogVisible = false" />
|
<Button type="button" :label="$t('close')" @click="dialogVisible = false" />
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
@@ -292,7 +298,7 @@ function showEventLogs() {
|
|||||||
<div class="flex w-full flex-column gap-y-5">
|
<div class="flex w-full flex-column gap-y-5">
|
||||||
<div class="m-0 flex flex-row justify-center gap-x-5">
|
<div class="m-0 flex flex-row justify-center gap-x-5">
|
||||||
<div
|
<div
|
||||||
class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
|
||||||
style="border: 1px solid green"
|
style="border: 1px solid green"
|
||||||
>
|
>
|
||||||
<div class="font-bold">
|
<div class="font-bold">
|
||||||
@@ -304,7 +310,7 @@ function showEventLogs() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
|
||||||
style="border: 1px solid purple"
|
style="border: 1px solid purple"
|
||||||
>
|
>
|
||||||
<div class="font-bold">
|
<div class="font-bold">
|
||||||
@@ -316,7 +322,7 @@ function showEventLogs() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
class="rounded-full w-32 h-32 flex flex-column align-items-center pt-4"
|
||||||
style="border: 1px solid fuchsia"
|
style="border: 1px solid fuchsia"
|
||||||
>
|
>
|
||||||
<div class="font-bold">
|
<div class="font-bold">
|
||||||
@@ -353,11 +359,11 @@ function showEventLogs() {
|
|||||||
<DataTable :value="peerRouteInfos" column-resize-mode="fit" table-style="width: 100%">
|
<DataTable :value="peerRouteInfos" column-resize-mode="fit" table-style="width: 100%">
|
||||||
<Column field="route.ipv4_addr" style="width: 100px;" :header="$t('virtual_ipv4')" />
|
<Column field="route.ipv4_addr" style="width: 100px;" :header="$t('virtual_ipv4')" />
|
||||||
<Column field="route.hostname" style="max-width: 250px;" :header="$t('hostname')" />
|
<Column field="route.hostname" style="max-width: 250px;" :header="$t('hostname')" />
|
||||||
<Column :field="routeCost" style="width: 60px;" :header="$t('route_cost')" />
|
<Column :field="routeCost" style="width: 100px;" :header="$t('route_cost')" />
|
||||||
<Column :field="latencyMs" style="width: 80px;" :header="$t('latency')" />
|
<Column :field="latencyMs" style="width: 80px;" :header="$t('latency')" />
|
||||||
<Column :field="txBytes" style="width: 80px;" :header="$t('upload_bytes')" />
|
<Column :field="txBytes" style="width: 80px;" :header="$t('upload_bytes')" />
|
||||||
<Column :field="rxBytes" style="width: 80px;" :header="$t('download_bytes')" />
|
<Column :field="rxBytes" style="width: 80px;" :header="$t('download_bytes')" />
|
||||||
<Column :field="lossRate" style="width: 60px;" :header="$t('loss_rate')" />
|
<Column :field="lossRate" style="width: 100px;" :header="$t('loss_rate')" />
|
||||||
</DataTable>
|
</DataTable>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
|
@@ -62,10 +62,6 @@ function addNewNetwork() {
|
|||||||
networkStore.curNetwork = networkStore.lastNetwork
|
networkStore.curNetwork = networkStore.lastNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
function networkMenuName(network: NetworkConfig) {
|
|
||||||
return `${network.network_name} (${network.instance_id})`
|
|
||||||
}
|
|
||||||
|
|
||||||
networkStore.$subscribe(async () => {
|
networkStore.$subscribe(async () => {
|
||||||
networkStore.saveToLocalStorage()
|
networkStore.saveToLocalStorage()
|
||||||
try {
|
try {
|
||||||
@@ -150,6 +146,10 @@ function toggle_setting_menu(event: any) {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
networkStore.loadFromLocalStorage()
|
networkStore.loadFromLocalStorage()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function isRunning(id: string) {
|
||||||
|
return networkStore.networkInstanceIds.includes(id)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -183,10 +183,38 @@ onMounted(async () => {
|
|||||||
<template #center>
|
<template #center>
|
||||||
<div class="min-w-80 mr-20">
|
<div class="min-w-80 mr-20">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-model="networkStore.curNetwork" :options="networkStore.networkList"
|
v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
||||||
:option-label="networkMenuName" :placeholder="$t('select_network')" :highlight-on-select="true"
|
:placeholder="$t('select_network')" class="w-full"
|
||||||
:checkmark="true" class="w-full md:w-32rem"
|
>
|
||||||
/>
|
<template #value="slotProps">
|
||||||
|
<div class="flex items-start content-center">
|
||||||
|
<div class="mr-3">
|
||||||
|
<span>{{ slotProps.value.network_name }}</span>
|
||||||
|
<span v-if="isRunning(slotProps.value.instance_id)" class="ml-3">
|
||||||
|
{{ slotProps.value.virtual_ipv4 }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Tag
|
||||||
|
class="my-auto" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
||||||
|
:value="$t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #option="slotProps">
|
||||||
|
<div class="flex flex-col items-start content-center">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="mr-3">
|
||||||
|
{{ $t('network_name') }}: {{ slotProps.option.network_name }}
|
||||||
|
</div>
|
||||||
|
<Tag
|
||||||
|
class="my-auto" :severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'"
|
||||||
|
:value="$t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>{{ slotProps.option.public_server_url }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -201,7 +229,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Stepper class="h-full overflow-y-auto" :active-step="activeStep">
|
<Stepper class="h-full overflow-y-auto" :active-step="activeStep">
|
||||||
<StepperPanel :header="$t('config_network')" class="w">
|
<StepperPanel :header="$t('config_network')">
|
||||||
<template #content="{ nextCallback }">
|
<template #content="{ nextCallback }">
|
||||||
<Config
|
<Config
|
||||||
:instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
:instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||||
@@ -233,11 +261,15 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="postcss">
|
||||||
#root {
|
#root {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p-dropdown :deep(.p-dropdown-panel .p-dropdown-items .p-dropdown-item) {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@@ -75,20 +75,15 @@ export const useNetworkStore = defineStore('networkStore', {
|
|||||||
loadFromLocalStorage() {
|
loadFromLocalStorage() {
|
||||||
let networkList: NetworkConfig[]
|
let networkList: NetworkConfig[]
|
||||||
|
|
||||||
try {
|
// if localStorage default is [{}], instanceId will be undefined
|
||||||
networkList = JSON.parse(localStorage.getItem('networkList') || '[{}]')
|
networkList = JSON.parse(localStorage.getItem('networkList') || '[]')
|
||||||
networkList = networkList.map((cfg) => {
|
networkList = networkList.map((cfg) => {
|
||||||
return { ...DEFAULT_NETWORK_CONFIG(), ...cfg } as NetworkConfig
|
return { ...DEFAULT_NETWORK_CONFIG(), ...cfg } as NetworkConfig
|
||||||
})
|
})
|
||||||
|
|
||||||
// prevent a empty list from localStorage, should not happen
|
// prevent a empty list from localStorage, should not happen
|
||||||
if (networkList.length === 0) {
|
if (networkList.length === 0)
|
||||||
networkList = [DEFAULT_NETWORK_CONFIG()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
networkList = [DEFAULT_NETWORK_CONFIG()]
|
networkList = [DEFAULT_NETWORK_CONFIG()]
|
||||||
}
|
|
||||||
|
|
||||||
this.networkList = networkList
|
this.networkList = networkList
|
||||||
this.curNetwork = this.networkList[0]
|
this.curNetwork = this.networkList[0]
|
||||||
|
Reference in New Issue
Block a user