物业代码生成

This commit is contained in:
2025-06-18 11:03:42 +08:00
commit 1262d4c745
1881 changed files with 249599 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
<script setup lang="ts">
import { computed } from 'vue';
import PreviewGroup from './preview-group.vue';
interface Props {
files?: string;
}
const props = withDefaults(defineProps<Props>(), { files: '() => []' });
const parsedFiles = computed(() => {
try {
return JSON.parse(decodeURIComponent(props.files ?? ''));
} catch {
return [];
}
});
</script>
<template>
<div class="border-border shadow-float relative rounded-xl border">
<div
class="not-prose relative w-full overflow-x-auto rounded-t-lg px-4 py-6"
>
<div class="flex w-full max-w-[700px] px-2">
<ClientOnly>
<slot v-if="parsedFiles.length > 0"></slot>
<div v-else class="text-destructive text-sm">
<span class="bg-destructive text-foreground rounded-sm px-1 py-1">
ERROR:
</span>
The preview directory does not exist. Please check the 'dir'
parameter.
</div>
</ClientOnly>
</div>
</div>
<PreviewGroup v-if="parsedFiles.length > 0" :files="parsedFiles">
<template v-for="file in parsedFiles" #[file]>
<slot :name="file"></slot>
</template>
</PreviewGroup>
</div>
</template>

View File

@@ -0,0 +1 @@
export { default as DemoPreview } from './demo-preview.vue';

View File

@@ -0,0 +1,110 @@
<script setup lang="ts">
import type { SetupContext } from 'vue';
import { computed, ref, useSlots } from 'vue';
import { VbenTooltip } from '@vben-core/shadcn-ui';
import { Code } from 'lucide-vue-next';
import {
TabsContent,
TabsIndicator,
TabsList,
TabsRoot,
TabsTrigger,
} from 'radix-vue';
defineOptions({
inheritAttrs: false,
});
const props = withDefaults(
defineProps<{
files?: string[];
}>(),
{ files: () => [] },
);
const open = ref(false);
const slots: SetupContext['slots'] = useSlots();
const tabs = computed(() => {
return props.files.map((file) => {
return {
component: slots[file],
label: file,
};
});
});
const currentTab = ref('index.vue');
const toggleOpen = () => {
open.value = !open.value;
};
</script>
<template>
<TabsRoot
v-model="currentTab"
class="bg-background-deep border-border overflow-hidden rounded-b-xl border-t"
@update:model-value="open = true"
>
<div class="border-border bg-background flex border-b-2 pr-2">
<div class="flex w-full items-center justify-between text-[13px]">
<TabsList class="relative flex">
<template v-if="open">
<TabsIndicator
class="absolute bottom-0 left-0 h-[2px] w-[--radix-tabs-indicator-size] translate-x-[--radix-tabs-indicator-position] rounded-full transition-[width,transform] duration-300"
>
<div class="size-full bg-[var(--vp-c-indigo-1)]"></div>
</TabsIndicator>
<TabsTrigger
v-for="(tab, index) in tabs"
:key="index"
:value="tab.label"
class="border-box text-foreground px-4 py-3 data-[state=active]:text-[var(--vp-c-indigo-1)]"
tabindex="-1"
>
{{ tab.label }}
</TabsTrigger>
</template>
</TabsList>
<div
:class="{
'py-2': !open,
}"
class="flex items-center"
>
<VbenTooltip side="top">
<template #trigger>
<Code
class="hover:bg-accent size-7 cursor-pointer rounded-full p-1.5"
@click="toggleOpen"
/>
</template>
{{ open ? 'Collapse code' : 'Expand code' }}
</VbenTooltip>
</div>
</div>
</div>
<div
:class="`${open ? 'h-[unset] max-h-[80vh]' : 'h-0'}`"
class="block overflow-y-scroll bg-[var(--vp-code-block-bg)] transition-all duration-300"
>
<TabsContent
v-for="tab in tabs"
:key="tab.label"
:value="tab.label"
as-child
class="rounded-xl"
>
<div class="text-foreground relative rounded-xl">
<component :is="tab.component" class="border-0" />
</div>
</TabsContent>
</div>
</TabsRoot>
</template>

View File

