chore: docs 迁移至 apps
45
apps/docs/.vitepress/components/demo-preview.vue
Normal 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>
|
||||
1
apps/docs/.vitepress/components/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as DemoPreview } from './demo-preview.vue';
|
||||
110
apps/docs/.vitepress/components/preview-group.vue
Normal 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>
|
||||
231
apps/docs/.vitepress/config/en.mts
Normal file
@@ -0,0 +1,231 @@
|
||||
import type { DefaultTheme } from 'vitepress';
|
||||
|
||||
import { defineConfig } from 'vitepress';
|
||||
|
||||
import { version } from '../../../package.json';
|
||||
|
||||
export const en = defineConfig({
|
||||
description: 'y-code-platform & 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 y-code-platform',
|
||||
},
|
||||
{
|
||||
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: 'y-code-platform',
|
||||
items: [
|
||||
{
|
||||
link: 'https://y-code-platform.shiyuegame.com',
|
||||
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: 'y-code-platform 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',
|
||||
// },
|
||||
];
|
||||
}
|
||||
24
apps/docs/.vitepress/config/index.mts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { withPwa } from '@vite-pwa/vitepress';
|
||||
import { defineConfigWithTheme } from 'vitepress';
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
143
apps/docs/.vitepress/config/plugins/demo-preview.ts
Normal 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);
|
||||
}
|
||||
164
apps/docs/.vitepress/config/shared.mts
Normal file
@@ -0,0 +1,164 @@
|
||||
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: '悦码',
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://ptdata-gitlab.shiyue.com/sy3570' },
|
||||
],
|
||||
},
|
||||
title: '悦码',
|
||||
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: ['王雪峰'],
|
||||
name: 'wangxuefeng',
|
||||
username: 'wangxuefeng',
|
||||
},
|
||||
],
|
||||
repoURL: () => 'https://ptdata-gitlab.shiyue.com/workbench/y-code',
|
||||
}),
|
||||
GitChangelogMarkdownSection(),
|
||||
viteArchiverPlugin({ outputDir: '.vitepress' }),
|
||||
groupIconVitePlugin(),
|
||||
await viteVxeTableImportsPlugin(),
|
||||
],
|
||||
server: {
|
||||
fs: {
|
||||
allow: ['../..'],
|
||||
},
|
||||
host: true,
|
||||
port: 6173,
|
||||
},
|
||||
|
||||
ssr: {
|
||||
external: ['@vue/repl'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function head(): HeadConfig[] {
|
||||
return [
|
||||
['meta', { content: 'wangxuefeng', 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:
|
||||
'y-code-platform is a low-code management platform based on Vue 3 & vtj.pro ',
|
||||
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: 'y-code-platform Doc',
|
||||
short_name: 'y-code-platform_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,
|
||||
},
|
||||
};
|
||||
}
|
||||
289
apps/docs/.vitepress/config/zh.mts
Normal file
@@ -0,0 +1,289 @@
|
||||
import type { DefaultTheme } from 'vitepress';
|
||||
|
||||
import { defineConfig } from 'vitepress';
|
||||
|
||||
import { version } from '../../../package.json';
|
||||
|
||||
export const zh = defineConfig({
|
||||
description: '悦码 & 低代码管理系统',
|
||||
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/platform',
|
||||
text: '关于悦码',
|
||||
},
|
||||
{ link: 'introduction/quick-start', text: '快速开始' },
|
||||
{
|
||||
base: '/',
|
||||
link: 'components/index',
|
||||
text: '组件文档',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '基础',
|
||||
items: [
|
||||
{ link: 'essentials/concept', text: '基础概念' },
|
||||
{ link: 'essentials/icons', text: '图标' },
|
||||
{ link: 'essentials/styles', text: '样式' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '深入',
|
||||
items: [{ link: 'in-depth/features', text: '常用功能' }],
|
||||
},
|
||||
{
|
||||
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: '技术支持',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
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-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/platform',
|
||||
text: '低代码管理平台',
|
||||
},
|
||||
{
|
||||
activeMatch: '^/renderer/',
|
||||
link: '/renderer/index',
|
||||
text: '渲染器',
|
||||
},
|
||||
{
|
||||
activeMatch: '^/designer/',
|
||||
link: '/designer/index',
|
||||
text: '设计器',
|
||||
},
|
||||
{
|
||||
activeMatch: '^/materials/',
|
||||
link: '/materials/index',
|
||||
text: '物料',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '低代码管理平台',
|
||||
items: [
|
||||
{
|
||||
text: '悦码 2.0',
|
||||
items: [
|
||||
{
|
||||
link: 'https://y-code-platform.shiyuegame.com',
|
||||
text: '生产版本',
|
||||
},
|
||||
{
|
||||
link: 'https://y-code-platform-pre.shiyue.com',
|
||||
text: '预发布版本',
|
||||
},
|
||||
{
|
||||
link: 'https://y-code-platform.shiyuegame.com',
|
||||
text: '演示版本',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '其他',
|
||||
items: [
|
||||
{
|
||||
link: 'https://custom-chart.shiyuegame.com/',
|
||||
text: '悦码 1.0(已废弃,不再更新功能)',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: version,
|
||||
items: [
|
||||
{
|
||||
link: './changelog',
|
||||
text: '更新日志',
|
||||
},
|
||||
{
|
||||
link: 'https://doc.weixin.qq.com/smartsheet/s3_Aa0ASwZ0AOEr2TbPuaMRoCvs1yzjA?scode=AOwAYgeoAAkoT6VFa0Aa0ASwZ0AOE&tab=q979lj&viewId=vpDUFs',
|
||||
text: '更新路线图',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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: '热心大姐',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
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: '保存至搜索历史',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
96
apps/docs/.vitepress/theme/components/site-layout.vue
Normal 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>
|
||||
29
apps/docs/.vitepress/theme/components/vben-contributors.vue
Normal 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>
|
||||
29
apps/docs/.vitepress/theme/index.ts
Normal 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;
|
||||
28
apps/docs/.vitepress/theme/plugins/hm.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
22
apps/docs/.vitepress/theme/styles/base.css
Normal 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);
|
||||
}
|
||||
4
apps/docs/.vitepress/theme/styles/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import '@vben/styles';
|
||||
|
||||
import './variables.css';
|
||||
import './base.css';
|
||||
127
apps/docs/.vitepress/theme/styles/variables.css
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
36
apps/docs/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@sy/y-code-docs",
|
||||
"version": "1.0.0-alpha.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vitepress build",
|
||||
"dev": "vitepress dev",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"imports": {
|
||||
"#/*": {
|
||||
"node": "./src/_env/node/*",
|
||||
"default": "./src/_env/*"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben/common-ui": "workspace:*",
|
||||
"@vben/locales": "workspace:*",
|
||||
"@vben/plugins": "workspace:*",
|
||||
"@vben/styles": "workspace:*",
|
||||
"ant-design-vue": "catalog:",
|
||||
"lucide-vue-next": "catalog:",
|
||||
"medium-zoom": "catalog:",
|
||||
"radix-vue": "catalog:",
|
||||
"vitepress-plugin-group-icons": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nolebase/vitepress-plugin-git-changelog": "catalog:",
|
||||
"@vben/vite-config": "workspace:*",
|
||||
"@vite-pwa/vitepress": "catalog:",
|
||||
"vitepress": "catalog:",
|
||||
"vue": "catalog:"
|
||||
}
|
||||
}
|
||||
128
apps/docs/src/_env/adapter/component.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
|
||||
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
||||
*/
|
||||
|
||||
import type { Component, SetupContext } from 'vue';
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { globalShareState } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputPassword,
|
||||
Mentions,
|
||||
notification,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
const withDefaultPlaceholder = <T extends Component>(
|
||||
component: T,
|
||||
type: 'input' | 'select',
|
||||
) => {
|
||||
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
||||
const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
|
||||
return h(component, { ...props, ...attrs, placeholder }, slots);
|
||||
};
|
||||
};
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type ComponentType =
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
| 'DatePicker'
|
||||
| 'DefaultButton'
|
||||
| 'Divider'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'InputPassword'
|
||||
| 'Mentions'
|
||||
| 'PrimaryButton'
|
||||
| 'Radio'
|
||||
| 'RadioGroup'
|
||||
| 'RangePicker'
|
||||
| 'Rate'
|
||||
| 'Select'
|
||||
| 'Space'
|
||||
| 'Switch'
|
||||
| 'Textarea'
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
| BaseFormComponentType;
|
||||
|
||||
async function initComponentAdapter() {
|
||||
const components: Partial<Record<ComponentType, Component>> = {
|
||||
// 如果你的组件体积比较大,可以使用异步加载
|
||||
// Button: () =>
|
||||
// import('xxx').then((res) => res.Button),
|
||||
|
||||
AutoComplete,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
// 自定义默认按钮
|
||||
DefaultButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
Divider,
|
||||
Input: withDefaultPlaceholder(Input, 'input'),
|
||||
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
|
||||
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
|
||||
Mentions: withDefaultPlaceholder(Mentions, 'input'),
|
||||
// 自定义主要按钮
|
||||
PrimaryButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'primary' }, slots);
|
||||
},
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select: withDefaultPlaceholder(Select, 'select'),
|
||||
Space,
|
||||
Switch,
|
||||
Textarea: withDefaultPlaceholder(Textarea, 'input'),
|
||||
TimePicker,
|
||||
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
|
||||
Upload,
|
||||
};
|
||||
|
||||
// 将组件注册到全局共享状态中
|
||||
globalShareState.setComponents(components);
|
||||
|
||||
// 定义全局共享状态中的消息提示
|
||||
globalShareState.defineMessage({
|
||||
// 复制成功消息提示
|
||||
copyPreferencesSuccess: (title, content) => {
|
||||
notification.success({
|
||||
description: content,
|
||||
message: title,
|
||||
placement: 'bottomRight',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export { initComponentAdapter };
|
||||
47
apps/docs/src/_env/adapter/form.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type {
|
||||
VbenFormSchema as FormSchema,
|
||||
VbenFormProps,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentType } from './component';
|
||||
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { initComponentAdapter } from './component';
|
||||
|
||||
initComponentAdapter();
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
baseModelPropName: 'value',
|
||||
// naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效
|
||||
emptyStateValue: null,
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
70
apps/docs/src/_env/adapter/vxe-table.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { h } from 'vue';
|
||||
|
||||
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { Button, Image } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from './form';
|
||||
|
||||
if (!import.meta.env.SSR) {
|
||||
setupVbenVxeTable({
|
||||
configVxeTable: (vxeUI) => {
|
||||
vxeUI.setConfig({
|
||||
grid: {
|
||||
align: 'center',
|
||||
border: false,
|
||||
columnConfig: {
|
||||
resizable: true,
|
||||
},
|
||||
|
||||
formConfig: {
|
||||
// 全局禁用vxe-table的表单配置,使用formOptions
|
||||
enabled: false,
|
||||
},
|
||||
minHeight: 180,
|
||||
proxyConfig: {
|
||||
autoLoad: true,
|
||||
response: {
|
||||
result: 'items',
|
||||
total: 'total',
|
||||
list: 'items',
|
||||
},
|
||||
showActiveMsg: true,
|
||||
showResponseMsg: false,
|
||||
},
|
||||
round: true,
|
||||
showOverflow: true,
|
||||
size: 'small',
|
||||
},
|
||||
});
|
||||
|
||||
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
||||
vxeUI.renderer.add('CellImage', {
|
||||
renderTableDefault(_renderOpts, params) {
|
||||
const { column, row } = params;
|
||||
return h(Image, { src: row[column.field] });
|
||||
},
|
||||
});
|
||||
|
||||
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
||||
vxeUI.renderer.add('CellLink', {
|
||||
renderTableDefault(renderOpts) {
|
||||
const { props } = renderOpts;
|
||||
return h(
|
||||
Button,
|
||||
{ size: 'small', type: 'link' },
|
||||
{ default: () => props?.text },
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
||||
// vxeUI.formats.add
|
||||
},
|
||||
useVbenForm,
|
||||
});
|
||||
}
|
||||
|
||||
export { useVbenVxeGrid };
|
||||
|
||||
export type * from '@vben/plugins/vxe-table';
|
||||
4
apps/docs/src/_env/node/adapter/form.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const useVbenForm = () => {};
|
||||
export const z = {};
|
||||
export type VbenFormSchema = any;
|
||||
export type VbenFormProps = any;
|
||||
3
apps/docs/src/_env/node/adapter/vxe-table.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type * from '@vben/plugins/vxe-table';
|
||||
|
||||
export const useVbenVxeGrid = () => {};
|
||||
111
apps/docs/src/components/introduction/index.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 快速开始 {#quick-start}
|
||||
|
||||
## 前置准备
|
||||
|
||||
::: info 环境要求
|
||||
|
||||
在启动项目前,你需要确保你的环境满足以下要求:
|
||||
|
||||
- [Node.js](https://nodejs.org/en) 20.15.0 及以上版本,推荐使用 [fnm](https://github.com/Schniz/fnm) 、 [nvm](https://github.com/nvm-sh/nvm) 或者直接使用[pnpm](https://pnpm.io/cli/env) 进行版本管理。
|
||||
- [Git](https://git-scm.com/) 任意版本。
|
||||
|
||||
验证你的环境是否满足以上要求,你可以通过以下命令查看版本:
|
||||
|
||||
```bash
|
||||
# 出现相应 node LTS版本即可
|
||||
node -v
|
||||
# 出现相应 git 版本即可
|
||||
git -v
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 启动项目
|
||||
|
||||
### 获取源码
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [GitHub]
|
||||
# clone 代码
|
||||
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||
```
|
||||
|
||||
```sh [Gitee]
|
||||
# clone 代码
|
||||
# Gitee 的代码可能不是最新的
|
||||
git clone https://gitee.com/annsion/vue-vben-admin.git
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: danger 注意
|
||||
|
||||
注意存放代码的目录及所有父级目录不能存在中文、韩文、日文以及空格,否则安装依赖后启动会出错。
|
||||
|
||||
:::
|
||||
|
||||
### 安装依赖
|
||||
|
||||
在你的代码目录内打开终端,并执行以下命令:
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd vue-vben-admin
|
||||
|
||||
# 使用项目指定的pnpm版本进行依赖安装
|
||||
corepack enable
|
||||
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
```
|
||||
|
||||
::: tip 注意
|
||||
|
||||
- 项目只支持使用 `pnpm` 进行依赖安装,默认会使用 `corepack` 来安装指定版本的 `pnpm`。:
|
||||
- 如果你的网络环境无法访问npm源,你可以设置系统的环境变量`COREPACK_NPM_REGISTRY=https://registry.npmmirror.com`,然后再执行`pnpm install`。
|
||||
- 如果你不想使用`corepack`,你需要禁用`corepack`,然后使用你自己的`pnpm`进行安装。
|
||||
|
||||
:::
|
||||
|
||||
### 运行项目
|
||||
|
||||
#### 选择项目
|
||||
|
||||
执行以下命令运行项目:
|
||||
|
||||
```bash
|
||||
# 启动项目
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
此时,你会看到类似如下的输出,选择你需要运行的项目:
|
||||
|
||||
```bash
|
||||
│
|
||||
◆ Select the app you need to run [dev]:
|
||||
│ ○ @vben/web-antd
|
||||
│ ○ @vben/web-ele
|
||||
│ ○ @vben/web-naive
|
||||
│ ○ @vben/docs
|
||||
│ ● @vben/playground
|
||||
└
|
||||
```
|
||||
|
||||
现在,你可以在浏览器访问 `http://localhost:5555` 查看项目。
|
||||
|
||||
#### 运行指定项目
|
||||
|
||||
如果你不想选择项目,可以直接运行以下命令运行你需要的应用:
|
||||
|
||||
```bash
|
||||
pnpm run dev:antd
|
||||
pnpm run dev:ele
|
||||
pnpm run dev:naive
|
||||
pnpm run dev:docs
|
||||
pnpm run dev:play
|
||||
```
|
||||
1
apps/docs/src/designer/index.md
Normal file
@@ -0,0 +1 @@
|
||||
设计器
|
||||
243
apps/docs/src/guide/essentials/build.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# 构建与部署
|
||||
|
||||
::: tip 前言
|
||||
|
||||
由于是展示项目,所以打包后相对较大,如果项目中没有用到的插件,可以删除对应的文件或者路由,不引用即可,没有引用就不会打包。
|
||||
|
||||
:::
|
||||
|
||||
## 构建
|
||||
|
||||
项目开发完成之后,执行以下命令进行构建:
|
||||
|
||||
**注意:** 请在项目根目录下执行以下命令
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
构建打包成功之后,会在根目录生成对应的应用下的 `dist` 文件夹,里面就是构建打包好的文件,例如: `apps/web-antd/dist/`
|
||||
|
||||
## 预览
|
||||
|
||||
发布之前可以在本地进行预览,有多种方式,这里介绍两种:
|
||||
|
||||
- 使用项目自定的命令进行预览(推荐)
|
||||
|
||||
**注意:** 请在项目根目录下执行以下命令
|
||||
|
||||
```bash
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
等待构建成功后,访问 `http://localhost:4173` 即可查看效果。
|
||||
|
||||
- 本地服务器预览
|
||||
|
||||
可以在电脑全局安装 `serve` 服务,如 `live-server`,
|
||||
|
||||
```bash
|
||||
npm i -g live-server
|
||||
```
|
||||
|
||||
然后在 `dist` 目录下执行 `live-server` 命令,即可在本地查看效果。
|
||||
|
||||
```bash
|
||||
cd apps/web-antd/dist
|
||||
# 本地预览,默认端口8080
|
||||
live-server
|
||||
# 指定端口
|
||||
live-server --port=9000
|
||||
```
|
||||
|
||||
## 压缩
|
||||
|
||||
### 开启 `gzip` 压缩
|
||||
|
||||
需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_COMPRESS=gzip
|
||||
```
|
||||
|
||||
### 开启 `brotli` 压缩
|
||||
|
||||
需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_COMPRESS=brotli
|
||||
```
|
||||
|
||||
### 同时开启 `gzip` 和 `brotli` 压缩
|
||||
|
||||
需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_COMPRESS=gzip,brotli
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
`gzip` 和 `brotli` 都需要安装特定模块才能使用。
|
||||
|
||||
:::
|
||||
|
||||
::: details gzip 与 brotli 在 nginx 内的配置
|
||||
|
||||
```bash
|
||||
http {
|
||||
# 开启gzip
|
||||
gzip on;
|
||||
# 开启gzip_static
|
||||
# gzip_static 开启后可能会报错,需要安装相应的模块, 具体安装方式可以自行查询
|
||||
# 只有这个开启,vue文件打包的.gz文件才会有效果,否则不需要开启gzip进行打包
|
||||
gzip_static on;
|
||||
gzip_proxied any;
|
||||
gzip_min_length 1k;
|
||||
gzip_buffers 4 16k;
|
||||
#如果nginx中使用了多层代理 必须设置这个才可以开启gzip。
|
||||
gzip_http_version 1.0;
|
||||
gzip_comp_level 2;
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
gzip_vary off;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
# 开启 brotli压缩
|
||||
# 需要安装对应的nginx模块,具体安装方式可以自行查询
|
||||
# 可以与gzip共存不会冲突
|
||||
brotli on;
|
||||
brotli_comp_level 6;
|
||||
brotli_buffers 16 8k;
|
||||
brotli_min_length 20;
|
||||
brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 构建分析
|
||||
|
||||
如果你的构建文件很大,可以通过项目内置 [rollup-plugin-analyzer](https://github.com/doesdev/rollup-plugin-analyzer) 插件进行代码体积分析,从而优化你的代码。只需要在`根目录`下执行以下命令:
|
||||
|
||||
```bash
|
||||
pnpm run build:analyze
|
||||
```
|
||||
|
||||
运行之后,在自动打开的页面可以看到具体的体积分布,以分析哪些依赖有问题。
|
||||
|
||||

|
||||
|
||||
## 部署
|
||||
|
||||
简单的部署只需要将最终生成的静态文件,dist 文件夹的静态文件发布到你的 cdn 或者静态服务器即可,需要注意的是其中的 index.html 通常会是你后台服务的入口页面,在确定了 js 和 css 的静态之后可能需要改变页面的引入路径。
|
||||
|
||||
例如上传到 nginx 服务器,可以将 dist 文件夹下的文件上传到服务器的 `/srv/www/project/index.html` 目录下,然后访问配置好的域名即可。
|
||||
|
||||
```bash
|
||||
# nginx配置
|
||||
location / {
|
||||
# 不缓存html,防止程序更新后缓存继续生效
|
||||
if ($request_filename ~* .*\.(?:htm|html)$) {
|
||||
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
|
||||
access_log on;
|
||||
}
|
||||
# 这里是vue打包文件dist内的文件的存放路径
|
||||
root /srv/www/project/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
```
|
||||
|
||||
部署时可能会发现资源路径不对,只需要修改`.env.production`文件即可。
|
||||
|
||||
```bash
|
||||
# 根据自己路径来配置更改
|
||||
# 注意需要以 / 开头和结尾
|
||||
VITE_BASE=/
|
||||
VITE_BASE=/xxx/
|
||||
```
|
||||
|
||||
### 前端路由与服务端的结合
|
||||
|
||||
项目前端路由使用的是 vue-router,所以你可以选择两种方式:history 和 hash。
|
||||
|
||||
- `hash` 默认会在 url 后面拼接`#`
|
||||
- `history` 则不会,不过 `history` 需要服务器配合
|
||||
|
||||
可在 `.env.production` 内进行 mode 修改
|
||||
|
||||
```bash
|
||||
VITE_ROUTER_HISTORY=hash
|
||||
```
|
||||
|
||||
### history 路由模式下服务端配置
|
||||
|
||||
开启 `history` 模式需要服务器配置,更多的服务器配置详情可以看 [history-mode](https://router.vuejs.org/guide/essentials/history-mode.html#html5-mode)
|
||||
|
||||
这里以 `nginx` 配置为例:
|
||||
|
||||
#### 部署到根目录
|
||||
|
||||
```bash {5}
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
# 用于配合 History 使用
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 部署到非根目录
|
||||
|
||||
- 首先需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_BASE = /sub/
|
||||
```
|
||||
|
||||
- 然后在 nginx 配置文件中配置
|
||||
|
||||
```bash {8}
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location /sub/ {
|
||||
# 这里是vue打包文件dist内的文件的存放路径
|
||||
alias /srv/www/project/;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /sub/index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 跨域处理
|
||||
|
||||
使用 nginx 处理项目部署后的跨域问题
|
||||
|
||||
1. 配置前端项目接口地址,在项目目录下的``.env.production`文件中配置:
|
||||
|
||||
```bash
|
||||
VITE_GLOB_API_URL=/api
|
||||
```
|
||||
|
||||
2. 在 nginx 配置请求转发到后台
|
||||
|
||||
```bash {10-11}
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
# 接口代理,用于解决跨域问题
|
||||
location /api {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# 后台接口地址
|
||||
proxy_pass http://110.110.1.1:8080/api;
|
||||
rewrite "^/api/(.*)$" /$1 break;
|
||||
proxy_redirect default;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Access-Control-Allow-Headers X-Requested-With;
|
||||
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
|
||||
}
|
||||
}
|
||||
```
|
||||
24
apps/docs/src/guide/essentials/concept.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# 基础概念
|
||||
|
||||
## 低代码引擎
|
||||
|
||||
新版本中,悦码整体进行了重构,现在我们将会介绍一些基础概念,以便于你更好的理解悦码是如何工作的。请务必仔细阅读这一部分。
|
||||
|
||||
## 悦码中的 DSL (领域特定语言)
|
||||
|
||||
> DSL (Domain-Specific Language,领域特定语言) 在低代码平台中是一种专门设计用来描述和定义应用程序特定方面的语言或表达方式。
|
||||
|
||||
核心特点:
|
||||
|
||||
- 专注于特定领域:DSL 专门针对特定问题域设计,如 UI 布局、工作流定义、数据模型等。
|
||||
- 声明式而非命令式:通常使用声明式语法描述"做什么"而非"怎么做"。
|
||||
- 抽象层次高:隐藏底层实现细节,让用户专注于业务逻辑。
|
||||
- 结构化数据格式:通常使用 JSON、YAML 或 XML 等格式表示。
|
||||
|
||||
主要作用:
|
||||
|
||||
- 抽象复杂性:将复杂的编程概念转化为领域专家能理解的表达方式。
|
||||
- 提高效率:通过声明式语法快速构建应用,减少编码工作。
|
||||
- 标准化:为应用开发提供统一的描述方式,便于团队协作。
|
||||
- 可视化编辑:DSL 通常可以通过可视化编辑器生成和修改,无需直接编写。
|
||||
- 跨平台兼容:同一套 DSL 可以被转译为不同平台的代码。
|
||||
1
apps/docs/src/guide/essentials/development.md
Normal file
@@ -0,0 +1 @@
|
||||
# 本地开发 {#development}
|
||||
1
apps/docs/src/guide/essentials/external-module.md
Normal file
@@ -0,0 +1 @@
|
||||
# 外部模块
|
||||
1
apps/docs/src/guide/essentials/icons.md
Normal file
@@ -0,0 +1 @@
|
||||
# icons
|
||||
1
apps/docs/src/guide/essentials/route.md
Normal file
@@ -0,0 +1 @@
|
||||
# route
|
||||
1
apps/docs/src/guide/essentials/server.md
Normal file
@@ -0,0 +1 @@
|
||||
server
|
||||
1
apps/docs/src/guide/essentials/settings.md
Normal file
@@ -0,0 +1 @@
|
||||
# settings
|
||||
1
apps/docs/src/guide/essentials/styles.md
Normal file
@@ -0,0 +1 @@
|
||||
# styles
|
||||
311
apps/docs/src/guide/in-depth/access.md
Normal file
@@ -0,0 +1,311 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 权限
|
||||
|
||||
框架内置了两种权限控制方式:
|
||||
|
||||
- 通过用户角色来判断菜单或者按钮是否可以访问
|
||||
- 通过接口来判断菜单或者按钮是否可以访问
|
||||
|
||||
## 前端访问控制
|
||||
|
||||
**实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登录后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoute` 添加到路由实例,实现权限的过滤。
|
||||
|
||||
**缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
|
||||
|
||||
### 步骤
|
||||
|
||||
- 确保当前模式为前端访问控制模式
|
||||
|
||||
调整对应应用目录下的`preferences.ts`,确保`accessMode='frontend'`。
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
// 默认值,可不填
|
||||
accessMode: 'frontend',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
- 配置路由权限
|
||||
|
||||
**如果不配置,默认可见**
|
||||
|
||||
```ts {3}
|
||||
{
|
||||
meta: {
|
||||
authority: ['super'],
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
- 确保接口返回的角色和路由表的权限匹配
|
||||
|
||||
可查看应用下的 `src/store/auth`,找到下面代码,
|
||||
|
||||
```ts
|
||||
// 设置登录用户信息,需要确保 userInfo.roles 是一个数组,且包含路由表中的权限
|
||||
// 例如:userInfo.roles=['super', 'admin']
|
||||
authStore.setUserInfo(userInfo);
|
||||
```
|
||||
|
||||
到这里,就已经配置完成,你需要确保登录后,接口返回的角色和路由表的权限匹配,否则无法访问。
|
||||
|
||||
### 菜单可见,但禁止访问
|
||||
|
||||
有时候,我们需要菜单可见,但是禁止访问,可以通过下面的方式实现,设置 `menuVisibleWithForbidden` 为 `true`,此时菜单可见,但是禁止访问,会跳转403页面。
|
||||
|
||||
```ts
|
||||
{
|
||||
meta: {
|
||||
menuVisibleWithForbidden: true,
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
## 后端访问控制
|
||||
|
||||
**实现原理**: 是通过接口动态生成路由表,且遵循一定的数据结构返回。前端根据需要处理该数据为可识别的结构,再通过 `router.addRoute` 添加到路由实例,实现权限的动态生成。
|
||||
|
||||
**缺点**: 后端需要提供符合规范的数据结构,前端需要处理数据结构,适合权限较为复杂的系统。
|
||||
|
||||
### 步骤
|
||||
|
||||
- 确保当前模式为后端访问控制模式
|
||||
|
||||
调整对应应用目录下的`preferences.ts`,确保`accessMode='backend'`。
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
accessMode: 'backend',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
- 确保接口返回的菜单数据结构正确
|
||||
|
||||
可查看应用下的 `src/router/access.ts`,找到下面代码,
|
||||
|
||||
```ts {5}
|
||||
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
return await generateAccessible(preferences.app.accessMode, {
|
||||
fetchMenuListAsync: async () => {
|
||||
// 这个接口为后端返回的菜单数据
|
||||
return await getAllMenus();
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- 接口返回菜单数据,可看注释说明
|
||||
|
||||
::: details 接口返回菜单数据示例
|
||||
|
||||
```ts
|
||||
const dashboardMenus = [
|
||||
{
|
||||
// 这里固定写死 BasicLayout,不可更改
|
||||
component: 'BasicLayout',
|
||||
meta: {
|
||||
order: -1,
|
||||
title: 'page.dashboard.title',
|
||||
},
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
redirect: '/analytics',
|
||||
children: [
|
||||
{
|
||||
name: 'Analytics',
|
||||
path: '/analytics',
|
||||
// 这里为页面的路径,需要去掉 views/ 和 .vue
|
||||
component: '/dashboard/analytics/index',
|
||||
meta: {
|
||||
affixTab: true,
|
||||
title: 'page.dashboard.analytics',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Workspace',
|
||||
path: '/workspace',
|
||||
component: '/dashboard/workspace/index',
|
||||
meta: {
|
||||
title: 'page.dashboard.workspace',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
到这里,就已经配置完成,你需要确保登录后,接口返回的菜单格式正确,否则无法访问。
|
||||
|
||||
## 按钮细粒度控制
|
||||
|
||||
在某些情况下,我们需要对按钮进行细粒度的控制,我们可以借助接口或者角色来控制按钮的显示。
|
||||
|
||||
### 权限码
|
||||
|
||||
权限码为接口返回的权限码,通过权限码来判断按钮是否显示,逻辑在`src/store/auth`下:
|
||||
|
||||
```ts
|
||||
const [fetchUserInfoResult, accessCodes] = await Promise.all([
|
||||
fetchUserInfo(),
|
||||
getAccessCodes(),
|
||||
]);
|
||||
|
||||
userInfo = fetchUserInfoResult;
|
||||
authStore.setUserInfo(userInfo);
|
||||
accessStore.setAccessCodes(accessCodes);
|
||||
```
|
||||
|
||||
找到 `getAccessCodes` 对应的接口,可根据业务逻辑进行调整。
|
||||
|
||||
权限码返回的数据结构为字符串数组,例如:`['AC_100100', 'AC_100110', 'AC_100120', 'AC_100010']`
|
||||
|
||||
有了权限码,就可以使用 `@vben/access` 提供的`AccessControl`组件及API来进行按钮的显示与隐藏。
|
||||
|
||||
#### 组件方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { AccessControl, useAccess } from '@vben/access';
|
||||
|
||||
const { accessMode, hasAccessByCodes } = useAccess();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 需要指明 type="code" -->
|
||||
<AccessControl :codes="['AC_100100']" type="code">
|
||||
<Button> Super 账号可见 ["AC_1000001"] </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['AC_100030']" type="code">
|
||||
<Button> Admin 账号可见 ["AC_100010"] </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['AC_1000001']" type="code">
|
||||
<Button> User 账号可见 ["AC_1000001"] </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['AC_100100', 'AC_100010']" type="code">
|
||||
<Button> Super & Admin 账号可见 ["AC_100100","AC_1000001"] </Button>
|
||||
</AccessControl>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### API方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { AccessControl, useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button v-if="hasAccessByCodes(['AC_100100'])">
|
||||
Super 账号可见 ["AC_1000001"]
|
||||
</Button>
|
||||
<Button v-if="hasAccessByCodes(['AC_100030'])">
|
||||
Admin 账号可见 ["AC_100010"]
|
||||
</Button>
|
||||
<Button v-if="hasAccessByCodes(['AC_1000001'])">
|
||||
User 账号可见 ["AC_1000001"]
|
||||
</Button>
|
||||
<Button v-if="hasAccessByCodes(['AC_100100', 'AC_1000001'])">
|
||||
Super & Admin 账号可见 ["AC_100100","AC_1000001"]
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 指令方式
|
||||
|
||||
> 指令支持绑定单个或多个权限码。单个时可以直接传入字符串或数组中包含一个权限码,多个权限码则传入数组。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Button class="mr-4" v-access:code="'AC_100100'">
|
||||
Super 账号可见 'AC_100100'
|
||||
</Button>
|
||||
<Button class="mr-4" v-access:code="['AC_100030']">
|
||||
Admin 账号可见 ["AC_100010"]
|
||||
</Button>
|
||||
<Button class="mr-4" v-access:code="['AC_1000001']">
|
||||
User 账号可见 ["AC_1000001"]
|
||||
</Button>
|
||||
<Button class="mr-4" v-access:code="['AC_100100', 'AC_1000001']">
|
||||
Super & Admin 账号可见 ["AC_100100","AC_1000001"]
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 角色
|
||||
|
||||
角色判断方式不需要接口返回的权限码,直接通过角色来判断按钮是否显示。
|
||||
|
||||
#### 组件方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { AccessControl } from '@vben/access';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccessControl :codes="['super']">
|
||||
<Button> Super 角色可见 </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['admin']">
|
||||
<Button> Admin 角色可见 </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['user']">
|
||||
<Button> User 角色可见 </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['super', 'admin']">
|
||||
<Button> Super & Admin 角色可见 </Button>
|
||||
</AccessControl>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### API方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByRoles } = useAccess();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button v-if="hasAccessByRoles(['super'])"> Super 账号可见 </Button>
|
||||
<Button v-if="hasAccessByRoles(['admin'])"> Admin 账号可见 </Button>
|
||||
<Button v-if="hasAccessByRoles(['user'])"> User 账号可见 </Button>
|
||||
<Button v-if="hasAccessByRoles(['super', 'admin'])">
|
||||
Super & Admin 账号可见
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 指令方式
|
||||
|
||||
> 指令支持绑定单个或多个角色。单个时可以直接传入字符串或数组中包含一个角色,多个角色均可访问则传入数组。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Button class="mr-4" v-access:role="'super'"> Super 角色可见 </Button>
|
||||
<Button class="mr-4" v-access:role="['super']"> Super 角色可见 </Button>
|
||||
<Button class="mr-4" v-access:role="['admin']"> Admin 角色可见 </Button>
|
||||
<Button class="mr-4" v-access:role="['user']"> User 角色可见 </Button>
|
||||
<Button class="mr-4" v-access:role="['super', 'admin']">
|
||||
Super & Admin 角色可见
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
48
apps/docs/src/guide/in-depth/check-updates.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 检查更新
|
||||
|
||||
## 介绍
|
||||
|
||||
当网站有更新时,您可能需要检查更新。框架提供了这一功能,通过定时检查更新,您可以在应用的 preferences.ts 文件中配置 `checkUpdatesInterval`和 `enableCheckUpdates` 字段,以开启和设置检查更新的时间间隔(单位:分钟)。
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
// 是否开启检查更新
|
||||
enableCheckUpdates: true,
|
||||
// 检查更新的时间间隔,单位为分钟
|
||||
checkUpdatesInterval: 1,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 效果
|
||||
|
||||
检测到更新时,会弹出提示框,询问用户是否刷新页面:
|
||||
|
||||

|
||||
|
||||
## 替换为其他检查更新方式
|
||||
|
||||
如果需要通过其他方式检查更新,例如通过接口来更灵活地控制更新逻辑(如强制刷新、显示更新内容等),你可以通过修改 `@vben/layouts` 下面的 `src/widgets/check-updates/check-updates.vue`文件来实现。
|
||||
|
||||
```ts
|
||||
// 这里可以替换为你的检查更新逻辑
|
||||
async function getVersionTag() {
|
||||
try {
|
||||
const response = await fetch('/', {
|
||||
cache: 'no-cache',
|
||||
method: 'HEAD',
|
||||
});
|
||||
|
||||
return (
|
||||
response.headers.get('etag') || response.headers.get('last-modified')
|
||||
);
|
||||
} catch {
|
||||
console.error('Failed to fetch version tag');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
84
apps/docs/src/guide/in-depth/features.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 常用功能
|
||||
|
||||
一些常用的功能合集。
|
||||
|
||||
## 登录认证过期
|
||||
|
||||
当接口返回`401`状态码时,框架会认为登录认证过期,登录超时会跳转到登录页或者打开登录弹窗。在应用目录下的`preferences.ts`可以配置:
|
||||
|
||||
### 跳转登录页面
|
||||
|
||||
登录超时会跳转到登录页
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
loginExpiredMode: 'page',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 打开登录弹窗
|
||||
|
||||
登录超时会打开登录弹窗
|
||||
|
||||

|
||||
|
||||
配置:
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
loginExpiredMode: 'modal',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 动态标题
|
||||
|
||||
- 默认值:`true`
|
||||
|
||||
开启后网页标题随着路由的`title`而变化。在应用目录下的`preferences.ts`,开启或者关闭即可。
|
||||
|
||||
```ts
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
dynamicTitle: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 页面水印
|
||||
|
||||
- 默认值:`false`
|
||||
|
||||
开启后网页会显示水印,在应用目录下的`preferences.ts`,开启或者关闭即可。
|
||||
|
||||
```ts
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
watermark: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
如果你想更新水印的内容,可以这么做,参数可以参考 [watermark-js-plus](https://zhensherlock.github.io/watermark-js-plus/):
|
||||
|
||||
```ts
|
||||
import { useWatermark } from '@vben/hooks';
|
||||
|
||||
const { destroyWatermark, updateWatermark } = useWatermark();
|
||||
|
||||
await updateWatermark({
|
||||
// 水印内容
|
||||
content: 'hello my watermark',
|
||||
});
|
||||
```
|
||||
1
apps/docs/src/guide/in-depth/layout.md
Normal file
@@ -0,0 +1 @@
|
||||
# 布局
|
||||
46
apps/docs/src/guide/in-depth/loading.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 全局loading
|
||||
|
||||
全局 loading 指的是页面刷新时出现的加载效果,通常是一个旋转的图标:
|
||||
|
||||

|
||||
|
||||
## 原理
|
||||
|
||||
由 `vite-plugin-inject-app-loading` 插件实现,插件会在每个应用都注入一个全局的 `loading html`。
|
||||
|
||||
## 关闭
|
||||
|
||||
如果你不需要全局 loading,可以在 `.env` 文件中关闭:
|
||||
|
||||
```bash
|
||||
VITE_INJECT_APP_LOADING=false
|
||||
```
|
||||
|
||||
## 自定义
|
||||
|
||||
如果你想要自定义全局 loading,可以在应用目录下,与`index.html`同级,创建一个`loading.html`文件,插件会自动读取并注入。这个html可以自行定义样式和动画。
|
||||
|
||||
::: tip
|
||||
|
||||
- 你可以使用跟`index.html`一样的语法,比如`VITE_APP_TITLE`变量,来获取应用的标题。
|
||||
- 必须保证有一个`id="__app-loading__"`的元素。
|
||||
- 给`id="__app-loading__"`的元素,加一个 `hidden` class。
|
||||
- 必须保证有一个`style[data-app-loading="inject-css"]`的元素。
|
||||
|
||||
```html{1,4}
|
||||
<style data-app-loading="inject-css">
|
||||
#__app-loading__.hidden {
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: all 1s ease-out;
|
||||
}
|
||||
/* ... */
|
||||
</style>
|
||||
<div id="__app-loading__">
|
||||
<!-- ... -->
|
||||
<div class="title"><%= VITE_APP_TITLE %></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
:::
|
||||
227
apps/docs/src/guide/in-depth/locale.md
Normal file
@@ -0,0 +1,227 @@
|
||||
# 国际化
|
||||
|
||||
项目已经集成了 [Vue i18n](https://kazupon.github.io/vue-i18n/),并且已经配置好了中文和英文的语言包。
|
||||
|
||||
## IDE 插件
|
||||
|
||||
如果你使用的 vscode 开发工具,则推荐安装 [i18n Ally](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally) 这个插件。它可以帮助你更方便的管理国际化的文案,安装了该插件后,你的代码内可以实时看到对应的语言内容:
|
||||
|
||||

|
||||
|
||||
## 配置默认语言
|
||||
|
||||
只需要覆盖默认的偏好设置即可,在对应的应用内,找到 `src/preferences.ts` 文件,修改 `locale` 的值即可:
|
||||
|
||||
```ts {3}
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
app: {
|
||||
locale: 'en-US',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 动态切换语言
|
||||
|
||||
切换语言有两部分组成:
|
||||
|
||||
- 更新偏好设置
|
||||
- 加载对应的语言包
|
||||
|
||||
```ts
|
||||
import type { SupportedLanguagesType } from '@vben/locales';
|
||||
import { loadLocaleMessages } from '@vben/locales';
|
||||
import { updatePreferences } from '@vben/preferences';
|
||||
|
||||
async function updateLocale(value: string) {
|
||||
// 1. 更新偏好设置
|
||||
const locale = value as SupportedLanguagesType;
|
||||
updatePreferences({
|
||||
app: {
|
||||
locale,
|
||||
},
|
||||
});
|
||||
// 2. 加载对应的语言包
|
||||
await loadLocaleMessages(locale);
|
||||
}
|
||||
|
||||
updateLocale('en-US');
|
||||
```
|
||||
|
||||
## 新增翻译文本
|
||||
|
||||
::: warning 注意
|
||||
|
||||
- 请不要将业务翻译文本放到 `@vben/locales` 内,这样可以更好的管理业务和通用的翻译文本。
|
||||
- 有多个语言包的情况下,新增翻译文本时,需要在所有语言包内新增对应的文本。
|
||||
|
||||
:::
|
||||
|
||||
新增翻译文本,只需要在对应的应用内,找到 `src/locales/langs/`,新增对应的文本即可,例:
|
||||
|
||||
**src/locales/langs/zh-CN/\*.json**
|
||||
|
||||
````ts
|
||||
```json
|
||||
{
|
||||
"about": {
|
||||
"desc": "y-code-platform 是一个现代的管理模版。"
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
**src/locales/langs/en-US.ts**
|
||||
|
||||
````ts
|
||||
```json
|
||||
{
|
||||
"about": {
|
||||
"desc": "y-code-platform is a modern management template."
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
## 使用翻译文本
|
||||
|
||||
通过 `@vben/locales`,你可以很方便的使用翻译文本:
|
||||
|
||||
### 在代码中使用
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
const items = computed(() => [{ title: $t('about.desc') }]);
|
||||
</script>
|
||||
<template>
|
||||
<div>{{ $t('about.desc') }}</div>
|
||||
<template v-for="item in items.value">
|
||||
<div>{{ item.title }}</div>
|
||||
</template>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 新增语言包
|
||||
|
||||
如果你需要新增语言包,需要按照以下步骤进行:
|
||||
|
||||
- 在 `packages/locales/langs` 目录下新增对应的语言包文件,例:`zh-TW.json`,并翻译对应的文本。
|
||||
- 在对应的应用内,找到 `src/locales/langs` 文件,新增对应的语言包 `zh-TW.json`
|
||||
- 在 `packages/constants/src/core.ts`内,新增对应的语言:
|
||||
|
||||
```ts
|
||||
export interface LanguageOption {
|
||||
label: string;
|
||||
value: 'en-US' | 'zh-CN'; // [!code --]
|
||||
value: 'en-US' | 'zh-CN' | 'zh-TW'; // [!code ++]
|
||||
}
|
||||
export const SUPPORT_LANGUAGES: LanguageOption[] = [
|
||||
{
|
||||
label: '简体中文',
|
||||
value: 'zh-CN',
|
||||
},
|
||||
{
|
||||
label: 'English',
|
||||
value: 'en-US',
|
||||
},
|
||||
{
|
||||
label: '繁体中文', // [!code ++]
|
||||
value: 'zh-TW', // [!code ++]
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
- 在 `packages/locales/typing.ts`内,新增 Typescript 类型:
|
||||
|
||||
```ts
|
||||
export type SupportedLanguagesType = 'en-US' | 'zh-CN'; // [!code --]
|
||||
export type SupportedLanguagesType = 'en-US' | 'zh-CN' | 'zh-TW'; // [!code ++]
|
||||
```
|
||||
|
||||
到这里,你就可以在项目内使用新增的语言包了。
|
||||
|
||||
## 界面切换语言功能
|
||||
|
||||
如果你想关闭界面上的语言切换显示按钮,在对应的应用内,找到 `src/preferences.ts` 文件,修改 `locale` 的值即可:
|
||||
|
||||
```ts {3}
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
widget: {
|
||||
languageToggle: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 远程加载语言包
|
||||
|
||||
::: tip 提示
|
||||
|
||||
通过项目自带的`request`工具进行接口请求时,默认请求头里会带上 [Accept-Language](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Language) ,服务端可根据请求头进行动态数据国际化处理。
|
||||
|
||||
:::
|
||||
|
||||
每个应用都有一个独立的语言包,它可以覆盖通用的语言配置,你可以通过远程加载的方式来获取对应的语言包,只需要在对应的应用内,找到 `src/locales/index.ts` 文件,修改 `loadMessages` 方法即可:
|
||||
|
||||
```ts {3-4}
|
||||
async function loadMessages(lang: SupportedLanguagesType) {
|
||||
const [appLocaleMessages] = await Promise.all([
|
||||
// 这里修改为远程接口加载数据即可
|
||||
localesMap[lang](),
|
||||
loadThirdPartyMessage(lang),
|
||||
]);
|
||||
return appLocaleMessages.default;
|
||||
}
|
||||
```
|
||||
|
||||
## 第三方语言包
|
||||
|
||||
不同应用内使用的第三方组件库或者插件国际化方式可能不一致,所以需要差别处理。 如果你需要引入第三方的语言包,你可以在对应的应用内,找到 `src/locales/index.ts` 文件,修改 `loadThirdPartyMessage` 方法即可:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* 加载dayjs的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||
let locale;
|
||||
switch (lang) {
|
||||
case 'zh-CN': {
|
||||
locale = await import('dayjs/locale/zh-cn');
|
||||
break;
|
||||
}
|
||||
case 'en-US': {
|
||||
locale = await import('dayjs/locale/en');
|
||||
break;
|
||||
}
|
||||
// 默认使用英语
|
||||
default: {
|
||||
locale = await import('dayjs/locale/en');
|
||||
}
|
||||
}
|
||||
if (locale) {
|
||||
dayjs.locale(locale);
|
||||
} else {
|
||||
console.error(`Failed to load dayjs locale for ${lang}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 移除国际化
|
||||
|
||||
首先,不是很建议移除国际化,因为国际化是一个很好的开发习惯,但是如果你真的需要移除国际化,你可以直接使用中文文案,然后保留项目自带的语言包即可,整体开发体验不会影响。移除国际化的步骤如下:
|
||||
|
||||
- 隐藏界面上的语言切换按钮,见:[界面切换语言功能](#界面切换语言功能)
|
||||
- 修改默认语言,见:[配置默认语言](#配置默认语言)
|
||||
- 关闭 `vue-i18n`的警告提示,在`src/locales/index.ts`文件内,修改`missingWarn`为`false`即可:
|
||||
|
||||
```ts
|
||||
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
|
||||
await coreSetup(app, {
|
||||
defaultLocale: preferences.app.locale,
|
||||
loadMessages,
|
||||
missingWarn: !import.meta.env.PROD, // [!code --]
|
||||
missingWarn: false, // [!code ++]
|
||||
...options,
|
||||
});
|
||||
}
|
||||
```
|
||||
220
apps/docs/src/guide/in-depth/login.md
Normal file
@@ -0,0 +1,220 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 登录
|
||||
|
||||
本文介绍如何去改造自己的应用程序登录页以及如何快速的对接登录页面接口。
|
||||
|
||||
## 登录页面调整
|
||||
|
||||
如果你想调整登录页面的标题、描述和图标以及工具栏,你可以通过配置 `AuthPageLayout` 组件的参数来实现。
|
||||
|
||||

|
||||
|
||||
只需要在应用下的 `src/layouts/auth.vue` 内,配置`AuthPageLayout`的 `props`参数即可:
|
||||
|
||||
```vue {2-7}
|
||||
<AuthPageLayout
|
||||
:copyright="true"
|
||||
:toolbar="true"
|
||||
:toolbarList="['color', 'language', 'layout', 'theme']"
|
||||
:app-name="appName"
|
||||
:logo="logo"
|
||||
:page-description="$t('authentication.pageDesc')"
|
||||
:page-title="$t('authentication.pageTitle')"
|
||||
>
|
||||
</AuthPageLayout>
|
||||
```
|
||||
|
||||
## 登录表单调整
|
||||
|
||||
如果你想调整登录表单的相关内容,你可以在应用下的 `src/views/_core/authentication/login.vue` 内,配置`AuthenticationLogin` 组件参数即可:
|
||||
|
||||
```vue
|
||||
<AuthenticationLogin
|
||||
:loading="authStore.loginLoading"
|
||||
@submit="authStore.authLogin"
|
||||
/>
|
||||
```
|
||||
|
||||
::: details AuthenticationLogin 组件参数
|
||||
|
||||
```ts
|
||||
{
|
||||
/**
|
||||
* @zh_CN 验证码登录路径
|
||||
*/
|
||||
codeLoginPath?: string;
|
||||
/**
|
||||
* @zh_CN 忘记密码路径
|
||||
*/
|
||||
forgetPasswordPath?: string;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否处于加载处理状态
|
||||
*/
|
||||
loading?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 二维码登录路径
|
||||
*/
|
||||
qrCodeLoginPath?: string;
|
||||
|
||||
/**
|
||||
* @zh_CN 注册路径
|
||||
*/
|
||||
registerPath?: string;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示验证码登录
|
||||
*/
|
||||
showCodeLogin?: boolean;
|
||||
/**
|
||||
* @zh_CN 是否显示忘记密码
|
||||
*/
|
||||
showForgetPassword?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示二维码登录
|
||||
*/
|
||||
showQrcodeLogin?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示注册按钮
|
||||
*/
|
||||
showRegister?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示记住账号
|
||||
*/
|
||||
showRememberMe?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 是否显示第三方登录
|
||||
*/
|
||||
showThirdPartyLogin?: boolean;
|
||||
|
||||
/**
|
||||
* @zh_CN 登录框子标题
|
||||
*/
|
||||
subTitle?: string;
|
||||
|
||||
/**
|
||||
* @zh_CN 登录框标题
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: tip Note
|
||||
|
||||
如果这些配置不能满足你的需求,你可以自行实现登录表单及相关登录逻辑或者给我们提交 `PR`。
|
||||
|
||||
:::
|
||||
|
||||
## 接口对接流程
|
||||
|
||||
这里将会快速的介绍如何快速对接自己的后端。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 首先文档用的后端服务,接口返回的格式统一如下:
|
||||
|
||||
```ts
|
||||
interface HttpResponse<T = any> {
|
||||
/**
|
||||
* 0 表示成功 其他表示失败
|
||||
* 0 means success, others means fail
|
||||
*/
|
||||
code: number;
|
||||
data: T;
|
||||
message: string;
|
||||
}
|
||||
```
|
||||
|
||||
如果你不符合这个格式,你需要先阅读 [服务端交互](../essentials/server.md) 文档,改造你的`request.ts`配置。
|
||||
|
||||
- 其次你需要在先将本地代理地址改为你的真实后端地址,你可以在应用下的 `vite.config.mts` 内配置:
|
||||
|
||||
```ts
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig(async () => {
|
||||
return {
|
||||
vite: {
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// 这里改为你的真实接口地址
|
||||
target: 'http://localhost:5320/api',
|
||||
ws: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
### 登录接口
|
||||
|
||||
为了能正常登录,你的后端最少需要提供 `2-3` 个接口:
|
||||
|
||||
- 登录接口
|
||||
|
||||
接口地址可在应用下的 `src/api/core/auth` 内修改,以下为默认接口地址:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
export async function loginApi(data: AuthApi.LoginParams) {
|
||||
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
|
||||
}
|
||||
|
||||
/** 只需要保证登录接口返回值有 `accessToken` 字段即可 */
|
||||
export interface LoginResult {
|
||||
accessToken: string;
|
||||
}
|
||||
```
|
||||
|
||||
- 获取用户信息接口
|
||||
|
||||
接口地址可在应用下的 `src/api/core/user` 内修改,以下为默认接口地址:
|
||||
|
||||
```ts
|
||||
export async function getUserInfoApi() {
|
||||
return requestClient.get<UserInfo>('/user/info');
|
||||
}
|
||||
|
||||
/** 只需要保证登录接口返回值有以下字段即可,多的字段可以自行使用 */
|
||||
export interface UserInfo {
|
||||
roles: string[];
|
||||
realName: string;
|
||||
}
|
||||
```
|
||||
|
||||
- 获取权限码 (可选)
|
||||
|
||||
这个接口用于获取用户的权限码,权限码是用于控制用户的权限的,接口地址可在应用下的 `src/api/core/auth` 内修改,以下为默认接口地址:
|
||||
|
||||
```ts
|
||||
export async function getAccessCodesApi() {
|
||||
return requestClient.get<string[]>('/auth/codes');
|
||||
}
|
||||
```
|
||||
|
||||
如果你不需要这个权限,你只需要把代码改为返回一个空数组即可。
|
||||
|
||||
```ts {2}
|
||||
export async function getAccessCodesApi() {
|
||||
// 这里返回一个空数组即可
|
||||
return [];
|
||||
}
|
||||
```
|
||||
1295
apps/docs/src/guide/in-depth/theme.md
Normal file
17
apps/docs/src/guide/in-depth/ui-framework.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# 组件库切换
|
||||
|
||||
`Vue Admin` 支持你自由选择组件库,目前演示站点的默认组件库是 `Ant Design Vue`,与旧版本保持一致。同时框架还内置了 `Element Plus` 版本和 `Naive UI` 版本,你可以根据自己的喜好选择。
|
||||
|
||||
## 新增组件库应用
|
||||
|
||||
如果你想用其他别的组件库,你只需要按以下步骤进行操作:
|
||||
|
||||
1. 在`apps`内创建一个新的文件夹,例如`apps/web-xxx`。
|
||||
2. 更改`apps/web-xxx/package.json`的`name`字段为`web-xxx`。
|
||||
3. 移除其他组件库依赖及代码,并用你的组件库进行替换相应逻辑,需要改动的地方不多。
|
||||
4. 调整`locales`内的语言文件。
|
||||
5. 调整 `app.vue` 内的组件。
|
||||
6. 自行适配组件库的主题,与 `y-code-platform` 契合。
|
||||
7. 调整 `.env` 内的应用名
|
||||
8. 在大仓根目录增加 `dev:xxx` 脚本
|
||||
9. 执行 `pnpm install` 安装依赖
|
||||
3
apps/docs/src/guide/introduction/changelog.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 更新日志
|
||||
|
||||
TODO
|
||||
17
apps/docs/src/guide/introduction/platform.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# 关于低代码管理平台
|
||||
|
||||
## 特点
|
||||
|
||||
- **最新技术栈**:使用 `Vue3`、`Vite`、`TypeScript` 等前端前沿技术开发。
|
||||
- **组件丰富**:提供了丰富的组件,可以满足大部分的业务需求。
|
||||
- **多UI库支持**:支持 `Ant Design Vue`、`Element Plus` 等主流 UI 库,不再限制于特定框架。
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
- **本地开发**推荐使用`Chrome 最新版`浏览器,**不支持**`Chrome 80`以下版本。
|
||||
|
||||
- **生产环境**支持现代浏览器,不支持 IE。
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)Safari |
|
||||
| :-: | :-: | :-: | :-: | :-: |
|
||||
| 不支持 | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
112
apps/docs/src/guide/introduction/quick-start.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 快速开始 {#quick-start}
|
||||
|
||||
## 前置准备
|
||||
|
||||
::: info 环境要求
|
||||
|
||||
在启动项目前,你需要确保你的环境满足以下要求:
|
||||
|
||||
- [Node.js](https://nodejs.org/en) 20.15.0 及以上版本,推荐使用 [fnm](https://github.com/Schniz/fnm) 、 [nvm](https://github.com/nvm-sh/nvm) 或者直接使用[pnpm](https://pnpm.io/cli/env) 进行版本管理。
|
||||
- [Git](https://git-scm.com/) 任意版本。
|
||||
|
||||
验证你的环境是否满足以上要求,你可以通过以下命令查看版本:
|
||||
|
||||
```bash
|
||||
# 出现相应 node LTS版本即可
|
||||
node -v
|
||||
# 出现相应 git 版本即可
|
||||
git -v
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
<!--
|
||||
## 启动项目
|
||||
|
||||
### 获取源码
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [GitHub]
|
||||
# clone 代码
|
||||
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||
```
|
||||
|
||||
```sh [Gitee]
|
||||
# clone 代码
|
||||
# Gitee 的代码可能不是最新的
|
||||
git clone https://gitee.com/annsion/vue-vben-admin.git
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: danger 注意
|
||||
|
||||
注意存放代码的目录及所有父级目录不能存在中文、韩文、日文以及空格,否则安装依赖后启动会出错。
|
||||
|
||||
:::
|
||||
|
||||
### 安装依赖
|
||||
|
||||
在你的代码目录内打开终端,并执行以下命令:
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd vue-vben-admin
|
||||
|
||||
# 使用项目指定的pnpm版本进行依赖安装
|
||||
corepack enable
|
||||
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
```
|
||||
|
||||
::: tip 注意
|
||||
|
||||
- 项目只支持使用 `pnpm` 进行依赖安装,默认会使用 `corepack` 来安装指定版本的 `pnpm`。:
|
||||
- 如果你的网络环境无法访问npm源,你可以设置系统的环境变量`COREPACK_NPM_REGISTRY=https://registry.npmmirror.com`,然后再执行`pnpm install`。
|
||||
- 如果你不想使用`corepack`,你需要禁用`corepack`,然后使用你自己的`pnpm`进行安装。
|
||||
|
||||
:::
|
||||
|
||||
### 运行项目
|
||||
|
||||
#### 选择项目
|
||||
|
||||
执行以下命令运行项目:
|
||||
|
||||
```bash
|
||||
# 启动项目
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
此时,你会看到类似如下的输出,选择你需要运行的项目:
|
||||
|
||||
```bash
|
||||
│
|
||||
◆ Select the app you need to run [dev]:
|
||||
│ ○ @vben/web-antd
|
||||
│ ○ @vben/web-ele
|
||||
│ ○ @vben/web-naive
|
||||
│ ○ @vben/docs
|
||||
│ ● @vben/playground
|
||||
└
|
||||
```
|
||||
|
||||
现在,你可以在浏览器访问 `http://localhost:5555` 查看项目。
|
||||
|
||||
#### 运行指定项目
|
||||
|
||||
如果你不想选择项目,可以直接运行以下命令运行你需要的应用:
|
||||
|
||||
```bash
|
||||
pnpm run dev:antd
|
||||
pnpm run dev:ele
|
||||
pnpm run dev:naive
|
||||
pnpm run dev:docs
|
||||
pnpm run dev:play
|
||||
``` -->
|
||||
3
apps/docs/src/guide/introduction/roadmap.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 路线图
|
||||
|
||||
TODO:
|
||||
159
apps/docs/src/guide/other/faq.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# 常见问题 #{faq}
|
||||
|
||||
::: tip 列举了一些常见的问题
|
||||
|
||||
有问题可以先来这里寻找,如果没有可以在 [GitHub Issue](https://github.com/vbenjs/vue-vben-admin/issues) 搜索或者提交你的问题, 如果是讨论性的问题可以在 [GitHub Discussions](https://github.com/vbenjs/vue-vben-admin/discussions)
|
||||
|
||||
:::
|
||||
|
||||
## 说明
|
||||
|
||||
遇到问题,可以先从以下几个方面查找
|
||||
|
||||
1. 对应模块的 GitHub 仓库 [issue](https://github.com/vbenjs/vue-vben-admin/issues) 搜索
|
||||
2. 从[google](https://www.google.com)搜索问题
|
||||
3. 从[百度](https://www.baidu.com)搜索问题
|
||||
4. 在下面列表找不到问题可以到 issue 提问 [issues](https://github.com/vbenjs/vue-vben-admin/issues)
|
||||
5. 如果不是问题类型的,需要讨论的,请到 [discussions](https://github.com/vbenjs/vue-vben-admin/discussions) 讨论
|
||||
|
||||
## 依赖问题
|
||||
|
||||
在 `Monorepo` 项目下,需要养成每次 `git pull`代码都要执行`pnpm install`的习惯,因为经常会有新的依赖包加入,项目在`.husky/git-merge`已经配置了自动执行`pnpm install`,但是有时候会出现问题,如果没有自动执行,建议手动执行一次。
|
||||
|
||||
## 关于缓存更新问题
|
||||
|
||||
项目配置默认是缓存在 `localStorage` 内,所以版本更新后可能有些配置没改变。
|
||||
|
||||
解决方式是每次更新代码的时候修改 `package.json` 内的 `version` 版本号. 因为 localStorage 的 key 是根据版本号来的。所以更新后版本不同前面的配置会失效。重新登录即可
|
||||
|
||||
## 关于修改配置文件的问题
|
||||
|
||||
当修改 `.env` 等环境文件以及 `vite.config.ts` 文件时,vite 会自动重启服务。
|
||||
|
||||
自动重启有几率出现问题,请重新运行项目即可解决.
|
||||
|
||||
## 本地运行报错
|
||||
|
||||
由于 vite 在本地没有转换代码,且代码中用到了可选链等比较新的语法。所以本地开发需要使用版本较高的浏览器(`Chrome 90+`)进行开发
|
||||
|
||||
## 页面切换后页面空白
|
||||
|
||||
这是由于开启了路由切换动画,且对应的页面组件存在多个根节点导致的,在页面最外层添加`<div></div>`即可
|
||||
|
||||
**错误示例**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 注释也算一个节点 -->
|
||||
<h1>text h1</h1>
|
||||
<h2>text h2</h2>
|
||||
</template>
|
||||
```
|
||||
|
||||
**正确示例**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h1>text h1</h1>
|
||||
<h2>text h2</h2>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
- 如果想使用多个根标签,可以禁用路由切换动画
|
||||
- template 下面的根注释节点也算一个节点
|
||||
|
||||
:::
|
||||
|
||||
## 我的代码本地开发可以,打包就不行了
|
||||
|
||||
目前发现这个原因可能有以下,可以从以下原因来排查,如果还有别的可能,欢迎补充
|
||||
|
||||
- 使用了 ctx 这个变量,ctx 本身未暴露出在实例类型内,Vue官方也是说了不要用这个属性。这个属性只是用于内部使用。
|
||||
|
||||
```ts
|
||||
import { getCurrentInstance } from 'vue';
|
||||
getCurrentInstance().ctx.xxxx;
|
||||
```
|
||||
|
||||
## 依赖安装问题
|
||||
|
||||
- 如果依赖安装不了或者启动报错可以尝试执行`pnpm run reinstall`。
|
||||
- 如果依赖安装不了或者报错,可以尝试切换手机热点来进行依赖安装。
|
||||
- 如果还是不行,可以自行配置国内镜像安装。
|
||||
- 也可以在项目根目录创建 `.npmrc` 文件,内容如下
|
||||
|
||||
```bash
|
||||
# .npmrc
|
||||
registry = https://registry.npmmirror.com/
|
||||
```
|
||||
|
||||
## 打包文件过大
|
||||
|
||||
- 首先,完整版由于引用了比较多的库文件,所以打包会比较大。可以使用精简版来进行开发
|
||||
|
||||
- 其次建议开启 gzip,使用之后体积会只有原先 1/3 左右。gzip 可以由服务器直接开启。如果是这样,前端不需要构建 `.gz` 格式的文件,如果前端构建了 `.gz` 文件,以 nginx 为例,nginx 需要开启 `gzip_static: on` 这个选项。
|
||||
|
||||
- 开启 gzip 的同时还可以同时开启 `brotli`,比 gzip 更好的压缩。两者可以共存
|
||||
|
||||
**注意**
|
||||
|
||||
- gzip_static: 这个模块需要 nginx 另外安装,默认的 nginx 没有安装这个模块。
|
||||
|
||||
- 开启 `brotli` 也需要 nginx 另外安装模块
|
||||
|
||||
## 运行错误
|
||||
|
||||
如果出现类似以下错误,请检查项目全路径(包含所有父级路径)不能出现中文、日文、韩文。否则将会出现路径访问 404 导致以下问题
|
||||
|
||||
```ts
|
||||
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/index.ts)
|
||||
```
|
||||
|
||||
## 控制台路由警告问题
|
||||
|
||||
如果看到控制台有如下警告,且页面**能正常打开** 可以忽略该警告。
|
||||
|
||||
后续 `vue-router` 可能会提供配置项来关闭警告
|
||||
|
||||
```ts
|
||||
[Vue Router warn]: No match found for location with path "xxxx"
|
||||
```
|
||||
|
||||
## 启动报错
|
||||
|
||||
当出现以下错误信息时,请检查你的 nodejs 版本号是否符合要求
|
||||
|
||||
```bash
|
||||
TypeError: str.matchAll is not a function
|
||||
at Object.extractor (vue-vben-admin-main\node_modules@purge-icons\core\dist\index.js:146:27)
|
||||
at Extract (vue-vben-admin-main\node_modules@purge-icons\core\dist\index.js:173:54)
|
||||
```
|
||||
|
||||
## nginx 部署
|
||||
|
||||
部署到 `nginx`后,可能会出现以下错误:
|
||||
|
||||
```bash
|
||||
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.
|
||||
```
|
||||
|
||||
解决方式一:
|
||||
|
||||
```bash
|
||||
http {
|
||||
#如果有此项配置需要注释掉
|
||||
#include mime.types;
|
||||
|
||||
types {
|
||||
application/javascript js mjs;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
解决方式二:
|
||||
|
||||
进入 `nginx` 下的`mime.types`文件, 将`application/javascript js;` 修改为 `application/javascript js mjs;`
|
||||
55
apps/docs/src/guide/other/project-update.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# 项目更新
|
||||
|
||||
## 为什么无法像 npm 插件一样更新
|
||||
|
||||
因为项目是一个完整的项目模版,不是一个插件或者安装包,无法像插件一样更新,你使用代码后,会根据业务需求,进行二次开发,需要自行手动合并升级。
|
||||
|
||||
## 我需要怎么做
|
||||
|
||||
项目采用了 `Monorepo` 的方式进行管理,并将一些比较核心的代码进行了抽离,比如 `packages/@core`、`packages/effects`,只要业务代码没有修改这部分代码,那么你可以直接拉取最新代码,然后合并到你的分支上,只需要简单的处理部分冲突即可。其余文件夹只会进行一些小的调整,不会对业务代码产生影响。
|
||||
|
||||
::: tip 推荐
|
||||
|
||||
建议关注仓库动态,积极去合并,不要长时间积累,否则将会导致合并冲突过多,增加合并难度。
|
||||
|
||||
:::
|
||||
|
||||
## 使用 Git 更新代码
|
||||
|
||||
1. 克隆代码
|
||||
|
||||
```bash
|
||||
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||
```
|
||||
|
||||
2. 添加自己的公司 git 源地址
|
||||
|
||||
```bash
|
||||
# up 为源名称,可以随意设置
|
||||
# gitUrl为开源最新代码
|
||||
git remote add up gitUrl;
|
||||
```
|
||||
|
||||
3. 提交代码到自己公司 git
|
||||
|
||||
```bash
|
||||
# 提交代码到自己公司
|
||||
# main为分支名 需要自行根据情况修改
|
||||
git push up main
|
||||
|
||||
# 同步公司的代码
|
||||
# main为分支名 需要自行根据情况修改
|
||||
git pull up main
|
||||
```
|
||||
|
||||
4. 如何同步开源最新代码
|
||||
|
||||
```bash
|
||||
git pull origin main
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
同步代码的时候会出现冲突。只需要把冲突解决即可
|
||||
|
||||
:::
|
||||
18
apps/docs/src/guide/other/remove-code.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# 移除代码
|
||||
|
||||
## 移除百度统计代码
|
||||
|
||||
在对应应用的 `index.html` 文件中,找到如下代码,删除即可:
|
||||
|
||||
```html
|
||||
<!-- apps/web-antd -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement('script');
|
||||
hm.src = 'https://hm.baidu.com/hm.js?d20a01273820422b6aa2ee41b6c9414d';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
```
|
||||
21
apps/docs/src/guide/project/changeset.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Changeset
|
||||
|
||||
项目内置了 [changeset](https://github.com/changesets/changesets) 作为版本管理工具。Changeset 是一个版本管理工具,它可以帮助我们更好的管理版本,生成 changelog,以及自动发布。
|
||||
|
||||
详细使用方式可查看官方文档,这里不再阐述。如果你不需要它,可以直接忽略。
|
||||
|
||||
## 命令行
|
||||
|
||||
changeset 命令在项目中已经内置:
|
||||
|
||||
### 交互式填写变更集
|
||||
|
||||
```bash
|
||||
pnpm run changeset
|
||||
```
|
||||
|
||||
### 统一提升版本号
|
||||
|
||||
```bash
|
||||
pnpm run version
|
||||
```
|
||||
113
apps/docs/src/guide/project/cli.md
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# CLI
|
||||
|
||||
项目中,提供了一些命令行工具,用于一些常用的操作,代码位于 `scrips` 内。
|
||||
|
||||
## vsh
|
||||
|
||||
用于一些项目操作,如清理项目、检查项目等。
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh [command] [options]
|
||||
```
|
||||
|
||||
### vsh check-circular
|
||||
|
||||
检查整个项目循环引用,如果有循环引用,会在控制台输出循环引用的模块。
|
||||
|
||||
#### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh check-circular
|
||||
```
|
||||
|
||||
#### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| ---------- | ----------------------------------- |
|
||||
| `--staged` | 只检查git暂存区内的文件,默认`false` |
|
||||
|
||||
### vsh check-dep
|
||||
|
||||
检查整个项目依赖情况,并在控制台输出`未使用的依赖`、`未安装的依赖`信息
|
||||
|
||||
#### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh check-dep
|
||||
```
|
||||
|
||||
#### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| ---------------- | --------------------------------------- |
|
||||
| `-r,--recursive` | 递归删除整个项目,默认`true` |
|
||||
| `--del-lock` | 是否删除`pnpm-lock.yaml`文件,默认`true` |
|
||||
|
||||
### vsh lint
|
||||
|
||||
对项目进行lint检查,检查项目中的代码是否符合规范。
|
||||
|
||||
#### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh lint
|
||||
```
|
||||
|
||||
#### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| ---------- | ------------------------------ |
|
||||
| `--format` | 检查并尝试修复错误,默认`false` |
|
||||
|
||||
### vsh publint
|
||||
|
||||
对 `Monorepo` 项目进行包规范检查,检查项目中的包是否符合规范。
|
||||
|
||||
#### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh publint
|
||||
```
|
||||
|
||||
#### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| --------- | ---------------------- |
|
||||
| `--check` | 仅执行检查,默认`false` |
|
||||
|
||||
### vsh code-workspace
|
||||
|
||||
生成 `vben-admin.code-workspace` 文件,目前不需要手动执行,会在代码提交时自动执行。
|
||||
|
||||
#### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh code-workspace
|
||||
```
|
||||
|
||||
#### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| --------------- | -------------------------------------- |
|
||||
| `--auto-commit` | `git commit`时候,自动提交,默认`false` |
|
||||
| `--spaces` | 缩进格式,默认 `2`个缩进 |
|
||||
|
||||
## turbo-run
|
||||
|
||||
用于快速执行大仓中脚本,并提供选项式交互选择。
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm turbo-run [command]
|
||||
```
|
||||
|
||||
### turbo-run dev
|
||||
|
||||
快速执行`dev`命令,并提供选项式交互选择。
|
||||
68
apps/docs/src/guide/project/dir.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 目录说明
|
||||
|
||||
目录使用 Monorepo 管理,项目结构如下:
|
||||
|
||||
```bash
|
||||
.
|
||||
├── Dockerfile # Docker 镜像构建文件
|
||||
├── README.md # 项目说明文档
|
||||
├── apps # 项目应用目录
|
||||
│ ├── backend-mock # 后端模拟服务应用
|
||||
│ ├── web-antd # 基于 Ant Design Vue 的前端应用
|
||||
│ ├── web-ele # 基于 Element Plus 的前端应用
|
||||
│ └── web-naive # 基于 Naive UI 的前端应用
|
||||
├── build-local-docker-image.sh # 本地构建 Docker 镜像脚本
|
||||
├── cspell.json # CSpell 配置文件
|
||||
├── docs # 项目文档目录
|
||||
├── eslint.config.mjs # ESLint 配置文件
|
||||
├── internal # 内部工具目录
|
||||
│ ├── lint-configs # 代码检查配置
|
||||
│ │ ├── commitlint-config # Commitlint 配置
|
||||
│ │ ├── eslint-config # ESLint 配置
|
||||
│ │ ├── prettier-config # Prettier 配置
|
||||
│ │ └── stylelint-config # Stylelint 配置
|
||||
│ ├── node-utils # Node.js 工具
|
||||
│ ├── tailwind-config # Tailwind 配置
|
||||
│ ├── tsconfig # 通用 tsconfig 配置
|
||||
│ └── vite-config # 通用Vite 配置
|
||||
├── package.json # 项目依赖配置
|
||||
├── packages # 项目包目录
|
||||
│ ├── @core # 核心包
|
||||
│ │ ├── base # 基础包
|
||||
│ │ │ ├── design # 设计相关
|
||||
│ │ │ ├── icons # 图标
|
||||
│ │ │ ├── shared # 共享
|
||||
│ │ │ └── typings # 类型定义
|
||||
│ │ ├── composables # 组合式 API
|
||||
│ │ ├── preferences # 偏好设置
|
||||
│ │ └── ui-kit # UI 组件集合
|
||||
│ │ ├── layout-ui # 布局 UI
|
||||
│ │ ├── menu-ui # 菜单 UI
|
||||
│ │ ├── shadcn-ui # shadcn UI
|
||||
│ │ └── tabs-ui # 标签页 UI
|
||||
│ ├── constants # 常量
|
||||
│ ├── effects # 副作用相关包
|
||||
│ │ ├── access # 访问控制
|
||||
│ │ ├── plugins # 第三方大型依赖插件
|
||||
│ │ ├── common-ui # 通用 UI
|
||||
│ │ ├── hooks # 组合式 API
|
||||
│ │ ├── layouts # 布局
|
||||
│ │ └── request # 请求
|
||||
│ ├── icons # 图标
|
||||
│ ├── locales # 国际化
|
||||
│ ├── preferences # 偏好设置
|
||||
│ ├── stores # 状态管理
|
||||
│ ├── styles # 样式
|
||||
│ ├── types # 类型定义
|
||||
│ └── utils # 工具
|
||||
├── playground # 演示目录
|
||||
├── pnpm-lock.yaml # pnpm 锁定文件
|
||||
├── pnpm-workspace.yaml # pnpm 工作区配置文件
|
||||
├── scripts # 脚本目录
|
||||
│ ├── turbo-run # Turbo 运行脚本
|
||||
│ └── vsh # VSH 脚本
|
||||
├── stylelint.config.mjs # Stylelint 配置文件
|
||||
├── turbo.json # Turbo 配置文件
|
||||
├── vben-admin.code-workspace # VS Code 工作区配置文件
|
||||
└── vitest.config.ts # Vite 配置文件
|
||||
```
|
||||
165
apps/docs/src/guide/project/standard.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# 规范
|
||||
|
||||
::: tip 贡献代码
|
||||
|
||||
- 如果你想向项目贡献代码,请确保你的代码符合项目的代码规范。
|
||||
- 如果你使用的是 `vscode`,需要安装以下插件:
|
||||
|
||||
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - 脚本代码检查
|
||||
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - 代码格式化
|
||||
- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - 单词语法检查
|
||||
- [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) - css 格式化
|
||||
|
||||
:::
|
||||
|
||||
## 作用
|
||||
|
||||
具备基本工程素养的同学都会注重编码规范,而代码风格检查(Code Linting,简称 Lint)是保障代码规范一致性的重要手段。
|
||||
|
||||
遵循相应的代码规范有以下好处:
|
||||
|
||||
- 较少 bug 错误率
|
||||
- 高效的开发效率
|
||||
- 更高的可读性
|
||||
|
||||
## 工具
|
||||
|
||||
项目的配置文件位于 `internal/lint-configs` 下,你可以在这里修改各种lint的配置。
|
||||
|
||||
项目内集成了以下几种代码校验工具:
|
||||
|
||||
- [ESLint](https://eslint.org/) 用于 JavaScript 代码检查
|
||||
- [Stylelint](https://stylelint.io/) 用于 CSS 样式检查
|
||||
- [Prettier](https://prettier.io/) 用于代码格式化
|
||||
- [Commitlint](https://commitlint.js.org/) 用于检查 git 提交信息的规范
|
||||
- [Publint](https://publint.dev/) 用于检查 npm 包的规范
|
||||
- [Lint Staged](https://github.com/lint-staged/lint-staged) 用于在 git 提交前运行代码校验
|
||||
- [Cspell](https://cspell.org/) 用于检查拼写错误
|
||||
|
||||
## ESLint
|
||||
|
||||
ESLint 是一个代码规范和错误检查工具,用于识别和报告 TypeScript 代码中的语法错误。
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm eslint .
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
eslint 配置文件为 `eslint.config.mjs`,其核心配置放在`internal/lint-configs/eslint-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
## Stylelint
|
||||
|
||||
Stylelint 用于校验项目内部 css 的风格,加上编辑器的自动修复,可以很好的统一项目内部 css 风格
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm stylelint "**/*.{vue,css,less.scss}"
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
Stylelint 配置文件为 `stylelint.config.mjs`,其核心配置放在`internal/lint-configs/stylelint-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
## Prettier
|
||||
|
||||
Prettier 可以用于统一项目代码风格,统一的缩进,单双引号,尾逗号等等风格
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm prettier .
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
Prettier 配置文件为 `.prettier.mjs`,其核心配置放在`internal/lint-configs/prettier-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
## CommitLint
|
||||
|
||||
在一个团队中,每个人的 git 的 commit 信息都不一样,五花八门,没有一个机制很难保证规范化,如何才能规范化呢?可能你想到的是 git 的 hook 机制,去写 shell 脚本去实现。这当然可以,其实 JavaScript 有一个很好的工具可以实现这个模板,它就是 commitlint(用于校验 git 提交信息规范)。
|
||||
|
||||
### 配置
|
||||
|
||||
CommitLint 配置文件为 `.commitlintrc.mjs`,其核心配置放在`internal/lint-configs/commitlint-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
### Git 提交规范
|
||||
|
||||
参考 [Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular)
|
||||
|
||||
- `feat` 增加新功能
|
||||
- `fix` 修复问题/BUG
|
||||
- `style` 代码风格相关无影响运行结果的
|
||||
- `perf` 优化/性能提升
|
||||
- `refactor` 重构
|
||||
- `revert` 撤销修改
|
||||
- `test` 测试相关
|
||||
- `docs` 文档/注释
|
||||
- `chore` 依赖更新/脚手架配置修改等
|
||||
- `workflow` 工作流改进
|
||||
- `ci` 持续集成
|
||||
- `types` 类型修改
|
||||
|
||||
### 关闭Git提交规范检查
|
||||
|
||||
如果你想关闭 Git 提交规范检查,有两种方式:
|
||||
|
||||
::: code-group
|
||||
|
||||
```bash [临时关闭]
|
||||
git commit -m 'feat: add home page' --no-verify
|
||||
```
|
||||
|
||||
```bash [永久关闭]
|
||||
# 在 .husky/commit-msg 内注释以下代码即可
|
||||
pnpm exec commitlint --edit "$1" # [!code --]
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Publint
|
||||
|
||||
Publint 是一个用于检查 npm 包的规范的工具,可以检查包的版本号是否符合规范,是否符合标准的 ESM 规范包等等。
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm vsh publint
|
||||
```
|
||||
|
||||
## Cspell
|
||||
|
||||
Cspell 是一个用于检查拼写错误的工具,可以检查代码中的拼写错误,避免因为拼写错误导致的 bug。
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm cspell lint \"**/*.ts\" \"**/README.md\" \".changeset/*.md\" --no-progress
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
cspell 配置文件为 `cspell.json`,可以根据项目需求进行修改。
|
||||
|
||||
## Git Hook
|
||||
|
||||
git hook 一般结合各种 lint,在 git 提交代码的时候进行代码风格校验,如果校验没通过,则不会进行提交。需要开发者自行修改后再次进行提交
|
||||
|
||||
### husky
|
||||
|
||||
有一个问题就是校验会校验全部代码,但是我们只想校验我们自己提交的代码,这个时候就可以使用 husky。
|
||||
|
||||
最有效的解决方案就是将 Lint 校验放到本地,常见做法是使用 husky 或者 pre-commit 在本地提交之前先做一次 Lint 校验。
|
||||
|
||||
项目在 `.husky` 内部定义了相应的 hooks
|
||||
|
||||
#### 如何关闭 Husky
|
||||
|
||||
如果你想关闭 Husky,直接删除 `.husky` 目录即可。
|
||||
|
||||
### lint-staged
|
||||
|
||||
用于自动修复提交文件风格问题,其配置文件为 `.lintstagedrc.mjs`,可以根据项目需求进行修改。
|
||||
13
apps/docs/src/guide/project/tailwindcss.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Tailwind CSS
|
||||
|
||||
[Tailwind CSS](https://tailwindcss.com/) 是一个实用性优先的CSS框架,用于快速构建自定义设计。
|
||||
|
||||
## 配置
|
||||
|
||||
项目的配置文件位于 `internal/tailwind-config` 下,你可以在这里修改 Tailwind CSS 的配置。
|
||||
|
||||
::: tip 包使用 tailwindcss 的限制
|
||||
|
||||
当前只有对应的包下面存在 `tailwind.config.mjs` 文件才会启用 tailwindcss 的编译,否则不会启用 tailwindcss。如果你是纯粹的 SDK 包,不需要使用 tailwindcss,可以不用创建 `tailwind.config.mjs` 文件。
|
||||
|
||||
:::
|
||||
33
apps/docs/src/guide/project/test.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# 单元测试
|
||||
|
||||
项目内置了 [Vitest](https://vitest.dev/) 作为单元测试工具。Vitest 是一个基于 Vite 的测试运行器,它提供了一套简单的 API 来编写测试用例。
|
||||
|
||||
## 编写测试用例
|
||||
|
||||
在项目中,我们约定将测试文件名以 `.test.ts` 结尾,或者存放到`__tests__`目录内。例如,创建一个 `utils.ts` 文件,然后同级目录`utils.test.ts` 文件,
|
||||
|
||||
```ts
|
||||
// utils.test.ts
|
||||
import { expect, test } from 'vitest';
|
||||
import { sum } from './sum';
|
||||
|
||||
test('adds 1 + 2 to equal 3', () => {
|
||||
expect(sum(1, 2)).toBe(3);
|
||||
});
|
||||
```
|
||||
|
||||
## 运行测试
|
||||
|
||||
在大仓根目录下运行以下命令即可:
|
||||
|
||||
```bash
|
||||
pnpm test:unit
|
||||
```
|
||||
|
||||
## 现有单元测试
|
||||
|
||||
项目中已经有一些单元测试用例,可以搜索以`.test.ts`结尾的文件查看,在你更改到相关代码时,可以运行单元测试来保证代码的正确性,建议在开发过程中,保持单元测试的覆盖率,且同时在 CI/CD 流程中运行单元测试,保证测试通过在进行项目部署。
|
||||
|
||||
现有单元测试情况:
|
||||
|
||||

|
||||
33
apps/docs/src/guide/project/vite.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Vite Config
|
||||
|
||||
项目封装了一层vite配置,并集成了一些插件,方便在多个包以及应用内复用,使用方式如下:
|
||||
|
||||
## 应用
|
||||
|
||||
```ts
|
||||
// vite.config.mts
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig(async () => {
|
||||
return {
|
||||
application: {},
|
||||
// vite配置,参考vite官方文档进行覆盖
|
||||
vite: {},
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
## 包
|
||||
|
||||
```ts
|
||||
// vite.config.mts
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig(async () => {
|
||||
return {
|
||||
library: {},
|
||||
// vite配置,参考vite官方文档进行覆盖
|
||||
vite: {},
|
||||
};
|
||||
});
|
||||
```
|
||||
53
apps/docs/src/index.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
sidebar: false
|
||||
|
||||
hero:
|
||||
name: y-code 悦码
|
||||
text: 低代码管理平台
|
||||
tagline: 全新升级,打开即用,简单高效
|
||||
image:
|
||||
src: https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp
|
||||
alt: 悦码
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 快速开始 ->
|
||||
link: /guide/introduction/platform
|
||||
- theme: alt
|
||||
text: 立即体验 ->
|
||||
link: https://y-code-platform.shiyuegame.com
|
||||
|
||||
features:
|
||||
- icon: 🚀
|
||||
title: 最新技术栈
|
||||
details: 基于 Vue3、Pinia、Vue Router、TypeScript、等最新技术栈。
|
||||
link: /guide/essentials/concept
|
||||
linkText: 基础概念
|
||||
- icon: 📚
|
||||
title: 低学习成本
|
||||
details: 专为前端开发者设计,无需改变您熟悉的前端开发流程和编码习惯。只需了解Vue,即可轻松上手,实现无缝对接,真正做到零学习成本。
|
||||
- icon: 🛠️
|
||||
title: 自由个性化
|
||||
details: 悦码设计器支持源码级别的自定义,可轻松适配个性化需求,理论上写代码开发能实现的在设计器上都能完成。
|
||||
---
|
||||
|
||||
<!-- <script setup>
|
||||
import {
|
||||
VPTeamPage,
|
||||
VPTeamPageTitle,
|
||||
VPTeamMembers,
|
||||
VPTeamPageSection
|
||||
} from 'vitepress/theme';
|
||||
|
||||
</script>
|
||||
|
||||
<VPTeamPage>
|
||||
<VPTeamPageTitle>
|
||||
<template #title>
|
||||
核心成员介绍
|
||||
</template>
|
||||
</VPTeamPageTitle>
|
||||
</VPTeamPage> -->
|
||||
|
||||
<!-- <VbenContributors /> -->
|
||||
1
apps/docs/src/materials/index.md
Normal file
@@ -0,0 +1 @@
|
||||
物料
|
||||
0
apps/docs/src/platform/index.md
Normal file
BIN
apps/docs/src/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
apps/docs/src/public/guide/devtools.png
Normal file
|
After Width: | Height: | Size: 393 KiB |
BIN
apps/docs/src/public/guide/loading.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
apps/docs/src/public/guide/locale.png
Normal file
|
After Width: | Height: | Size: 471 KiB |
BIN
apps/docs/src/public/guide/login-expired.png
Normal file
|
After Width: | Height: | Size: 556 KiB |
BIN
apps/docs/src/public/guide/login.png
Normal file
|
After Width: | Height: | Size: 469 KiB |
BIN
apps/docs/src/public/guide/preferences.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
apps/docs/src/public/guide/qq.png
Normal file
|
After Width: | Height: | Size: 447 KiB |
BIN
apps/docs/src/public/guide/qq_channel.png
Normal file
|
After Width: | Height: | Size: 448 KiB |
BIN
apps/docs/src/public/guide/report.png
Normal file
|
After Width: | Height: | Size: 1000 KiB |
BIN
apps/docs/src/public/guide/test.png
Normal file
|
After Width: | Height: | Size: 249 KiB |
BIN
apps/docs/src/public/guide/update-notice.png
Normal file
|
After Width: | Height: | Size: 400 KiB |
42
apps/docs/src/public/logos/nitro.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<!-- nitro logo -->
|
||||
<svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_115_108)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M35.2166 7.02016C28.0478 -1.38317 15.4241 -2.38397 7.02077 4.78481C-1.38256 11.9536 -2.38336 24.5773 4.78542 32.9806C11.9542 41.3839 24.5779 42.3847 32.9812 35.216C41.3846 28.0472 42.3854 15.4235 35.2166 7.02016ZM25.2525 17.5175C26.0233 17.5175 26.5155 18.3527 26.1287 19.0194L26.0175 19.2111L18.4696 31.6294C18.3293 31.8602 18.0788 32.001 17.8088 32.001H17.0883C16.5946 32.001 16.2336 31.5349 16.3573 31.0569L18.4054 23.1384C18.5691 22.5053 18.0912 21.888 17.4373 21.888H14.2914C13.6375 21.888 13.1596 21.2708 13.3232 20.6377L16.4137 8.68289C16.5261 8.28056 16.8904 7.99734 17.3081 8.00208C17.3587 8.00266 17.4046 8.0035 17.4427 8.0047L20.6109 8.00465C21.217 8.00436 21.684 8.53896 21.6023 9.13949L21.5828 9.28246L20.3746 16.349C20.2702 16.9598 20.7406 17.5175 21.3603 17.5175H25.2525Z"
|
||||
fill="url(#paint0_diamond_115_108)" />
|
||||
<mask id="mask0_115_108" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0"
|
||||
width="40" height="41">
|
||||
<circle cx="20" cy="20.001" r="20" fill="url(#paint1_diamond_115_108)" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_115_108)">
|
||||
<g filter="url(#filter0_f_115_108)">
|
||||
<path
|
||||
d="M1.11145 13.4267C0.0703174 16.4179 -0.245523 19.6136 0.189923 22.7507C0.62537 25.8879 1.79965 28.8768 3.61611 31.4713C5.43256 34.0659 7.83925 36.192 10.6381 37.6746C13.4369 39.1572 16.5478 39.9538 19.7147 39.999C22.8816 40.0442 26.0139 39.3366 28.8539 37.9345C31.6939 36.5324 34.1602 34.4758 36.05 31.9341C37.9397 29.3924 39.1988 26.4383 39.7236 23.3148C40.2483 20.1914 40.0238 16.9879 39.0684 13.9682L33.2532 15.808C33.9172 17.9068 34.0732 20.1333 33.7085 22.3042C33.3438 24.4751 32.4687 26.5283 31.1552 28.2949C29.8418 30.0615 28.1276 31.4908 26.1537 32.4653C24.1799 33.4399 22.0028 33.9316 19.8017 33.9002C17.6006 33.8688 15.4384 33.3151 13.4932 32.2847C11.5479 31.2543 9.87518 29.7766 8.61269 27.9733C7.35019 26.1699 6.53403 24.0926 6.23138 21.9122C5.92873 19.7317 6.14825 17.5106 6.87187 15.4316L1.11145 13.4267Z"
|
||||
fill="white" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_115_108" x="-10" y="3.42667" width="60" height="46.5744"
|
||||
filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="5" result="effect1_foregroundBlur_115_108" />
|
||||
</filter>
|
||||
<radialGradient id="paint0_diamond_115_108" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(4.00069 20.0004) scale(39.0007 397.71)">
|
||||
<stop stop-color="#31B2F3" />
|
||||
<stop offset="0.473958" stop-color="#F27CEC" />
|
||||
<stop offset="1" stop-color="#FD6641" />
|
||||
</radialGradient>
|
||||
<radialGradient id="paint1_diamond_115_108" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(4 20.0011) scale(39 397.703)">
|
||||
<stop stop-color="#F27CEC" />
|
||||
<stop offset="0.484375" stop-color="#31B2F3" />
|
||||
<stop offset="1" stop-color="#7D7573" />
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_115_108">
|
||||
<rect width="146" height="40.001" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
1
apps/docs/src/public/logos/shadcn-ui.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg class="h-6 w-6" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_102_1338)"><path d="M208 128L128 208" stroke="#41B883" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"></path><path d="M192 40L40 192" stroke="#41B883" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"></path></g><defs><clipPath id="clip0_102_1338"><rect width="256" height="256" fill="white"></rect></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 472 B |
32
apps/docs/src/public/logos/turborepo.svg
Normal file
@@ -0,0 +1,32 @@
|
||||
<svg width="104" height="104" viewBox="0 0 104 104" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_17)">
|
||||
<path d="M26.0192 7C42.0962 -2.28203 61.9038 -2.28203 77.9808 7C94.0577 16.282 103.962 33.4359 103.962 52C103.962 70.5641 94.0577 87.718 77.9808 97C61.9038 106.282 42.0962 106.282 26.0192 97C9.94229 87.718 0.038475 70.5641 0.038475 52C0.038475 33.4359 9.94229 16.282 26.0192 7Z" fill="black" fill-opacity="0.64"/>
|
||||
<path d="M26.0192 7C42.0962 -2.28203 61.9038 -2.28203 77.9808 7C94.0577 16.282 103.962 33.4359 103.962 52C103.962 70.5641 94.0577 87.718 77.9808 97C61.9038 106.282 42.0962 106.282 26.0192 97C9.94229 87.718 0.038475 70.5641 0.038475 52C0.038475 33.4359 9.94229 16.282 26.0192 7Z" fill="url(#paint0_linear_1_17)" fill-opacity="0.15"/>
|
||||
<path d="M26.0192 7C42.0962 -2.28203 61.9038 -2.28203 77.9808 7C94.0577 16.282 103.962 33.4359 103.962 52C103.962 70.5641 94.0577 87.718 77.9808 97C61.9038 106.282 42.0962 106.282 26.0192 97C9.94229 87.718 0.038475 70.5641 0.038475 52C0.038475 33.4359 9.94229 16.282 26.0192 7Z" fill="black" fill-opacity="0.5"/>
|
||||
<path d="M0.538475 52C0.538475 33.6146 10.347 16.6257 26.2692 7.43301C42.1915 -1.7597 61.8085 -1.7597 77.7308 7.43301C93.653 16.6257 103.462 33.6146 103.462 52C103.462 70.3854 93.653 87.3743 77.7308 96.567C61.8085 105.76 42.1915 105.76 26.2692 96.567C10.347 87.3743 0.538475 70.3854 0.538475 52Z" stroke="url(#paint1_radial_1_17)" stroke-opacity="0.15"/>
|
||||
<path d="M0.538475 52C0.538475 33.6146 10.347 16.6257 26.2692 7.43301C42.1915 -1.7597 61.8085 -1.7597 77.7308 7.43301C93.653 16.6257 103.462 33.6146 103.462 52C103.462 70.3854 93.653 87.3743 77.7308 96.567C61.8085 105.76 42.1915 105.76 26.2692 96.567C10.347 87.3743 0.538475 70.3854 0.538475 52Z" stroke="url(#paint2_linear_1_17)" stroke-opacity="0.5"/>
|
||||
<path d="M51.8878 37.9262C44.1892 37.9262 37.9258 44.1896 37.9258 51.8882C37.9258 59.5868 44.1892 65.8502 51.8878 65.8502C59.5864 65.8502 65.8498 59.5868 65.8498 51.8882C65.8498 44.1896 59.5864 37.9262 51.8878 37.9262ZM51.8878 59.1136C47.8968 59.1136 44.6624 55.8792 44.6624 51.8882C44.6624 47.8972 47.8968 44.6628 51.8878 44.6628C55.8788 44.6628 59.1132 47.8972 59.1132 51.8882C59.1132 55.8792 55.8788 59.1136 51.8878 59.1136Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.0581 35.633V30.42C64.3889 31.0258 73.3901 40.4066 73.3901 51.8882C73.3901 63.3698 64.3889 72.748 53.0581 73.3564V68.1434C61.5029 67.5402 68.1901 60.4838 68.1901 51.8882C68.1901 43.2926 61.5029 36.2362 53.0581 35.633ZM39.5745 62.5482C37.3359 59.9638 35.8929 56.6722 35.6355 53.0582H30.4199C30.6903 58.1152 32.7131 62.7042 35.8825 66.2376L39.5719 62.5482H39.5745ZM50.7182 73.3564V68.1434C47.1016 67.886 43.81 66.4456 41.2256 64.2044L37.5362 67.8938C41.0722 71.0658 45.6612 73.086 50.7156 73.3564H50.7182Z" fill="url(#paint3_linear_1_17)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1_17" x1="52" y1="-8" x2="52" y2="112" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3286F1"/>
|
||||
<stop offset="1" stop-color="#C43AC4"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint1_radial_1_17" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(52 -7.99999) rotate(90) scale(154.286 154.286)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint2_linear_1_17" x1="-8" y1="-8" x2="18.25" y2="40.75" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_1_17" x1="53.9007" y1="33.4389" x2="32.7679" y2="54.5717" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0096FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_1_17">
|
||||
<rect width="104" height="104" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
15
apps/docs/src/public/logos/vite.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#41D1FF"/>
|
||||
<stop offset="1" stop-color="#BD34FE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFEA83"/>
|
||||
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||
<stop offset="1" stop-color="#FFA800"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
apps/docs/src/renderer/index.md
Normal file
@@ -0,0 +1 @@
|
||||
渲染器
|
||||
11
apps/docs/tailwind.config.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
import tailwindcssConfig from '@vben/tailwind-config';
|
||||
|
||||
export default {
|
||||
...tailwindcssConfig,
|
||||
content: [
|
||||
...tailwindcssConfig.content,
|
||||
'.vitepress/**/*.{js,mts,ts,vue}',
|
||||
'src/demos/**/*.{js,mts,ts,vue}',
|
||||
'src/**/*.md',
|
||||
],
|
||||
};
|
||||
19
apps/docs/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"#/*": ["./src/_env/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
".vitepress/*.mts",
|
||||
".vitepress/**/*.ts",
|
||||
".vitepress/**/*.vue",
|
||||
"src/*.mts",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.vue"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||