feat: 1.插件加载时会有全局提示。2.重命名miaoMessageProvider为miaoAlertTipProvider

This commit is contained in:
539943419 committed 2024-09-05 00:10:36 +08:00
1 parent 3a18bb3cbd
commit ec4ed10afd
16 files changed
+481 -465

No files matched your search

+13 -4
View File
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { h, onMounted, reactive, ref, shallowRef } from 'vue' import { h, onMounted, provide, reactive, ref, shallowRef, useTemplateRef } from 'vue'
import { NIcon, NScrollbar, NDropdown } from 'naive-ui' import { NIcon, NScrollbar, NDropdown } from 'naive-ui'
import { import {
EyeOffOutline, EyeOffOutline,
@@ -11,10 +11,12 @@ import {
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import VirtualDirectory, { VirtualFile } from './class/VirtualDirectory' import VirtualDirectory, { VirtualFile } from './class/VirtualDirectory'
import MiaoMask from './components/miaoMask.vue' import MiaoMask from './components/miaoMask.vue'
import MiaoMessageProvider from './components/miaoAlertTipProvider.vue'
import config from './config' import config from './config'
import useVirtualPages from './hooks/useVirtualPages' import useVirtualPages from './hooks/useVirtualPages'
import init from './hooks/init' import init from './hooks/init'
import usePluginCenter from './hooks/usePluginCenter' import usePluginCenter from './hooks/usePluginCenter'
import PluginCenter from './class/PluginCenter'
const { baseUrl } = config const { baseUrl } = config
@@ -25,8 +27,10 @@ const modalData = shallowRef<{
props: any props: any
}>() }>()
const messageProviderRef = useTemplateRef('messageProviderRef')
const views = useVirtualPages() const views = useVirtualPages()
const pluginCenter = usePluginCenter() let pluginCenter: PluginCenter
const rootDirectory = reactive( const rootDirectory = reactive(
new VirtualDirectory({ new VirtualDirectory({
@@ -105,14 +109,18 @@ const handleMenuSelect = async (key: string) => {
} }
onMounted(async () => { onMounted(async () => {
await init() // @ts-ignore
await init(messageProviderRef.value?.alertTip)
pluginCenter = usePluginCenter()
pluginCenter.usePlugin('miaoDirectory', [rootDirectory], []) pluginCenter.usePlugin('miaoDirectory', [rootDirectory], [])
// createView(miaoDirectory, [rootDirectory])
}) })
provide('globalAlertTip', messageProviderRef.value?.alertTip)
</script> </script>
<template> <template>
<div class="view"> <div class="view">
<miao-message-provider ref="messageProviderRef">
<div class="view-controller"> <div class="view-controller">
<n-scrollbar x-scrollable> <n-scrollbar x-scrollable>
<div class="tabs-container"> <div class="tabs-container">
@@ -199,6 +207,7 @@ onMounted(async () => {
</div> </div>
</transition-group> </transition-group>
</div> </div>
</miao-message-provider>
</div> </div>
<!-- 模态框展示,用来显示分享二维码,设置菜单之类的东西 --> <!-- 模态框展示,用来显示分享二维码,设置菜单之类的东西 -->
<MiaoMask v-model:show="showModal" @click="showModal = false"> <MiaoMask v-model:show="showModal" @click="showModal = false">
+94 -109
View File
@@ -1,124 +1,109 @@
import useVirtualPages from '@/hooks/useVirtualPages' import useVirtualPages from "@/hooks/useVirtualPages";
import VirtualDirectory, { VirtualFile } from './VirtualDirectory' import VirtualDirectory, { VirtualFile } from "./VirtualDirectory";
import { ShallowRef, shallowRef } from 'vue' import { ShallowRef, shallowRef } from "vue";
import { alertTipType } from "@/types/type";
export enum PluginGroup { export enum PluginGroup {
default = 'default', default = "default",
mainMenu = 'mainMenu' mainMenu = "mainMenu"
} }
export interface registerComponentOption { export interface registerComponentOption {
key: string key: string;
name: string name: string;
icon?: any icon?: any;
getComponent: () => Promise<any> getComponent: () => Promise<any>;
group?: PluginGroup[] group?: PluginGroup[];
filter: (VDirectories: VirtualDirectory[], VFiles: VirtualFile[]) => boolean filter: (VDirectories: VirtualDirectory[], VFiles: VirtualFile[]) => boolean;
disable?: boolean disable?: boolean;
exitConfirm?: boolean exitConfirm?: boolean;
} }
type Plugin = { type Plugin = {
// 用于显示的名字 // 用于显示的名字
name: string name: string;
// 代表插件的icon // 代表插件的icon
icon?: ShallowRef<any> icon?: ShallowRef<any>;
// 是否禁用插件 // 是否禁用插件
disable: boolean disable: boolean;
// 插件所属的group,用于区分插件使用的场合 // 插件所属的group,用于区分插件使用的场合
group: PluginGroup[] group: PluginGroup[];
// 判断在未禁用时是否可以使用这个组件 // 判断在未禁用时是否可以使用这个组件
// 使用组件即调用下面的func // 使用组件即调用下面的func
filter: (VDirectories: VirtualDirectory[], VFiles: VirtualFile[]) => boolean filter: (VDirectories: VirtualDirectory[], VFiles: VirtualFile[]) => boolean;
func: ( func: (VDirectories: VirtualDirectory[], VFiles: VirtualFile[], ...args: any[]) => void;
VDirectories: VirtualDirectory[], };
VFiles: VirtualFile[],
...args: any[]
) => void
}
export default class PluginCenter { export default class PluginCenter {
constructor() { constructor(globalAlertTip: alertTipType) {
this.pluginsMap = {} this.pluginsMap = {};
} this.globalAlertTip = globalAlertTip;
pluginsMap: { }
[key: string]: Plugin pluginsMap: {
} [key: string]: Plugin;
};
register(key: string, plugin: Plugin) { globalAlertTip: alertTipType;
this.pluginsMap[key] = plugin
}
/** register(key: string, plugin: Plugin) {
* 便捷地注册一个组件为插件,触发时直接用新页面打开 this.pluginsMap[key] = plugin;
*/ }
registerComponent(option: registerComponentOption): void {
const {
key,
name,
getComponent,
icon,
filter,
group = [PluginGroup.default],
disable = false,
exitConfirm = false
} = option
let _component: any
const views = useVirtualPages()
this.register(key, {
name,
filter,
icon: shallowRef(icon),
group,
disable,
func: async (
VDirectories: VirtualDirectory[],
VFiles: VirtualFile[]
) => {
if (filter(VDirectories, VFiles) === false) {
return
}
if (_component === undefined) _component = await getComponent()
views.push(_component, VDirectories, VFiles, {
VirtualPageOption: {
exitConfirm
}
})
}
})
}
getUsablePlugin( /**
VDirectories: VirtualDirectory[], * 便捷地注册一个组件为插件,触发时直接用新页面打开
VFiles: VirtualFile[], */
option?: { registerComponent(option: registerComponentOption): void {
group?: PluginGroup[] const { key, name, getComponent, icon, filter, group = [PluginGroup.default], disable = false, exitConfirm = false } = option;
} let _component: any;
) { const views = useVirtualPages();
const { group = [PluginGroup.default] } = option ?? {} this.register(key, {
const usableList = [] name,
for (let key of Object.keys(this.pluginsMap)) { filter,
const _plugin = this.pluginsMap[key] icon: shallowRef(icon),
if ( group,
!_plugin.disable && disable,
_plugin.group.filter((v) => group.includes(v)).length > 0 && func: async (VDirectories: VirtualDirectory[], VFiles: VirtualFile[]) => {
_plugin.filter(VDirectories, VFiles) if (filter(VDirectories, VFiles) === false) {
) { return;
usableList.push(key) }
}
}
return usableList.map((key) => ({
key,
...this.pluginsMap[key]
}))
}
usePlugin( const setAlertTip = key !== 'miaoDirectory' && this.globalAlertTip(`加载 ${name} 插件中`);
key: string, if (_component === undefined) _component = await getComponent();
VDirectories?: VirtualDirectory[], setAlertTip && setAlertTip(`加载 ${name} 插件完成`, {
VFiles?: VirtualFile[], type: "success",
...args: any[] timeout: 2000
) { });
this.pluginsMap[key].func(VDirectories ?? [], VFiles ?? [], ...args) views.push(_component, VDirectories, VFiles, {
} VirtualPageOption: {
exitConfirm
}
});
}
});
}
getUsablePlugin(
VDirectories: VirtualDirectory[],
VFiles: VirtualFile[],
option?: {
group?: PluginGroup[];
}
) {
const { group = [PluginGroup.default] } = option ?? {};
const usableList = [];
for (let key of Object.keys(this.pluginsMap)) {
const _plugin = this.pluginsMap[key];
if (!_plugin.disable && _plugin.group.filter(v => group.includes(v)).length > 0 && _plugin.filter(VDirectories, VFiles)) {
usableList.push(key);
}
}
return usableList.map(key => ({
key,
...this.pluginsMap[key]
}));
}
usePlugin(key: string, VDirectories?: VirtualDirectory[], VFiles?: VirtualFile[], ...args: any[]) {
this.pluginsMap[key].func(VDirectories ?? [], VFiles ?? [], ...args);
}
} }
@@ -18,12 +18,7 @@ import generateId from '@/hooks/generateId';
import { NIcon } from 'naive-ui'; import { NIcon } from 'naive-ui';
import { computed, onMounted, ref, shallowReactive } from 'vue'; import { computed, onMounted, ref, shallowReactive } from 'vue';
import { InformationCircle, CheckmarkCircle, CloseCircle } from '@vicons/ionicons5' import { InformationCircle, CheckmarkCircle, CloseCircle } from '@vicons/ionicons5'
import { AlertTipMessageOptions } from '@/types/type';
interface MessageOptions {
type?: 'info' | 'success' | 'error'
timeout?: number
id?: number
}
interface Message { interface Message {
icon: any, icon: any,
@@ -48,7 +43,7 @@ const messageList = computed(() => {
...messageMap[v], ...messageMap[v],
})) }))
}) })
const message = (content: string, options?: MessageOptions) => { const alertTip = (content: string, options?: AlertTipMessageOptions) => {
let timer: any let timer: any
const { timeout = 0, type = 'info', id = generateId() } = options ?? {} const { timeout = 0, type = 'info', id = generateId() } = options ?? {}
if (timeout > 0) { if (timeout > 0) {
@@ -63,10 +58,10 @@ const message = (content: string, options?: MessageOptions) => {
content, content,
timer timer
} }
return (newContent: string, newOptions?: MessageOptions) => { return (newContent: string, newOptions?: AlertTipMessageOptions) => {
newOptions = newOptions ?? {} newOptions = newOptions ?? {}
newOptions.id = id newOptions.id = id
message(newContent, newOptions) alertTip(newContent, newOptions)
} }
} }
@@ -86,7 +81,7 @@ onMounted(async () => {
}) })
defineExpose({ message }); defineExpose({ alertTip });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -105,6 +100,7 @@ defineExpose({ message });
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
overflow: visible; overflow: visible;
z-index: 70582140539943419;
.miao-message { .miao-message {
display: flex; display: flex;
+3 -2
View File
@@ -1,5 +1,6 @@
import { alertTipType } from "@/types/type";
import initPlugin from "./init/initPlugin"; import initPlugin from "./init/initPlugin";
export default async function init() { export default async function init(globalAlertTip: alertTipType) {
await initPlugin() await initPlugin(globalAlertTip)
} }
+21 -14
View File
@@ -1,16 +1,23 @@
import usePluginCenter, { initPluginCenter } from "@/hooks/usePluginCenter";
import { registerComponentOption } from "@/class/PluginCenter";
import { alertTipType } from "@/types/type";
import usePluginCenter from '@/hooks/usePluginCenter' const initPlugin = async (globalAlertTip: alertTipType) => {
import { registerComponentOption } from '@/class/PluginCenter' initPluginCenter(globalAlertTip)
const modulesFiles = import.meta.glob("@/plugins/**/export.ts");
const pluginCenter = usePluginCenter();
const promiseArray = [];
for (let pluginConfig of Object.values(modulesFiles)) {
promiseArray.push(
new Promise(async resolve => {
const _config = pluginConfig as () => Promise<{
default: registerComponentOption;
}>;
resolve(pluginCenter.registerComponent((await _config()).default));
})
);
}
await Promise.all(promiseArray);
};
const initPlugin = async () => { export default initPlugin;
const modulesFiles = import.meta.glob('@/plugins/**/export.ts')
const pluginCenter = usePluginCenter()
for (let pluginConfig of Object.values(modulesFiles)) {
const _config = pluginConfig as () => Promise<{
default: registerComponentOption
}>
pluginCenter.registerComponent((await _config()).default)
}
}
export default initPlugin
+252 -269
View File
@@ -1,291 +1,274 @@
import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory' import VirtualDirectory, { VirtualFile } from "@/class/VirtualDirectory";
import { import {
getOption, getOption,
getResponse, getResponse,
miaoFetchConfig, miaoFetchConfig,
deleteOption, deleteOption,
uploadOption, uploadOption,
queryOption, queryOption,
renameOption, renameOption,
cutOption, cutOption,
mkdirOption mkdirOption,
} from '@/types/fetch' getFileOption
import Config from '@/config' } from "@/types/fetch";
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios' import Config from "@/config";
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
export const useMiaoFetch = (option?: miaoFetchConfig) => { export const useMiaoFetch = (option?: miaoFetchConfig) => {
const retry = option?.retry ?? 0 const retry = option?.retry ?? 0;
const controller = new AbortController() const controller = new AbortController();
let flag = true let flag = true;
return { return {
miaoFetch: async ( miaoFetch: async (config: AxiosRequestConfig<any>): Promise<AxiosResponse<any, any>> => {
config: AxiosRequestConfig<any> let counter = 0;
): Promise<AxiosResponse<any, any>> => { let response: any;
let counter = 0 let errList = [];
let response: any while (flag && counter <= retry) {
let errList = [] try {
while (flag && counter <= retry) { response = await axios({
try { ...config,
response = await axios({ signal: controller.signal
...config, });
signal: controller.signal return response;
}) } catch (_e) {
return response errList.push(_e);
} catch (_e) { counter += 1;
errList.push(_e) }
counter += 1 }
} throw errList;
} },
throw errList /**
}, * 强制终止miaoFetch
/** */
* 强制终止miaoFetch abortMiaoFetch: () => {
*/ flag = false;
abortMiaoFetch: () => { controller.abort();
flag = false }
controller.abort() };
} };
}
}
const miaoFetchApi = { const miaoFetchApi = {
/** /**
* 获取对应文件夹下的数据 * 获取对应文件夹下的数据
*/ */
async get( async get(vDirectory: VirtualDirectory, option?: getOption): Promise<getResponse> {
vDirectory: VirtualDirectory, const { baseUrl, api } = Config;
option?: getOption const url = `${baseUrl}${api.get}${vDirectory.path}`;
): Promise<getResponse> { const retry = option?.retry ?? 0;
const { baseUrl, api } = Config
const url = `${baseUrl}${api.get}${vDirectory.path}`
const retry = option?.retry ?? 0
const { miaoFetch } = useMiaoFetch({ const { miaoFetch } = useMiaoFetch({
retry retry
}) });
const response = await miaoFetch({ const response = await miaoFetch({
url url
}) });
return response.data return response.data;
}, },
/** /**
* 获取对应文件下的数据 * 获取对应文件下的数据
*/ */
async getFile( getFile(vFile: VirtualFile, option?: getFileOption) {
vFile: VirtualFile, // const { baseUrl, api } = Config
option?: getOption const url = vFile.url;
) { const retry = option?.retry ?? 0;
// const { baseUrl, api } = Config const axiosOption = option?.axiosOption ?? {};
const url = vFile.url
const retry = option?.retry ?? 0
const axiosOption = option?.axiosOption ?? {}
const { miaoFetch } = useMiaoFetch({ const { miaoFetch, abortMiaoFetch: abort } = useMiaoFetch({
retry retry
}) });
const response = await miaoFetch({ const response = miaoFetch({
url, url,
responseType: 'arraybuffer', responseType: "arraybuffer",
...axiosOption onDownloadProgress(progressEvent) {
}) option && option.onProcess && option.onProcess(progressEvent.total ? progressEvent.loaded / progressEvent.total : -1);
return new Uint8Array(response.data) },
}, ...axiosOption
});
return {
response: new Promise<Uint8Array>(async resolve => {
resolve(new Uint8Array((await response).data));
}),
abort
};
},
/** /**
* 上传单个文件到对应文件夹, 后续要将data类型改成buffer以支持分片上传 * 上传单个文件到对应文件夹, 后续要将data类型改成buffer以支持分片上传
* 正常上传文件请使用uploadQueue * 正常上传文件请使用uploadQueue
*/ */
upload(data: File, targetVDir: VirtualDirectory, option?: uploadOption) { upload(data: File, targetVDir: VirtualDirectory, option?: uploadOption) {
const { baseUrl, api } = Config const { baseUrl, api } = Config;
const url = `${baseUrl}${api.upload}${targetVDir.path}` const url = `${baseUrl}${api.upload}${targetVDir.path}`;
const retry = option?.retry ?? 0 const retry = option?.retry ?? 0;
const { miaoFetch, abortMiaoFetch } = useMiaoFetch({ const { miaoFetch, abortMiaoFetch } = useMiaoFetch({
retry retry
}) });
const sizeTotal = data.size const sizeTotal = data.size;
const opType = option?.type ?? 'write' const opType = option?.type ?? "write";
const formData = new FormData() const formData = new FormData();
formData.append('file', data) formData.append("file", data);
// 必须这样传,不然中文乱码 // 必须这样传,不然中文乱码
formData.append('fileName', data.name) formData.append("fileName", data.name);
formData.append('operateType', opType) formData.append("operateType", opType);
return { return {
abort: () => abortMiaoFetch(), abort: () => abortMiaoFetch(),
response: new Promise<{ response: new Promise<{
message: 'success' | 'failed' message: "success" | "failed";
error?: string error?: string;
}>(async (resolve) => { }>(async resolve => {
const response = await miaoFetch({ const response = await miaoFetch({
url, url,
method: 'post', method: "post",
data: formData, data: formData,
onUploadProgress(progressEvent) { onUploadProgress(progressEvent) {
option && option && option.onProcess && option.onProcess(progressEvent.loaded / sizeTotal);
option.onProcess && }
option.onProcess(progressEvent.loaded / sizeTotal) });
} resolve(response.data);
}) })
resolve(response.data) };
}) },
}
},
/** /**
* 在目标文件夹下创建新的文件夹 * 在目标文件夹下创建新的文件夹
*/ */
mkdir( mkdir(targetVDir: VirtualDirectory, newFolderName: string, option?: mkdirOption) {
targetVDir: VirtualDirectory, const { baseUrl, api } = Config;
newFolderName: string, const url = `${baseUrl}${api.mkdir}${targetVDir.path}`;
option?: mkdirOption const { miaoFetch } = useMiaoFetch({
) { retry: option?.retry ?? 0
const { baseUrl, api } = Config });
const url = `${baseUrl}${api.mkdir}${targetVDir.path}`
const { miaoFetch } = useMiaoFetch({
retry: option?.retry ?? 0
})
return new Promise<{ return new Promise<{
message: 'success' | 'failed' message: "success" | "failed";
error?: string error?: string;
}>(async (resolve) => { }>(async resolve => {
const response = await miaoFetch({ const response = await miaoFetch({
url, url,
method: 'post', method: "post",
data: { data: {
folderName: newFolderName folderName: newFolderName
} }
}) });
resolve(response.data) resolve(response.data);
}) });
}, },
/** /**
* 在目标文件夹下创建新的空文件,实际上是通过upload空文件实现 * 在目标文件夹下创建新的空文件,实际上是通过upload空文件实现
*/ */
mkFile( mkFile(targetVDir: VirtualDirectory, newFileName: string, option?: uploadOption) {
targetVDir: VirtualDirectory, const emptyBlob = new Blob([]);
newFileName: string, const emptyFile = new File([emptyBlob], newFileName, {
option?: uploadOption type: "text/plain"
) { });
const emptyBlob = new Blob([]) return this.upload(emptyFile, targetVDir, option);
const emptyFile = new File([emptyBlob], newFileName, { },
type: 'text/plain'
})
return this.upload(emptyFile, targetVDir, option)
},
/** /**
* 删除对应的文件或文件夹,若为文件夹会递归删除 * 删除对应的文件或文件夹,若为文件夹会递归删除
*/ */
delete(target: VirtualFile | VirtualDirectory, option?: deleteOption) { delete(target: VirtualFile | VirtualDirectory, option?: deleteOption) {
const { baseUrl, api } = Config const { baseUrl, api } = Config;
const url = `${baseUrl}${api.delete}${target.path}` const url = `${baseUrl}${api.delete}${target.path}`;
const retry = option?.retry ?? 0 const retry = option?.retry ?? 0;
const { miaoFetch } = useMiaoFetch({ const { miaoFetch } = useMiaoFetch({
retry retry
}) });
return { return {
response: new Promise<{ response: new Promise<{
eventId: number eventId: number;
}>(async (resolve) => { }>(async resolve => {
const response = await miaoFetch({ const response = await miaoFetch({
url, url,
method: 'post' method: "post"
}) });
resolve(response.data) resolve(response.data);
}) })
} };
}, },
/** /**
* 重命名对应的文件或文件夹 * 重命名对应的文件或文件夹
*/ */
rename( rename(target: VirtualFile | VirtualDirectory, newName: string, option?: renameOption) {
target: VirtualFile | VirtualDirectory, const { baseUrl, api } = Config;
newName: string, const url = `${baseUrl}${api.rename}${target.path}`;
option?: renameOption const retry = option?.retry ?? 0;
) { const { miaoFetch } = useMiaoFetch({
const { baseUrl, api } = Config retry
const url = `${baseUrl}${api.rename}${target.path}` });
const retry = option?.retry ?? 0
const { miaoFetch } = useMiaoFetch({
retry
})
return { return {
response: new Promise<{ response: new Promise<{
eventId: number eventId: number;
}>(async (resolve) => { }>(async resolve => {
const response = await miaoFetch({ const response = await miaoFetch({
url, url,
method: 'post', method: "post",
data: { newName } data: { newName }
}) });
resolve(response.data) resolve(response.data);
}) })
} };
}, },
/** /**
* 剪切文件或文件夹 * 剪切文件或文件夹
*/ */
cut( cut(target: VirtualFile | VirtualDirectory, to: VirtualDirectory, option?: cutOption) {
target: VirtualFile | VirtualDirectory, const newPath = to.path;
to: VirtualDirectory, const { baseUrl, api } = Config;
option?: cutOption const url = `${baseUrl}${api.cut}${target.path}`;
) { const retry = option?.retry ?? 0;
const newPath = to.path const { miaoFetch } = useMiaoFetch({
const { baseUrl, api } = Config retry
const url = `${baseUrl}${api.cut}${target.path}` });
const retry = option?.retry ?? 0 return {
const { miaoFetch } = useMiaoFetch({ response: new Promise<{
retry eventId: number;
}) }>(async resolve => {
return { const response = await miaoFetch({
response: new Promise<{ url,
eventId: number method: "post",
}>(async (resolve) => { data: { newPath }
const response = await miaoFetch({ });
url, resolve(response.data);
method: 'post', })
data: { newPath } };
}) },
resolve(response.data)
})
}
},
/** /**
* 查询服务端操作事件的进展 * 查询服务端操作事件的进展
*/ */
query(eventId: number, option?: queryOption) { query(eventId: number, option?: queryOption) {
const { baseUrl, api } = Config const { baseUrl, api } = Config;
const url = `${baseUrl}${api.query}` const url = `${baseUrl}${api.query}`;
const retry = option?.retry ?? 0 const retry = option?.retry ?? 0;
const { miaoFetch } = useMiaoFetch({ const { miaoFetch } = useMiaoFetch({
retry retry
}) });
return { return {
response: new Promise<{ response: new Promise<{
status: 'process' | 'success' | 'failed' status: "process" | "success" | "failed";
error?: unknown error?: unknown;
}>(async (resolve) => { }>(async resolve => {
const response = await miaoFetch({ const response = await miaoFetch({
url, url,
method: 'get', method: "get",
params: { params: {
id: eventId id: eventId
} }
}) });
resolve(response.data) resolve(response.data);
}) })
} };
} }
} };
export default function useMiaoFetchApi() { export default function useMiaoFetchApi() {
return miaoFetchApi return miaoFetchApi;
} }
+6 -2
View File
@@ -1,11 +1,15 @@
import PluginCenter from "@/class/PluginCenter"; import PluginCenter from "@/class/PluginCenter";
import { alertTipType } from "@/types/type";
import { reactive } from "vue"; import { reactive } from "vue";
let pluginCenter: PluginCenter let pluginCenter: PluginCenter
export default function usePluginCenter(){ export const initPluginCenter = (globalAlertTip: alertTipType) => {
if(!pluginCenter) { if(!pluginCenter) {
pluginCenter = reactive(new PluginCenter()) pluginCenter = reactive(new PluginCenter(globalAlertTip))
} }
}
export default function usePluginCenter(){
return pluginCenter return pluginCenter
} }
+8 -15
View File
@@ -1,18 +1,11 @@
<template> <template>
<miao-message-provider ref="miaoMessage"> <miao-message-provider ref="miaoMessage">
<div <div class="codemirror-container" ref="rootRef" :class="`codemirror-container-${theme}`">
class="codemirror-container"
ref="rootRef"
:class="`codemirror-container-${theme}`">
<div class="codemirror-container-top"> <div class="codemirror-container-top">
<div <div class="codemirror-container-btn codemirror-container-btn-save" @click="handleSave">
class="codemirror-container-btn codemirror-container-btn-save"
@click="handleSave">
保存 保存
</div> </div>
<div <div class="codemirror-container-btn codemirror-container-btn-save" @click="handleReset">
class="codemirror-container-btn codemirror-container-btn-save"
@click="handleReset">
恢复到上次保存 恢复到上次保存
</div> </div>
</div> </div>
@@ -26,7 +19,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import miaoMessageProvider from '@/components/miaoMessageProvider.vue' import miaoMessageProvider from '@/components/miaoAlertTipProvider.vue'
import { VirtualFile } from '@/class/VirtualDirectory' import { VirtualFile } from '@/class/VirtualDirectory'
import useMiaoFetchApi from '@/hooks/useMiaoFetchApi' import useMiaoFetchApi from '@/hooks/useMiaoFetchApi'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
@@ -53,7 +46,7 @@ const theme = ref<'light' | 'dark'>('light')
const handleSave = async () => { const handleSave = async () => {
if (bakData.value === codeData.value || !vFile.value) { if (bakData.value === codeData.value || !vFile.value) {
miaoMessage.value?.message('内容未改动', { miaoMessage.value?.alertTip('内容未改动', {
timeout: 3000, timeout: 3000,
type: 'info' type: 'info'
}) })
@@ -69,7 +62,7 @@ const handleSave = async () => {
const { response } = miaoFetchApi.upload(file, parentDir) const { response } = miaoFetchApi.upload(file, parentDir)
false && miaoFetchApi.upload(file_bak, parentDir) false && miaoFetchApi.upload(file_bak, parentDir)
await response await response
miaoMessage.value?.message('保存成功', { miaoMessage.value?.alertTip('保存成功', {
timeout: 5000, timeout: 5000,
type: 'success' type: 'success'
}) })
@@ -152,13 +145,13 @@ onMounted(async () => {
vFile.value = currentFiles.value[0] vFile.value = currentFiles.value[0]
const fileName = currentFiles.value[0].name const fileName = currentFiles.value[0].name
codeData.value = decoder.decode( codeData.value = decoder.decode(
await miaoFetchApi.getFile(currentFiles.value[0], { await (miaoFetchApi.getFile(currentFiles.value[0], {
axiosOption: { axiosOption: {
headers: { headers: {
'Cache-Control': 'no-cache' 'Cache-Control': 'no-cache'
} }
} }
}) }).response)
) )
bakData.value = codeData.value bakData.value = codeData.value
extensions.value = [...(await getLangExtensions(fileName))] extensions.value = [...(await getLangExtensions(fileName))]
+51 -28
View File
@@ -3,36 +3,29 @@
<div class="miaoCrypto-container"> <div class="miaoCrypto-container">
<div class="miaoCrypto-container-encrypt miaoCrypto-container-item"> <div class="miaoCrypto-container-encrypt miaoCrypto-container-item">
<n-scrollbar style="max-height: 100%"> <n-scrollbar style="max-height: 100%">
<div <div v-for="item of workItemList" :key="item.id" class="miaoCrypto-container-item-item">
v-for="item of workItemList" <div class="miaoCrypto-container-item-percentBar" :style="{
class="miaoCrypto-container-item-item"> zIndex: 0,
<div width: item.getBufferProgress !== -1 ? `${item.getBufferProgress * 100}%` : `0%`,
class="miaoCrypto-container-item-percentBar" backgroundColor: 'blue'
:style="{ }"></div>
zIndex: 0, <div class="miaoCrypto-container-item-percentBar" :style="{
width: item.buffer ? `100%` : `0%`, zIndex: 1,
backgroundColor: 'blue' width: item.status === 'finish' ? `100%` : `0%`,
}"></div> backgroundColor: 'green'
<div }"></div>
class="miaoCrypto-container-item-percentBar"
:style="{
zIndex: 1,
width: item.status === 'finish' ? `100%` : `0%`,
backgroundColor: 'green'
}"></div>
<span class="miaoCrypto-container-item-content">{{ item.name }}{{ item.status }}</span> <span class="miaoCrypto-container-item-content">{{ item.name }}{{ item.status }}</span>
</div> </div>
</n-scrollbar> </n-scrollbar>
</div> </div>
<div <div class="miaoCrypto-container-decrypt miaoCrypto-container-item"></div>
class="miaoCrypto-container-decrypt miaoCrypto-container-item"></div>
</div> </div>
</miao-drop-handler> </miao-drop-handler>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { NScrollbar } from 'naive-ui' import { NScrollbar } from 'naive-ui'
import { ref } from 'vue' import { onUnmounted, ref, shallowReactive } from 'vue'
import aes_ctr_worker from './hooks/AES' import aes_ctr_worker from './hooks/AES'
import { VirtualFile } from '@/class/VirtualDirectory' import { VirtualFile } from '@/class/VirtualDirectory'
import miaoDropHandler from '@/components/miaoDropHandler.vue' import miaoDropHandler from '@/components/miaoDropHandler.vue'
@@ -44,9 +37,12 @@ import { computed } from 'vue'
const miaoFetchApi = useMiaoFetchApi() const miaoFetchApi = useMiaoFetchApi()
interface workItem { interface workItem {
id: number
status: 'wait' | 'process' | 'finish' status: 'wait' | 'process' | 'finish'
buffer?: Uint8Array buffer?: Uint8Array
getBuffer: () => Promise<Uint8Array> getBuffer: () => Promise<Uint8Array>
getBufferProgress: number
getBufferAbort?: () => void
isGettingBuffer: boolean isGettingBuffer: boolean
name: string name: string
} }
@@ -55,14 +51,16 @@ class Work {
constructor(func: (key: string, data: Uint8Array) => Promise<Uint8Array>) { constructor(func: (key: string, data: Uint8Array) => Promise<Uint8Array>) {
this.queue = [] this.queue = []
this.func = func this.func = func
this.abortLoop = false
} }
queue: workItem[] queue: workItem[]
func: (key: string, data: Uint8Array) => Promise<Uint8Array> func: (key: string, data: Uint8Array) => Promise<Uint8Array>
abortLoop: boolean
get isBusy() { get isBusy() {
return this.queue.filter((v) => v.status === 'process').length > 0 return this.queue.filter((v) => v.status === 'process').length > 0
} }
async funcLoop() { async funcLoop() {
if (this.isBusy) { if (this.isBusy || this.abortLoop) {
return return
} }
const currentItem = this.queue.filter((v) => v.status === 'wait')[0] const currentItem = this.queue.filter((v) => v.status === 'wait')[0]
@@ -78,7 +76,7 @@ class Work {
const _list = this.queue.filter( const _list = this.queue.filter(
(v) => v.isGettingBuffer === false && v.buffer === undefined (v) => v.isGettingBuffer === false && v.buffer === undefined
) )
if (_list.length === 0) { if (_list.length === 0 || this.abortLoop) {
return return
} }
const item = _list[0] const item = _list[0]
@@ -94,12 +92,25 @@ class Work {
} }
pushVFiles(itemList: VirtualFile[]) { pushVFiles(itemList: VirtualFile[]) {
this.push( this.push(
itemList.map((v) => ({ itemList.map((v) => {
name: v.name, const item: workItem = shallowReactive({
getBuffer: () => miaoFetchApi.getFile(v), id: Math.random(),
status: 'wait', status: 'wait',
isGettingBuffer: false name: v.name,
})) getBuffer: () => {
const { response, abort } = miaoFetchApi.getFile(v, {
onProcess(percent) {
item.getBufferProgress = percent
}
})
item.getBufferAbort = abort
return response
},
isGettingBuffer: false,
getBufferProgress: 0
})
return item
})
) )
} }
} }
@@ -125,6 +136,13 @@ const handleDropVirtualFiles = (vFiles: VirtualFile[]) => {
onMounted(() => { onMounted(() => {
encryptWork.pushVFiles(currentFiles.value) encryptWork.pushVFiles(currentFiles.value)
}) })
onUnmounted(() => {
encryptWork.abortLoop = true
encryptWork.queue.forEach(item=>{
item.getBufferAbort && item.getBufferAbort()
})
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -133,12 +151,14 @@ onMounted(() => {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
&-item { &-item {
max-height: 100%; max-height: 100%;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
&-item { &-item {
margin: 10px auto; margin: 10px auto;
width: 95%; width: 95%;
@@ -148,12 +168,15 @@ onMounted(() => {
0 0 3px 1px #43434359; 0 0 3px 1px #43434359;
border-radius: 5px; border-radius: 5px;
position: relative; position: relative;
.miaoCrypto-container-item-percentBar { .miaoCrypto-container-item-percentBar {
height: 100%; height: 100%;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
border-radius: 5px;
} }
.miaoCrypto-container-item-content { .miaoCrypto-container-item-content {
position: relative; position: relative;
z-index: 3 z-index: 3
@@ -3,7 +3,6 @@ import aesJs from 'aes-js'
self.onmessage = async function (event) { self.onmessage = async function (event) {
const data = event.data.data as Uint8Array const data = event.data.data as Uint8Array
const key = event.data.key as Uint8Array const key = event.data.key as Uint8Array
console.log(event)
var aesCtr = new aesJs.ModeOfOperation.ctr(key, new aesJs.Counter(7058)) var aesCtr = new aesJs.ModeOfOperation.ctr(key, new aesJs.Counter(7058))
const encryptData = aesCtr.encrypt(data) const encryptData = aesCtr.encrypt(data)
self.postMessage(encryptData) self.postMessage(encryptData)
+3 -3
View File
@@ -62,7 +62,7 @@
<script setup lang="ts"> <script setup lang="ts">
import miaoMask from '@/components/miaoMask.vue' import miaoMask from '@/components/miaoMask.vue'
import miaoDropHandler from '@/components/miaoDropHandler.vue' import miaoDropHandler from '@/components/miaoDropHandler.vue'
import miaoMessageProvider from '@/components/miaoMessageProvider.vue' import miaoMessageProvider from '@/components/miaoAlertTipProvider.vue'
import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory' import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory'
import { uniq } from 'lodash' import { uniq } from 'lodash'
import { computed, nextTick, onMounted, ref, watch } from 'vue' import { computed, nextTick, onMounted, ref, watch } from 'vue'
@@ -199,7 +199,7 @@ const handleDrop = async (vItems: (VirtualFile | VirtualDirectory)[]) => {
// @ts-ignore // @ts-ignore
await updateImageSrcList(vItems) await updateImageSrcList(vItems)
} }
miaoMessageRef.value?.message( miaoMessageRef.value?.alertTip(
length_before === imageFileList.value.length length_before === imageFileList.value.length
? '未发现任何可添加图片' ? '未发现任何可添加图片'
: `导入${imageFileList.value.length - length_before}张图片`, : `导入${imageFileList.value.length - length_before}张图片`,
@@ -247,7 +247,7 @@ onMounted(async () => {
for (const i of [currentFiles.value, ...currentDirectories.value]) { for (const i of [currentFiles.value, ...currentDirectories.value]) {
await updateImageSrcList(i) await updateImageSrcList(i)
} }
miaoMessageRef.value?.message( miaoMessageRef.value?.alertTip(
length_before === imageFileList.value.length length_before === imageFileList.value.length
? '未发现任何新增图片' ? '未发现任何新增图片'
: `导入${imageFileList.value.length - length_before}张图片`, : `导入${imageFileList.value.length - length_before}张图片`,
+1 -1
View File
@@ -28,7 +28,7 @@ const md = new MarkdownIt()
onMounted(async () => { onMounted(async () => {
markdownSrc.value = currentFiles.value[0].url markdownSrc.value = currentFiles.value[0].url
const text = decoder.decode(await miaoFetchApi.getFile(currentFiles.value[0])) const text = decoder.decode(await(miaoFetchApi.getFile(currentFiles.value[0])).response)
renderedMarkdown.value = md.render(text) renderedMarkdown.value = md.render(text)
}) })
</script> </script>
+2 -2
View File
@@ -52,7 +52,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import miaoMessageProvider from '@/components/miaoMessageProvider.vue' import miaoMessageProvider from '@/components/miaoAlertTipProvider.vue'
import { onMounted, ref, shallowReactive } from 'vue' import { onMounted, ref, shallowReactive } from 'vue'
import PdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs?worker' import PdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs?worker'
import { VirtualFile } from '@/class/VirtualDirectory' import { VirtualFile } from '@/class/VirtualDirectory'
@@ -89,7 +89,7 @@ const handleResetMargin = () => {
} }
onMounted(async () => { onMounted(async () => {
const setLoadingMessage = miaoMessageRef.value?.message('加载PDF插件中', { const setLoadingMessage = miaoMessageRef.value?.alertTip('加载PDF插件中', {
type: 'info', type: 'info',
timeout: -1 timeout: -1
}) })
+4 -4
View File
@@ -3,11 +3,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { VirtualFile } from '@/class/VirtualDirectory' // import { VirtualFile } from '@/class/VirtualDirectory'
const currentFiles = defineModel<VirtualFile[]>('currentFiles', { // const currentFiles = defineModel<VirtualFile[]>('currentFiles', {
required: true // required: true
}) // })
</script> </script>
+9 -1
View File
@@ -8,12 +8,20 @@ export type miaoFetchConfig = {
export type getOption = {} & miaoFetchConfig; export type getOption = {} & miaoFetchConfig;
export type getFileOption = {
/**
* 下载进度回调函数
* @param {any} percent 进度百分比,float类型, 0-1
*/
onProcess?: (percent: number) => void;
} & miaoFetchConfig;
export type getResponse = (file | directory)[]; export type getResponse = (file | directory)[];
export type uploadOption = { export type uploadOption = {
/** /**
* 上传进度回调函数 * 上传进度回调函数
* @param {any} percent 进度百分比,float类型 * @param {any} percent 进度百分比,float类型, 0-1
*/ */
onProcess?: (percent: number) => void; onProcess?: (percent: number) => void;
/** /**
+8
View File
@@ -20,3 +20,11 @@ export type directory = {
stats: stats; stats: stats;
parent?: directory; parent?: directory;
}; };
export type alertTipType = ((content: string, options?: AlertTipMessageOptions) => (newContent: string, newOptions?: AlertTipMessageOptions) => void)
export interface AlertTipMessageOptions {
type?: 'info' | 'success' | 'error'
timeout?: number
id?: number
}