@@ -0,0 +1,231 @@
import type { DefaultTheme } from 'vitepress';
import { defineConfig } from 'vitepress';
import { version } from '../../../package.json';
export const en = defineConfig({
description: 'Vben Admin & Enterprise level management system framework',
lang: 'en-US',
themeConfig: {
darkModeSwitchLabel: 'Theme',
darkModeSwitchTitle: 'Switch to Dark Mode',
docFooter: {
next: 'Next Page',
prev: 'Previous Page',
},
editLink: {
pattern:
'https://github.com/vbenjs/vue-vben-admin/edit/main/docs/src/:path',
text: 'Edit this page on GitHub',
},
footer: {
copyright: `Copyright © 2020-${new Date().getFullYear()} Vben`,
message: 'Released under the MIT License.',
},
langMenuLabel: 'Language',
lastUpdated: {
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium',
},
text: 'Last updated on',
},
lightModeSwitchTitle: 'Switch to Light Mode',
nav: nav(),
outline: {
label: 'Navigate',
},
returnToTopLabel: 'Back to top',
sidebar: {
'/en/commercial/': {
base: '/en/commercial/',
items: sidebarCommercial(),
},
'/en/guide/': { base: '/en/guide/', items: sidebarGuide() },
},
},
});
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
collapsed: false,
text: 'Introduction',
items: [
{
link: 'introduction/vben',
text: 'About Vben Admin',
},
{
link: 'introduction/why',
text: 'Why Choose Us?',
},
{ link: 'introduction/quick-start', text: 'Quick Start' },
{ link: 'introduction/thin', text: 'Lite Version' },
],
},
{
text: 'Basics',
items: [
{ link: 'essentials/concept', text: 'Basic Concepts' },
{ link: 'essentials/development', text: 'Local Development' },
{ link: 'essentials/route', text: 'Routing and Menu' },
{ link: 'essentials/settings', text: 'Configuration' },
{ link: 'essentials/icons', text: 'Icons' },
{ link: 'essentials/styles', text: 'Styles' },
{ link: 'essentials/external-module', text: 'External Modules' },
{ link: 'essentials/build', text: 'Build and Deployment' },
{ link: 'essentials/server', text: 'Server Interaction and Data Mock' },
],
},
{
text: 'Advanced',
items: [
{ link: 'in-depth/login', text: 'Login' },
{ link: 'in-depth/theme', text: 'Theme' },
{ link: 'in-depth/access', text: 'Access Control' },
{ link: 'in-depth/locale', text: 'Internationalization' },
{ link: 'in-depth/features', text: 'Common Features' },
{ link: 'in-depth/check-updates', text: 'Check Updates' },
{ link: 'in-depth/loading', text: 'Global Loading' },
{ link: 'in-depth/ui-framework', text: 'UI Framework Switching' },
],
},
{
text: 'Engineering',
items: [
{ link: 'project/standard', text: 'Standards' },
{ link: 'project/cli', text: 'CLI' },
{ link: 'project/dir', text: 'Directory Explanation' },
{ link: 'project/test', text: 'Unit Testing' },
{ link: 'project/tailwindcss', text: 'Tailwind CSS' },
{ link: 'project/changeset', text: 'Changeset' },
{ link: 'project/vite', text: 'Vite Config' },
],
},
{
text: 'Others',
items: [
{ link: 'other/project-update', text: 'Project Update' },
{ link: 'other/remove-code', text: 'Remove Code' },
{ link: 'other/faq', text: 'FAQ' },
],
},
];
}
function sidebarCommercial(): DefaultTheme.SidebarItem[] {
return [
{
link: 'community',
text: 'Community',
},
{
link: 'technical-support',
text: 'Technical-support',
},
{
link: 'customized',
text: 'Customized',
},
];
}
function nav(): DefaultTheme.NavItem[] {
return [
{
activeMatch: '^/en/(guide|components)/',
text: 'Doc',
items: [
{
activeMatch: '^/en/guide/',
link: '/en/guide/introduction/vben',
text: 'Guide',
},
// {
// activeMatch: '^/en/components/',
// link: '/en/components/introduction',
// text: 'Components',
// },
{
text: 'Historical Versions',
items: [
{
link: 'https://doc.vvbin.cn',
text: '2.x Version Documentation',
},
],
},
],
},
{
text: 'Demo',
items: [
{
text: 'Vben Admin',
items: [
{
link: 'https://www.vben.pro',
text: 'Demo Version',
},
{
link: 'https://ant.vben.pro',
text: 'Ant Design Vue Version',
},
{
link: 'https://naive.vben.pro',
text: 'Naive Version',
},
{
link: 'https://ele.vben.pro',
text: 'Element Plus Version',
},
],
},
{
text: 'Others',
items: [
{
link: 'https://vben.vvbin.cn',
text: 'Vben Admin 2.x',
},
],
},
],
},
{
text: version,
items: [
{
link: 'https://github.com/vbenjs/vue-vben-admin/releases',
text: 'Changelog',
},
{
link: 'https://github.com/orgs/vbenjs/projects/5',
text: 'Roadmap',
},
{
link: 'https://github.com/vbenjs/vue-vben-admin/blob/main/.github/contributing.md',
text: 'Contribution',
},
],
},
{
link: '/commercial/technical-support',
text: '🦄 Tech Support',
},
{
link: '/sponsor/personal',
text: '✨ Sponsor',
},
{
link: '/commercial/community',
text: '👨‍👦‍👦 Community',
},
// {
// link: '/friend-links/',
// text: '🤝 Friend Links',
// },
];
}

