Bläddra i källkod

初步实现插件机制

mxd 3 år sedan
förälder
incheckning
cbc7052845

+ 2 - 2
src/App.vue

@@ -11,8 +11,8 @@ try {
 if (window.MAGIC_EDITOR_CONFIG) {
 	defaultConfig = {...defaultConfig, ...window.MAGIC_EDITOR_CONFIG}
 }
-defaultConfig.baseURL = import.meta.env.MODE === 'development' ? 'http://192.168.101.4:9999/magic/web' : './';
-defaultConfig.serverURL = import.meta.env.MODE === 'development' ? 'http://192.168.101.4:9999/' : './';
+defaultConfig.baseURL = import.meta.env.MODE === 'development' ? 'http://127.0.0.1:9999/magic/web' : './';
+defaultConfig.serverURL = import.meta.env.MODE === 'development' ? 'http://127.0.0.1:9999/' : './';
 defaultConfig.inJar = true;
 </script>
 <style>

+ 4 - 4
src/components/common/icon/magic-icon.vue

@@ -1,11 +1,10 @@
 <template>
-	<svg aria-hidden="true" :class="prefix">
-		<use :xlink:href="symbolId" :class="prefix + '-' +icon"/>
+	<svg aria-hidden="true" class="magic-icon">
+		<use :xlink:href="symbolId" :class="className"/>
 	</svg>
 </template>
 <script setup>
 import { computed } from 'vue'
-
 const props = defineProps({
 	prefix: {
 		type: String,
@@ -14,7 +13,8 @@ const props = defineProps({
 	icon: String,
 	size: String,
 });
-const symbolId = computed(() => `#${props.prefix}-${props.icon}`)
+const symbolId = computed(() => props.icon&&props.icon.startsWith('#') ? props.icon : `#${props.prefix}-${props.icon}`)
+const className = computed(() => props.icon&&props.icon.startsWith('#') ? props.icon.substring(1) : `${props.prefix}-${props.icon}`)
 </script>
 <style scoped>
 svg {

+ 37 - 17
src/components/magic-editor.vue

@@ -1,13 +1,13 @@
 <template>
     <div class="magic-editor" :style="themeStyle" @contextmenu.prevent="" ref="root">
         <!-- 顶部区域 -->
-        <magic-header :themeStyle="themeStyle"/>
-        <magic-main ref="componentMain" @onLoad="init"/>
+        <magic-header :themeStyle="themeStyle" v-if="loadFinish"/>
+        <magic-main ref="componentMain" @onLoad="init" v-if="loadFinish"/>
         <!-- 状态条 -->
-        <magic-status-bar :config="config" />
-		<magic-login v-model:value="showLogin" />
+        <magic-status-bar :config="config" v-if="loadFinish" />
+		<magic-login v-model:value="showLogin" v-if="loadFinish"/>
 		<!-- 消息通知区域 -->
-		<magic-notify />
+		<magic-notify v-if="loadFinish" />
         <div class="magic-mounts"></div>
     </div>
 </template>
@@ -27,11 +27,11 @@ import JavaClass from '../scripts/editor/java-class.js'
 import MagicWebSocket from '../scripts/websocket.js'
 import Socket from '../scripts/constants/socket.js'
 import Message from '../scripts/constants/message.js'
-import { randomString, replaceURL } from '../scripts/utils.js'
+import { loadPlugin, randomString, replaceURL } from '../scripts/utils.js'
 import store from '../scripts/store.js'
 import * as monaco from 'monaco-editor'
 import modal from './common/dialog/magic-modal.js'
-import $i from '../scripts/i18n.js'
+import { default as $i, add as i18nAdd} from '../scripts/i18n.js'
 initializeMagicScript()
 provide('bus', bus)
 self.MonacoEnvironment = {
@@ -52,6 +52,7 @@ props.config.header = props.config.header || {}
 const showLogin = ref(false)
 const componentMain = ref(null)
 const root = ref(null)
+const loadFinish = ref(false)
 provide('ELEMENT_ROOT', root)
 provide('activateUserFiles', ref({}))
 let websocket = null
@@ -69,9 +70,31 @@ if (constants.BASE_URL.startsWith('http')) { // http开头
 }
 request.setBaseURL(constants.BASE_URL)
 bus.status('message.loadClass')
-Promise.all([JavaClass.initClasses(), JavaClass.initImportClass()]).then(()=> bus.status('message.loadClassFinish')).catch((e)=>{
+provide('i18n.format', $i)
+const installPlugin = () => {
+	return new Promise(resolve => {
+		request.sendGet('/plugins').success(plugins => Promise.all((plugins || []).filter(it => it.javascriptFilename).map(plugin => new Promise(r => {
+			bus.status('plugin.loading', true, plugin.name)
+			loadPlugin(replaceURL(link + '/plugins/' + plugin.javascriptFilename)).then(() => {
+				constants.PLUGINS.push(window['MagicTask']({
+					'i18n': { add: i18nAdd, format: $i },
+					request,
+					constants,
+					Message,
+					bus
+				}))
+				bus.status('plugin.loaded', true, plugin.name)
+				r();
+			})
+		}))).then(() => resolve())).error(() => resolve())
+	})
+}
+Promise.all([JavaClass.initClasses(), JavaClass.initImportClass(), installPlugin()]).then(()=> bus.status('message.loadClassFinish')).catch((e)=>{
 	bus.status('message.loadClassError')
-}).finally(() => hideLoadingElement)
+}).finally(() => {
+	loadFinish.value = true
+	hideLoadingElement()
+})
 const options = props.config.options || []
 provide('options', options)
 if(options.length === 0){
@@ -86,20 +109,17 @@ const onLogin = () => {
 }
 bus.$on(Message.LOGINED, onLogin)
 const checkUpdate = () => {
-	fetch('http://124.223.103.66:8082/magic-api/update/version').then(r => r.text().then(text => {
-		
-	}))
-	fetch('https://img.shields.io/maven-metadata/v.json?label=maven-central&metadataUrl=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Forg%2Fssssssss%2Fmagic-api%2Fmaven-metadata.xml')
+	fetch('https://console.ssssssss.org.cn/latest?group=org.ssssssss&artifactId=magic-api')
 		.then(response => {
 			if(response.status === 200){
 				response.json().then(json => {
-					if (constants.config.version !== json.value.replace('v', '')) {
-						bus.status('message.newVersionRelease', true, json.value)
+					if (json.version && json.version !== 'unknown' && constants.config.version !== json.version) {
+						bus.status('message.newVersionRelease', true, json.version)
 						if (json.value !== store.get(constants.IGNORE_VERSION)) {
 							bus.$emit(Message.NOTIFY, {
 								title: $i('message.tips'),
 								icon: 'warning',
-								content: $i('message.versionUpdate', json.value),
+								content: $i('message.versionUpdate', json.version),
 								buttons: [{
 									title: $i('message.changelog'),
 									onClick: () => {
@@ -108,7 +128,7 @@ const checkUpdate = () => {
 								}, {
 									title: $i('message.ignore'),
 									onClick: () => {
-										store.set(constants.IGNORE_VERSION, json.value)
+										store.set(constants.IGNORE_VERSION, json.version)
 									}
 								}]
 							})

+ 12 - 2
src/components/panel/footer/magic-toolbar.vue

@@ -16,17 +16,18 @@
     </div>
 </template>
 <script setup>
-import { provide, reactive, ref, resolveDynamicComponent, shallowRef } from 'vue'
+import {  provide, reactive, ref, resolveDynamicComponent, shallowRef } from 'vue'
 import bus from '../../../scripts/bus.js'
 import $i from '../../../scripts/i18n.js'
 import Message from '../../../scripts/constants/message.js'
+import constants from '../../../scripts/constants.js'
 const navbar = ref(null)
 const toolbars = reactive([
     { type: 'api', title: $i('api.title'), icon: 'parameter', component: shallowRef(resolveDynamicComponent('magic-api-info')) },
 
     { id: 'response', type: 'api', title: $i('toolbars.response'), icon: 'run', component: shallowRef(resolveDynamicComponent('magic-api-response')) },
 
-    { type: 'task', title: $i('task.title'), icon: 'parameter', component: shallowRef(resolveDynamicComponent('magic-task-info')) },
+    // { type: 'task', title: $i('task.title'), icon: 'parameter', component: shallowRef(resolveDynamicComponent('magic-task-info')) },
 
     { type: 'function', title: $i('fn.title'), icon: 'parameter', component: shallowRef(resolveDynamicComponent('magic-function-info')) },
 
@@ -44,6 +45,15 @@ const toolbars = reactive([
 
     { id: 'event', title: $i('toolbars.event'), icon: 'event', component: shallowRef(resolveDynamicComponent('magic-event')), style: { float: 'right'} }
 ])
+constants.PLUGINS.filter(it => it.toolbars && it.toolbars.length > 0).map(it => it.toolbars).forEach(res => res.forEach(it => {
+	toolbars.unshift({
+        id: it.id,
+		type: it.type,
+		icon: it.icon,
+		title: $i(it.title),
+        component: shallowRef(it.component)
+	})
+}))
 const opened = ref({})
 const info = ref({})
 provide('opened', opened)

+ 1 - 1
src/components/panel/header/magic-push.vue

@@ -1,5 +1,5 @@
 <template>
-	<magic-dialog :title="$i('message.push')" v-model:value="show" :shade="false" padding="0" width="400px" top="60px" overflow="hidden">
+	<magic-dialog :title="$i('message.push')" v-model:value="show" :shade="false" padding="0" width="450px" top="60px" overflow="hidden">
 		<magic-resource-choose ref="resource" v-model:value="selected"/>
 		<div class="magic-push-form">
 			<div>

+ 18 - 11
src/components/panel/main/magic-main.vue

@@ -47,7 +47,6 @@ import request from '../../../scripts/request.js'
 import bus from '../../../scripts/bus.js'
 import MagicAPI from '../../../scripts/service/magic-api.js'
 import MagicFunction from '../../../scripts/service/magic-function.js'
-import MagicTask from '../../../scripts/service/magic-task.js'
 import MagicDatasource from '../../../scripts/service/magic-datasource.js'
 import Message from '../../../scripts/constants/message.js'
 import { replaceURL } from '../../../scripts/utils.js'
@@ -92,15 +91,23 @@ provide('findResource', id => {
 })
 const leftNavbars = [
 	{ type: 'api', title: $i('api.name'), icon: 'api'},
-	{ type: 'function', title: $i('fn.name'), icon: 'function'},
-	{ type: 'task', title: $i('task.name'), icon: 'task'}
+	{ type: 'function', title: $i('fn.name'), icon: 'function'}
 ]
 const services = {
 	api: MagicAPI,
 	function: MagicFunction,
-	task: MagicTask,
 	datasource: MagicDatasource
 }
+constants.PLUGINS.filter(it => it.resource && it.resource.length > 0).map(it => it.resource).forEach(res => res.forEach(it => {
+	leftNavbars.push({
+		type: it.type,
+		icon: it.icon,
+		title: $i(it.title),
+	})
+	if(it.service){
+		services[it.type] = it.service
+	}
+}))
 provide('service', services)
 leftNavbars.map(it => it.type).forEach(key => resources.value[key] = [])
 const rightNavbars = [
@@ -123,10 +130,10 @@ Object.values(services).forEach(service => service.injectResources && service.in
 nextTick(() => nextRender.value = true)
 const processNode = item => {
 	return {
-		...item.node, 
-		folder: item.node.parentId !== undefined, 
-		opened: item.node.parentId !== undefined, 
-		children: item.children&& item.children.length ? item.children.map(it => processNode(it)) : undefined 
+		...item.node,
+		folder: item.node.parentId !== undefined,
+		opened: item.node.parentId !== undefined,
+		children: item.children&& item.children.length ? item.children.map(it => processNode(it)) : undefined
 	}
 }
 const loadAllResources = (type, callback) => {
@@ -149,7 +156,7 @@ bus.$on(Message.RELOAD_RESOURCES, () => {
 		bus.status('message.reloadResourceSuccess')
 		loadAllResources(null, () => bus.$emit(Message.RELOAD_RESOURCES_FINISH))
 	}).end(() => loading.value = false)
-	
+
 })
 const loadResources = type => loadAllResources(type, () => bus.$emit(Message.LOAD_RESOURCES_FINISH, type))
 bus.$on(Message.LOAD_RESOURCES, loadResources)
@@ -200,7 +207,7 @@ const resourceOnLoad = () => {
 	width: 100%;
 }
 .magic-main-body-wrapper{
-	display:flex; 
+	display:flex;
 	flex:1;
 	flex-direction: column;
 }
@@ -212,4 +219,4 @@ const resourceOnLoad = () => {
 .magic-toolbar :deep(.magic-resizer-y .magic-resizer-event){
 	top: auto;
 }
-</style>
+</style>

+ 0 - 49
src/components/panel/task/magic-task-info.vue

@@ -1,49 +0,0 @@
-<template>
-	<div class="magic-task-info">
-		<form>
-			<label>{{ $i('message.enable') }}</label>
-			<magic-checkbox v-model:value="info.enabled" />
-			<label>cron</label>
-			<magic-input v-model:value="info.cron" :placeholder="$i('task.form.placeholder.cron')" width="250px"/>
-			<label>{{ $i('task.form.name') }}</label>
-			<magic-input v-model:value="info.name" :placeholder="$i('task.form.placeholder.name')" width="250px"/>
-			<label>{{ $i('task.form.path') }}</label>
-			<magic-input v-model:value="info.path" :placeholder="$i('task.form.placeholder.path')" width="auto" style="flex:1"/>
-		</form>
-		<div style="flex:1;padding-top:5px;">
-			<magic-textarea v-model:value="info.description" :placeholder="$i('task.form.placeholder.description')"/>
-		</div>
-	</div>
-</template>
-<script setup>
-import { inject } from 'vue'
-import $i from '../../../scripts/i18n.js'
-const info = inject('info')
-</script>
-<style scoped>
-.magic-task-info{
-	display: flex;
-	flex-direction: column;
-	flex: 1;
-	padding: 5px;
-}
-.magic-task-info form{
-	display: flex;
-}
-.magic-task-info form label{
-	display: inline-block;
-	width: 65px;
-	height: 22px;
-	line-height: 22px;
-	font-weight: 400;
-	text-align: right;
-	padding: 0 5px;
-}
-.magic-task-info form :deep(.magic-checkbox){
-	width: 22px;
-	height: 22px;
-}
-.magic-task-info form :deep(.magic-textarea){
-	margin: 5px;
-}
-</style>

+ 2 - 16
src/scripts/bus.js

@@ -6,21 +6,7 @@ class Bus {
 	constructor(){
 		this.listeners = {}
 		this.statusLog = ref([])
-		try {
-			let element = document.createElement("script");
-			element.src = "https://s4.cnzz.com/z_stat.php?id=1280031557&web_id=1280031557";
-			element.setAttribute('async', true);
-			let s = document.getElementsByTagName("script")[0];
-			s.parentNode.insertBefore(element, s);
-			const _this = this
-			element.onload = element.onreadystatechange = function () {
-				if (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') {
-					_this.report(constants.MAGIC_API_VERSION);
-				}
-			}
-		} catch (ignored) {
-		
-		}
+		this.report(constants.MAGIC_API_VERSION);
 	}
 	$on(name, callback){
 		this.listeners[name] = this.listeners[name] || [];
@@ -41,7 +27,7 @@ class Bus {
 	}
 	report(eventId){
 		try {
-			window._czc.push(["_trackEvent",eventId,eventId])
+			fetch('https://console.ssssssss.org.cn/event?e=' + eventId)
 		} catch (ignored) {
 	
 		}

+ 1 - 0
src/scripts/constants.js

@@ -38,6 +38,7 @@ const constants = {
 			theme: 'theme',
 			token: 'token'
 		},
+		PLUGINS: [],
 		GLOBAL: {
 			parameters: [],
 			headers: []

+ 20 - 1
src/scripts/i18n.js

@@ -19,6 +19,25 @@ export default function doLocale(key, ...args){
     }
 }
 
+export function add(addLocalName, value){
+    let addLocale = zhCN
+    if(addLocalName === 'en'){
+        addLocale = en
+    }
+    const appendValue = (target, object) => {
+        Object.entries(object).forEach(([key, value]) => {
+            if(typeof value === 'string'){
+                target[key] = value
+            } else {
+                if(!target[key]){
+                    target[key] = {}
+                }
+                appendValue(target[key], value)
+            }
+        })
+    }
+    appendValue(addLocale, value)
+}
 
 export function translateCode(code, message){
     if(zhCN === locale){
@@ -39,4 +58,4 @@ export function translateCode(code, message){
         }
     }
     return message
-}
+}

+ 4 - 14
src/scripts/i18n/en.js

@@ -110,6 +110,10 @@ export default {
         validatePattern: 'Regex attern',
         validateExpression: 'Expression'
     },
+    plugin: {
+        loading: 'Load plugin 「{0}」',
+        loaded: 'Plugin「{0}」 Loaded'
+    },
     resource: {
         createGroup: 'Create Group',
         updateGroup: 'Update Group',
@@ -235,20 +239,6 @@ export default {
             other: 'Others'
         }
     },
-    task: {
-        title: 'Task Info',
-        name: 'Task',
-        form: {
-            name: 'Task Name',
-            path: 'Task Path',
-            placeholder: {
-                cron: 'Please Enter Cron Expression',
-                name: 'Please Enter Task Name',
-                path: 'Please Enter Task Path',
-                description: 'Please Enter Task Description'
-            }
-        }
-    },
     fn: {
         title: 'Function Info',
         name: 'Function',

+ 4 - 0
src/scripts/i18n/zh-cn.js

@@ -111,6 +111,10 @@ export default {
         validateExpression: '表达式验证',
 
     },
+    plugin: {
+        loading: '加载插件「{0}」',
+        loaded: '已加载插件「{0}」'
+    },
     resource: {
         createGroup: '创建分组',
         updateGroup: '修改分组',

+ 0 - 39
src/scripts/service/magic-task.js

@@ -1,39 +0,0 @@
-import bus from '../bus.js'
-import constants from '../constants.js'
-import $i from '../i18n.js'
-import Message from '../constants/message.js'
-import request from '../request.js'
-
-export default {
-	getIcon: item => 'task',
-	name: $i('task.name'),
-	doTest: (opened) => {
-		opened.running = true
-		const info = opened.item
-		const requestConfig = {
-			baseURL: constants.SERVER_URL,
-			url: '/task/execute',
-			method: 'POST',
-			responseType: 'json',
-			headers: {},
-			withCredentials: true
-		}
-		bus.$emit(Message.SWITCH_TOOLBAR, 'log')
-		requestConfig.headers[constants.HEADER_REQUEST_CLIENT_ID] = constants.CLIENT_ID
-		requestConfig.headers[constants.HEADER_REQUEST_SCRIPT_ID] = opened.item.id
-		requestConfig.headers[constants.HEADER_MAGIC_TOKEN] = constants.HEADER_MAGIC_TOKEN_VALUE
-		// 设置断点
-		requestConfig.headers[constants.HEADER_REQUEST_BREAKPOINTS] = (opened.decorations || []).filter(it => it.options.linesDecorationsClassName === 'breakpoints').map(it => it.range.startLineNumber).join(',')
-		const fullName = opened.path()
-		bus.status(`开始测试定时任务「${fullName}」`)
-		request.sendPost('/task/execute', { id: info.id }, requestConfig).success(res => {
-			opened.running = false
-		}).end(() => {
-			bus.status(`定时任务「${fullName}」测试完毕`)
-			opened.running = false
-		})
-	},
-	runnable: true,
-	requirePath: true,
-	merge: item => item
-}

+ 1 - 1
vite.config.js

@@ -1,6 +1,6 @@
 import { defineConfig } from 'vite'
 import vue from '@vitejs/plugin-vue'
-import viteSvgIcons from 'vite-plugin-svg-icons';
+import viteSvgIcons from 'vite-plugin-svg-icons'
 import { MonacoEditorNls } from './src/plugins/monaco-editor-nls.js'
 import { MonacoExcludeLanguages } from './src/plugins/monaco-exclude-languages.js'
 import path from 'path'