feat: 优化路由权限逻辑
自动部署 / build (push) Failing after 12s Details

master
绝弹 2024-01-11 21:18:49 +08:00
parent 21de506907
commit 9436f5feee
89 changed files with 239 additions and 202 deletions

File diff suppressed because one or more lines are too long

View File

@ -2,9 +2,10 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>%VITE_TITLE% | %VITE_SUBTITLE%</title> <meta name="description" content="%VITE_SUBTITLE%" />
<link rel="icon" href="./favicon.ico" />
<title>%VITE_TITLE%</title>
</head> </head>
<body> <body>
<div id="app" class="dark:bg-slate-900 dark:text-slate-200"> <div id="app" class="dark:bg-slate-900 dark:text-slate-200">
@ -13,6 +14,8 @@
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHN0eWxlPSJtYXJnaW46IGF1dG87IGJhY2tncm91bmQ6IHJnYigyNTUsIDI1NSwgMjU1KTsgZGlzcGxheTogYmxvY2s7IHNoYXBlLXJlbmRlcmluZzogYXV0bzsiIHdpZHRoPSIyMDBweCIgaGVpZ2h0PSIyMDBweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1MCA1MCkiPjxnPjxhbmltYXRlVHJhbnNmb3JtIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIgdHlwZT0icm90YXRlIiB2YWx1ZXM9IjA7NDUiIGtleVRpbWVzPSIwOzEiIGR1cj0iMC4ycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiPjwvYW5pbWF0ZVRyYW5zZm9ybT48cGF0aCBkPSJNMjkuNDkxNTI0MjA2MTE3MjU1IC01LjUgTDM3LjQ5MTUyNDIwNjExNzI1NSAtNS41IEwzNy40OTE1MjQyMDYxMTcyNTUgNS41IEwyOS40OTE1MjQyMDYxMTcyNTUgNS41IEEzMCAzMCAwIDAgMSAyNC43NDI3NDQwNTAxOTg3MzggMTYuOTY0NTY5NDU3MTQ2NzEyIEwyNC43NDI3NDQwNTAxOTg3MzggMTYuOTY0NTY5NDU3MTQ2NzEyIEwzMC4zOTk1OTgyOTk2OTExMTcgMjIuNjIxNDIzNzA2NjM5MDkyIEwyMi42MjE0MjM3MDY2MzkwOTYgMzAuMzk5NTk4Mjk5NjkxMTE0IEwxNi45NjQ1Njk0NTcxNDY3MTYgMjQuNzQyNzQ0MDUwMTk4NzM0IEEzMCAzMCAwIDAgMSA1LjUgMjkuNDkxNTI0MjA2MTE3MjU1IEw1LjUgMjkuNDkxNTI0MjA2MTE3MjU1IEw1LjUgMzcuNDkxNTI0MjA2MTE3MjU1IEwtNS40OTk5OTk5OTk5OTk5OTcgMzcuNDkxNTI0MjA2MTE3MjU1IEwtNS40OTk5OTk5OTk5OTk5OTcgMjkuNDkxNTI0MjA2MTE3MjU1IEEzMCAzMCAwIDAgMSAtMTYuOTY0NTY5NDU3MTQ2NzA1IDI0Ljc0Mjc0NDA1MDE5ODczOCBMLTE2Ljk2NDU2OTQ1NzE0NjcwNSAyNC43NDI3NDQwNTAxOTg3MzggTC0yMi42MjE0MjM3MDY2MzkwODUgMzAuMzk5NTk4Mjk5NjkxMTE3IEwtMzAuMzk5NTk4Mjk5NjkxMTE3IDIyLjYyMTQyMzcwNjYzOTA5MiBMLTI0Ljc0Mjc0NDA1MDE5ODczOCAxNi45NjQ1Njk0NTcxNDY3MTIgQTMwIDMwIDAgMCAxIC0yOS40OTE1MjQyMDYxMTcyNTUgNS41MDAwMDAwMDAwMDAwMDkgTC0yOS40OTE1MjQyMDYxMTcyNTUgNS41MDAwMDAwMDAwMDAwMDkgTC0zNy40OTE1MjQyMDYxMTcyNTUgNS41MDAwMDAwMDAwMDAwMSBMLTM3LjQ5MTUyNDIwNjExNzI1NSAtNS41MDAwMDAwMDAwMDAwMDEgTC0yOS40OTE1MjQyMDYxMTcyNTUgLTUuNTAwMDAwMDAwMDAwMDAyIEEzMCAzMCAwIDAgMSAtMjQuNzQyNzQ0MDUwMTk4NzM4IC0xNi45NjQ1Njk0NTcxNDY3MDUgTC0yNC43NDI3NDQwNTAxOTg3MzggLTE2Ljk2NDU2OTQ1NzE0NjcwNSBMLTMwLjM5OTU5ODI5OTY5MTExNyAtMjIuNjIxNDIzNzA2NjM5MDg1IEwtMjIuNjIxNDIzNzA2NjM5MDkyIC0zMC4zOTk1OTgyOTk2OTExMTcgTC0xNi45NjQ1Njk0NTcxNDY3MTIgLTI0Ljc0Mjc0NDA1MDE5ODczOCBBMzAgMzAgMCAwIDEgLTUuNTAwMDAwMDAwMDAwMDExIC0yOS40OTE1MjQyMDYxMTcyNTUgTC01LjUwMDAwMDAwMDAwMDAxMSAtMjkuNDkxNTI0MjA2MTE3MjU1IEwtNS41MDAwMDAwMDAwMDAwMTIgLTM3LjQ5MTUyNDIwNjExNzI1NSBMNS40OTk5OTk5OTk5OTk5OTggLTM3LjQ5MTUyNDIwNjExNzI1NSBMNS41IC0yOS40OTE1MjQyMDYxMTcyNTUgQTMwIDMwIDAgMCAxIDE2Ljk2NDU2OTQ1NzE0NjcwMiAtMjQuNzQyNzQ0MDUwMTk4NzQgTDE2Ljk2NDU2OTQ1NzE0NjcwMiAtMjQuNzQyNzQ0MDUwMTk4NzQgTDIyLjYyMTQyMzcwNjYzOTA4IC0zMC4zOTk1OTgyOTk2OTExMiBMMzAuMzk5NTk4Mjk5NjkxMTE3IC0yMi42MjE0MjM3MDY2MzkxIEwyNC43NDI3NDQwNTAxOTg3MzggLTE2Ljk2NDU2OTQ1NzE0NjcxNiBBMzAgMzAgMCAwIDEgMjkuNDkxNTI0MjA2MTE3MjU1IC01LjUwMDAwMDAwMDAwMDAxMyBNMCAtMjBBMjAgMjAgMCAxIDAgMCAyMCBBMjAgMjAgMCAxIDAgMCAtMjAiIGZpbGw9IiMwOWYiPjwvcGF0aD48L2c+PC9nPjwvc3ZnPgo=" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHN0eWxlPSJtYXJnaW46IGF1dG87IGJhY2tncm91bmQ6IHJnYigyNTUsIDI1NSwgMjU1KTsgZGlzcGxheTogYmxvY2s7IHNoYXBlLXJlbmRlcmluZzogYXV0bzsiIHdpZHRoPSIyMDBweCIgaGVpZ2h0PSIyMDBweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1MCA1MCkiPjxnPjxhbmltYXRlVHJhbnNmb3JtIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIgdHlwZT0icm90YXRlIiB2YWx1ZXM9IjA7NDUiIGtleVRpbWVzPSIwOzEiIGR1cj0iMC4ycyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiPjwvYW5pbWF0ZVRyYW5zZm9ybT48cGF0aCBkPSJNMjkuNDkxNTI0MjA2MTE3MjU1IC01LjUgTDM3LjQ5MTUyNDIwNjExNzI1NSAtNS41IEwzNy40OTE1MjQyMDYxMTcyNTUgNS41IEwyOS40OTE1MjQyMDYxMTcyNTUgNS41IEEzMCAzMCAwIDAgMSAyNC43NDI3NDQwNTAxOTg3MzggMTYuOTY0NTY5NDU3MTQ2NzEyIEwyNC43NDI3NDQwNTAxOTg3MzggMTYuOTY0NTY5NDU3MTQ2NzEyIEwzMC4zOTk1OTgyOTk2OTExMTcgMjIuNjIxNDIzNzA2NjM5MDkyIEwyMi42MjE0MjM3MDY2MzkwOTYgMzAuMzk5NTk4Mjk5NjkxMTE0IEwxNi45NjQ1Njk0NTcxNDY3MTYgMjQuNzQyNzQ0MDUwMTk4NzM0IEEzMCAzMCAwIDAgMSA1LjUgMjkuNDkxNTI0MjA2MTE3MjU1IEw1LjUgMjkuNDkxNTI0MjA2MTE3MjU1IEw1LjUgMzcuNDkxNTI0MjA2MTE3MjU1IEwtNS40OTk5OTk5OTk5OTk5OTcgMzcuNDkxNTI0MjA2MTE3MjU1IEwtNS40OTk5OTk5OTk5OTk5OTcgMjkuNDkxNTI0MjA2MTE3MjU1IEEzMCAzMCAwIDAgMSAtMTYuOTY0NTY5NDU3MTQ2NzA1IDI0Ljc0Mjc0NDA1MDE5ODczOCBMLTE2Ljk2NDU2OTQ1NzE0NjcwNSAyNC43NDI3NDQwNTAxOTg3MzggTC0yMi42MjE0MjM3MDY2MzkwODUgMzAuMzk5NTk4Mjk5NjkxMTE3IEwtMzAuMzk5NTk4Mjk5NjkxMTE3IDIyLjYyMTQyMzcwNjYzOTA5MiBMLTI0Ljc0Mjc0NDA1MDE5ODczOCAxNi45NjQ1Njk0NTcxNDY3MTIgQTMwIDMwIDAgMCAxIC0yOS40OTE1MjQyMDYxMTcyNTUgNS41MDAwMDAwMDAwMDAwMDkgTC0yOS40OTE1MjQyMDYxMTcyNTUgNS41MDAwMDAwMDAwMDAwMDkgTC0zNy40OTE1MjQyMDYxMTcyNTUgNS41MDAwMDAwMDAwMDAwMSBMLTM3LjQ5MTUyNDIwNjExNzI1NSAtNS41MDAwMDAwMDAwMDAwMDEgTC0yOS40OTE1MjQyMDYxMTcyNTUgLTUuNTAwMDAwMDAwMDAwMDAyIEEzMCAzMCAwIDAgMSAtMjQuNzQyNzQ0MDUwMTk4NzM4IC0xNi45NjQ1Njk0NTcxNDY3MDUgTC0yNC43NDI3NDQwNTAxOTg3MzggLTE2Ljk2NDU2OTQ1NzE0NjcwNSBMLTMwLjM5OTU5ODI5OTY5MTExNyAtMjIuNjIxNDIzNzA2NjM5MDg1IEwtMjIuNjIxNDIzNzA2NjM5MDkyIC0zMC4zOTk1OTgyOTk2OTExMTcgTC0xNi45NjQ1Njk0NTcxNDY3MTIgLTI0Ljc0Mjc0NDA1MDE5ODczOCBBMzAgMzAgMCAwIDEgLTUuNTAwMDAwMDAwMDAwMDExIC0yOS40OTE1MjQyMDYxMTcyNTUgTC01LjUwMDAwMDAwMDAwMDAxMSAtMjkuNDkxNTI0MjA2MTE3MjU1IEwtNS41MDAwMDAwMDAwMDAwMTIgLTM3LjQ5MTUyNDIwNjExNzI1NSBMNS40OTk5OTk5OTk5OTk5OTggLTM3LjQ5MTUyNDIwNjExNzI1NSBMNS41IC0yOS40OTE1MjQyMDYxMTcyNTUgQTMwIDMwIDAgMCAxIDE2Ljk2NDU2OTQ1NzE0NjcwMiAtMjQuNzQyNzQ0MDUwMTk4NzQgTDE2Ljk2NDU2OTQ1NzE0NjcwMiAtMjQuNzQyNzQ0MDUwMTk4NzQgTDIyLjYyMTQyMzcwNjYzOTA4IC0zMC4zOTk1OTgyOTk2OTExMiBMMzAuMzk5NTk4Mjk5NjkxMTE3IC0yMi42MjE0MjM3MDY2MzkxIEwyNC43NDI3NDQwNTAxOTg3MzggLTE2Ljk2NDU2OTQ1NzE0NjcxNiBBMzAgMzAgMCAwIDEgMjkuNDkxNTI0MjA2MTE3MjU1IC01LjUwMDAwMDAwMDAwMDAxMyBNMCAtMjBBMjAgMjAgMCAxIDAgMCAyMCBBMjAgMjAgMCAxIDAgMCAtMjAiIGZpbGw9IiMwOWYiPjwvcGF0aD48L2c+PC9nPjwvc3ZnPgo="
alt="loading" alt="loading"
class="loading-image" class="loading-image"
width="64"
height="64"
/> />
<h1 class="loading-title">欢迎访问%VITE_TITLE%</h1> <h1 class="loading-title">欢迎访问%VITE_TITLE%</h1>
<div class="loading-tip">资源加载中, 请稍等...</div> <div class="loading-tip">资源加载中, 请稍等...</div>