View File

@@ -0,0 +1,25 @@
import { withPwa } from '@vite-pwa/vitepress';
import { defineConfigWithTheme } from 'vitepress';
import { en } from './en.mts';
import { shared } from './shared.mts';
import { zh } from './zh.mts';
export default withPwa(
defineConfigWithTheme({
...shared,
locales: {
en: {
label: 'English',
lang: 'en',
link: '/en/',
...en,
},
root: {
label: '简体中文',
lang: 'zh-CN',
...zh,
},
},
}),
);

View File

@@ -0,0 +1,143 @@
import type { MarkdownEnv, MarkdownRenderer } from 'vitepress';
import crypto from 'node:crypto';
import { readdirSync } from 'node:fs';
import { join } from 'node:path';
export const rawPathRegexp =
// eslint-disable-next-line regexp/no-super-linear-backtracking, regexp/strict
/^(.+?(?:\.([\da-z]+))?)(#[\w-]+)?(?: ?{(\d+(?:[,-]\d+)*)? ?(\S+)?})? ?(?:\[(.+)])?$/;
function rawPathToToken(rawPath: string) {
const [
filepath = '',
extension = '',
region = '',
lines = '',
lang = '',
rawTitle = '',
] = (rawPathRegexp.exec(rawPath) || []).slice(1);
const title = rawTitle || filepath.split('/').pop() || '';
return { extension, filepath, lang, lines, region, title };
}
export const demoPreviewPlugin = (md: MarkdownRenderer) => {
md.core.ruler.after('inline', 'demo-preview', (state) => {
const insertComponentImport = (importString: string) => {
const index = state.tokens.findIndex(
(i) => i.type === 'html_block' && i.content.match(/<script setup>/g),
);
if (index === -1) {
const importComponent = new state.Token('html_block', '', 0);
importComponent.content = `<script setup>\n${importString}\n</script>\n`;
state.tokens.splice(0, 0, importComponent);
} else {
if (state.tokens[index]) {
const content = state.tokens[index].content;
state.tokens[index].content = content.replace(
'</script>',
`${importString}\n</script>`,
);
}
}
};
// Define the regular expression to match the desired pattern
const regex = /<DemoPreview[^>]*\sdir="([^"]*)"/g;
// Iterate through the Markdown content and replace the pattern
state.src = state.src.replaceAll(regex, (_match, dir) => {
const componentDir = join(process.cwd(), 'src', dir).replaceAll(
'\\',
'/',
);
let childFiles: string[] = [];
let dirExists = true;
try {
childFiles =
readdirSync(componentDir, {
encoding: 'utf8',
recursive: false,
withFileTypes: false,
}) || [];
} catch {
dirExists = false;
}
if (!dirExists) {
return '';
}
const uniqueWord = generateContentHash(componentDir);
const ComponentName = `DemoComponent_${uniqueWord}`;
insertComponentImport(
`import ${ComponentName} from '${componentDir}/index.vue'`,
);
const { path: _path } = state.env as MarkdownEnv;
const index = state.tokens.findIndex((i) => i.content.match(regex));
if (!state.tokens[index]) {
return '';
}
const firstString = 'index.vue';
childFiles = childFiles.sort((a, b) => {
if (a === firstString) return -1;
if (b === firstString) return 1;
return a.localeCompare(b, 'en', { sensitivity: 'base' });
});
state.tokens[index].content =
`<DemoPreview files="${encodeURIComponent(JSON.stringify(childFiles))}" ><${ComponentName}/>
`;
const _dummyToken = new state.Token('', '', 0);
const tokenArray: Array<typeof _dummyToken> = [];
childFiles.forEach((filename) => {
// const slotName = filename.replace(extname(filename), '');
const templateStart = new state.Token('html_inline', '', 0);
templateStart.content = `<template #${filename}>`;
tokenArray.push(templateStart);
const resolvedPath = join(componentDir, filename);
const { extension, filepath, lang, lines, title } =
rawPathToToken(resolvedPath);
// Add code tokens for each line
const token = new state.Token('fence', 'code', 0);
token.info = `${lang || extension}${lines ? `{${lines}}` : ''}${
title ? `[${title}]` : ''
}`;
token.content = `<<< ${filepath}`;
(token as any).src = [resolvedPath];
tokenArray.push(token);
const templateEnd = new state.Token('html_inline', '', 0);
templateEnd.content = '</template>';
tokenArray.push(templateEnd);
});
const endTag = new state.Token('html_inline', '', 0);
endTag.content = '</DemoPreview>';
tokenArray.push(endTag);
state.tokens.splice(index + 1, 0, ...tokenArray);
// console.log(
// state.md.renderer.render(state.tokens, state?.options ?? [], state.env),
// );
return '';
});
});
};
function generateContentHash(input: string, length: number = 10): string {
// 使用 SHA-256 生成哈希值
const hash = crypto.createHash('sha256').update(input).digest('hex');
// 将哈希值转换为 Base36 编码,并取指定长度的字符作为结果
return Number.parseInt(hash, 16).toString(36).slice(0, length);
}

