Code refactoring (#2785)

* chore: pretty theme menu in sidebar

* refactor: renaming component templates

* refactor: create custom `a-statistic` component

* fix: display button text only on large screens

* chore: remove loading background in overview page

* fix: show `Version` text when xray version is unknown
This commit is contained in:
Shishkevich D. 2025-03-17 18:26:07 +07:00 committed by GitHub
parent e3120c4028
commit db62a07fb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 155 additions and 79 deletions

View file

@ -473,8 +473,8 @@
</transition>
</a-layout>
{{template "js" .}}
{{template "component/themeSwitcher" .}}
{{template "component/password" .}}
{{template "component/aThemeSwitch" .}}
{{template "component/aPasswordInput" .}}
<script>
class User {
constructor() {

View file

@ -1,33 +1,23 @@
{{define "menuItems"}}
<a-menu-item key="{{ .base_path }}panel/">
<a-icon type="dashboard"></a-icon>
<span>
<b>{{ i18n "menu.dashboard"}}</b>
</span>
<span>{{ i18n "menu.dashboard"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}panel/inbounds">
<a-icon type="user"></a-icon>
<span>
<b>{{ i18n "menu.inbounds"}}</b>
</span>
<span>{{ i18n "menu.inbounds"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}panel/settings">
<a-icon type="setting"></a-icon>
<span>
<b>{{ i18n "menu.settings"}}</b>
</span>
<span>{{ i18n "menu.settings"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}panel/xray">
<a-icon type="tool"></a-icon>
<span>
<b>{{ i18n "menu.xray"}}</b>
</span>
<span>{{ i18n "menu.xray"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}logout">
<a-icon type="logout"></a-icon>
<span>
<b>{{ i18n "menu.logout"}}</b>
</span>
<span>{{ i18n "menu.logout"}}</span>
</a-menu-item>
{{end}}

View file

@ -0,0 +1,55 @@
{{define "component/customStatistic"}}
<template>
<a-statistic :title="title" :value="value">
<template #prefix>
<slot name="prefix"></slot>
</template>
<template #suffix>
<slot name="suffix"></slot>
</template>
</a-statistic>
</template>
{{end}}
{{define "component/aCustomStatistic"}}
<style>
.dark .ant-statistic-title,
.dark .ant-statistic-content {
color: var(--dark-color-text-primary) !important
}
.dark .ant-statistic-title {
user-select: none;
opacity: 0.55;
}
.ant-statistic-title {
margin-bottom: 0 !important;
}
.ant-statistic-content-prefix {
margin-right: 6px !important;
}
.ant-statistic-content-prefix,
.ant-statistic-content-value {
font-size: 1.05rem;
}
</style>
<script>
Vue.component('a-custom-statistic', {
props: {
'title': {
type: String,
required: false,
},
'value': {
type: String,
required: false
}
},
template: `{{template "component/customStatistic"}}`,
});
</script>
{{end}}

View file

@ -12,7 +12,7 @@
</template>
{{end}}
{{define "component/password"}}
{{define "component/aPasswordInput"}}
<script>
Vue.component('a-password-input', {
props: {

View file

@ -12,7 +12,7 @@
</template>
{{end}}
{{define "component/persianDatepicker"}}
{{define "component/aPersianDatepicker"}}
<link rel="stylesheet" href="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.css?{{ .cur_ver }}" />
<script src="{{ .base_path }}assets/moment/moment-jalali.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.js?{{ .cur_ver }}"></script>

View file

@ -18,18 +18,10 @@
</a-list-item>
{{end}}
{{define "component/setting"}}
{{define "component/aSettingListItem"}}
<script>
Vue.component('a-setting-list-item', {
props: {
'title': {
type: String,
required: true,
},
'description': {
type: String,
required: false,
},
'paddings': {
type: String,
required: false,

View file

@ -3,7 +3,7 @@
@click="clickHandler" />
{{end}}
{{define "component/sortableTable"}}
{{define "component/aTableSortable"}}
<script>
const DRAGGABLE_ROW_CLASS = 'draggable-row';
const findParentRowElement = (el) => {

View file

@ -4,15 +4,15 @@
<a-sub-menu>
<span slot="title">
<a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
<span>Theme</span>
<span>{{ i18n "menu.theme" }}</span>
</span>
<a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()"> Dark
<a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme"
@change="themeSwitcher.toggleTheme()"></a-switch>
<a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()">
<span>{{ i18n "menu.dark" }}</span>
<a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
</a-menu-item>
<a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch"
@mousedown="themeSwitcher.animationsOffUltra()"> Ultra <a-checkbox style="margin-left: 2px;"
:checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
<a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOffUltra()">
<span>{{ i18n "menu.ultraDark" }}</span>
<a-checkbox style="margin-left: 2px;" :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
</a-menu-item>
</a-sub-menu>
</a-menu>
@ -36,7 +36,7 @@
</template>
{{end}}
{{define "component/themeSwitcher"}}
{{define "component/aThemeSwitch"}}
<script>
function createThemeSwitcher() {
const isDarkTheme = localStorage.getItem('dark-mode') === 'true';

View file

@ -548,8 +548,8 @@
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
{{template "component/themeSwitcher" .}}
{{template "component/persianDatepicker" .}}
{{template "component/aThemeSwitch" .}}
{{template "component/aPersianDatepicker" .}}
<script>
const columns = [{
title: "ID",

View file

@ -27,8 +27,6 @@
.dark .ant-backup-list-item svg,
.dark .ant-card-actions>li>*,
.dark .ant-badge-status-text,
.dark .ant-statistic-title,
.dark .ant-statistic-content,
.dark .ant-card-extra {
color: var(--dark-color-text-primary) !important;
}
@ -44,12 +42,6 @@
.ant-card-actions {
background: transparent !important;
}
.ant-statistic-content-prefix {
font-size: 20px;
}
.ant-statistic-content-value {
font-size: 18px;
}
.ip-hidden {
filter: blur(10px);
}
@ -123,14 +115,22 @@
</transition>
<transition name="list" appear>
<template v-if="!status.isLoaded">
<a-card hoverable style="text-align: center; padding: 30px 0; margin-top: 10px;">
<div style="text-align: center; padding: 30px 0; margin-top: 10px;">
<a-spin size="large"></a-spin>
</a-card>
</div>
</template>
<template v-else>
<a-row>
<a-col :sm="24" :lg="12">
<a-card title='{{ i18n "pages.index.xrayStatus" }}' hoverable>
<a-card hoverable>
<template #title>
<a-space direction="horizontal">
<span>{{ i18n "pages.index.xrayStatus" }}</span>
<a-tag v-if="isMobile && status.xray.version != 'Unknown'" color="green">
v[[ status.xray.version ]]
</a-tag>
</a-space>
</template>
<template #extra>
<template v-if="status.xray.state != State.Error">
<a-badge :text="status.xray.state" :color="status.xray.color" style="text-transform: capitalize;"/>
@ -150,15 +150,17 @@
<template #actions>
<a-space direction="horizontal" @click="stopXrayService" style="justify-content: center;">
<a-icon type="poweroff"></a-icon>
<span>{{ i18n "pages.index.stopXray" }}</span>
<span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span>
</a-space>
<a-space direction="horizontal" @click="restartXrayService" style="justify-content: center;">
<a-icon type="reload"></a-icon>
<span>{{ i18n "pages.index.restartXray" }}</span>
<span v-if="!isMobile">{{ i18n "pages.index.restartXray" }}</span>
</a-space>
<a-space direction="horizontal" @click="openSelectV2rayVersion" style="justify-content: center;">
<a-icon type="tool"></a-icon>
<span>v[[ status.xray.version ]]</span>
<span v-if="!isMobile">
[[ status.xray.version != 'Unknown' ? `v${status.xray.version}` : '{{ i18n "pages.index.xraySwitch" }}' ]]
</span>
</a-space>
</template>
</a-card>
@ -168,15 +170,15 @@
<template #actions>
<a-space direction="horizontal" @click="openLogs()" style="justify-content: center;">
<a-icon type="bars"></a-icon>
<span>{{ i18n "pages.index.logs" }}</span>
<span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span>
</a-space>
<a-space direction="horizontal" @click="openConfig" style="justify-content: center;">
<a-icon type="control"></a-icon>
<span>{{ i18n "pages.index.config" }}</span>
<span v-if="!isMobile">{{ i18n "pages.index.config" }}</span>
</a-space>
<a-space direction="horizontal" @click="openBackup" style="justify-content: center;">
<a-icon type="cloud-server"></a-icon>
<span>{{ i18n "pages.index.backup" }}</span>
<span v-if="!isMobile">{{ i18n "pages.index.backup" }}</span>
</a-space>
</template>
</a-card>
@ -223,18 +225,18 @@
</template>
<a-row :class="showIp ? 'ip-visible' : 'ip-hidden'">
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title="IPv4" :value="status.publicIP.ipv4">
<a-custom-statistic title="IPv4" :value="status.publicIP.ipv4">
<template #prefix>
<a-icon type="global" />
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title="IPv6" :value="status.publicIP.ipv6">
<a-custom-statistic title="IPv6" :value="status.publicIP.ipv6">
<template #prefix>
<a-icon type="global" />
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
</a-row>
</a-card>
@ -243,18 +245,18 @@
<a-card title='{{ i18n "pages.index.connectionCount" }}' hoverable>
<a-row>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title="TCP" :value="status.tcpCount">
<a-custom-statistic title="TCP" :value="status.tcpCount">
<template #prefix>
<a-icon type="swap" />
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title="UDP" :value="status.udpCount">
<a-custom-statistic title="UDP" :value="status.udpCount">
<template #prefix>
<a-icon type="swap" />
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
</a-row>
</a-card>
@ -263,24 +265,24 @@
<a-card title='{{ i18n "pages.index.overallSpeed" }}' hoverable>
<a-row>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)">
<a-custom-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)">
<template #prefix>
<a-icon type="arrow-up" />
</template>
<template #suffix>
/s
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title='{{ i18n "pages.index.download" }}' :value="SizeFormatter.sizeFormat(status.netIO.down)">
<a-custom-statistic title='{{ i18n "pages.index.download" }}' :value="SizeFormatter.sizeFormat(status.netIO.down)">
<template #prefix>
<a-icon type="arrow-down" />
</template>
<template #suffix>
/s
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
</a-row>
</a-card>
@ -289,18 +291,18 @@
<a-card title='{{ i18n "pages.index.totalData" }}' hoverable>
<a-row>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)">
<a-custom-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)">
<template #prefix>
<a-icon type="cloud-upload" />
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
<a-statistic title='{{ i18n "pages.index.received" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.recv)">
<a-custom-statistic title='{{ i18n "pages.index.received" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.recv)">
<template #prefix>
<a-icon type="cloud-download" />
</template>
</a-statistic>
</a-custom-statistic>
</a-col>
</a-row>
</a-card>
@ -397,7 +399,8 @@
</a-modal>
</a-layout>
{{template "js" .}}
{{template "component/themeSwitcher" .}}
{{template "component/aThemeSwitch" .}}
{{template "component/aCustomStatistic" .}}
{{template "textModal"}}
<script>
const State = {

View file

@ -663,9 +663,9 @@
</a-layout>
{{template "js" .}}
<script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
{{template "component/themeSwitcher" .}}
{{template "component/password" .}}
{{template "component/setting"}}
{{template "component/aThemeSwitch" .}}
{{template "component/aPasswordInput" .}}
{{template "component/aSettingListItem" .}}
<script>
const app = new Vue({
delimiters: ['[[', ']]'],

View file

@ -788,9 +788,9 @@
</a-layout>
</a-layout>
{{template "js" .}}
{{template "component/themeSwitcher" .}}
{{template "component/sortableTable" .}}
{{template "component/setting"}}
{{template "component/aThemeSwitch" .}}
{{template "component/aTableSortable" .}}
{{template "component/aSettingListItem" .}}
{{template "ruleModal"}}
{{template "outModal"}}
{{template "reverseModal"}}

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "No added reverse proxies."
[menu]
"theme" = "Theme"
"dark" = "Dark"
"ultraDark" = "Ultra Dark"
"dashboard" = "Overview"
"inbounds" = "Inbounds"
"settings" = "Panel Settings"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "No hay proxies inversos añadidos."
[menu]
"theme" = "Tema"
"dark" = "Oscuro"
"ultraDark" = "Ultra Oscuro"
"dashboard" = "Estado del Sistema"
"inbounds" = "Entradas"
"settings" = "Configuraciones"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "هیچ پروکسی معکوس اضافه نشده است."
[menu]
"theme" = "تم"
"dark" = "تیره"
"ultraDark" = "فوق تیره"
"dashboard" = "نمای کلی"
"inbounds" = "ورودی‌ها"
"settings" = "تنظیمات پنل"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "Tidak ada proxy terbalik yang ditambahkan."
[menu]
"theme" = "Tema"
"dark" = "Gelap"
"ultraDark" = "Sangat Gelap"
"dashboard" = "Ikhtisar"
"inbounds" = "Masuk"
"settings" = "Pengaturan Panel"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "追加されたリバースプロキシはありません。"
[menu]
"theme" = "テーマ"
"dark" = "ダーク"
"ultraDark" = "ウルトラダーク"
"dashboard" = "ダッシュボード"
"inbounds" = "インバウンド一覧"
"settings" = "パネル設定"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "Nenhum proxy reverso adicionado."
[menu]
"theme" = "Tema"
"dark" = "Escuro"
"ultraDark" = "Ultra Escuro"
"dashboard" = "Visão Geral"
"inbounds" = "Inbounds"
"settings" = "Panel Settings"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "Нет добавленных обратных прокси."
[menu]
"theme" = "Тема"
"dark" = "Темная"
"ultraDark" = "Ультра темная"
"dashboard" = "Статус системы"
"inbounds" = "Подключения"
"settings" = "Настройки панели"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "Eklenmiş ters proxy yok."
[menu]
"theme" = "Tema"
"dark" = "Koyu"
"ultraDark" = "Ultra Koyu"
"dashboard" = "Genel Bakış"
"inbounds" = "Gelenler"
"settings" = "Panel Ayarları"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "Немає доданих зворотних проксі."
[menu]
"theme" = "Тема"
"dark" = "Темна"
"ultraDark" = "Ультра темна"
"dashboard" = "Огляд"
"inbounds" = "Вхідні"
"settings" = "Параметри панелі"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "Không có proxy ngược nào được thêm."
[menu]
"theme" = "Chủ đề"
"dark" = "Tối"
"ultraDark" = "Siêu tối"
"dashboard" = "Trạng thái hệ thống"
"inbounds" = "Đầu vào khách hàng"
"settings" = "Cài đặt bảng điều khiển"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "未添加反向代理。"
[menu]
"theme" = "主题"
"dark" = "暗色"
"ultraDark" = "超暗色"
"dashboard" = "系统状态"
"inbounds" = "入站列表"
"settings" = "面板设置"

View file

@ -67,6 +67,9 @@
"emptyReverseDesc" = "未添加反向代理。"
[menu]
"theme" = "主題"
"dark" = "深色"
"ultraDark" = "超深色"
"dashboard" = "系統狀態"
"inbounds" = "入站列表"
"settings" = "面板設定"