View File

@ -0,0 +1,36 @@
import fs from 'fs';
import { Plugin, ResolvedConfig } from 'vite';
/**
*
* @description
* index.xx.vue
* 退 index.vue
*/
export default function plugin(): Plugin {
let config: ResolvedConfig;
let extension: string;
return {
name: 'vite:extension',
enforce: 'pre',
configResolved(resolvedConfig) {
config = resolvedConfig;
extension = config.env.VITE_EXTENTION ?? config.isProduction ? 'prod' : 'dev';
},
load(id) {
if (!extension || !id.includes('src')) {
return;
}
if (id.includes('?vue')) {
return;
}
const targetPath = id.replace(/\.([^.]*?)$/, `.${extension}.$1`);
if (targetPath && fs.existsSync(targetPath)) {
return fs.readFileSync(targetPath, 'utf-8');
}
},
};
}

View File

@ -1,7 +1,6 @@
import { spawn } from "child_process"; import { spawn } from 'child_process';
import fs from "fs"; import { Plugin, ResolvedConfig } from 'vite';
import { Plugin, ResolvedConfig } from "vite"; import pkg from '../../package.json';
import pkg from "../../package.json";
/** /**
* logo * logo
@ -21,15 +20,15 @@ const LOGO = ` _ _______ _______ ____ _____ _____ ________ ____
* @returns Promise<string | void> * @returns Promise<string | void>
*/ */
const exec = (cmd: string) => { const exec = (cmd: string) => {
return new Promise<string | void>((resolve) => { return new Promise<string | void>(resolve => {
if (!cmd) { if (!cmd) {
return resolve(); return resolve();
} }
const child = spawn(cmd, [], { shell: true }); const child = spawn(cmd, [], { shell: true });
child.stdout.once("data", (data) => { child.stdout.once('data', data => {
resolve(data.toString().replace(/"|\n/g, "")); resolve(data.toString().replace(/"|\n/g, ''));
}); });
child.stderr.once("data", () => { child.stderr.once('data', () => {
resolve(); resolve();
}); });
}); });
@ -40,9 +39,9 @@ const exec = (cmd: string) => {
* @returns Promise<string> * @returns Promise<string>
*/ */
const getBuildInfo = async () => { const getBuildInfo = async () => {
const hash = await exec("git log --format=%h -n 1"); const hash = await exec('git log --format=%h -n 1');
const time = new Date().toLocaleString("zh-Hans-CN"); const time = new Date().toLocaleString('zh-Hans-CN');
const latestTag = await exec("git describe --tags --abbrev=0"); const latestTag = await exec('git describe --tags --abbrev=0');
const commits = await exec(`git rev-list --count ${latestTag}..HEAD`); const commits = await exec(`git rev-list --count ${latestTag}..HEAD`);
const version = commits ? `${latestTag}.${commits}` : `v${pkg.version}`; const version = commits ? `${latestTag}.${commits}` : `v${pkg.version}`;
const content = `欢迎访问!版本: ${version} 标识: ${hash} 构建: ${time}`; const content = `欢迎访问!版本: ${version} 标识: ${hash} 构建: ${time}`;
@ -57,39 +56,24 @@ const getBuildInfo = async () => {
*/ */
export default function plugin(): Plugin { export default function plugin(): Plugin {
let config: ResolvedConfig; let config: ResolvedConfig;
let extension: string;
return { return {
name: "vite:customizer", name: 'vite:info',
enforce: "pre", enforce: 'pre',
configResolved(resolvedConfig) { configResolved(resolvedConfig) {
config = resolvedConfig; config = resolvedConfig;
extension = config.env.VITE_EXTENTION ?? config.isProduction ? "prod" : "dev";
}, },
async transformIndexHtml() { async transformIndexHtml() {
const script = await getBuildInfo(); const script = await getBuildInfo();
return [ return [
{ {
tag: "script", tag: 'script',
injectTo: "body", injectTo: 'body',
children: script, children: script,
}, },
]; ];
}, },
load(id) {
if (!extension || !id.includes("src")) {
return;
}
if (id.includes("?vue")) {
return;
}
const targetPath = id.replace(/\.([^.]*?)$/, `.${extension}.$1`);
if (targetPath && fs.existsSync(targetPath)) {
return fs.readFileSync(targetPath, "utf-8");
}
},
}; };
} }

View File

@ -2,7 +2,7 @@
<a-config-provider> <a-config-provider>
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<keep-alive :include="menuStore.caches"> <keep-alive :include="menuStore.caches">
<component v-if="hasAuth(route)" :is="Component"></component> <component v-if="hasAuth" :is="Component"></component>
<AnForbidden v-else></AnForbidden> <AnForbidden v-else></AnForbidden>
</keep-alive> </keep-alive>
</router-view> </router-view>
@ -10,27 +10,36 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { RouteLocationNormalizedLoaded } from 'vue-router';
import { useUserStore } from '@/store/user';
import { useMenuStore } from '@/store/menu'; import { useMenuStore } from '@/store/menu';
import { useUserStore } from '@/store/user';
const route = useRoute();
const userStore = useUserStore(); const userStore = useUserStore();
const menuStore = useMenuStore(); const menuStore = useMenuStore();
const hasAuth = (route: RouteLocationNormalizedLoaded) => { const hasAuth = computed(() => {
const neddAuth = route.meta.auth; return route.matched.every(item => {
const userAuth = userStore.auth; console.log('i', item);
if (!neddAuth?.length) { const needAuth = item.meta.auth;
return true; const userAuth = userStore.auth;
} if (needAuth?.includes('*')) {
if (neddAuth.some(i => i === '*')) { return true;
return true; }
} if (!userStore.accessToken && needAuth?.includes('unlogin')) {
if (userAuth.some(i => neddAuth.some(j => j === i))) { return true;
return true; }
} if (!userStore.accessToken) {
return false; return false;
}; }
if (!needAuth) {
return true;
}
if (userAuth.some(i => needAuth.some(j => j === i))) {
return true;
}
return false;
});
});
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -1,15 +1,15 @@
<template> <template>
<div class="h-full overflow-hidden grid grid-rows-[auto_1fr]"> <div class="h-full overflow-hidden grid grid-rows-[auto_1fr]">
<div class="bg-white px-4 py-2"> <div class="bg-white px-4 py-2 border-b border-gray-200">
<div class="flex justify-between gap-4"> <div class="flex justify-between gap-4">
<BreadCrumb></BreadCrumb> <BreadCrumb></BreadCrumb>
<div> <div>
<a-link>需要帮助</a-link> <a-link>需要帮助</a-link>
<a-link @click="router.push({ path: route.path, query: { s: Math.random() }, force: true })"> <a-button size="mini" @click="router.push({ path: route.path, query: { s: Math.random() }, force: true })">
<template #icon> <template #icon>
<i class="icon-park-outline-refresh"></i> <i class="icon-park-outline-refresh"></i>
</template> </template>
</a-link> </a-button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,11 +1,12 @@
<template> <template>
<div :style="style"></div> <div :style="style" class="w-full h-full bg-orange-500 flex items-center justify-center text-white text-lg">
图片组件
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType } from 'vue'; import { CSSProperties, PropType } from 'vue';
import { Image } from './interface'; import { Image } from './interface';
import { CSSProperties } from 'vue';
const props = defineProps({ const props = defineProps({
data: { data: {

View File

@ -1,13 +1,12 @@
<template> <template>
<div :style="style" class="w-full h-full bg-brand-500 flex items-center justify-center text-white text-lg"> <div :style="style" class="w-full h-full bg-blue-500 flex items-center justify-center text-white text-lg">
视频组件 视频组件
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType } from 'vue'; import { CSSProperties, PropType } from 'vue';
import { Video } from './interface'; import { Video } from './interface';
import { CSSProperties } from 'vue';
const props = defineProps({ const props = defineProps({
data: { data: {

View File

@ -1,6 +1,7 @@
<!-- 修改自: https://github.com/zuley/vue-color-picker --> <!-- 修改自: https://github.com/zuley/vue-color-picker -->
<script setup lang="ts"> <script setup lang="ts">
// @ts-nocheck
import { onClickOutside } from "@vueuse/core"; import { onClickOutside } from "@vueuse/core";
import { computed, ref } from "vue"; import { computed, ref } from "vue";

View File

@ -1,12 +1,5 @@
<template> <template>
<a-modal <a-modal v-model:visible="show" :fullscreen="true" :footer="false" class="an-editor">
v-model:visible="show"
mask-animation-name=""
modal-animation-name=""
:fullscreen="true"
:footer="false"
class="ani-modal"
>
<div class="w-full h-full bg-slate-100 grid grid-rows-[auto_1fr] select-none"> <div class="w-full h-full bg-slate-100 grid grid-rows-[auto_1fr] select-none">
<div class="h-13 bg-white border-b border-slate-200 z-10"> <div class="h-13 bg-white border-b border-slate-200 z-10">
<EditorHeader <EditorHeader
@ -144,7 +137,7 @@ onMounted(loadData);
</script> </script>
<style lang="less"> <style lang="less">
.ani-modal { .an-editor {
.muti-form-item .arco-form-item .arco-form-item-label { .muti-form-item .arco-form-item .arco-form-item-label {
line-height: 1; line-height: 1;
} }

View File

@ -1,6 +1,6 @@
import { Ref } from "vue"; import { Ref } from "vue";
import { Block } from "./block";
import { getClosestValInSortedArr } from "../utils/closest"; import { getClosestValInSortedArr } from "../utils/closest";
import { Block } from "./block";
/** /**
* 线 * 线
@ -100,8 +100,8 @@ export const useReferenceLine = (blocks: Ref<Block[]>, current: Ref<Block | null
* 6. 线 * 6. 线
*/ */
function updateRefLine(rect: DragRect) { function updateRefLine(rect: DragRect) {
const allXLines = []; const allXLines: any[] = [];
const allYLines = []; const allYLines: any[] = [];
const box = getRectBox(rect); const box = getRectBox(rect);
let offsetX: number | undefined; let offsetX: number | undefined;
let offsetY: number | undefined; let offsetY: number | undefined;
@ -138,7 +138,7 @@ export const useReferenceLine = (blocks: Ref<Block[]>, current: Ref<Block | null
} else if (isEqualNum(closetDistX, distMaxX)) { } else if (isEqualNum(closetDistX, distMaxX)) {
offsetX = closetMaxX - box.maxX; offsetX = closetMaxX - box.maxX;
} else { } else {
throw new Error("un"); throw new Error('un');
} }
} }
@ -150,7 +150,7 @@ export const useReferenceLine = (blocks: Ref<Block[]>, current: Ref<Block | null
} else if (isEqualNum(closetDistY, distMaxY)) { } else if (isEqualNum(closetDistY, distMaxY)) {
offsetY = closetMaxY - box.maxY; offsetY = closetMaxY - box.maxY;
} else { } else {
throw new Error("un"); throw new Error('un');
} }
} }

View File

@ -1,4 +1,3 @@
export function getModel(model: Recordable) { export function getModel(model: Recordable) {
const data: Recordable = {}; const data: Recordable = {};
@ -42,7 +41,7 @@ function rmString(str: string) {
} }
function setModelArray(data: Recordable, key: string) { function setModelArray(data: Recordable, key: string) {
const result = []; const result: any[] = [];
const field = rmString(key); const field = rmString(key);
for (const key of field.split(',')) { for (const key of field.split(',')) {
result.push(data[key]); result.push(data[key]);

View File

@ -11,7 +11,7 @@
{{ currentFormated }} {{ currentFormated }}
</div> </div>
<div class="w-96"> <div class="w-96">
<audio ref="audioRef" src="" class="hidden" @timeupdate="onTimeUpdate"></audio> <audio ref="audioRef" src="" class="hidden"></audio>
<a-slider class="block!"></a-slider> <a-slider class="block!"></a-slider>
</div> </div>
<div> <div>
@ -19,10 +19,7 @@
</div> </div>
<div class="dd"> <div class="dd">
<a-popover> <a-popover>
<div <div class="text-xl hover:bg-[rgba(255,255,255,.1)] h-8 px-1.5 flex items-center justify-center rounded">
@click="onMuteToggle"
class="text-xl hover:bg-[rgba(255,255,255,.1)] h-8 px-1.5 flex items-center justify-center rounded"
>
<i :class="volumeIcon"></i> <i :class="volumeIcon"></i>
</div> </div>
<template #content> <template #content>
@ -39,6 +36,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import numeral from 'numeral';
const playing = ref(true); const playing = ref(true);
const volume = ref(50); const volume = ref(50);
const volumeLast = ref(50); const volumeLast = ref(50);

View File

@ -104,7 +104,7 @@
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
import { getIcon } from '@/pages/content/material/components/util'; import { getIcon } from '@/pages/content/material/util';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import DPlayer from 'dplayer'; import DPlayer from 'dplayer';
import numeral from 'numeral'; import numeral from 'numeral';

View File

@ -39,6 +39,7 @@ const router = useRouter();
{ {
"meta": { "meta": {
"title": "404", "title": "404",
"auth": ["*"],
"icon": "icon-park-outline-home" "icon": "icon-park-outline-home"
} }
} }

21
src/pages/_demo/index.vue Normal file
View File

@ -0,0 +1,21 @@
<template>
<div>
本页面可直接访问!
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
<route lang="json">
{
"meta": {
"name": "DemoPage",
"sort": 101,
"title": "测试页面",
"auth": ["*"],
"icon": "icon-park-outline-home"
}
}
</route>

View File

@ -30,7 +30,7 @@ export default defineComponent({
function renderItem(routes: MenuItem[], level = 1) { function renderItem(routes: MenuItem[], level = 1) {
return routes.map((route): any => { return routes.map((route): any => {
const icon = route.icon ? () => <i class={route.icon} /> : null; const icon = route.icon ? () => <i class={route.icon} /> : null;
if (level < 3 && route.children?.some(i => !i.hide)) { if (level < 3 && route.children?.some(i => i.hide !== true)) {
return ( return (
<> <>
<a-divider margin={6} class="!border-slate-100 px-2"></a-divider> <a-divider margin={6} class="!border-slate-100 px-2"></a-divider>
@ -45,7 +45,7 @@ export default defineComponent({
<div>{route.title}</div> <div>{route.title}</div>
<div class="text-xs text-gray-400"> <div class="text-xs text-gray-400">
{/* <a-badge count={8}>8</a-badge> */} {/* <a-badge count={8}>8</a-badge> */}
{route.hide === 'prod' ? <a-tag color="blue">{'开发'}</a-tag> : null} {route.hide === 'prod' ? <a-tag color="red">{'开发'}</a-tag> : null}
</div> </div>
</div> </div>
</a-menu-item> </a-menu-item>

View File

@ -91,7 +91,7 @@ import userDropdown from './UserDropdown.vue';
defineOptions({ name: 'LayoutPage' }); defineOptions({ name: 'LayoutPage' });
const route = useRoute() const route = useRoute();
const appStore = useAppStore(); const appStore = useAppStore();
const menuStore = useMenuStore(); const menuStore = useMenuStore();
const isCollapsed = ref(false); const isCollapsed = ref(false);
@ -131,13 +131,6 @@ const buttons = [
window.open('https://github.com/appnify/starter-vue', '_blank'); window.open('https://github.com/appnify/starter-vue', '_blank');
}, },
}, },
{
icon: 'icon-park-outline-info',
tooltip: '关于',
onClick: () => {
window.open('https://github.com/appnify/starter-vue', '_blank');
},
},
]; ];
</script> </script>

View File

@ -136,6 +136,7 @@ const onSubmitForm = async () => {
"name": "LoginPage", "name": "LoginPage",
"sort": 101, "sort": 101,
"title": "登录", "title": "登录",
"auth": ["unlogin"],
"icon": "icon-park-outline-home" "icon": "icon-park-outline-home"
} }
} }

View File

@ -1,4 +1,4 @@
<script></script> <template></template>
<route lang="json"> <route lang="json">
{ {
"component": null, "component": null,

View File

@ -1,14 +1,14 @@
<template> <template>
<bread-page> <bread-page>
<div class="h-full grid grid-cols-[auto_auto_1fr]"> <div class="h-full grid grid-cols-[1fr_auto_1fr]">
<div class="w-[300px]"> <div>
<a-tabs type="capsule" @change="onChange"> <a-tabs @change="onChange">
<a-tab-pane v-for="tag in tags" :key="tag.name" :title="tag.description"> <a-tab-pane v-for="tag in tags" :key="tag.name" :title="tag.description">
<a-form :model="{}" layout="vertical"> <a-form :model="{}" layout="vertical">
<a-form-item label="新增接口"> <a-form-item label="新增接口">
<a-radio-group type="button" v-model="type.create"> <a-radio-group type="button" v-model="type.create">
<a-radio <a-radio
v-for="route in routes.filter((i) => i.tag === tag.name)" v-for="route in routes.filter(i => i.tag === tag.name)"
:value="route.operationId" :value="route.operationId"
:key="route.path" :key="route.path"
> >
@ -19,7 +19,7 @@
<a-form-item label="修改接口"> <a-form-item label="修改接口">
<a-radio-group type="button" v-model="type.modify"> <a-radio-group type="button" v-model="type.modify">
<a-radio <a-radio
v-for="route in routes.filter((i) => i.tag === tag.name)" v-for="route in routes.filter(i => i.tag === tag.name)"
:value="route.operationId" :value="route.operationId"
:key="route.path" :key="route.path"
> >
@ -30,7 +30,7 @@
<a-form-item label="查询接口"> <a-form-item label="查询接口">
<a-radio-group type="button" v-model="type.select"> <a-radio-group type="button" v-model="type.select">
<a-radio <a-radio
v-for="route in routes.filter((i) => i.tag === tag.name)" v-for="route in routes.filter(i => i.tag === tag.name)"
:value="route.operationId" :value="route.operationId"
:key="route.path" :key="route.path"
> >
@ -41,7 +41,7 @@
<a-form-item label="删除接口"> <a-form-item label="删除接口">
<a-radio-group type="button" v-model="type.delete"> <a-radio-group type="button" v-model="type.delete">
<a-radio <a-radio
v-for="route in routes.filter((i) => i.tag === tag.name)" v-for="route in routes.filter(i => i.tag === tag.name)"
:value="route.operationId" :value="route.operationId"
:key="route.path" :key="route.path"
> >
@ -65,12 +65,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import ejs from "ejs"; import ejs from 'ejs';
import doc from "./components/data.json"; import doc from './data.json';
import editorModal from "./components/editor.vue"; import editorModal from './editor.vue';
import template from "./components/page.ejs?raw"; import template from './page.ejs?raw';
const content = ref(""); const content = ref('');
const { tags, routes } = doc; const { tags, routes } = doc;
const type = ref({ const type = ref({
create: undefined, create: undefined,
@ -85,8 +85,8 @@ const onChange = (value: string | number) => {
const onOpen = () => { const onOpen = () => {
const data = { const data = {
tag: "", tag: '',
operationId: "", operationId: '',
create: {}, create: {},
select: {}, select: {},
modify: {}, modify: {},
@ -114,9 +114,9 @@ const onOpen = () => {
<route lang="json"> <route lang="json">
{ {
"only": "dev",
"meta": { "meta": {
"sort": 20010, "sort": 20010,
"hide": "prod",
"title": "接口生成", "title": "接口生成",
"icon": "icon-park-outline-code" "icon": "icon-park-outline-code"
} }

View File

@ -1,5 +1,3 @@
<template></template>
<template> <template>
<div></div> <div></div>
</template> </template>
@ -9,6 +7,7 @@
"component": null, "component": null,
"meta": { "meta": {
"sort": 120012, "sort": 120012,
"hide": "prod",
"title": "前端导航", "title": "前端导航",
"link": "https://nav.juetan.cn", "link": "https://nav.juetan.cn",
"icon": "icon-park-outline-mail" "icon": "icon-park-outline-mail"

View File

@ -1,14 +1,12 @@
<template> <template></template>
<div></div>
</template>
<route lang="json"> <route lang="json">
{ {
"component": null, "component": null,
"meta": { "meta": {
"sort": 30000,
"title": "日志管理", "title": "日志管理",
"icon": "icon-park-outline-log", "icon": "icon-park-outline-log"
"sort": 30000
} }
} }
</route> </route>

View File

@ -8,7 +8,7 @@
</template> </template>
添加 添加
</a-button> </a-button>
<ani-editor v-model:visible="visible"></ani-editor> <Editor v-model:visible="visible"></Editor>
</template> </template>
</LoginLogTable> </LoginLogTable>
</BreadPage> </BreadPage>
@ -16,8 +16,8 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { api } from '@/api'; import { api } from '@/api';
import { Editor } from '@/components/AnEditor';
import { useTable } from '@/components/AnTable'; import { useTable } from '@/components/AnTable';
import { Editor as aniEditor } from '@/components/editor';
import { TableColumnData } from '@arco-design/web-vue'; import { TableColumnData } from '@arco-design/web-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -124,7 +124,7 @@ const { component: LoginLogTable } = useTable({
{ {
field: 'nickname', field: 'nickname',
label: '登陆账号', label: '登陆账号',
setter: 'input', setter: 'search',
}, },
], ],
}, },

View File

@ -1,6 +1,6 @@
<template> <template>
<bread-page> <bread-page>
<a-form :model="{}" :label-col-props="{ span: 3 }" label-align="left" layout="vertical"> <a-form :model="{}" :label-col-props="{ span: 3 }" label-align="left" layout="vertical" class="space-y-6">
<a-form-item label="站点LOGO"> <a-form-item label="站点LOGO">
<a-avatar :size="64"> <a-avatar :size="64">
<img :src="appStore.logo" alt="" /> <img :src="appStore.logo" alt="" />

View File

@ -7,7 +7,7 @@
:disabled="!mail.enable" :disabled="!mail.enable"
layout="vertical" layout="vertical"
label-align="left" label-align="left"
class="w-[580px]! divide-y divide-gray-100" class="w-[580px]! space-y-6"
> >
<a-form-item label="是否启用" :disabled="false"> <a-form-item label="是否启用" :disabled="false">
<a-radio-group v-model="mail.enable"> <a-radio-group v-model="mail.enable">

View File

@ -98,7 +98,7 @@ const { component: UserTable } = useTable({
{ {
field: 'nickname', field: 'nickname',
label: '用户昵称', label: '用户昵称',
setter: 'input', setter: 'search',
}, },
], ],
create: { create: {

View File

@ -1,6 +1,4 @@
<template> <template></template>
<div></div>
</template>
<route lang="json"> <route lang="json">
{ {

View File

@ -59,7 +59,7 @@ const { component: RoleTable } = useTable({
{ {
field: 'name', field: 'name',
label: '角色名称', label: '角色名称',
setter: 'input', setter: 'search',
}, },
], ],
create: { create: {

View File

@ -32,7 +32,7 @@ const { component: PasswordModal, open } = useFormModal({
const usernameRender: TableColumnRender = ({ record }) => ( const usernameRender: TableColumnRender = ({ record }) => (
<div class="flex items-center gap-4 w-full overflow-hidden"> <div class="flex items-center gap-4 w-full overflow-hidden">
<a-avatar size={32} class="!bg-brand-500"> <a-avatar size={32}>
{record.avatar?.startsWith('/') ? <img src={record.avatar} alt="" /> : record.nickname?.[0]} {record.avatar?.startsWith('/') ? <img src={record.avatar} alt="" /> : record.nickname?.[0]}
</a-avatar> </a-avatar>
<div class="w-full flex-1 overflow-hidden"> <div class="w-full flex-1 overflow-hidden">
@ -98,7 +98,7 @@ const { component: UserTable } = useTable({
{ {
field: 'nickname', field: 'nickname',
label: '用户昵称', label: '用户昵称',
setter: 'input', setter: 'search',
}, },
], ],
create: { create: {
@ -176,6 +176,7 @@ const { component: UserTable } = useTable({
"cache": true, "cache": true,
"sort": 10301, "sort": 10301,
"title": "用户管理", "title": "用户管理",
"auth": ["*"],
"icon": "icon-park-outline-user" "icon": "icon-park-outline-user"
} }
} }

View File

@ -9,9 +9,6 @@ import { menus } from '../menus';
import { APP_HOME_NAME } from '../routes/base'; import { APP_HOME_NAME } from '../routes/base';
import { APP_ROUTE_NAME, routes } from '../routes/page'; import { APP_ROUTE_NAME, routes } from '../routes/page';
const WHITE_LIST = ['/:all(.*)*'];
const UNSIGNIN_LIST = ['/login'];
/** /**
* *
* @param to * @param to
@ -31,17 +28,12 @@ export function useAuthGuard(router: Router) {
const menuStore = useMenuStore(store); const menuStore = useMenuStore(store);
// 手动指定直接通过 // 手动指定直接通过
if (to.meta.auth?.some(i => i === '*')) { if (to.meta.auth?.includes('*')) {
return true;
}
// 在白名单内直接通过
if (WHITE_LIST.includes(to.path)) {
return true; return true;
} }
// 未登陆才能访问的页面 // 未登陆才能访问的页面
if (UNSIGNIN_LIST.includes(to.path)) { if (to.meta.auth?.includes('unlogin')) {
// 未登陆则允许通过 // 未登陆则允许通过
if (!userStore.accessToken) { if (!userStore.accessToken) {
return true; return true;

View File

@ -1,8 +1,5 @@
import generatedRoutes from 'virtual:generated-pages'; import generatedRoutes from 'virtual:generated-pages';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import { routes as vroutes } from 'vue-router/auto/routes';
console.log({ vroutes, generatedRoutes });
export const TOP_ROUTE_PREF = '_'; export const TOP_ROUTE_PREF = '_';
export const APP_ROUTE_NAME = '_layout'; export const APP_ROUTE_NAME = '_layout';

View File

@ -59,30 +59,30 @@ declare module 'vue' {
ATextarea: typeof import('@arco-design/web-vue')['Textarea'] ATextarea: typeof import('@arco-design/web-vue')['Textarea']
ATooltip: typeof import('@arco-design/web-vue')['Tooltip'] ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
AUpload: typeof import('@arco-design/web-vue')['Upload'] AUpload: typeof import('@arco-design/web-vue')['Upload']
BaseOption: typeof import('./../components/editor/components/BaseOption.vue')['default'] BaseOption: typeof import('./../components/AnEditor/components/BaseOption.vue')['default']
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default'] BreadCrumb: typeof import('./../components/AnBreadcrumb/bread-crumb.vue')['default']
BreadPage: typeof import('./../components/breadcrumb/bread-page.vue')['default'] BreadPage: typeof import('./../components/AnBreadcrumb/bread-page.vue')['default']
ColorPicker: typeof import('./../components/editor/components/ColorPicker.vue')['default'] ColorPicker: typeof import('./../components/AnEditor/components/ColorPicker.vue')['default']
ContextMenu: typeof import('./../components/editor/components/ContextMenu.vue')['default'] ContextMenu: typeof import('./../components/AnEditor/components/ContextMenu.vue')['default']
ContextMenuList: typeof import('./../components/editor/components/ContextMenuList.vue')['default'] ContextMenuList: typeof import('./../components/AnEditor/components/ContextMenuList.vue')['default']
DragResizer: typeof import('./../components/editor/components/DragResizer.vue')['default'] DragResizer: typeof import('./../components/AnEditor/components/DragResizer.vue')['default']
Editor: typeof import('./../components/editor/components/Editor.vue')['default'] Editor: typeof import('./../components/AnEditor/components/Editor.vue')['default']
EditorConfig: typeof import('./../components/editor/components/EditorConfig.vue')['default'] EditorConfig: typeof import('./../components/AnEditor/components/EditorConfig.vue')['default']
EditorHeader: typeof import('./../components/editor/components/EditorHeader.vue')['default'] EditorHeader: typeof import('./../components/AnEditor/components/EditorHeader.vue')['default']
EditorLeft: typeof import('./../components/editor/components/EditorLeft.vue')['default'] EditorLeft: typeof import('./../components/AnEditor/components/EditorLeft.vue')['default']
EditorMain: typeof import('./../components/editor/components/EditorMain.vue')['default'] EditorMain: typeof import('./../components/AnEditor/components/EditorMain.vue')['default']
EditorMainBlock: typeof import('./../components/editor/components/EditorMainBlock.vue')['default'] EditorMainBlock: typeof import('./../components/AnEditor/components/EditorMainBlock.vue')['default']
EditorMainHeader: typeof import('./../components/editor/components/EditorMainHeader.vue')['default'] EditorMainHeader: typeof import('./../components/AnEditor/components/EditorMainHeader.vue')['default']
EditorPreview: typeof import('./../components/editor/components/EditorPreview.vue')['default'] EditorPreview: typeof import('./../components/AnEditor/components/EditorPreview.vue')['default']
EditorRight: typeof import('./../components/editor/components/EditorRight.vue')['default'] EditorRight: typeof import('./../components/AnEditor/components/EditorRight.vue')['default']
EditorSetting: typeof import('./../components/editor/components/EditorSetting.vue')['default'] EditorSetting: typeof import('./../components/AnEditor/components/EditorSetting.vue')['default']
ImagePicker: typeof import('./../components/editor/components/ImagePicker.vue')['default'] ImagePicker: typeof import('./../components/AnEditor/components/ImagePicker.vue')['default']
InputColor: typeof import('./../components/editor/components/InputColor.vue')['default'] InputColor: typeof import('./../components/AnEditor/components/InputColor.vue')['default']
InputImage: typeof import('./../components/editor/components/InputImage.vue')['default'] InputImage: typeof import('./../components/AnEditor/components/InputImage.vue')['default']
InputTexter: typeof import('./../components/editor/components/InputTexter.vue')['default'] InputTexter: typeof import('./../components/AnEditor/components/InputTexter.vue')['default']
Marquee: typeof import('./../components/editor/blocks/text/marquee.vue')['default'] Marquee: typeof import('./../components/AnEditor/blocks/text/marquee.vue')['default']
Option: typeof import('./../components/editor/blocks/date/option.vue')['default'] Option: typeof import('./../components/AnEditor/blocks/date/option.vue')['default']
Render: typeof import('./../components/editor/blocks/date/render.vue')['default'] Render: typeof import('./../components/AnEditor/blocks/date/render.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
} }

View File

@ -39,6 +39,7 @@ import type {
declare module 'vue-router/auto/routes' { declare module 'vue-router/auto/routes' {
export interface RouteNamedMap { export interface RouteNamedMap {
'/_demo/': RouteRecordInfo<'/_demo/', '/_demo', Record<never, never>, Record<never, never>>,
'/_layout/': RouteRecordInfo<'/_layout/', '/_layout', Record<never, never>, Record<never, never>>, '/_layout/': RouteRecordInfo<'/_layout/', '/_layout', Record<never, never>, Record<never, never>>,
'/_login/': RouteRecordInfo<'/_login/', '/_login', Record<never, never>, Record<never, never>>, '/_login/': RouteRecordInfo<'/_login/', '/_login', Record<never, never>, Record<never, never>>,
'/[..._all]/': RouteRecordInfo<'/[..._all]/', '/:_all(.*)', { _all: ParamValue<true> }, { _all: ParamValue<false> }>, '/[..._all]/': RouteRecordInfo<'/[..._all]/', '/:_all(.*)', { _all: ParamValue<true> }, { _all: ParamValue<false> }>,

View File

@ -1,19 +1,20 @@
import Vue from '@vitejs/plugin-vue'; import Vue from '@vitejs/plugin-vue';
import VueJsx from '@vitejs/plugin-vue-jsx'; import VueJsx from '@vitejs/plugin-vue-jsx';
import Unocss from 'unocss/vite';
import AutoImport from 'unplugin-auto-import/vite';
import AutoComponent from 'unplugin-vue-components/vite';
import router from 'unplugin-vue-router/vite';
import Page from 'vite-plugin-pages';
import iconFile from './scripts/vite/file.json';
import iconFmt from './scripts/vite/fmt.json';
import plugin from './scripts/vite/plugin';
import { resolve } from 'path'; import { resolve } from 'path';
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
import { presetIcons, presetUno } from 'unocss'; import { presetIcons, presetUno } from 'unocss';
import Unocss from 'unocss/vite';
import AutoImport from 'unplugin-auto-import/vite';
import { ArcoResolver } from 'unplugin-vue-components/resolvers'; import { ArcoResolver } from 'unplugin-vue-components/resolvers';
import AutoComponent from 'unplugin-vue-components/vite';
import router from 'unplugin-vue-router/vite';
import { defineConfig, loadEnv } from 'vite'; import { defineConfig, loadEnv } from 'vite';
import Page from 'vite-plugin-pages';
import { arcoToUnoColor } from './scripts/vite/color'; import { arcoToUnoColor } from './scripts/vite/color';
import iconFile from './scripts/vite/file.json';
import iconFmt from './scripts/vite/fmt.json';
import extension from './scripts/vite/plugin-extension';
import info from './scripts/vite/plugin-info';
/** /**
* vite * vite
@ -78,7 +79,7 @@ export default defineConfig(({ mode }) => {
*/ */
Page({ Page({
exclude: ['**/components/*', '**/*.*.*', '**/!(index).*'], exclude: ['**/components/*', '**/*.*.*', '**/!(index).*'],
importMode: 'async', importMode: 'sync',
extensions: ['vue'], extensions: ['vue'],
onRoutesGenerated(routes) { onRoutesGenerated(routes) {
const isProd = mode !== 'development'; const isProd = mode !== 'development';
@ -133,10 +134,16 @@ export default defineConfig(({ mode }) => {
}), }),
/** /**
* *
* @see ./scripts/vite/plugin.ts * @see ./scripts/vite/plugin-info.ts
*/ */
plugin(), info(),
/**
*
* @see ./scripts/vite/plugin-extension.ts
*/
extension(),
], ],
resolve: { resolve: {
alias: [ alias: [