View File

@@ -0,0 +1,172 @@
import type { PwaOptions } from '@vite-pwa/vitepress';
import type { HeadConfig } from 'vitepress';
import { resolve } from 'node:path';
import {
viteArchiverPlugin,
viteVxeTableImportsPlugin,
} from '@vben/vite-config';
import {
GitChangelog,
GitChangelogMarkdownSection,
} from '@nolebase/vitepress-plugin-git-changelog/vite';
import tailwind from 'tailwindcss';
import { defineConfig, postcssIsolateStyles } from 'vitepress';
import {
groupIconMdPlugin,
groupIconVitePlugin,
} from 'vitepress-plugin-group-icons';
import { demoPreviewPlugin } from './plugins/demo-preview';
import { search as zhSearch } from './zh.mts';
export const shared = defineConfig({
appearance: 'dark',
head: head(),
markdown: {
preConfig(md) {
md.use(demoPreviewPlugin);
md.use(groupIconMdPlugin);
},
},
pwa: pwa(),
srcDir: 'src',
themeConfig: {
i18nRouting: true,
logo: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
search: {
options: {
locales: {
...zhSearch,
},
},
provider: 'local',
},
siteTitle: 'Vben Admin',
socialLinks: [
{ icon: 'github', link: 'https://github.com/vbenjs/vue-vben-admin' },
],
},
title: 'Vben Admin',
vite: {
build: {
chunkSizeWarningLimit: Infinity,
minify: 'terser',
},
css: {
postcss: {
plugins: [
tailwind(),
postcssIsolateStyles({ includeFiles: [/vp-doc\.css/] }),
],
},
preprocessorOptions: {
scss: {
api: 'modern',
},
},
},
json: {
stringify: true,
},
plugins: [
GitChangelog({
mapAuthors: [
{
mapByNameAliases: ['Vben'],
name: 'vben',
username: 'anncwb',
},
{
name: 'vince',
username: 'vince292007',
},
{
name: 'Li Kui',
username: 'likui628',
},
],
repoURL: () => 'https://github.com/vbenjs/vue-vben-admin',
}),
GitChangelogMarkdownSection(),
viteArchiverPlugin({ outputDir: '.vitepress' }),
groupIconVitePlugin(),
await viteVxeTableImportsPlugin(),
],
server: {
fs: {
allow: ['../..'],
},
host: true,
port: 6173,
},
ssr: {
external: ['@vue/repl'],
},
},
});
function head(): HeadConfig[] {
return [
['meta', { content: 'Vbenjs Team', name: 'author' }],
[
'meta',
{
content: 'vben, vitejs, vite, shacdn-ui, vue',
name: 'keywords',
},
],
['link', { href: '/favicon.ico', rel: 'icon', type: 'image/svg+xml' }],
[
'meta',
{
content:
'width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no',
name: 'viewport',
},
],
['meta', { content: 'vben admin docs', name: 'keywords' }],
['link', { href: '/favicon.ico', rel: 'icon' }],
// [
// 'script',
// {
// src: 'https://cdn.tailwindcss.com',
// },
// ],
];
}
function pwa(): PwaOptions {
return {
includeManifestIcons: false,
manifest: {
description:
'Vben Admin is a modern admin dashboard template based on Vue 3. ',
icons: [
{
sizes: '192x192',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/pwa-icon-192.png',
type: 'image/png',
},
{
sizes: '512x512',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/pwa-icon-512.png',
type: 'image/png',
},
],
id: '/',
name: 'Vben Admin Doc',
short_name: 'vben_admin_doc',
theme_color: '#ffffff',
},
outDir: resolve(process.cwd(), '.vitepress/dist'),
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{css,js,html,svg,png,ico,txt,woff2}'],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
},
};
}

