feat: 使App.vue和VirtualDirectory解耦
This commit is contained in:
1 parent
6d550505a0
commit
4aba71d451
8 files changed
+162
-66
No files matched your search
+12
-25
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, provide, reactive, ref, shallowRef, useTemplateRef } from 'vue'
|
||||
import { computed, onMounted, provide, ref, shallowRef, useTemplateRef } from 'vue'
|
||||
import { NIcon, NScrollbar, NDropdown } from 'naive-ui'
|
||||
import {
|
||||
EyeOffOutline,
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
QrCodeOutline,
|
||||
ExtensionPuzzleOutline
|
||||
} from '@vicons/ionicons5'
|
||||
import VirtualDirectory, { VirtualFile } from './class/VirtualDirectory'
|
||||
import type VirtualDirectory from './class/VirtualDirectory'
|
||||
import type { VirtualFile } from './class/VirtualDirectory'
|
||||
import MiaoMask from './components/miaoMask.vue'
|
||||
import MiaoMessageProvider from './components/miaoAlertTipProvider.vue'
|
||||
import config from './config'
|
||||
@@ -20,6 +21,7 @@ import usePluginCenter from './hooks/usePluginCenter'
|
||||
import PluginCenter, { PluginGroup } from './class/PluginCenter'
|
||||
import { renderIcon } from './hooks/miaoTools'
|
||||
import { DropdownMixedOption } from 'naive-ui/es/dropdown/src/interface'
|
||||
import useRootVDirectory from './hooks/useRootVDirectory'
|
||||
|
||||
const { baseUrl } = config
|
||||
|
||||
@@ -37,17 +39,7 @@ const messageProviderRef = useTemplateRef('messageProviderRef')
|
||||
const views = useVirtualPages()
|
||||
let pluginCenter = ref<PluginCenter>()
|
||||
|
||||
const rootDirectory = reactive(
|
||||
new VirtualDirectory({
|
||||
name: '根目录',
|
||||
stats: {
|
||||
atimeMs: 0,
|
||||
birthtimeMs: 0,
|
||||
ctimeMs: 0,
|
||||
mtimeMs: 0
|
||||
}
|
||||
})
|
||||
)
|
||||
const rootDirectory = useRootVDirectory()
|
||||
|
||||
// component就是一个vue组件,类似于miaoDirectory
|
||||
const createView = (
|
||||
@@ -62,9 +54,6 @@ const createView = (
|
||||
|
||||
const deleteView = (index: number) => {
|
||||
views.deleteView(index)
|
||||
if (views.length === 0) {
|
||||
pluginCenter.value && pluginCenter.value.usePlugin('miaoDirectory', [rootDirectory], [])
|
||||
}
|
||||
}
|
||||
|
||||
// 点击标题是隐藏其他标签或显示其他标签,还挺好用的
|
||||
@@ -127,8 +116,6 @@ onMounted(async () => {
|
||||
// @ts-ignore
|
||||
await init(messageProviderRef.value?.alertTip)
|
||||
pluginCenter.value = usePluginCenter()
|
||||
// console.log({ pluginCenter })
|
||||
pluginCenter.value.usePlugin('miaoDirectory', [rootDirectory], [])
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
@@ -188,12 +175,12 @@ provide('rootDirectory', rootDirectory)
|
||||
</div>
|
||||
<div class="view-container">
|
||||
<!-- <transition-group name="page"> -->
|
||||
<div class="view-container-item" v-for="(view, index) of views._views"
|
||||
:class="!view.visible ? 'view-container-item-hidden' : ''" :key="view.id">
|
||||
<component :is="view.component" v-model:current-directories="view.currentDirectories"
|
||||
v-model:current-files="view.currentFiles" :id="view.id" :color="view.color" :view="view"
|
||||
:views="views" @exit="deleteView(index)"></component>
|
||||
</div>
|
||||
<div class="view-container-item" v-for="(view, index) of views._views"
|
||||
:class="!view.visible ? 'view-container-item-hidden' : ''" :key="view.id">
|
||||
<component :is="view.component" v-model:current-directories="view.currentDirectories"
|
||||
v-model:current-files="view.currentFiles" :id="view.id" :color="view.color" :view="view"
|
||||
:views="views" @exit="deleteView(index)"></component>
|
||||
</div>
|
||||
<!-- </transition-group> -->
|
||||
</div>
|
||||
</miao-message-provider>
|
||||
@@ -210,7 +197,7 @@ $controller-height: 25px;
|
||||
$tag-width: 170px;
|
||||
|
||||
.view {
|
||||
|
||||
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
@@ -3,10 +3,13 @@ import VirtualDirectory, { VirtualFile } from "./VirtualDirectory";
|
||||
import { ShallowRef, shallowRef } from "vue";
|
||||
import { alertTipType, usePluginHooksType } from "@/types/type";
|
||||
import VirtualPage, { VirtualPages } from "./VirtualPage";
|
||||
import useRootVDirectory from "@/hooks/useRootVDirectory";
|
||||
|
||||
export enum PluginGroup {
|
||||
default = "default",
|
||||
mainMenu = "mainMenu"
|
||||
mainMenu = "mainMenu",
|
||||
// 导入的时候会立即执行函数一次
|
||||
immediately = "immediately"
|
||||
}
|
||||
|
||||
export interface registerComponentOption {
|
||||
@@ -53,6 +56,13 @@ export default class PluginCenter {
|
||||
|
||||
register(key: string, plugin: Plugin) {
|
||||
this.pluginsMap[key] = plugin;
|
||||
if (plugin.group.includes(PluginGroup.immediately)) {
|
||||
this.usePlugin(key, [], [], this.usePluginHook)
|
||||
}
|
||||
}
|
||||
|
||||
deregister(key: string) {
|
||||
delete this.pluginsMap[key]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,18 +134,21 @@ export default class PluginCenter {
|
||||
return res;
|
||||
}
|
||||
|
||||
usePlugin(key: string, VDirectories?: VirtualDirectory[], VFiles?: VirtualFile[], ...args: any[]): void;
|
||||
// usePlugin(key: string, VPages: VirtualPages, ...args: any[]) : void
|
||||
get usePluginHook(): usePluginHooksType {
|
||||
return {
|
||||
globalAlertTip: this.globalAlertTip,
|
||||
getViews: useVirtualPages,
|
||||
getPluginCenter: () => this,
|
||||
getRootVDirectory: () => useRootVDirectory()
|
||||
}
|
||||
}
|
||||
|
||||
usePlugin(key: string, VDirectories?: VirtualDirectory[], VFiles?: VirtualFile[], ...args: any[]): void;
|
||||
usePlugin(key: string, VDirectories?: VirtualDirectory[], VFiles?: VirtualFile[], ...args: any[]) {
|
||||
this.pluginsMap[key].func(
|
||||
VDirectories ?? [],
|
||||
VFiles ?? [],
|
||||
{
|
||||
globalAlertTip: this.globalAlertTip,
|
||||
getViews: useVirtualPages,
|
||||
getPluginCenter: () => this
|
||||
},
|
||||
this.usePluginHook,
|
||||
...args
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,25 +6,19 @@ const initPlugin = async (globalAlertTip: alertTipType) => {
|
||||
initPluginCenter(globalAlertTip);
|
||||
const modulesFiles = import.meta.glob("@/plugins/**/export*.ts", { eager: true });
|
||||
const pluginCenter = usePluginCenter();
|
||||
const promiseArray = [];
|
||||
for (let pluginConfig of Object.values(modulesFiles)) {
|
||||
promiseArray.push(
|
||||
new Promise(async resolve => {
|
||||
const _config = pluginConfig as {
|
||||
default: registerComponentOption | Plugin;
|
||||
type: PluginExportType;
|
||||
key?: string;
|
||||
};
|
||||
const { type, key } = _config;
|
||||
if (type === PluginExportType.component) {
|
||||
resolve(pluginCenter.registerComponent(_config.default as registerComponentOption));
|
||||
} else if (type === PluginExportType.default) {
|
||||
resolve(pluginCenter.register(key as string, _config.default as Plugin));
|
||||
}
|
||||
})
|
||||
);
|
||||
const _config = pluginConfig as {
|
||||
default: registerComponentOption | Plugin;
|
||||
type: PluginExportType;
|
||||
key?: string;
|
||||
};
|
||||
const { type, key } = _config;
|
||||
if (type === PluginExportType.component) {
|
||||
pluginCenter.registerComponent(_config.default as registerComponentOption);
|
||||
} else if (type === PluginExportType.default) {
|
||||
pluginCenter.register(key as string, _config.default as Plugin);
|
||||
}
|
||||
}
|
||||
await Promise.all(promiseArray);
|
||||
};
|
||||
|
||||
export default initPlugin;
|
||||
@@ -0,0 +1,17 @@
|
||||
import VirtualDirectory from "@/class/VirtualDirectory";
|
||||
import { reactive } from "vue";
|
||||
|
||||
const root = reactive(
|
||||
new VirtualDirectory({
|
||||
name: '根目录',
|
||||
stats: {
|
||||
atimeMs: 0,
|
||||
birthtimeMs: 0,
|
||||
ctimeMs: 0,
|
||||
mtimeMs: 0
|
||||
}
|
||||
})
|
||||
)
|
||||
export default function useRootVDirectory() {
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Plugin, PluginGroup } from "@/class/PluginCenter";
|
||||
import { PluginExportType } from "@/types/type";
|
||||
import { watchEffect } from "vue";
|
||||
|
||||
const pluginConfig: Plugin = {
|
||||
name: "初始化完成之后打开miaoDirectory",
|
||||
filter: () => {
|
||||
return false;
|
||||
},
|
||||
disable: false,
|
||||
group: [PluginGroup.immediately],
|
||||
func: (_, __, hooks) => {
|
||||
const { getPluginCenter, getRootVDirectory, getViews } = hooks
|
||||
const views = getViews();
|
||||
const pc = getPluginCenter();
|
||||
const root = getRootVDirectory();
|
||||
setTimeout(() => {
|
||||
// if (views.length === 0) {
|
||||
// pc.usePlugin('miaoDirectory', [root])
|
||||
// }
|
||||
pc.deregister('miaoDirectory_onInit')
|
||||
watchEffect(() => {
|
||||
if(views.length === 0) {
|
||||
pc.usePlugin('miaoDirectory', [root])
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
priority: -1
|
||||
};
|
||||
|
||||
export default pluginConfig;
|
||||
|
||||
export const type = PluginExportType.default;
|
||||
|
||||
export const key = "miaoDirectory_onInit";
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { Plugin, registerComponentOption } from "@/class/PluginCenter";
|
||||
|
||||
export function checkNormalPlugin(obj: any): obj is Plugin {
|
||||
return (
|
||||
typeof obj === "object" &&
|
||||
obj!== null &&
|
||||
typeof obj.name === "string" &&
|
||||
(obj.icon === undefined || typeof obj.icon === "object") &&
|
||||
typeof obj.disable === "boolean" &&
|
||||
Array.isArray(obj.group) &&
|
||||
obj.group.every((group: any) => typeof group === "string") &&
|
||||
typeof obj.priority === "number" &&
|
||||
typeof obj.filter === "function" &&
|
||||
typeof obj.func === "function"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function checkComponentPlugin(
|
||||
obj: any
|
||||
): obj is registerComponentOption {
|
||||
return (
|
||||
typeof obj === "object" &&
|
||||
obj!== null &&
|
||||
typeof obj.key === "string" &&
|
||||
typeof obj.name === "string" &&
|
||||
(obj.icon === undefined || typeof obj.icon === "object") &&
|
||||
typeof obj.getComponent === "function" &&
|
||||
((obj.group === undefined) || (Array.isArray(obj.group) && obj.group.every((item: any) => typeof item === "string"))) &&
|
||||
typeof obj.filter === "function" &&
|
||||
(obj.disable === undefined || typeof obj.disable === "boolean") &&
|
||||
(obj.exitConfirm === undefined || typeof obj.exitConfirm === "boolean") &&
|
||||
(obj.single === undefined || typeof obj.single === "boolean") &&
|
||||
(obj.priority === undefined || typeof obj.priority === "number")
|
||||
);
|
||||
}
|
||||
@@ -2,24 +2,35 @@ import VirtualDirectory, { VirtualFile } from "@/class/VirtualDirectory";
|
||||
// import useMiaoFetchApi from "@/hooks/useMiaoFetchApi";
|
||||
import { PluginExportType, usePluginHooksType } from "@/types/type";
|
||||
import type { Plugin, registerComponentOption } from "@/class/PluginCenter";
|
||||
import { checkComponentPlugin, checkNormalPlugin } from "./checkPluginAvailable";
|
||||
|
||||
// const miaoFetchApi = useMiaoFetchApi()
|
||||
|
||||
export default async function registerExternalPlugin(_VDirectories: VirtualDirectory[], VFiles: VirtualFile[], hooks: usePluginHooksType) {
|
||||
const targetPluginVFile = VFiles[0] as VirtualFile
|
||||
// const pluginU8iArr = await miaoFetchApi.getFile(targetPluginVFile).response
|
||||
// const pluginText = new TextDecoder().decode(pluginU8iArr)
|
||||
// const plugin = await import(/* @vite-ignore */'')
|
||||
const plugin = await import(/* @vite-ignore */ targetPluginVFile.url) as {
|
||||
default: registerComponentOption | Plugin;
|
||||
type: PluginExportType;
|
||||
key?: string;
|
||||
}
|
||||
// console.log(plugin)
|
||||
const pc = hooks.getPluginCenter()
|
||||
if(plugin.type === PluginExportType.default) {
|
||||
pc.register(`${Math.random()}`, plugin.default as Plugin)
|
||||
} else {
|
||||
pc.registerComponent(plugin.default as registerComponentOption)
|
||||
try {
|
||||
const targetPluginVFile = VFiles[0] as VirtualFile
|
||||
const plugin = await import(/* @vite-ignore */ targetPluginVFile.url) as {
|
||||
default: registerComponentOption | Plugin;
|
||||
type: PluginExportType;
|
||||
key?: string;
|
||||
}
|
||||
const pc = hooks.getPluginCenter()
|
||||
if (plugin.type === PluginExportType.default) {
|
||||
if (checkNormalPlugin(plugin.default) === false) {
|
||||
throw('插件不合法')
|
||||
}
|
||||
pc.register(plugin.key ?? `${Math.random()}`, plugin.default as Plugin)
|
||||
} else {
|
||||
if (checkComponentPlugin(plugin.default) === false) {
|
||||
throw('插件不合法')
|
||||
}
|
||||
pc.registerComponent(plugin.default as registerComponentOption)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
hooks.globalAlertTip('导入外部插件失败', {
|
||||
type: 'error',
|
||||
timeout: 3000
|
||||
})
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
import PluginCenter from "@/class/PluginCenter";
|
||||
import { VirtualPages } from "@/class/VirtualPage";
|
||||
import VirtualDirectory from "@/class/VirtualDirectory";
|
||||
import type { VirtualPages } from "@/class/VirtualPage";
|
||||
|
||||
export type stats = {
|
||||
atimeMs: number; // 上次访问时间(毫秒)
|
||||
@@ -41,6 +42,7 @@ export interface usePluginHooksType {
|
||||
globalAlertTip: alertTipType;
|
||||
getViews: () => VirtualPages;
|
||||
getPluginCenter: () => PluginCenter;
|
||||
getRootVDirectory: () => VirtualDirectory;
|
||||
}
|
||||
|
||||
export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
|
||||
Reference in new issue
Block a user