Kaynağa Gözat

插件优化,支持数据源插件

mxd 3 yıl önce
ebeveyn
işleme
9e08abd447

+ 50 - 37
src/components/common/form/magic-input.vue

@@ -1,56 +1,69 @@
 <template>
-    <input class="magic-input" :class="{'magic-input__border': border}" :value="value" :placeholder="placeholder" :readonly="readonly" :style="style" :type="type"
-        @input="e => $emit('update:value', e.target.value)"/>
+	<input class="magic-input" :class="{'magic-input__border': border}" :value="value" :placeholder="placeholder" :readonly="readonly" :style="style" :type="type"
+		   @input="e => $emit('update:value', e.target.value)" :autocomplete="autocomplete"/>
 </template>
 <script setup>
+import { computed } from "@vue/reactivity"
+
 const props = defineProps({
-    value: [String, Number, Object],
-    placeholder: String,
-    defaultValue: [String, Number, Object],
-    readonly: {
-        type: Boolean,
-        default: false
-    },
-    type: {
-        type: String,
-        default: 'text'
-    },
-    width: {
-        type: String,
-        default: '100%'
-    },
-    border: {
-        type: Boolean,
-        default: true
-    }
+	value: [String, Number, Object],
+	placeholder: String,
+	defaultValue: [String, Number, Object],
+	readonly: {
+		type: Boolean,
+		default: false
+	},
+	type: {
+		type: String,
+		default: 'text'
+	},
+	width: {
+		type: String,
+		default: '100%'
+	},
+	border: {
+		type: Boolean,
+		default: true
+	}
 })
+const autocomplete = computed(() => props.type === 'password' ? 'new-password' : null)
 const emit = defineEmits(['update:value'])
 if(!props.value && props.defaultValue){
-    emit('update:value', props.defaultValue)
+	emit('update:value', props.defaultValue)
 }
 const style = {
-    width: props.width
+	width: props.width
 }
 </script>
 <style scoped>
 .magic-input{
-    outline: 0;
-    height: 22px;
-    line-height: 22px;
-    border-radius: 0;
-    outline: 0;
-    border: 1px solid transparent;
-    padding:0;
-    margin:0;
-    padding-left: 5px;
-    background-color: var(--input-background-color);
-    color: var(--mian-color);
+	outline: 0;
+	height: 22px;
+	line-height: 22px;
+	border-radius: 0;
+	outline: 0;
+	border: 1px solid transparent;
+	padding:0;
+	margin:0;
+	padding-left: 5px;
+	background-color: var(--input-background-color);
+	color: var(--main-color);
+}
+.magic-input:autofill {
+	background: var(--input-background-color);
+}
+.magic-input:-webkit-autofill,
+.magic-input:-webkit-autofill:hover,
+.magic-input:-webkit-autofill:focus,
+.magic-input:-webkit-autofill:active {
+	transition: background-color 5000s;
+	-webkit-text-fill-color: var(--main-color) !important;
 }
 .magic-input:focus{
-    border-color: var(--input-focus-color);
+	border-color: var(--input-focus-color);
 }
 .magic-input__border{
-    border-color: var(--input-border-color);
+	border-color: var(--input-border-color);
 }
 .magic-input::-webkit-outer-spin-button,
 .magic-input::-webkit-inner-spin-button {
@@ -59,4 +72,4 @@ const style = {
 .magic-input[type="number"] {
 	-moz-appearance: textfield;
 }
-</style>
+</style>

+ 16 - 5
src/components/magic-editor.vue

@@ -21,7 +21,7 @@ import { defineTheme } from '../scripts/theme.js'
 import bus from '../scripts/bus.js'
 import EditorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"
 import JsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"
-import { nextTick, onMounted, onUnmounted, provide, reactive, ref } from 'vue'
+import { getCurrentInstance, nextTick, onMounted, onUnmounted, provide, reactive, ref } from 'vue'
 import { initializeMagicScript } from '../scripts/editor/magic-script.js'
 import JavaClass from '../scripts/editor/java-class.js'
 import MagicWebSocket from '../scripts/websocket.js'
@@ -76,7 +76,7 @@ const installPlugin = () => {
 		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']({
+				constants.PLUGINS.push(window[plugin.globalName]({
 					'i18n': { add: i18nAdd, format: $i },
 					request,
 					constants,
@@ -85,13 +85,24 @@ const installPlugin = () => {
 				}))
 				bus.status('plugin.loaded', true, plugin.name)
 				r();
+			}).catch(e => {
+				bus.status('plugin.loadFailed', false, plugin.name)
+				r()
 			})
 		}))).then(() => resolve())).error(() => resolve())
 	})
 }