View File

@@ -0,0 +1,358 @@
import type { DefaultTheme } from 'vitepress';
import { defineConfig } from 'vitepress';
import { version } from '../../../package.json';
export const zh = defineConfig({
description: 'Vben Admin & 企业级管理系统框架',
lang: 'zh-Hans',
themeConfig: {
darkModeSwitchLabel: '主题',
darkModeSwitchTitle: '切换到深色模式',
docFooter: {
next: '下一页',
prev: '上一页',
},
editLink: {
pattern:
'https://github.com/vbenjs/vue-vben-admin/edit/main/docs/src/:path',
text: '在 GitHub 上编辑此页面',
},
footer: {
copyright: `Copyright © 2020-${new Date().getFullYear()} Vben`,
message: '基于 MIT 许可发布.',
},
langMenuLabel: '多语言',
lastUpdated: {
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium',
},
text: '最后更新于',
},
lightModeSwitchTitle: '切换到浅色模式',
nav: nav(),
outline: {
label: '页面导航',
},
returnToTopLabel: '回到顶部',
sidebar: {
'/commercial/': { base: '/commercial/', items: sidebarCommercial() },
'/components/': { base: '/components/', items: sidebarComponents() },
'/guide/': { base: '/guide/', items: sidebarGuide() },
},
sidebarMenuLabel: '菜单',
},
});
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
collapsed: false,
text: '简介',
items: [
{
link: 'introduction/vben',
text: '关于 Vben Admin',
},
{
link: 'introduction/why',
text: '为什么选择我们?',
},
{ link: 'introduction/quick-start', text: '快速开始' },
{ link: 'introduction/thin', text: '精简版本' },
{
base: '/',
link: 'components/introduction',
text: '组件文档',
},
],
},
{
text: '基础',
items: [
{ link: 'essentials/concept', text: '基础概念' },
{ link: 'essentials/development', text: '本地开发' },
{ link: 'essentials/route', text: '路由和菜单' },
{ link: 'essentials/settings', text: '配置' },
{ link: 'essentials/icons', text: '图标' },
{ link: 'essentials/styles', text: '样式' },
{ link: 'essentials/external-module', text: '外部模块' },
{ link: 'essentials/build', text: '构建与部署' },
{ link: 'essentials/server', text: '服务端交互与数据Mock' },
],
},
{
text: '深入',
items: [
{ link: 'in-depth/login', text: '登录' },
// { link: 'in-depth/layout', text: '布局' },
{ link: 'in-depth/theme', text: '主题' },
{ link: 'in-depth/access', text: '权限' },
{ link: 'in-depth/locale', text: '国际化' },
{ link: 'in-depth/features', text: '常用功能' },
{ link: 'in-depth/check-updates', text: '检查更新' },
{ link: 'in-depth/loading', text: '全局loading' },
{ link: 'in-depth/ui-framework', text: '组件库切换' },
],
},
{
text: '工程',
items: [
{ link: 'project/standard', text: '规范' },
{ link: 'project/cli', text: 'CLI' },
{ link: 'project/dir', text: '目录说明' },
{ link: 'project/test', text: '单元测试' },
{ link: 'project/tailwindcss', text: 'Tailwind CSS' },
{ link: 'project/changeset', text: 'Changeset' },
{ link: 'project/vite', text: 'Vite Config' },
],
},
{
text: '其他',
items: [
{ link: 'other/project-update', text: '项目更新' },
{ link: 'other/remove-code', text: '移除代码' },
{ link: 'other/faq', text: '常见问题' },
],
},
];
}
function sidebarCommercial(): DefaultTheme.SidebarItem[] {
return [
{
link: 'community',
text: '交流群',
},
{
link: 'technical-support',
text: '技术支持',
},
{
link: 'customized',
text: '定制开发',
},
];
}
function sidebarComponents(): DefaultTheme.SidebarItem[] {
return [
{
text: '组件',
items: [
{
link: 'introduction',
text: '介绍',
},
],
},
{
collapsed: false,
text: '布局组件',
items: [
{
link: 'layout-ui/page',
text: 'Page 页面',
},
],
},
{
collapsed: false,
text: '通用组件',
items: [
{
link: 'common-ui/vben-api-component',
text: 'ApiComponent Api组件包装器',
},
{
link: 'common-ui/vben-alert',
text: 'Alert 轻量提示框',
},
{
link: 'common-ui/vben-modal',
text: 'Modal 模态框',
},
{
link: 'common-ui/vben-drawer',
text: 'Drawer 抽屉',
},
{
link: 'common-ui/vben-form',
text: 'Form 表单',
},
{
link: 'common-ui/vben-vxe-table',
text: 'Vxe Table 表格',
},
{
link: 'common-ui/vben-count-to-animator',
text: 'CountToAnimator 数字动画',
},
{
link: 'common-ui/vben-ellipsis-text',
text: 'EllipsisText 省略文本',
},
],
},
];
}
function nav(): DefaultTheme.NavItem[] {
return [
{
activeMatch: '^/(guide|components)/',
text: '文档',
items: [
{
activeMatch: '^/guide/',
link: '/guide/introduction/vben',
text: '指南',
},
{
activeMatch: '^/components/',
link: '/components/introduction',
text: '组件',
},
{
text: '历史版本',
items: [
{
link: 'https://doc.vvbin.cn',
text: '2.x版本文档',
},
],
},
],
},
{
text: '演示',
items: [
{
text: 'Vben Admin',
items: [
{
link: 'https://www.vben.pro',
text: '演示版本',
},
{
link: 'https://ant.vben.pro',
text: 'Ant Design Vue 版本',
},
{
link: 'https://naive.vben.pro',
text: 'Naive 版本',
},
{
link: 'https://ele.vben.pro',
text: 'Element Plus版本',
},
],
},
{
text: '其他',
items: [
{
link: 'https://vben.vvbin.cn',
text: 'Vben Admin 2.x',
},
],
},
],
},
{
text: version,
items: [
{
link: 'https://github.com/vbenjs/vue-vben-admin/releases',
text: '更新日志',
},
{
link: 'https://github.com/orgs/vbenjs/projects/5',
text: '路线图',
},
{
link: 'https://github.com/vbenjs/vue-vben-admin/blob/main/.github/contributing.md',
text: '贡献',
},
],
},
{
link: '/commercial/technical-support',
text: '🦄 技术支持',
},
{
link: '/sponsor/personal',
text: '✨ 赞助',
},
{
link: '/commercial/community',
text: '👨‍👦‍👦 交流群',
// items: [
// {
// link: 'https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&appChannel=share&inviteCode=22ySzj7pKiw&businessType=9&from=246610&biz=ka&mainSourceId=share&subSourceId=others&jumpsource=shorturl#/pc',
// text: 'QQ频道',
// },
// {
// link: 'https://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=mjZmlhgVzzUxvdxllB6C1vHpX8O8QRL0&authKey=DBdFbBwERmfaKY95JvRWqLCJIRGJAmKyZbrpzZ41EKDMZ5SR6MfbjOBaaNRN73fr&noverify=0&group_code=4286109',
// text: 'QQ群',
// },
// {
// link: 'https://discord.gg/VU62jTecad',
// text: 'Discord',
// },
// ],
},
// {
// link: '/friend-links/',
// text: '🤝 友情链接',
// },
];
}
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
root: {
placeholder: '搜索文档',
translations: {
button: {
buttonAriaLabel: '搜索文档',
buttonText: '搜索文档',
},
modal: {
errorScreen: {
helpText: '你可能需要检查你的网络连接',
titleText: '无法获取结果',
},
footer: {
closeText: '关闭',
navigateText: '切换',
searchByText: '搜索提供者',
selectText: '选择',
},
noResultsScreen: {
noResultsText: '无法找到相关结果',
reportMissingResultsLinkText: '点击反馈',
reportMissingResultsText: '你认为该查询应该有结果?',
suggestedQueryText: '你可以尝试查询',
},
searchBox: {
cancelButtonAriaLabel: '取消',
cancelButtonText: '取消',
resetButtonAriaLabel: '清除查询条件',
resetButtonTitle: '清除查询条件',
},
startScreen: {
favoriteSearchesTitle: '收藏',
noRecentSearchesText: '没有搜索历史',
recentSearchesTitle: '搜索历史',
removeFavoriteSearchButtonTitle: '从收藏中移除',
removeRecentSearchButtonTitle: '从搜索历史中移除',
saveRecentSearchButtonTitle: '保存至搜索历史',
},
},
},
},
};

