refactor: new loading logic, icons for tabs
Some checks failed
Release 3X-UI / build (386) (push) Has been cancelled
Release 3X-UI / build (amd64) (push) Has been cancelled
Release 3X-UI / build (arm64) (push) Has been cancelled
Release 3X-UI / build (armv5) (push) Has been cancelled
Release 3X-UI / build (armv6) (push) Has been cancelled
Release 3X-UI / build (armv7) (push) Has been cancelled
Release 3X-UI / build (s390x) (push) Has been cancelled

This commit is contained in:
Shishkevich D. 2025-06-22 00:27:09 +07:00 committed by GitHub
parent d642774a44
commit 4b20f16024
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1979 additions and 1866 deletions

File diff suppressed because it is too large Load diff

View file

@ -4,15 +4,6 @@
.ant-layout-content { .ant-layout-content {
margin: 24px 16px; margin: 24px 16px;
} }
.ant-card-hoverable {
margin-inline: 0.3rem;
}
.ant-alert-error {
margin-inline: 0.3rem;
}
}
.ant-col-sm-24 {
margin-top: 10px;
} }
.ant-card-dark h2 { .ant-card-dark h2 {
color: var(--dark-color-text-primary); color: var(--dark-color-text-primary);
@ -84,9 +75,9 @@
<a-sidebar></a-sidebar> <a-sidebar></a-sidebar>
<a-layout id="content-layout"> <a-layout id="content-layout">
<a-layout-content> <a-layout-content>
<a-spin :spinning="spinning" :delay="200" :tip="loadingTip"> <a-spin :spinning="loadingStates.spinning" :delay="200" :tip="loadingTip">
<transition name="list" appear> <transition name="list" appear>
<a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }" <a-alert type="error" v-if="showAlert && loadingStates.fetched" :style="{ marginBottom: '10px' }"
message='{{ i18n "secAlertTitle" }}' message='{{ i18n "secAlertTitle" }}'
color="red" color="red"
description='{{ i18n "secAlertSsl" }}' description='{{ i18n "secAlertSsl" }}'
@ -95,15 +86,15 @@
</transition> </transition>
<transition name="list" appear> <transition name="list" appear>
<template> <template>
<a-row v-if="!status.isLoaded"> <a-row v-if="!loadingStates.fetched">
<a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }"> <a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
<a-spin tip='{{ i18n "loading" }}'></a-spin> <a-spin tip='{{ i18n "loading" }}'></a-spin>
</a-card> </a-card>
</a-row> </a-row>
<a-row v-else> <a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else>
<a-row> <a-col>
<a-card hoverable> <a-card hoverable>
<a-row> <a-row :gutter="[0, isMobile ? 16 : 0]">
<a-col :sm="24" :md="12"> <a-col :sm="24" :md="12">
<a-row> <a-row>
<a-col :span="12" :style="{ textAlign: 'center' }"> <a-col :span="12" :style="{ textAlign: 'center' }">
@ -153,7 +144,7 @@
</a-col> </a-col>
</a-row> </a-row>
</a-card> </a-card>
</a-row> </a-col>
<a-col :sm="24" :lg="12"> <a-col :sm="24" :lg="12">
<a-card hoverable> <a-card hoverable>
<template #title> <template #title>
@ -488,7 +479,7 @@
} }
class Status { class Status {
constructor(data, isLoaded = false) { constructor(data) {
this.cpu = new CurTotal(0, 0); this.cpu = new CurTotal(0, 0);
this.cpuCores = 0; this.cpuCores = 0;
this.logicalPro = 0; this.logicalPro = 0;
@ -512,7 +503,6 @@
return; return;
} }
this.isLoaded = isLoaded;
this.cpu = new CurTotal(data.cpu, 100); this.cpu = new CurTotal(data.cpu, 100);
this.cpuCores = data.cpuCores; this.cpuCores = data.cpuCores;
this.logicalPro = data.logicalPro; this.logicalPro = data.logicalPro;
@ -632,32 +622,39 @@
mixins: [MediaQueryMixin], mixins: [MediaQueryMixin],
data: { data: {
themeSwitcher, themeSwitcher,
loadingStates: {
fetched: false,
spinning: false
},
status: new Status(), status: new Status(),
versionModal, versionModal,
logModal, logModal,
backupModal, backupModal,
spinning: false,
loadingTip: '{{ i18n "loading"}}', loadingTip: '{{ i18n "loading"}}',
showAlert: false, showAlert: false,
showIp: false showIp: false
}, },
methods: { methods: {
loading(spinning, tip = '{{ i18n "loading"}}') { loading(spinning, tip = '{{ i18n "loading"}}') {
this.spinning = spinning; this.loadingStates.spinning = spinning;
this.loadingTip = tip; this.loadingTip = tip;
}, },
async getStatus() { async getStatus() {
try { try {
const msg = await HttpUtil.post('/server/status'); const msg = await HttpUtil.post('/server/status');
if (msg.success) { if (msg.success) {
if (!this.loadingStates.fetched) {
this.loadingStates.fetched = true;
}
this.setStatus(msg.obj, true); this.setStatus(msg.obj, true);
} }
} catch (e) { } catch (e) {
console.error("Failed to get status:", e); console.error("Failed to get status:", e);
} }
}, },
setStatus(data, isLoaded = false) { setStatus(data) {
this.status = new Status(data, isLoaded); this.status = new Status(data);
}, },
async openSelectV2rayVersion() { async openSelectV2rayVersion() {
this.loading(true); this.loading(true);

View file

@ -65,9 +65,9 @@
<a-sidebar></a-sidebar> <a-sidebar></a-sidebar>
<a-layout id="content-layout"> <a-layout id="content-layout">
<a-layout-content> <a-layout-content>
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'> <a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'>
<transition name="list" appear> <transition name="list" appear>
<a-alert type="error" v-if="confAlerts.length>0" :style="{ marginBottom: '10px' }" <a-alert type="error" v-if="confAlerts.length>0 && loadingStates.fetched" :style="{ marginBottom: '10px' }"
message='{{ i18n "secAlertTitle" }}' message='{{ i18n "secAlertTitle" }}'
color="red" color="red"
show-icon closable> show-icon closable>
@ -77,46 +77,79 @@
</template> </template>
</a-alert> </a-alert>
</transition> </transition>
<a-space direction="vertical"> <transition name="list" appear>
<a-card hoverable :style="{ marginBottom: '.5rem', overflowX: 'hidden' }"> <template>
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }"> <a-row v-if="!loadingStates.fetched">
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }"> <a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
<a-space direction="horizontal"> <a-spin tip='{{ i18n "loading" }}'></a-spin>
<a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button> </a-card>
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button> </a-row>
</a-space> <a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else>
<a-col>
<a-card hoverable>
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
<a-space direction="horizontal">
<a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button>
</a-space>
</a-col>
<a-col :xs="24" :sm="14">
<template>
<div>
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top>
<a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }"
message='{{ i18n "pages.settings.infoDesc" }}'
show-icon>
</a-alert>
</div>
</template>
</a-col>
</a-row>
</a-card>
</a-col> </a-col>
<a-col :xs="24" :sm="14"> <a-col>
<template> <a-tabs default-active-key="1">
<div> <a-tab-pane key="1" :style="{ paddingTop: '20px' }">
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top> <template #tab>
<a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }" <a-icon type="setting"></a-icon>
message='{{ i18n "pages.settings.infoDesc" }}' <span>{{ i18n "pages.settings.panelSettings" }}</span>
show-icon> </template>
</a-alert> {{ template "settings/panel/general" . }}
</div> </a-tab-pane>
</template> <a-tab-pane key="2" :style="{ paddingTop: '20px' }">
<template #tab>
<a-icon type="safety"></a-icon>
<span>{{ i18n "pages.settings.securitySettings" }}</span>
</template>
{{ template "settings/panel/security" . }}
</a-tab-pane>
<a-tab-pane key="3" :style="{ paddingTop: '20px' }">
<template #tab>
<a-icon type="message"></a-icon>
<span>{{ i18n "pages.settings.TGBotSettings" }}</span>
</template>
{{ template "settings/panel/telegram" . }}
</a-tab-pane>
<a-tab-pane key="4" :style="{ paddingTop: '20px' }">
<template #tab>
<a-icon type="cloud-server"></a-icon>
<span>{{ i18n "pages.settings.subSettings" }}</span>
</template>
{{ template "settings/panel/subscription/general" . }}
</a-tab-pane>
<a-tab-pane key="5" v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }">
<template #tab>
<a-icon type="code"></a-icon>
<span>{{ i18n "pages.settings.subSettings" }} (JSON)</span>
</template>
{{ template "settings/panel/subscription/json" . }}
</a-tab-pane>
</a-tabs>
</a-col> </a-col>
</a-row> </a-row>
</a-card> </template>
<a-tabs default-active-key="1"> </transition>
<a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings" }}' :style="{ paddingTop: '20px' }">
{{ template "settings/panel/general" . }}
</a-tab-pane>
<a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings" }}' :style="{ paddingTop: '20px' }">
{{ template "settings/panel/security" . }}
</a-tab-pane>
<a-tab-pane key="3" tab='{{ i18n "pages.settings.TGBotSettings" }}' :style="{ paddingTop: '20px' }">
{{ template "settings/panel/telegram" . }}
</a-tab-pane>
<a-tab-pane key="4" tab='{{ i18n "pages.settings.subSettings" }}' :style="{ paddingTop: '20px' }">
{{ template "settings/panel/subscription/general" . }}
</a-tab-pane>
<a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }} Json' v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }">
{{ template "settings/panel/subscription/json" . }}
</a-tab-pane>
</a-tabs>
</a-space>
</a-spin> </a-spin>
</a-layout-content> </a-layout-content>
</a-layout> </a-layout>
@ -132,10 +165,14 @@
<script> <script>
const app = new Vue({ const app = new Vue({
delimiters: ['[[', ']]'], delimiters: ['[[', ']]'],
mixins: [MediaQueryMixin],
el: '#app', el: '#app',
data: { data: {
themeSwitcher, themeSwitcher,
spinning: false, loadingStates: {
fetched: false,
spinning: false
},
oldAllSetting: new AllSetting(), oldAllSetting: new AllSetting(),
allSetting: new AllSetting(), allSetting: new AllSetting(),
saveBtnDisable: true, saveBtnDisable: true,
@ -248,13 +285,16 @@
}, },
methods: { methods: {
loading(spinning = true) { loading(spinning = true) {
this.spinning = spinning; this.loadingStates.spinning = spinning;
}, },
async getAllSetting() { async getAllSetting() {
this.loading(true);
const msg = await HttpUtil.post("/panel/setting/all"); const msg = await HttpUtil.post("/panel/setting/all");
this.loading(false);
if (msg.success) { if (msg.success) {
if (!this.loadingStates.fetched) {
this.loadingStates.fetched = true
}
this.oldAllSetting = new AllSetting(msg.obj); this.oldAllSetting = new AllSetting(msg.obj);
this.allSetting = new AllSetting(msg.obj); this.allSetting = new AllSetting(msg.obj);
app.changeRemarkSample(); app.changeRemarkSample();

View file

@ -1,5 +1,5 @@
{{define "settings/xray/advanced"}} {{define "settings/xray/advanced"}}
<a-space direction="vertical" size="small"> <a-space direction="vertical" size="small" :style="{ marginTop: '20px' }">
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}' <a-list-item-meta title='{{ i18n "pages.xray.Template"}}'
description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta> description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }" <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }"

File diff suppressed because it is too large Load diff