Import x-panel source

This commit is contained in:
2026-05-03 11:34:48 +08:00
commit e98e780360
312 changed files with 90189 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
{{define "component/sidebar/content"}}
<template>
<div class="ant-sidebar">
<a-layout-sider :theme="themeSwitcher.currentTheme" collapsible :collapsed="collapsed"
@collapse="(isCollapsed, type) => collapseHandle(isCollapsed, type)" breakpoint="md" width="200">
<div class="sider-flex-wrapper">
<div class="sider-top">
<a-theme-switch></a-theme-switch>
</div>
<div class="sider-menu-container">
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
@click="({key}) => openLink(key)">
<a-menu-item v-for="tab in tabs" :key="tab.key">
<a-icon :type="tab.icon"></a-icon>
<span v-text="tab.title"></span>
</a-menu-item>
</a-menu>
</div>
<div class="sidebar-copyright-wrapper" v-show="!collapsed">
<div class="copyright-content">
<div class="brand-row">
<a-icon type="safety-certificate" theme="filled" class="brand-icon"></a-icon>
<span class="brand-name">X-Panel</span>
</div>
<div class="year-row">
<span>Copyright <a-icon type="copyright"></a-icon> 2022-2026</span>
</div>
</div>
</div>
</div>
</a-layout-sider>
<a-drawer placement="left" :closable="false" @close="closeDrawer" :visible="visible"
:wrap-class-name="themeSwitcher.currentTheme" :wrap-style="{ padding: 0 }" :style="{ height: '100%' }">
<div class="drawer-handle" @click="toggleDrawer" slot="handle">
<a-icon :type="visible ? 'close' : 'menu-fold'"></a-icon>
</div>
<a-theme-switch></a-theme-switch>
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
@click="({key}) => openLink(key)">
<a-menu-item v-for="tab in tabs" :key="tab.key">
<a-icon :type="tab.icon"></a-icon>
<span v-text="tab.title"></span>
</a-menu-item>
</a-menu>
</a-drawer>
</div>
</template>
{{end}}
{{define "component/aSidebar"}}
<style>
/* 1. 基础高度设置 */
.ant-sidebar, .ant-sidebar > .ant-layout-sider {
height: 100%;
}
/* [布局容器] */
.sider-flex-wrapper {
display: flex;
flex-direction: column; /* 从上到下排列 */
height: 100%;
overflow: hidden;
}
/* [菜单容器] */
.sider-menu-container {
/* [修改说明]:删除了 flex: 1。菜单高度现在由内容决定不会自动撑满 */
/* 添加 flex-shrink: 1 只是为了防止屏幕特别矮时菜单被切断,让它能出滚动条 */
flex-shrink: 1;
overflow-y: auto;
overflow-x: hidden;
}
.sider-menu-container .ant-menu {
border-right: none;
}
/* 2. 版权区域 */
.sidebar-copyright-wrapper {
/* [核心修改]margin-top: auto 是关键 */
/* 它的作用是:自动占据上方所有的空白空间,从而把自己推到容器的最底部 */
margin-top: auto;
flex-shrink: 0;
width: 100%;
padding: 15px 0 20px 0;
text-align: center;
background: inherit;
/* [已删除] border-top: ... (那条横线已经彻底删除了) */
}
/* 3. 品牌行样式 */
.brand-row {
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: 600;
margin-bottom: 4px;
opacity: 0.85;
letter-spacing: 0.5px;
}
.brand-icon {
margin-right: 6px;
font-size: 15px;
opacity: 0.8;
}
/* 4. 年份版权行样式 */
.year-row {
font-size: 11px;
opacity: 0.45;
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
display: flex;
justify-content: center;
align-items: center;
}
.year-row .anticon-copyright {
font-size: 10px;
margin: 0 3px;
position: relative;
}
/* 滚动条样式美化 */
.sider-menu-container::-webkit-scrollbar {
width: 4px;
}
.sider-menu-container::-webkit-scrollbar-thumb {
background: rgba(128, 128, 128, 0.2);
border-radius: 2px;
}
.sider-menu-container::-webkit-scrollbar-track {
background: transparent;
}
</style>
<script>
const SIDEBAR_COLLAPSED_KEY = "isSidebarCollapsed"
Vue.component('a-sidebar', {
data() {
return {
tabs: [
{
key: '{{ .base_path }}panel/',
icon: 'dashboard',
title: '{{ i18n "menu.dashboard"}}'
},
{
key: '{{ .base_path }}panel/inbounds',
icon: 'user',
title: '{{ i18n "menu.inbounds"}}'
},
{
key: '{{ .base_path }}panel/settings',
icon: 'setting',
title: '{{ i18n "menu.settings"}}'
},
{
key: '{{ .base_path }}panel/xray',
icon: 'tool',
title: '{{ i18n "menu.xray"}}'
},
{
key: '{{ .base_path }}panel/servers',
icon: 'cloud-server',
title: '{{ i18n "pages.controlledmanagement.title"}}'
},
{
key: '{{ .base_path }}panel/navigation',
icon: 'link',
title: '{{ i18n "menu.navigation"}}'
},
{
key: '{{ .base_path }}logout/',
icon: 'logout',
title: '{{ i18n "menu.logout"}}'
},
],
activeTab: [
'{{ .request_uri }}'
],
visible: false,
collapsed: JSON.parse(localStorage.getItem(SIDEBAR_COLLAPSED_KEY)),
}
},
methods: {
openLink(key) {
return key.startsWith('http') ?
window.open(key) :
location.href = key
},
closeDrawer() {
this.visible = false;
},
toggleDrawer() {
this.visible = !this.visible;
},
collapseHandle(collapsed, type) {
if (type === "clickTrigger") {
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, collapsed);
this.collapsed = JSON.parse(localStorage.getItem(SIDEBAR_COLLAPSED_KEY));
}
}
},
template: `{{template "component/sidebar/content"}}`,
});
</script>
{{end}}