View File

@@ -0,0 +1,96 @@
<script lang="ts" setup>
import {
computed,
nextTick,
onBeforeUnmount,
onMounted,
ref,
watch,
} from 'vue';
// import { useAntdDesignTokens } from '@vben/hooks';
// import { initPreferences } from '@vben/preferences';
import { ConfigProvider, theme } from 'ant-design-vue';
import mediumZoom from 'medium-zoom';
import { useRoute } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
const { Layout } = DefaultTheme;
const route = useRoute();
// const { tokens } = useAntdDesignTokens();
const initZoom = () => {
// mediumZoom('[data-zoomable]', { background: 'var(--vp-c-bg)' });
mediumZoom('.VPContent img', { background: 'var(--vp-c-bg)' });
};
const isDark = ref(true);
watch(
() => route.path,
() => nextTick(() => initZoom()),
);
// initPreferences({
// namespace: 'docs',
// });
onMounted(() => {
initZoom();
});
// 使用该函数
const observer = watchDarkModeChange((dark) => {
isDark.value = dark;
});
onBeforeUnmount(() => {
observer?.disconnect();
});
function watchDarkModeChange(callback: (isDark: boolean) => void) {
if (typeof window === 'undefined') {
return;
}
const htmlElement = document.documentElement;
const observer = new MutationObserver(() => {
const isDark = htmlElement.classList.contains('dark');
callback(isDark);
});
observer.observe(htmlElement, {
attributeFilter: ['class'],
attributes: true,
});
const initialIsDark = htmlElement.classList.contains('dark');
callback(initialIsDark);
return observer;
}
const tokenTheme = computed(() => {
const algorithm = isDark.value
? [theme.darkAlgorithm]
: [theme.defaultAlgorithm];
return {
algorithm,
// token: tokens,
};
});
</script>
<template>
<ConfigProvider :theme="tokenTheme">
<Layout />
</ConfigProvider>
</template>
<style>
.medium-zoom-overlay,
.medium-zoom-image--opened {
z-index: 2147483647;
}
</style>