+const app = getCurrentInstance().appContext.app
 Promise.all([JavaClass.initClasses(), JavaClass.initImportClass(), installPlugin()]).then(()=> bus.status('message.loadClassFinish')).catch((e)=>{
 	bus.status('message.loadClassError')
 }).finally(() => {
+	constants.PLUGINS.forEach(it => {
+		if(it.datasources){
+			it.datasources.filter(it => it.component).forEach(ds => {
+				app.component(`magic-datasource-${ds.type}`, ds.component)
+			})
+		}
+	})
 	loadFinish.value = true
 	hideLoadingElement()
 })
@@ -132,8 +143,8 @@ const checkUpdate = () => {
 									}
 								}]
 							})
-							
-						} 
+
+						}
 					} else {
 						bus.status('message.versionLastest')
 					}
@@ -240,4 +251,4 @@ bus.$on(Message.LOGOUT, () => {
 	websocket && websocket.close()
 	websocket = null
 })
-</script>
+</script>

+ 9 - 7
src/components/panel/main/magic-data-resource.vue

@@ -62,16 +62,16 @@ const datasources = computed(() => {
 const service = inject('service')[props.type]
 // 搜索条的按钮
 const buttons = [
-	{ 
+	{
 		name: $i('message.createDataSource', props.title),
-		icon: 'plus', 
+		icon: 'plus',
 		onClick:() => {
 			dataResourceDialogTitle.value = $i('message.createDataSource', props.title)
 			saveButtonText.value = $i('message.create')
 			dataResourceObj.value = {}
 			dataResourceDialogVisible.value = true
 			loading.value = false
-		} 
+		}
 	},
 ]
 const doTest = () => {
@@ -169,7 +169,7 @@ const onContextMenu = (item, event) => {
 					item.lock = constants.UNLOCK
 					bus.report(`resource-unlock`)
 				}
-					
+
 			})
 		})
 	} else {
@@ -213,7 +213,7 @@ const onContextMenu = (item, event) => {
 .magic-data-resource .magic-data-resource-header ul {
 	display: inline-flex;
 	align-items: center;
-	
+
 }
 .magic-data-resource .magic-data-resource-header ul li{
 	display: inline-block;
@@ -233,7 +233,9 @@ const onContextMenu = (item, event) => {
 .magic-data-resource .magic-data-resource-header ul li:hover{
 	background: var(--main-hover-icon-background-color);
 }
-
+.magic-data-resource .magic-data-resource-header svg{
+	height: 100%;
+}
 .magic-data-resource > ul li{
 	line-height: 22px;
 	padding-left: 10px;
@@ -275,4 +277,4 @@ const onContextMenu = (item, event) => {
 	flex: 1;
 	width: auto;
 }
-</style>
+</style>

+ 9 - 1
src/components/panel/main/magic-main.vue

@@ -24,7 +24,7 @@
 			<magic-navbar-item v-for="(navbar, index) in rightNavbars" :key="index" :title="navbar.title" :icon="navbar.icon">
 				<magic-resizer :max="320" :min="240" direction='x' :reverse="true" v-if="nextRender">
 					<magic-loading :loading="loading">
-						<magic-data-resource  :type="navbar.type" :title="navbar.name" :data="resources[navbar.type]" :component="navbar.component"/>
+						<magic-data-resource  :type="navbar.type" :title="navbar.name" :data="resources[navbar.type]"/>
 					</magic-loading>
 				</magic-resizer>
 			</magic-navbar-item>
@@ -113,6 +113,14 @@ leftNavbars.map(it => it.type).forEach(key => resources.value[key] = [])
 const rightNavbars = [
 	{ type: 'datasource', title: $i('datasource.title'), icon: 'datasource', name: $i('datasource.name')}
 ]
+constants.PLUGINS.filter(it => it.datasources && it.datasources.length > 0).map(it => it.datasources).forEach(res => res.forEach(it => {
+	rightNavbars.push({
+		type: it.type,
+		icon: it.icon,
+		title: it.title,
+		name: it.name
+	})
+}))
 rightNavbars.map(it => it.type).forEach(key => resources.value[key] = [])
 provide('resources', () => {
     const navbars = [...leftNavbars, ...rightNavbars]

+ 14 - 11
src/components/panel/main/magic-resource.vue

@@ -99,9 +99,9 @@ const tree = computed(() => props.data)
 const emits = defineEmits(['close', 'onLoad'])
 // 搜索条的按钮
 const buttons = ref([
-	{ 
+	{
 		name: $i('resource.createGroup'),
-		icon: 'group-add', 
+		icon: 'group-add',
 		onClick:() => {
 			groupObj.value = {
 				type: props.type,
@@ -109,7 +109,7 @@ const buttons = ref([
 			}
 			mode.value = true
 			showGroupDialog.value = true
-		} 
+		}
 	},
 	{ name: $i('resource.header.expand'), icon: 'expand-all', onClick: () => processTree(tree.value, it => it.opened = true) },
 	{ name: $i('resource.header.collapse'), icon: 'collapse-all', onClick: () => processTree(tree.value, it => it.opened = false) },
@@ -123,7 +123,7 @@ const buttons = ref([
 		}
 	} },
 	{ name: $i('message.hide'), icon: 'minimize', onClick:() => emits('close')},
-	
+
 ])
 const deepFind = (itemOrId, array, nameStack, pathStack, folderStack) => {
 	folderStack = folderStack || []
@@ -163,9 +163,9 @@ const onItemClick = item => {
 			let opened = cachedItems.find(it => it.item == item)
 			if(!opened){
 				opened = reactive({
-					type: props.type, 
+					type: props.type,
 					title: props.title,
-					item: config.merge(item), 
+					item: config.merge(item),
 					path: () => getFullPath(item),
 					requestPath: () => {
 						const pathStack = []
@@ -209,7 +209,7 @@ const saveGroup = () => {
 			bus.status('resource.saveGroupFailed', false, props.title, path)
 			proxy.$alert($i('resource.saveGroupFailed', props.title, path))
 		}
-		
+
 	})
 }
 const onMove = (src, target) => new Promise(reslove => request.send('/resource/move', { src: src.id, groupId: target.groupId || target.id }).success(r => {
@@ -222,7 +222,7 @@ const onMove = (src, target) => new Promise(reslove => request.send('/resource/m
 			bus.status(msgId + 'Success', true, path)
 		}
 		src[src.folder ? 'parentId' : 'groupId'] = target.groupId || target.id
-		
+
 	}else{
 		if(src.folder){
 			bus.status(msgId + 'Failed', false, props.title, path)
@@ -260,7 +260,7 @@ const updateNode = (node, data) => {
 	} else if(node.parentId === '0'){
 		data.push(node)
 		return true
-	} else { 
+	} else {
 		if(data.some(it => updateNode(node, it.children || []))){
 			return true
 		}
@@ -545,7 +545,7 @@ onMounted(() => emits('onLoad'))
 .magic-resource .magic-resource-header ul {
 	display: inline-flex;
 	align-items: center;
-	
+
 }
 .magic-resource .magic-resource-header ul li{
 	display: inline-block;
@@ -555,6 +555,9 @@ onMounted(() => emits('onLoad'))
 	line-height: 20px;
 	border-radius: 3px;
 }
+.magic-resource .magic-resource-header svg{
+	height: 100%;
+}
 .magic-resource .magic-resource-header ul li.separator{
 	background-color: var(--main-border-color);
 	width:1px;
@@ -591,4 +594,4 @@ onMounted(() => emits('onLoad'))
 .magic-resource :deep(.magic-avatar-group){
 	margin-left: 5px;
 }
-</style>
+</style>

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

@@ -32,7 +32,7 @@ export default {
         loading: 'Loading...',
         nodata: 'no data.',
         cancel: 'Cancel',
-        update: 'Save',
+        update: 'Update',
         create: 'Create',
         username: 'Username',
         password: 'Password',
@@ -112,7 +112,8 @@ export default {
     },
     plugin: {
         loading: 'Load plugin 「{0}」',
-        loaded: 'Plugin「{0}」 Loaded'
+        loaded: 'Plugin「{0}」 Loaded',
+        loadFailed: 'Failed to load plugin「{0}」'
     },
     resource: {
         createGroup: 'Create Group',
@@ -348,4 +349,4 @@ export default {
         '1035': 'Api Not Found'
     }
 
-}
+}

+ 3 - 2
src/scripts/i18n/zh-cn.js

@@ -113,7 +113,8 @@ export default {
     },
     plugin: {
         loading: '加载插件「{0}」',
-        loaded: '已加载插件「{0}」'
+        loaded: '已加载插件「{0}」',
+        loadFailed: '加载插件「{0}」失败'
     },
     resource: {
         createGroup: '创建分组',
@@ -364,4 +365,4 @@ export default {
         '1034': '未启用备份,无法操作',
         '1035': '找不到接口'
     }
-}
+}

+ 6 - 3
src/scripts/utils.js

@@ -231,7 +231,7 @@ export function definition2TreeList(definition) {
 }
 
 export function loadPlugin(url) {
-	return new Promise(resolve => {
+	return new Promise((resolve, reject) => {
 		const head = document.getElementsByTagName('head')[0];
 		const element = document.createElement('script');
 		element.setAttribute('type', 'text/javascript');
@@ -247,7 +247,10 @@ export function loadPlugin(url) {
 		} else {
 			element.onload = function(){
 				resolve()
-			};
+			}
+			element.onerror = function(){
+				reject()
+			}
 		}
 	})
-}
+}