📝 docs: Add documentation
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"hash": "7e859c1e",
|
||||
"configHash": "1f757845",
|
||||
"lockfileHash": "e3b0c442",
|
||||
"browserHash": "1e554b7a",
|
||||
"optimized": {
|
||||
"vue": {
|
||||
"src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "3b2af542",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vitepress > @vue/devtools-api": {
|
||||
"src": "../../../../node_modules/@vue/devtools-api/dist/index.js",
|
||||
"file": "vitepress___@vue_devtools-api.js",
|
||||
"fileHash": "64692441",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vitepress > @vueuse/core": {
|
||||
"src": "../../../../node_modules/@vueuse/core/index.mjs",
|
||||
"file": "vitepress___@vueuse_core.js",
|
||||
"fileHash": "98a22420",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vitepress-theme-teek": {
|
||||
"src": "../../../../node_modules/vitepress-theme-teek/es/index.mjs",
|
||||
"file": "vitepress-theme-teek.js",
|
||||
"fileHash": "124483c7",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-BY246V3E": {
|
||||
"file": "chunk-BY246V3E.js"
|
||||
},
|
||||
"chunk-PZ5AY32C": {
|
||||
"file": "chunk-PZ5AY32C.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
+13019
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+10
@@ -0,0 +1,10 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
|
||||
export {
|
||||
__export
|
||||
};
|
||||
//# sourceMappingURL=chunk-PZ5AY32C.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
+26830
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 it is too large
Load Diff
File diff suppressed because one or more lines are too long
+9732
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+348
@@ -0,0 +1,348 @@
|
||||
import {
|
||||
BaseTransition,
|
||||
BaseTransitionPropsValidators,
|
||||
Comment,
|
||||
DeprecationTypes,
|
||||
EffectScope,
|
||||
ErrorCodes,
|
||||
ErrorTypeStrings,
|
||||
Fragment,
|
||||
KeepAlive,
|
||||
ReactiveEffect,
|
||||
Static,
|
||||
Suspense,
|
||||
Teleport,
|
||||
Text,
|
||||
TrackOpTypes,
|
||||
Transition,
|
||||
TransitionGroup,
|
||||
TriggerOpTypes,
|
||||
VueElement,
|
||||
assertNumber,
|
||||
callWithAsyncErrorHandling,
|
||||
callWithErrorHandling,
|
||||
camelize,
|
||||
capitalize,
|
||||
cloneVNode,
|
||||
compatUtils,
|
||||
compile,
|
||||
computed,
|
||||
createApp,
|
||||
createBaseVNode,
|
||||
createBlock,
|
||||
createCommentVNode,
|
||||
createElementBlock,
|
||||
createHydrationRenderer,
|
||||
createPropsRestProxy,
|
||||
createRenderer,
|
||||
createSSRApp,
|
||||
createSlots,
|
||||
createStaticVNode,
|
||||
createTextVNode,
|
||||
createVNode,
|
||||
customRef,
|
||||
defineAsyncComponent,
|
||||
defineComponent,
|
||||
defineCustomElement,
|
||||
defineEmits,
|
||||
defineExpose,
|
||||
defineModel,
|
||||
defineOptions,
|
||||
defineProps,
|
||||
defineSSRCustomElement,
|
||||
defineSlots,
|
||||
devtools,
|
||||
effect,
|
||||
effectScope,
|
||||
getCurrentInstance,
|
||||
getCurrentScope,
|
||||
getCurrentWatcher,
|
||||
getTransitionRawChildren,
|
||||
guardReactiveProps,
|
||||
h,
|
||||
handleError,
|
||||
hasInjectionContext,
|
||||
hydrate,
|
||||
hydrateOnIdle,
|
||||
hydrateOnInteraction,
|
||||
hydrateOnMediaQuery,
|
||||
hydrateOnVisible,
|
||||
initCustomFormatter,
|
||||
initDirectivesForSSR,
|
||||
inject,
|
||||
isMemoSame,
|
||||
isProxy,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isRef,
|
||||
isRuntimeOnly,
|
||||
isShallow,
|
||||
isVNode,
|
||||
markRaw,
|
||||
mergeDefaults,
|
||||
mergeModels,
|
||||
mergeProps,
|
||||
nextTick,
|
||||
nodeOps,
|
||||
normalizeClass,
|
||||
normalizeProps,
|
||||
normalizeStyle,
|
||||
onActivated,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onBeforeUpdate,
|
||||
onDeactivated,
|
||||
onErrorCaptured,
|
||||
onMounted,
|
||||
onRenderTracked,
|
||||
onRenderTriggered,
|
||||
onScopeDispose,
|
||||
onServerPrefetch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
onWatcherCleanup,
|
||||
openBlock,
|
||||
patchProp,
|
||||
popScopeId,
|
||||
provide,
|
||||
proxyRefs,
|
||||
pushScopeId,
|
||||
queuePostFlushCb,
|
||||
reactive,
|
||||
readonly,
|
||||
ref,
|
||||
registerRuntimeCompiler,
|
||||
render,
|
||||
renderList,
|
||||
renderSlot,
|
||||
resolveComponent,
|
||||
resolveDirective,
|
||||
resolveDynamicComponent,
|
||||
resolveFilter,
|
||||
resolveTransitionHooks,
|
||||
setBlockTracking,
|
||||
setDevtoolsHook,
|
||||
setTransitionHooks,
|
||||
shallowReactive,
|
||||
shallowReadonly,
|
||||
shallowRef,
|
||||
ssrContextKey,
|
||||
ssrUtils,
|
||||
stop,
|
||||
toDisplayString,
|
||||
toHandlerKey,
|
||||
toHandlers,
|
||||
toRaw,
|
||||
toRef,
|
||||
toRefs,
|
||||
toValue,
|
||||
transformVNodeArgs,
|
||||
triggerRef,
|
||||
unref,
|
||||
useAttrs,
|
||||
useCssModule,
|
||||
useCssVars,
|
||||
useHost,
|
||||
useId,
|
||||
useModel,
|
||||
useSSRContext,
|
||||
useShadowRoot,
|
||||
useSlots,
|
||||
useTemplateRef,
|
||||
useTransitionState,
|
||||
vModelCheckbox,
|
||||
vModelDynamic,
|
||||
vModelRadio,
|
||||
vModelSelect,
|
||||
vModelText,
|
||||
vShow,
|
||||
version,
|
||||
warn,
|
||||
watch,
|
||||
watchEffect,
|
||||
watchPostEffect,
|
||||
watchSyncEffect,
|
||||
withAsyncContext,
|
||||
withCtx,
|
||||
withDefaults,
|
||||
withDirectives,
|
||||
withKeys,
|
||||
withMemo,
|
||||
withModifiers,
|
||||
withScopeId
|
||||
} from "./chunk-BY246V3E.js";
|
||||
import "./chunk-PZ5AY32C.js";
|
||||
export {
|
||||
BaseTransition,
|
||||
BaseTransitionPropsValidators,
|
||||
Comment,
|
||||
DeprecationTypes,
|
||||
EffectScope,
|
||||
ErrorCodes,
|
||||
ErrorTypeStrings,
|
||||
Fragment,
|
||||
KeepAlive,
|
||||
ReactiveEffect,
|
||||
Static,
|
||||
Suspense,
|
||||
Teleport,
|
||||
Text,
|
||||
TrackOpTypes,
|
||||
Transition,
|
||||
TransitionGroup,
|
||||
TriggerOpTypes,
|
||||
VueElement,
|
||||
assertNumber,
|
||||
callWithAsyncErrorHandling,
|
||||
callWithErrorHandling,
|
||||
camelize,
|
||||
capitalize,
|
||||
cloneVNode,
|
||||
compatUtils,
|
||||
compile,
|
||||
computed,
|
||||
createApp,
|
||||
createBlock,
|
||||
createCommentVNode,
|
||||
createElementBlock,
|
||||
createBaseVNode as createElementVNode,
|
||||
createHydrationRenderer,
|
||||
createPropsRestProxy,
|
||||
createRenderer,
|
||||
createSSRApp,
|
||||
createSlots,
|
||||
createStaticVNode,
|
||||
createTextVNode,
|
||||
createVNode,
|
||||
customRef,
|
||||
defineAsyncComponent,
|
||||
defineComponent,
|
||||
defineCustomElement,
|
||||
defineEmits,
|
||||
defineExpose,
|
||||
defineModel,
|
||||
defineOptions,
|
||||
defineProps,
|
||||
defineSSRCustomElement,
|
||||
defineSlots,
|
||||
devtools,
|
||||
effect,
|
||||
effectScope,
|
||||
getCurrentInstance,
|
||||
getCurrentScope,
|
||||
getCurrentWatcher,
|
||||
getTransitionRawChildren,
|
||||
guardReactiveProps,
|
||||
h,
|
||||
handleError,
|
||||
hasInjectionContext,
|
||||
hydrate,
|
||||
hydrateOnIdle,
|
||||
hydrateOnInteraction,
|
||||
hydrateOnMediaQuery,
|
||||
hydrateOnVisible,
|
||||
initCustomFormatter,
|
||||
initDirectivesForSSR,
|
||||
inject,
|
||||
isMemoSame,
|
||||
isProxy,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isRef,
|
||||
isRuntimeOnly,
|
||||
isShallow,
|
||||
isVNode,
|
||||
markRaw,
|
||||
mergeDefaults,
|
||||
mergeModels,
|
||||
mergeProps,
|
||||
nextTick,
|
||||
nodeOps,
|
||||
normalizeClass,
|
||||
normalizeProps,
|
||||
normalizeStyle,
|
||||
onActivated,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onBeforeUpdate,
|
||||
onDeactivated,
|
||||
onErrorCaptured,
|
||||
onMounted,
|
||||
onRenderTracked,
|
||||
onRenderTriggered,
|
||||
onScopeDispose,
|
||||
onServerPrefetch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
onWatcherCleanup,
|
||||
openBlock,
|
||||
patchProp,
|
||||
popScopeId,
|
||||
provide,
|
||||
proxyRefs,
|
||||
pushScopeId,
|
||||
queuePostFlushCb,
|
||||
reactive,
|
||||
readonly,
|
||||
ref,
|
||||
registerRuntimeCompiler,
|
||||
render,
|
||||
renderList,
|
||||
renderSlot,
|
||||
resolveComponent,
|
||||
resolveDirective,
|
||||
resolveDynamicComponent,
|
||||
resolveFilter,
|
||||
resolveTransitionHooks,
|
||||
setBlockTracking,
|
||||
setDevtoolsHook,
|
||||
setTransitionHooks,
|
||||
shallowReactive,
|
||||
shallowReadonly,
|
||||
shallowRef,
|
||||
ssrContextKey,
|
||||
ssrUtils,
|
||||
stop,
|
||||
toDisplayString,
|
||||
toHandlerKey,
|
||||
toHandlers,
|
||||
toRaw,
|
||||
toRef,
|
||||
toRefs,
|
||||
toValue,
|
||||
transformVNodeArgs,
|
||||
triggerRef,
|
||||
unref,
|
||||
useAttrs,
|
||||
useCssModule,
|
||||
useCssVars,
|
||||
useHost,
|
||||
useId,
|
||||
useModel,
|
||||
useSSRContext,
|
||||
useShadowRoot,
|
||||
useSlots,
|
||||
useTemplateRef,
|
||||
useTransitionState,
|
||||
vModelCheckbox,
|
||||
vModelDynamic,
|
||||
vModelRadio,
|
||||
vModelSelect,
|
||||
vModelText,
|
||||
vShow,
|
||||
version,
|
||||
warn,
|
||||
watch,
|
||||
watchEffect,
|
||||
watchPostEffect,
|
||||
watchSyncEffect,
|
||||
withAsyncContext,
|
||||
withCtx,
|
||||
withDefaults,
|
||||
withDirectives,
|
||||
withKeys,
|
||||
withMemo,
|
||||
withModifiers,
|
||||
withScopeId
|
||||
};
|
||||
//# sourceMappingURL=vue.js.map
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
interface Sponsor {
|
||||
logo: string;
|
||||
title: string;
|
||||
description: string;
|
||||
expiryDate: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
loadingText?: string;
|
||||
errorText?: string;
|
||||
emptyText?: string;
|
||||
visitTitle?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loadingText: "Loading...",
|
||||
errorText: "Failed to load sponsors data",
|
||||
emptyText: "No sponsors available",
|
||||
visitTitle: "Visit sponsor",
|
||||
});
|
||||
|
||||
const sponsors = ref<Sponsor[]>([]);
|
||||
const loading = ref(true);
|
||||
const error = ref("");
|
||||
|
||||
const isExpired = (expiryDate: string): boolean => {
|
||||
try {
|
||||
const expiry = new Date(expiryDate);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
expiry.setHours(0, 0, 0, 0);
|
||||
return expiry < today;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://raw.githubusercontent.com/perfect-panel/ppanel-assets/main/billing/index.json"
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to fetch sponsors data");
|
||||
|
||||
const data = await response.json();
|
||||
const allSponsors = [
|
||||
...(data.billing || []),
|
||||
...(data.dashboard || []),
|
||||
...(data.payment || []),
|
||||
];
|
||||
sponsors.value = allSponsors.filter(
|
||||
(sponsor: Sponsor) => !isExpired(sponsor.expiryDate)
|
||||
);
|
||||
} catch (e) {
|
||||
error.value = props.errorText;
|
||||
console.error(e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sponsors-container">
|
||||
<div v-if="loading" class="loading">{{ loadingText }}</div>
|
||||
<div v-else-if="error" class="error">{{ error }}</div>
|
||||
<div v-else-if="sponsors.length === 0" class="empty-state">{{ emptyText }}</div>
|
||||
<div v-else class="sponsors-grid">
|
||||
<a
|
||||
v-for="sponsor in sponsors"
|
||||
:key="sponsor.title"
|
||||
:href="sponsor.href"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="sponsor-card"
|
||||
:title="sponsor.title"
|
||||
>
|
||||
<img
|
||||
:src="sponsor.logo"
|
||||
:alt="sponsor.title"
|
||||
class="sponsor-logo"
|
||||
/>
|
||||
<div class="sponsor-content">
|
||||
<h3 class="sponsor-title">{{ sponsor.title }}</h3>
|
||||
<p class="sponsor-description">{{ sponsor.description }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sponsors-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1.5rem 2.5rem;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.error {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.loading {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--vp-c-danger);
|
||||
}
|
||||
|
||||
.sponsors-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.sponsor-card {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
padding: 1.25rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sponsor-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--vp-c-brand) 0%, var(--vp-c-brand-dark) 100%);
|
||||
transform: scaleX(0);
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
.sponsor-card:hover::before {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
.sponsor-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
|
||||
border-color: var(--vp-c-brand-light);
|
||||
}
|
||||
|
||||
.sponsor-logo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
flex-shrink: 0;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
padding: 0.5rem;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
.sponsor-card:hover .sponsor-logo {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.sponsor-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.sponsor-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.sponsor-description {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 5rem 2rem;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sponsors-container {
|
||||
padding: 0 1rem 2rem;
|
||||
}
|
||||
|
||||
.sponsors-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.sponsor-card {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.sponsor-logo {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,367 @@
|
||||
import { defineConfig } from "vitepress";
|
||||
import { defineTeekConfig } from "vitepress-theme-teek/config";
|
||||
|
||||
// Teek 主题配置
|
||||
const teekConfig = defineTeekConfig({
|
||||
// 文章元数据配置
|
||||
article: {
|
||||
author: "PPanel",
|
||||
},
|
||||
// 页脚配置
|
||||
footer: {
|
||||
copyright: "Copyright © 2025 PPanel",
|
||||
},
|
||||
// 评论按钮配置 - 跳转到 Telegram 群组
|
||||
toComment: {
|
||||
enabled: true,
|
||||
done: () => {
|
||||
window.open("https://t.me/PPanelChat", "_blank");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
extends: teekConfig,
|
||||
|
||||
title: "PPanel",
|
||||
description:
|
||||
"PPanel is a pure, professional, and perfect open-source proxy panel tool, designed to be your ideal choice for learning and practical use.",
|
||||
|
||||
// 国际化配置
|
||||
locales: {
|
||||
root: {
|
||||
label: "English",
|
||||
lang: "en-US",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: "Home", link: "/" },
|
||||
{ text: "Guide", link: "/zh/guide/intro" },
|
||||
{ text: "Features", link: "/zh/admin/dashboard" },
|
||||
{ text: "API", link: "/zh/api/reference" },
|
||||
{ text: "Sponsors", link: "/zh/sponsors" },
|
||||
{
|
||||
text: "Stats",
|
||||
link: "https://stats.ppanel.dev",
|
||||
target: "_blank",
|
||||
},
|
||||
{ text: "Buy Theme", link: "https://bygga.app", target: "_blank" },
|
||||
],
|
||||
sidebar: {
|
||||
"/api/": [
|
||||
{
|
||||
text: "API",
|
||||
items: [
|
||||
{ text: "Overview", link: "/api/reference" },
|
||||
{ text: "Common Service", link: "/api/common" },
|
||||
{ text: "User Service", link: "/api/user" },
|
||||
{ text: "Admin Service", link: "/api/admin" },
|
||||
{ text: "Gateway", link: "/api/gateway" },
|
||||
],
|
||||
},
|
||||
],
|
||||
"/guide/": [
|
||||
{
|
||||
text: "Guide",
|
||||
items: [
|
||||
{ text: "Introduction", link: "/guide/intro" },
|
||||
{ text: "Contributing", link: "/guide/contributing" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Installation",
|
||||
items: [
|
||||
{ text: "Overview", link: "/guide/installation/" },
|
||||
{
|
||||
text: "Docker Compose",
|
||||
link: "/guide/installation/docker-compose",
|
||||
},
|
||||
{
|
||||
text: "Docker Run",
|
||||
link: "/guide/installation/docker-run",
|
||||
},
|
||||
{
|
||||
text: "Binary Deployment",
|
||||
link: "/guide/installation/binary",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Separation Deployment",
|
||||
items: [
|
||||
{
|
||||
text: "Backend Deployment",
|
||||
link: "/guide/separation/backend",
|
||||
},
|
||||
{
|
||||
text: "Frontend Deployment",
|
||||
link: "/guide/separation/frontend",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Node Agent",
|
||||
items: [
|
||||
{
|
||||
text: "Installation",
|
||||
link: "/guide/node/installation",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"/admin/": [
|
||||
{
|
||||
text: "Dashboard",
|
||||
items: [{ text: "Overview", link: "/admin/dashboard" }],
|
||||
},
|
||||
{
|
||||
text: "Maintenance",
|
||||
items: [
|
||||
{
|
||||
text: "Server Management",
|
||||
link: "/admin/maintenance/servers",
|
||||
},
|
||||
{
|
||||
text: "Node Management",
|
||||
link: "/admin/maintenance/nodes",
|
||||
},
|
||||
{
|
||||
text: "Subscribe Config",
|
||||
link: "/admin/maintenance/subscribe",
|
||||
},
|
||||
{
|
||||
text: "Product Management",
|
||||
link: "/admin/maintenance/products",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Commerce",
|
||||
items: [
|
||||
{ text: "Order Management", link: "/admin/commerce/orders" },
|
||||
{
|
||||
text: "Coupon Management",
|
||||
link: "/admin/commerce/coupons",
|
||||
},
|
||||
{
|
||||
text: "Marketing Management",
|
||||
link: "/admin/commerce/marketing",
|
||||
},
|
||||
{
|
||||
text: "Announcement Management",
|
||||
link: "/admin/commerce/announcements",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Users & Support",
|
||||
items: [
|
||||
{
|
||||
text: "User Management",
|
||||
link: "/admin/users-support/users",
|
||||
},
|
||||
{
|
||||
text: "Ticket Management",
|
||||
link: "/admin/users-support/tickets",
|
||||
},
|
||||
{
|
||||
text: "Document Management",
|
||||
link: "/admin/users-support/documents",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "System",
|
||||
items: [
|
||||
{ text: "System Config", link: "/admin/system/config" },
|
||||
{ text: "Auth Control", link: "/admin/system/auth-control" },
|
||||
{ text: "Payment Config", link: "/admin/system/payment" },
|
||||
{ text: "ADS Config", link: "/admin/system/ads" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Logs & Analytics",
|
||||
items: [
|
||||
{ text: "Login Logs", link: "/admin/logs/login" },
|
||||
{ text: "Register Logs", link: "/admin/logs/register" },
|
||||
{ text: "Email Logs", link: "/admin/logs/email" },
|
||||
{ text: "Mobile Logs", link: "/admin/logs/mobile" },
|
||||
{ text: "Subscribe Logs", link: "/admin/logs/subscribe" },
|
||||
{
|
||||
text: "Reset Subscribe Logs",
|
||||
link: "/admin/logs/reset-subscribe",
|
||||
},
|
||||
{
|
||||
text: "Subscribe Traffic",
|
||||
link: "/admin/logs/subscribe-traffic",
|
||||
},
|
||||
{
|
||||
text: "Server Traffic",
|
||||
link: "/admin/logs/server-traffic",
|
||||
},
|
||||
{
|
||||
text: "Traffic Details",
|
||||
link: "/admin/logs/traffic-details",
|
||||
},
|
||||
{ text: "Balance Logs", link: "/admin/logs/balance" },
|
||||
{ text: "Commission Logs", link: "/admin/logs/commission" },
|
||||
{ text: "Gift Logs", link: "/admin/logs/gift" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
zh: {
|
||||
label: "简体中文",
|
||||
lang: "zh-CN",
|
||||
link: "/zh/",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: "首页", link: "/zh/" },
|
||||
{ text: "指南", link: "/zh/guide/intro" },
|
||||
{ text: "功能文档", link: "/zh/admin/dashboard" },
|
||||
{ text: "API 文档", link: "/zh/api/reference" },
|
||||
{ text: "赞助商", link: "/zh/sponsors" },
|
||||
{
|
||||
text: "统计",
|
||||
link: "https://stats.ppanel.dev",
|
||||
target: "_blank",
|
||||
},
|
||||
{ text: "主题购买", link: "https://bygga.app", target: "_blank" },
|
||||
],
|
||||
sidebar: {
|
||||
"/zh/api/": [
|
||||
{
|
||||
text: "API",
|
||||
items: [
|
||||
{ text: "概览", link: "/zh/api/reference" },
|
||||
{ text: "通用服务", link: "/zh/api/common" },
|
||||
{ text: "用户服务", link: "/zh/api/user" },
|
||||
{ text: "管理端服务", link: "/zh/api/admin" },
|
||||
{ text: "网关", link: "/zh/api/gateway" },
|
||||
],
|
||||
},
|
||||
],
|
||||
"/zh/guide/": [
|
||||
{
|
||||
text: "指南",
|
||||
items: [
|
||||
{ text: "简介", link: "/zh/guide/intro" },
|
||||
{ text: "贡献指南", link: "/zh/guide/contributing" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "安装部署",
|
||||
items: [
|
||||
{ text: "概览", link: "/zh/guide/installation/" },
|
||||
{
|
||||
text: "Docker Compose",
|
||||
link: "/zh/guide/installation/docker-compose",
|
||||
},
|
||||
{
|
||||
text: "Docker Run",
|
||||
link: "/zh/guide/installation/docker-run",
|
||||
},
|
||||
{
|
||||
text: "二进制部署",
|
||||
link: "/zh/guide/installation/binary",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "分离部署",
|
||||
items: [
|
||||
{
|
||||
text: "后端部署",
|
||||
link: "/zh/guide/separation/backend",
|
||||
},
|
||||
{
|
||||
text: "前端部署",
|
||||
link: "/zh/guide/separation/frontend",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "节点端",
|
||||
items: [
|
||||
{
|
||||
text: "安装",
|
||||
link: "/zh/guide/node/installation",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"/zh/admin/": [
|
||||
{
|
||||
text: "仪表盘",
|
||||
items: [{ text: "总览", link: "/zh/admin/dashboard" }],
|
||||
},
|
||||
{
|
||||
text: "运维管理",
|
||||
items: [
|
||||
{ text: "服务器管理", link: "/zh/admin/maintenance/servers" },
|
||||
{ text: "节点管理", link: "/zh/admin/maintenance/nodes" },
|
||||
{ text: "订阅配置", link: "/zh/admin/maintenance/subscribe" },
|
||||
{ text: "产品管理", link: "/zh/admin/maintenance/products" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "商务管理",
|
||||
items: [
|
||||
{ text: "订单管理", link: "/zh/admin/commerce/orders" },
|
||||
{ text: "优惠券管理", link: "/zh/admin/commerce/coupons" },
|
||||
{ text: "营销管理", link: "/zh/admin/commerce/marketing" },
|
||||
{ text: "公告管理", link: "/zh/admin/commerce/announcements" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "用户与支持",
|
||||
items: [
|
||||
{ text: "用户管理", link: "/zh/admin/users-support/users" },
|
||||
{ text: "工单管理", link: "/zh/admin/users-support/tickets" },
|
||||
{ text: "文档管理", link: "/zh/admin/users-support/documents" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "系统管理",
|
||||
items: [
|
||||
{ text: "系统配置", link: "/zh/admin/system/config" },
|
||||
{ text: "认证控制", link: "/zh/admin/system/auth-control" },
|
||||
{ text: "支付配置", link: "/zh/admin/system/payment" },
|
||||
{ text: "广告配置", link: "/zh/admin/system/ads" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "日志与分析",
|
||||
items: [
|
||||
{ text: "登录日志", link: "/zh/admin/logs/login" },
|
||||
{ text: "注册日志", link: "/zh/admin/logs/register" },
|
||||
{ text: "邮件日志", link: "/zh/admin/logs/email" },
|
||||
{ text: "短信日志", link: "/zh/admin/logs/mobile" },
|
||||
{ text: "订阅日志", link: "/zh/admin/logs/subscribe" },
|
||||
{
|
||||
text: "重置订阅日志",
|
||||
link: "/zh/admin/logs/reset-subscribe",
|
||||
},
|
||||
{ text: "订阅流量", link: "/zh/admin/logs/subscribe-traffic" },
|
||||
{ text: "服务器流量", link: "/zh/admin/logs/server-traffic" },
|
||||
{ text: "流量详情", link: "/zh/admin/logs/traffic-details" },
|
||||
{ text: "余额日志", link: "/zh/admin/logs/balance" },
|
||||
{ text: "佣金日志", link: "/zh/admin/logs/commission" },
|
||||
{ text: "赠送日志", link: "/zh/admin/logs/gift" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
themeConfig: {
|
||||
logo: "/logo.svg",
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/perfect-panel/ppanel" },
|
||||
],
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<iframe
|
||||
class="scalar-iframe"
|
||||
:src="iframeSrc"
|
||||
frameborder="0"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
scrolling="no"
|
||||
:style="{ height: iframeHeight }"
|
||||
@wheel.stop
|
||||
@touchstart.stop
|
||||
@touchmove.stop
|
||||
@mousedown.stop
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { withBase } from "vitepress";
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
specUrl: string;
|
||||
title?: string;
|
||||
theme?: string;
|
||||
layout?: "modern" | "classic";
|
||||
operationId?: string;
|
||||
}>(),
|
||||
{
|
||||
title: "API Reference",
|
||||
theme: "saturn",
|
||||
layout: "classic",
|
||||
}
|
||||
);
|
||||
|
||||
const iframeHeight = ref("1000px");
|
||||
|
||||
const iframeSrc = computed(() => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("spec", props.specUrl);
|
||||
params.set("title", props.title);
|
||||
params.set("theme", props.theme);
|
||||
params.set("layout", props.layout);
|
||||
if (props.operationId) {
|
||||
params.set("operationId", props.operationId);
|
||||
}
|
||||
return withBase(`/scalar/index.html?${params.toString()}`);
|
||||
});
|
||||
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.origin !== window.location.origin) return;
|
||||
const data = event.data;
|
||||
if (data?.type === "scalar-height" && typeof data.height === "number") {
|
||||
iframeHeight.value = `${Math.max(data.height, 800)}px`;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("message", handleMessage);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("message", handleMessage);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scalar-iframe {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
// https://vitepress.dev/guide/custom-theme
|
||||
import Teek from "vitepress-theme-teek";
|
||||
import "vitepress-theme-teek/index.css";
|
||||
import type { Theme } from "vitepress";
|
||||
import ScalarIframe from "./components/ScalarIframe.vue";
|
||||
|
||||
const theme: Theme = {
|
||||
extends: Teek,
|
||||
enhanceApp(ctx) {
|
||||
Teek.enhanceApp?.(ctx);
|
||||
ctx.app.component("ScalarIframe", ScalarIframe);
|
||||
},
|
||||
};
|
||||
|
||||
export default theme;
|
||||
Reference in New Issue
Block a user