View File

@@ -0,0 +1,29 @@
<script setup lang="ts"></script>
<template>
<div class="vp-doc vben-contributors">
<p>Contributors</p>
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
<img
alt="Contributors"
src="https://opencollective.com/vbenjs/contributors.svg?button=false"
/>
</a>
</div>
</template>
<style scoped>
.vben-contributors {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 60px;
p {
margin-bottom: 50px;
font-size: 30px;
font-weight: 700;
}
}
</style>

View File

@@ -0,0 +1,29 @@
// https://vitepress.dev/guide/custom-theme
import type { EnhanceAppContext, Theme } from 'vitepress';
import { NolebaseGitChangelogPlugin } from '@nolebase/vitepress-plugin-git-changelog/client';
import DefaultTheme from 'vitepress/theme';
import { DemoPreview } from '../components';
import SiteLayout from './components/site-layout.vue';
import VbenContributors from './components/vben-contributors.vue';
import { initHmPlugin } from './plugins/hm';
import './styles';
import 'virtual:group-icons.css';
import '@nolebase/vitepress-plugin-git-changelog/client/style.css';
export default {
async enhanceApp(ctx: EnhanceAppContext) {
const { app } = ctx;
app.component('VbenContributors', VbenContributors);
app.component('DemoPreview', DemoPreview);
app.use(NolebaseGitChangelogPlugin);
// 百度统计
initHmPlugin();
},
extends: DefaultTheme,
Layout: SiteLayout,
} satisfies Theme;

View File

@@ -0,0 +1,28 @@
import { inBrowser } from 'vitepress';
const SITE_ID = '2e443a834727c065877c01d89921545e';
declare global {
interface Window {
_hmt: any;
}
}
function registerAnalytics() {
window._hmt = window._hmt || [];
const script = document.createElement('script');
script.innerHTML = `var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?${SITE_ID}";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})()`;
document.querySelector('head')?.append(script);
}
export function initHmPlugin() {
if (inBrowser && import.meta.env.PROD) {
registerAnalytics();
}
}

View File

@@ -0,0 +1,22 @@
html.dark {
color-scheme: dark;
}
.dark .VPContent {
/* background-color: #14161a; */
}
.form-valid-error p {
margin: 0;
}
/* 顶部导航栏选中项样式 */
.VPNavBarMenuLink,
.VPNavBarMenuGroup {
border-bottom: 1px solid transparent;
}
.VPNavBarMenuLink.active,
.VPNavBarMenuGroup.active {
border-bottom-color: var(--vp-c-brand-1);
}

View File

@@ -0,0 +1,4 @@
import '@vben/styles';
import './variables.css';
import './base.css';

View File

@@ -0,0 +1,132 @@
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
*
* Each colors have exact same color scale system with 3 levels of solid
* colors with different brightness, and 1 soft color.
*
* - `XXX-1`: The most solid color used mainly for colored text. It must
* satisfy the contrast ratio against when used on top of `XXX-soft`.
*
* - `XXX-2`: The color used mainly for hover state of the button.
*
* - `XXX-3`: The color for solid background, such as bg color of the button.
* It must satisfy the contrast ratio with pure white (#ffffff) text on
* top of it.
*
* - `XXX-soft`: The color used for subtle background such as custom container
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
* on top of it.
*
* The soft color must be semi transparent alpha channel. This is crucial
* because it allows adding multiple "soft" colors on top of each other
* to create a accent, such as when having inline code block inside
* custom containers.
*
* - `default`: The color used purely for subtle indication without any
* special meanings attched to it such as bg color for menu hover state.
*
* - `brand`: Used for primary brand colors, such as link text, button with
* brand theme, etc.
*
* - `tip`: Used to indicate useful information. The default theme uses the
* brand color for this by default.
*
* - `warning`: Used to indicate warning to the users. Used in custom
* container, badges, etc.
*
* - `danger`: Used to show error, or dangerous message to the users. Used
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
:root {
/* --vp-c-indigo-1: #4f69fd; */
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
/**
* Component: Button
* -------------------------------------------------------------------------- */
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
/**
* Component: Home
* -------------------------------------------------------------------------- */
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: linear-gradient(
120deg,
var(--vp-c-indigo-1) 30%,
#18cefe
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#18cefe 50%,
#c279ed 50%
);
--vp-home-hero-image-filter: blur(44px);
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
/**
* modal zIndex
*/
--popup-z-index: 1000;
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}