提交 052e43ff 编写于 作者: M mxd

2.0 first commit

上级 9903ab6b
......@@ -6,7 +6,7 @@
<parent>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-parent</artifactId>
<version>1.7.2</version>
<version>2.0.0-alpha.1</version>
</parent>
<artifactId>magic-api-spring-boot-starter</artifactId>
<packaging>jar</packaging>
......
......@@ -22,6 +22,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
......@@ -29,7 +30,6 @@ import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
......@@ -50,13 +50,16 @@ import org.ssssssss.magicapi.interceptor.*;
import org.ssssssss.magicapi.logging.LoggerManager;
import org.ssssssss.magicapi.model.Constants;
import org.ssssssss.magicapi.model.DataType;
import org.ssssssss.magicapi.model.MagicEntity;
import org.ssssssss.magicapi.model.Options;
import org.ssssssss.magicapi.modules.*;
import org.ssssssss.magicapi.provider.*;
import org.ssssssss.magicapi.provider.impl.*;
import org.ssssssss.magicapi.service.MagicDynamicRegistry;
import org.ssssssss.magicapi.service.MagicResourceService;
import org.ssssssss.magicapi.service.impl.*;
import org.ssssssss.magicapi.utils.ClassScanner;
import org.ssssssss.magicapi.utils.Mapping;
import org.ssssssss.magicapi.utils.PathUtils;
import org.ssssssss.script.MagicResourceLoader;
import org.ssssssss.script.MagicScript;
import org.ssssssss.script.MagicScriptEngine;
......@@ -70,7 +73,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
......@@ -85,7 +87,7 @@ import java.util.function.BiFunction;
@Configuration
@ConditionalOnClass({RequestMappingHandlerMapping.class})
@EnableConfigurationProperties(MagicAPIProperties.class)
@Import({MagicRedisAutoConfiguration.class, MagicMongoAutoConfiguration.class, MagicSwaggerConfiguration.class, MagicJsonAutoConfiguration.class, ApplicationUriPrinter.class})
@Import({MagicRedisAutoConfiguration.class, MagicMongoAutoConfiguration.class, MagicJsonAutoConfiguration.class, ApplicationUriPrinter.class})
@EnableWebSocket
public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketConfigurer {
......@@ -136,6 +138,10 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
private final ObjectProvider<MagicNotifyService> magicNotifyServiceProvider;
private final ObjectProvider<List<MagicDynamicRegistry<? extends MagicEntity>>> magicDynamicRegistriesProvider;
private final ObjectProvider<List<MagicResourceStorage<? extends MagicEntity>>> magicResourceStoragesProvider;
private final ObjectProvider<DataSourceEncryptProvider> dataSourceEncryptProvider;
private final Environment environment;
......@@ -174,6 +180,8 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
ObjectProvider<AuthorizationInterceptor> authorizationInterceptorProvider,
ObjectProvider<List<NamedTableInterceptor>> namedTableInterceptorsProvider,
ObjectProvider<DataSourceEncryptProvider> dataSourceEncryptProvider,
ObjectProvider<List<MagicDynamicRegistry<? extends MagicEntity>>> magicDynamicRegistriesProvider,
ObjectProvider<List<MagicResourceStorage<? extends MagicEntity>>> magicResourceStoragesProvider,
Environment environment,
ApplicationContext applicationContext
) {
......@@ -189,6 +197,8 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
this.authorizationInterceptorProvider = authorizationInterceptorProvider;
this.namedTableInterceptorsProvider = namedTableInterceptorsProvider;
this.dataSourceEncryptProvider = dataSourceEncryptProvider;
this.magicDynamicRegistriesProvider = magicDynamicRegistriesProvider;
this.magicResourceStoragesProvider = magicResourceStoragesProvider;
this.environment = environment;
this.applicationContext = applicationContext;
}
......@@ -278,13 +288,13 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
// 配置静态资源路径
registry.addResourceHandler(web + "/**").addResourceLocations("classpath:/magic-editor/");
try {
Mapping mapping = Mapping.create(requestMappingHandlerMapping);
// 默认首页设置
mapping.register(mapping.paths(web).build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("redirectIndex", HttpServletRequest.class))
// 读取配置
.register(mapping.paths(web + "/config.json").build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readConfig"))
// 读取配置
.register(mapping.paths(web + "/classes.txt").produces("text/plain").build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readClass"));
Mapping mapping = Mapping.create(requestMappingHandlerMapping, web, properties.getPrefix());
// 默认首页设置
mapping.register(mapping.paths(web).build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("redirectIndex", HttpServletRequest.class));
// 读取配置
mapping.register("GET", web + "/config.json", this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readConfig"));
// 读取配置
mapping.register(mapping.paths(web + "/classes.txt").methods(RequestMethod.GET).produces("text/plain").build(), this, MagicAPIAutoConfiguration.class.getDeclaredMethod("readClass"));
} catch (NoSuchMethodException ignored) {
}
}
......@@ -337,39 +347,49 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
return new DefaultSqlCache(cacheConfig.getCapacity(), cacheConfig.getTtl());
}
/**
* 注入接口映射
*/
@Bean
public MappingHandlerMapping mappingHandlerMapping() throws NoSuchMethodException {
String prefix = StringUtils.isNotBlank(properties.getPrefix()) ? PathUtils.replaceSlash("/" + properties.getPrefix() + "/") : null;
return new MappingHandlerMapping(prefix, properties.isAllowOverride());
@ConditionalOnMissingBean
public MagicResourceService magicResourceService(Resource workspace) {
return new DefaultMagicResourceService(workspace, magicResourceStoragesProvider.getObject(), applicationContext);
}
@Bean
@ConditionalOnMissingBean(FunctionServiceProvider.class)
public FunctionServiceProvider functionServiceProvider(GroupServiceProvider groupServiceProvider, Resource magicResource) {
return new DefaultFunctionServiceProvider(groupServiceProvider, magicResource);
@ConditionalOnMissingBean
public ApiInfoMagicResourceStorage apiInfoMagicResourceStorage() {
return new ApiInfoMagicResourceStorage();
}
/**
* 注入分组存储service
*/
@Bean
@ConditionalOnMissingBean(GroupServiceProvider.class)
public GroupServiceProvider groupServiceProvider(Resource magicResource) {
return new DefaultGroupServiceProvider(magicResource);
@ConditionalOnMissingBean
public RequestMagicDynamicRegistry magicRequestMagicDynamicRegistry(ApiInfoMagicResourceStorage apiInfoMagicResourceStorage) throws NoSuchMethodException {
return new RequestMagicDynamicRegistry(apiInfoMagicResourceStorage, Mapping.create(requestMappingHandlerMapping, properties.getWeb(), properties.getPrefix()));
}
@Bean
@ConditionalOnMissingBean
public FunctionInfoMagicResourceStorage functionInfoMagicResourceStorage() {
return new FunctionInfoMagicResourceStorage();
}
/**
* 注入接口存储service
*/
@Bean
@ConditionalOnMissingBean(ApiServiceProvider.class)
public ApiServiceProvider apiServiceProvider(GroupServiceProvider groupServiceProvider, Resource magicResource) {
return new DefaultApiServiceProvider(groupServiceProvider, magicResource);
@ConditionalOnMissingBean
public FunctionMagicDynamicRegistry functionMagicDynamicRegistry(FunctionInfoMagicResourceStorage functionInfoMagicResourceStorage) {
return new FunctionMagicDynamicRegistry(functionInfoMagicResourceStorage);
}
@Bean
@ConditionalOnMissingBean
public DataSourceInfoMagicResourceStorage dataSourceInfoMagicResourceStorage() {
return new DataSourceInfoMagicResourceStorage();
}
@Bean
@ConditionalOnMissingBean
public DataSourceMagicDynamicRegistry dataSourceMagicDynamicRegistry(DataSourceInfoMagicResourceStorage dataSourceInfoMagicResourceStorage) {
return new DataSourceMagicDynamicRegistry(dataSourceInfoMagicResourceStorage);
}
@Bean
@ConditionalOnMissingBean(MagicNotifyService.class)
public MagicNotifyService magicNotifyService() {
......@@ -385,26 +405,13 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
return new MagicFileBackupService(new File(properties.getBackupConfig().getLocation()));
}
@Bean
public MagicFunctionManager magicFunctionManager(GroupServiceProvider groupServiceProvider, FunctionServiceProvider functionServiceProvider) {
return new MagicFunctionManager(groupServiceProvider, functionServiceProvider);
}
/**
* 注入API调用Service
*/
@Bean
@ConditionalOnMissingBean
public MagicAPIService magicAPIService(MappingHandlerMapping mappingHandlerMapping,
ApiServiceProvider apiServiceProvider,
FunctionServiceProvider functionServiceProvider,
GroupServiceProvider groupServiceProvider,
ResultProvider resultProvider,
MagicDynamicDataSource magicDynamicDataSource,
MagicFunctionManager magicFunctionManager,
Resource workspace,
MagicBackupService magicBackupService) {
return new DefaultMagicAPIService(mappingHandlerMapping, apiServiceProvider, functionServiceProvider, groupServiceProvider, resultProvider, magicDynamicDataSource, magicFunctionManager, magicNotifyServiceProvider.getObject(), properties.getClusterConfig().getInstanceId(), workspace, magicBackupService, dataSourceEncryptProvider.getIfAvailable() , properties.isThrowException());
public MagicAPIService magicAPIService(ResultProvider resultProvider, MagicResourceService magicResourceService) {
return new DefaultMagicAPIService(resultProvider, properties.getClusterConfig().getInstanceId(), magicResourceService, properties.isThrowException());
}
/**
......@@ -522,18 +529,15 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
@Bean
public MagicConfiguration magicConfiguration(MagicDynamicDataSource dynamicDataSource,
SQLModule sqlModule,
List<MagicModule> magicModules,
SQLModule sqlModule,
List<MagicModule> magicModules,
List<LanguageProvider> languageProviders,
Resource magicResource,
ResultProvider resultProvider,
MagicResourceService magicResourceService,
MagicAPIService magicAPIService,
ApiServiceProvider apiServiceProvider,
GroupServiceProvider groupServiceProvider,
MappingHandlerMapping mappingHandlerMapping,
FunctionServiceProvider functionServiceProvider,
MagicNotifyService magicNotifyService,
MagicFunctionManager magicFunctionManager,
RequestMagicDynamicRegistry requestMagicDynamicRegistry,
MagicBackupService magicBackupService) throws NoSuchMethodException {
logger.info("magic-api工作目录:{}", magicResource);
AsyncCall.setThreadPoolExecutorSize(properties.getThreadPoolExecutorSize());
......@@ -550,10 +554,8 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
configuration.setMagicAPIService(magicAPIService);
configuration.setMagicNotifyService(magicNotifyService);
configuration.setInstanceId(properties.getClusterConfig().getInstanceId());
configuration.setApiServiceProvider(apiServiceProvider);
configuration.setGroupServiceProvider(groupServiceProvider);
configuration.setMappingHandlerMapping(mappingHandlerMapping);
configuration.setFunctionServiceProvider(functionServiceProvider);
configuration.setMagicResourceService(magicResourceService);
configuration.setMagicDynamicRegistries(magicDynamicRegistriesProvider.getObject());
configuration.setMagicBackupService(magicBackupService);
SecurityConfig securityConfig = properties.getSecurityConfig();
configuration.setDebugTimeout(properties.getDebugConfig().getTimeout());
......@@ -568,32 +570,19 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
// 向页面传递配置信息时不传递用户名密码,增强安全性
securityConfig.setUsername(null);
securityConfig.setPassword(null);
requestMagicDynamicRegistry.setHandler(new RequestHandler(configuration, requestMagicDynamicRegistry));
// 构建UI请求处理器
String base = properties.getWeb();
mappingHandlerMapping.setRequestMappingHandlerMapping(requestMappingHandlerMapping);
MagicDataSourceController dataSourceController = new MagicDataSourceController(configuration);
Mapping mapping = Mapping.create(requestMappingHandlerMapping, base, properties.getPrefix());
MagicWorkbenchController magicWorkbenchController = new MagicWorkbenchController(configuration, properties.getSecretKey());
if (base != null) {
configuration.setEnableWeb(true);
List<MagicController> controllers = new ArrayList<>(Arrays.asList(
new MagicAPIController(configuration),
dataSourceController,
magicWorkbenchController,
new MagicGroupController(configuration),
new MagicFunctionController(configuration)
));
controllers.forEach(item -> mappingHandlerMapping.registerController(item, base));
mapping.registerController(magicWorkbenchController).registerController(new MagicResourceController(configuration));
}
// 注册接收推送的接口
if (StringUtils.isNotBlank(properties.getSecretKey())) {
Mapping mapping = Mapping.create(requestMappingHandlerMapping);
RequestMappingInfo requestMappingInfo = mapping.paths(properties.getPushPath()).build();
Method method = MagicWorkbenchController.class.getDeclaredMethod("receivePush", MultipartFile.class, String.class, Long.class, String.class);
mapping.register(requestMappingInfo, magicWorkbenchController, method);
mapping.register(mapping.paths(properties.getPushPath()).methods(RequestMethod.POST).build(), magicWorkbenchController, MagicWorkbenchController.class.getDeclaredMethod("receivePush", MultipartFile.class, String.class, Long.class, String.class));
}
// 注册数据源
magicAPIService.registerAllDataSource();
// 设置拦截器信息
this.requestInterceptorsProvider.getIfAvailable(Collections::emptyList).forEach(interceptor -> {
logger.info("注册请求拦截器:{}", interceptor.getClass());
......@@ -603,16 +592,6 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
if (this.properties.isBanner()) {
configuration.printBanner();
}
configuration.setMagicFunctionManager(magicFunctionManager);
// 注册函数加载器
magicFunctionManager.registerFunctionLoader();
// 注册所有函数
magicFunctionManager.registerAllFunction();
mappingHandlerMapping.setHandler(new RequestHandler(configuration));
mappingHandlerMapping.setMagicApiService(apiServiceProvider);
mappingHandlerMapping.setGroupServiceProvider(groupServiceProvider);
// 注册所有映射
mappingHandlerMapping.registerAllMapping();
// 备份清理
if (properties.getBackupConfig().getMaxHistory() > 0) {
long interval = properties.getBackupConfig().getMaxHistory() * 86400000L;
......
......@@ -147,9 +147,6 @@ public class MagicAPIProperties {
@NestedConfigurationProperty
private DebugConfig debugConfig = new DebugConfig();
@NestedConfigurationProperty
private SwaggerConfig swaggerConfig = new SwaggerConfig();
@NestedConfigurationProperty
private ResourceConfig resource = new ResourceConfig();
......@@ -265,14 +262,6 @@ public class MagicAPIProperties {
this.prefix = prefix;
}
public SwaggerConfig getSwaggerConfig() {
return swaggerConfig;
}
public void setSwaggerConfig(SwaggerConfig swaggerConfig) {
this.swaggerConfig = swaggerConfig;
}
public String getAutoImportModule() {
return autoImportModule;
}
......
package org.ssssssss.magicapi.spring.boot.starter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.ssssssss.magicapi.config.MappingHandlerMapping;
import org.ssssssss.magicapi.provider.GroupServiceProvider;
import org.ssssssss.magicapi.swagger.SwaggerEntity;
import org.ssssssss.magicapi.swagger.SwaggerProvider;
import org.ssssssss.magicapi.utils.Mapping;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import javax.servlet.ServletContext;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Swagger配置类
*
* @author mxd
*/
@Configuration
@AutoConfigureAfter({MagicAPIAutoConfiguration.class})
@EnableConfigurationProperties(MagicAPIProperties.class)
@ConditionalOnClass(name = "springfox.documentation.swagger.web.SwaggerResourcesProvider")
public class MagicSwaggerConfiguration {
private final MagicAPIProperties properties;
private final ApplicationContext applicationContext;
@Autowired
@Lazy
private RequestMappingHandlerMapping requestMappingHandlerMapping;
public MagicSwaggerConfiguration(MagicAPIProperties properties, ApplicationContext applicationContext) {
this.properties = properties;
this.applicationContext = applicationContext;
}
@Bean
@Primary
public SwaggerResourcesProvider magicSwaggerResourcesProvider(MappingHandlerMapping handlerMapping, GroupServiceProvider groupServiceProvider, ServletContext servletContext) throws NoSuchMethodException {
SwaggerConfig config = properties.getSwaggerConfig();
Mapping mapping = Mapping.create(requestMappingHandlerMapping);
RequestMappingInfo requestMappingInfo = mapping.paths(config.getLocation()).build();
// 构建文档信息
SwaggerProvider swaggerProvider = new SwaggerProvider();
swaggerProvider.setGroupServiceProvider(groupServiceProvider);
swaggerProvider.setMappingHandlerMapping(handlerMapping);
swaggerProvider.setPersistenceResponseBody(properties.isPersistenceResponseBody());
SwaggerEntity.License license = new SwaggerEntity.License("MIT", "https://gitee.com/ssssssss-team/magic-api/blob/master/LICENSE");
swaggerProvider.setInfo(new SwaggerEntity.Info(config.getDescription(), config.getVersion(), config.getTitle(), license, config.getConcat()));
swaggerProvider.setBasePath(servletContext.getContextPath());
// 注册swagger.json
mapping.register(requestMappingInfo, swaggerProvider, SwaggerProvider.class.getDeclaredMethod("swaggerJson"));
return () -> {
List<SwaggerResource> resources = new ArrayList<>();
// 追加Magic Swagger信息
resources.add(swaggerResource(config.getName(), config.getLocation()));
Map<String, SwaggerResourcesProvider> beans = applicationContext.getBeansOfType(SwaggerResourcesProvider.class);
// 获取已定义的文档信息
for (Map.Entry<String, SwaggerResourcesProvider> entry : beans.entrySet()) {
if (!"magicSwaggerResourcesProvider".equalsIgnoreCase(entry.getKey())) {
resources.addAll(entry.getValue().get());
}
}
return resources;
};
}
/**
* 构建 SwaggerResource
*
* @param name 名字
* @param location 位置
*/
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource resource = new SwaggerResource();
resource.setName(name);
resource.setLocation(location);
resource.setSwaggerVersion("2.0");
return resource;
}
}
package org.ssssssss.magicapi.spring.boot.starter;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.ssssssss.magicapi.swagger.SwaggerEntity;
/**
* Swagger 配置
*
* @author mxd
*/
public class SwaggerConfig {
/**
* 资源名称
*/
private String name = "MagicAPI接口";
/**
* 资源位置
*/
private String location = "/v2/api-docs/magic-api/swagger2.json";
/**
* 文档标题
*/
private String title = "MagicAPI Swagger Docs";
/**
* 文档描述
*/
private String description = "MagicAPI 接口信息";
@NestedConfigurationProperty
private SwaggerEntity.Concat concat = new SwaggerEntity.Concat();
/**
* 文档版本
*/
private String version = "1.0";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public SwaggerEntity.Concat getConcat() {
return concat;
}
public void setConcat(SwaggerEntity.Concat concat) {
this.concat = concat;
}
}
......@@ -6,74 +6,10 @@
<parent>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-parent</artifactId>
<version>1.7.2</version>
<version>2.0.0-alpha.1</version>
</parent>
<artifactId>magic-editor</artifactId>
<packaging>jar</packaging>
<name>magic-editor</name>
<description>magic-editor</description>
<build>
<plugins>
<!-- npm install && npm run build -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>exec-npm-install</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>npm</executable>
<arguments>
<argument>install</argument>
</arguments>
<workingDirectory>${basedir}/src/console</workingDirectory>
</configuration>
</execution>
<execution>
<id>exec-npm-run-build</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>npm</executable>
<arguments>
<argument>run</argument>
<argument>build</argument>
</arguments>
<workingDirectory>${basedir}/src/console</workingDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>copy-resource</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes/magic-editor</outputDirectory>
<resources>
<resource>
<directory>${basedir}/src/console/dist</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/essential'
// '@vue/standard'
],
rules: {
'no-console': 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
// allow async-await
'generator-star-spacing': 'off',
'space-before-function-paren': 0,
"vue/no-parsing-error": [2, {
"x-invalid-end-tag": false
}],
'space-in-parens': [0, 'never'] //小括号里面要不要有空格
},
parserOptions: {
parser: 'babel-eslint'
}
}
.DS_Store
node_modules
/dist
package-lock.json
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
\ No newline at end of file
{
"printWidth": 300,
"tabWidth": 2,
"singleQuote": true,
"semi": false
}
\ No newline at end of file
# magic-editor
本项目为`magic-api`的前端页面,单独引用请[参阅文档](http://ssssssss.org)
\ No newline at end of file
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
{
"name": "magic-editor",
"version": "1.7.2",
"private": false,
"description": "magic-editor for magic-api",
"main": "dist/magic-editor.umd.min.js",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"build:lib": "vue-cli-service build --name magic-editor --target lib --entry ./src/index.js"
},
"dependencies": {
"axios": "^0.21.0",
"core-js": "^3.6.5",
"monaco-editor": "^0.29.1",
"qs": "^6.9.4",
"vue": "^2.6.11",
"worker-loader": "^3.0.6"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.44.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {
"generator-star-spacing": "off",
"no-tabs": "off",
"no-unused-vars": "off",
"no-console": "off",
"no-irregular-whitespace": "off",
"no-debugger": "off"
}
},
"files": [
"/dist/*",
"/src/*",
"*.js"
],
"repository": {
"type": "git",
"url": "https://gitee.com/ssssssss-team/magic-api.git"
},
"license": "MIT",
"bugs": {
"url": "https://gitee.com/ssssssss-team/magic-api/issues"
},
"author": "javaxd",
"homepage": "https://ssssssss.org",
"keywords": [
"vue",
"magic-api",
"magic-editor",
"magic-api-vue"
]
}
'use strict'
var webpackVersion = require('webpack/package.json').version;
var local = null;
function compilerHook (compilation) {
if (webpackVersion < '4') {
compilation.plugin('succeed-module', compilationHook);
} else {
compilation.hooks.succeedModule.tap('MonacoEditorLocalesPlugin', compilationHook);
}
};
function compilationHook (wpModule) {
if(!wpModule.resource || !wpModule.resource.indexOf || wpModule.resource.replace(/\\+/g, "/").indexOf("esm/vs/nls.js")<0){
return;
}
var langStr = local.getSelectLangStr();
var endl = "\r\n";
var code = wpModule._source._value;
code = code.replace("export function localize", " function _ocalize");
code += endl + "function localize(data, message) {";
code += endl + " if(typeof(message) === 'string'){";
code += endl + " var idx = localize.mapLangIdx[message] || -1;";
code += endl + " var nlsLang = localize.mapNlsLang[localize.selectLang] || {};";
code += endl + "";
code += endl + " if(idx in nlsLang){";
code += endl + " message = nlsLang[idx];";
code += endl + " }";
if(local.options.logUnmatched){
code += endl + " else{";
code += endl + " console.info('unknown lang:' + message);";
code += endl + " }";
}
code += endl + " }";
code += endl + "";
code += endl + " var args = [];";
code += endl + " for(var i = 0; i < arguments.length; ++i){";
code += endl + " args.push(arguments[i]);";
code += endl + " }";
code += endl + " args[1] = message;";
code += endl + " return _ocalize.apply(this, args);";
code += endl + "}";
code += endl + "localize.selectLang = " + local.getSelectLangStr() + ";";
if(langStr.indexOf('(') >= 0){
code += endl + "try{ localize.selectLang = eval(localize.selectLang); }catch(ex){}";
}
code += endl + "localize.mapLangIdx = " + JSON.stringify(local.mapLangIdx) + ";";
code += endl + "localize.mapNlsLang = " + JSON.stringify(local.lang) + ";";
code += endl + "";
wpModule._source._value = code;
};
function MonacoEditorLocalesPlugin(options){
this.options = {
/**
* support languages list, .eg ["de"]
* embed language base on monaco-editor@0.14.6
* all available embed languages: de,es,fr,it,ja,ko,ru,zh-cn,zh-tw
* just add what you need to reduce the size
*/
languages: options.languages || [],
/**
* default language name, .eg "de"
* use function string to set dynamic, .eg "$.cookie('language')"
*/
defaultLanguage: options.defaultLanguage || options.languages[0] || "",
/**
* log on console if unmatched
*/
logUnmatched: options.logUnmatched || false,
/**
* self languages map, .eg {"zh-cn": {"Find": "查找", "Search": "搜索"}, "de":{}, ... }
*/
mapLanguages: options.mapLanguages || {},
};
this.langIdx = 0;
this.mapLangIdx = {};
this.lang = {};
this.mapEmbedLangName = {};
this.mapEmbedLangNameSelf = {};
this.init();
this.initLang();
}
module.exports = MonacoEditorLocalesPlugin;
MonacoEditorLocalesPlugin.prototype.apply = function(compiler) {
local = this;
if (webpackVersion < '4') {
compiler.plugin("compilation", compilerHook);
} else {
compiler.hooks.compilation.tap('MonacoEditorLocalesPlugin', compilerHook);
}
}
MonacoEditorLocalesPlugin.prototype.initLang = function(){
var arr = this.options.languages;
for(var i = 0 ;i < arr.length; ++i){
if(!(arr[i] in this.mapEmbedLangName)) {
return;
}
var obj = this.mapEmbedLangName[arr[i]];
if(!obj){
continue;
}
var rstObj = this.lang[arr[i]] || (this.lang[arr[i]] = {});
this.initOneLang(rstObj, this.mapEmbedLangName["en"], obj);
var objSelf = this.mapEmbedLangNameSelf[arr[i]];
if(!objSelf){
continue;
}
this.initSelfOneLang(rstObj, objSelf);
objSelf = this.options.mapLanguages[arr[i]];
if(!objSelf){
continue;
}
this.initSelfOneLang(rstObj, objSelf);
}
}
MonacoEditorLocalesPlugin.prototype.initOneLang = function(rstObj, en, langObj){
for (var key in en) {
// console.info("aaa:", key, ",", !!en[key], ",", !!langObj);
if (en && langObj && typeof(en[key]) === "string" && typeof(langObj[key]) === "string") {
var idx = this.getLangIdx(en[key]);
rstObj[idx] = langObj[key];
} else if (en && langObj && en[key] && langObj[key] && typeof(en[key]) === "object" && typeof(langObj[key]) === "object") {
this.initOneLang(rstObj, en[key], langObj[key]);
}
}
}
MonacoEditorLocalesPlugin.prototype.initSelfOneLang = function(rstObj, obj){
for (var key in obj) {
var idx = this.getLangIdx(key);
rstObj[idx] = obj[key];
}
}
MonacoEditorLocalesPlugin.prototype.getSelectLangStr = function(){
var str = this.options.defaultLanguage;
str = str.replace(/'/g, "\\'");
str = str.replace(/\r\n/g, "\\r\\n");
str = str.replace(/\n/g, "\\n");
return "'" + str + "'";
// return "'(" + str + ")'";
}
MonacoEditorLocalesPlugin.prototype.getLangIdx = function(en){
if(en in this.mapLangIdx){
return this.mapLangIdx[en];
}
var idx = this.langIdx;
this.mapLangIdx[en] = idx;
this.langIdx++;
return idx;
}
MonacoEditorLocalesPlugin.prototype.init = function(){
//{en:index}
this.mapLangIdx = {};
//{de:{enIndex:"deLang"}, es:{}, ...}
this.lang = { };
this.mapEmbedLangName = {
"zh-cn": require("./editor.main.nls.zh-cn"),
"en": require("./editor.main.nls.en")
};
this.mapEmbedLangNameSelf = {
"zh-cn": {
},
"en": {
}
}
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.png" />
<title><%= htmlWebpackPlugin.options.title %></title>
<style>
.ma-loading-wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 10000000;
text-align: center;
background: #fff;
}
.ma-loading {
position: absolute;
top: 50%;
left: 50%;
width: 500px;
height: 100px;
margin-left: -250px;
margin-top: -50px;
text-align: center;
}
.ma-loading .ma-title {
font-size: 0;
color: #0075ff;
letter-spacing: 0;
}
.ma-loading .ma-title label {
font-size: 14px;
display: inline-block;
margin-top: 5px;
vertical-align: top;
}
.ma-loading .ma-title span {
font-size: 20px;
display: inline-block;
padding: 0 3px;
animation: stretch 1s infinite;
}
.ma-loading .ma-title span:nth-child(1) {
animation-delay: calc(1s / 8 * 0 / 2);
}
.ma-loading .ma-title span:nth-child(2) {
animation-delay: calc(1s / 8 * 1 / 2);
}
.ma-loading .ma-title span:nth-child(3) {
animation-delay: calc(1s / 8 * 2 / 2);
}
.ma-loading .ma-title span:nth-child(4) {
animation-delay: calc(1s / 8 * 3 / 2);
}
.ma-loading .ma-title span:nth-child(5) {
animation-delay: calc(1s / 8 * 4 / 2);
}
.ma-loading .ma-title span:nth-child(6) {
animation-delay: calc(1s / 8 * 5 / 2);
}
.ma-loading .ma-title span:nth-child(7) {
animation-delay: calc(1s / 8 * 6 / 2);
}
.ma-loading .ma-title span:nth-child(8) {
animation-delay: calc(1s / 8 * 7 / 2);
}
.ma-loading .ma-loading-text {
text-align: center;
font-weight: bolder;
font-style: italic;
color: #889aa4;
font-size: 14px;
margin-top: 5px;
animation: blink-loading 2s ease-in infinite;
}
@keyframes stretch {
0% {
transform: scale(1);
}
25% {
transform: scale(1.2);
}
50% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
@keyframes blink-loading {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
</style>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- loading begin -->
<div class="ma-loading-wrapper" id="ma-loading-wrapper">
<div class="ma-loading">
<div class="ma-title">
<span>L</span>
<span>o</span>
<span>a</span>
<span>d</span>
<span>i</span>
<span>n</span>
<span>g</span>
</div>
<div class="ma-loading-text" id="ma-loading-text"></div>
</div>
</div>
<script>
function showMaLoadingText() {
let defaultConfig = {
title: 'magic-api',
version: '<%= process.env.VUE_APP_MA_VERSION %>'
}
defaultConfig = { ...defaultConfig, ...window.MAGIC_EDITOR_CONFIG }
let $dom = document.getElementById('ma-loading-text')
$dom.innerText = 'By ' + defaultConfig.title + ' ' + defaultConfig.version
}
function hideMaLoading() {
document.getElementById('ma-loading-wrapper').style.display = 'none'
}
</script>
<script src="./config-js" onload="showMaLoadingText()"></script>
<!-- loading end -->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<div id="app">
<magic-editor :config="config"/>
</div>
</template>
<script>
import MagicEditor from '@/components/magic-editor.vue'
export default {
name: 'App',
components: {
MagicEditor,
},
data() {
let defaultConfig = {};
try {
if (parent && parent.MAGIC_EDITOR_CONFIG) {
defaultConfig = {...parent.MAGIC_EDITOR_CONFIG};
}
} catch (ignored) {
}
if (window.MAGIC_EDITOR_CONFIG) {
defaultConfig = {...defaultConfig, ...window.MAGIC_EDITOR_CONFIG}
}
defaultConfig.baseURL = process.env.NODE_ENV === 'development' ? 'http://localhost:9999/magic/web' : './';
defaultConfig.serverURL = process.env.NODE_ENV === 'development' ? 'http://localhost:9999/' : './';
defaultConfig.inJar = true;
return {
config: defaultConfig
}
}
}
</script>
<style>
html,
body,
#app {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
import axios from 'axios'
import Qs from 'qs'
import {modal} from '@/components/common/modal'
import contants from '@/scripts/contants.js'
import bus from '@/scripts/bus.js'
const config = {
// 请求路径
baseURL: '',
// 默认请求方法
method: 'post',
// 请求超时时间(毫秒)
timeout: 0,
// 是否携带cookie信息
withCredentials: true,
// 响应格式,可选项 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json',
// 自定义添加头部
headers: {
// ;charset=UTF-8
'Content-Type': 'application/x-www-form-urlencoded'
},
// `transformRequest` 允许在向服务器发送前,修改请求数据
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
transformRequest: [
function (data) {
if(data instanceof FormData){
return data;
}
return Qs.stringify(data, {
// a:[1,2] => a=1&a=2
arrayFormat: 'repeat',
// a[b]:1 => a.b=1
allowDots: true
})
}
],
paramsSerializer(data) {
return Qs.stringify(data, {
// a:[1,2] => a=1&a=2
arrayFormat: 'repeat',
// a[b]:1 => a.b=1
allowDots: true
})
}
}
class HttpResponse {
// 请求成功调用 code == 1的
successHandle = null
// 请求异常,包含js异常调用
errorHandle = null
endHandle = null
constructor() {
}
// 异常回调,实际上的code != 1的
exceptionHandle = (code, message) => {
modal.magicAlert({
title: `请求出错,异常代码(${code})`,
content: message
})
}
// 调用返回成功需要注入的变量
success(handle) {
this.successHandle = handle
return this
}
exception(handle) {
this.exceptionHandle = handle
return this
}
// 调用异常需要注入的变量
error(handle) {
this.errorHandle = handle
return this
}
end(handle) {
this.endHandle = handle;
}
}
class HttpRequest {
_axios = null
constructor() {
this._axios = axios.create(config)
}
// 返回初始化过后的axios
getAxios() {
return this._axios
}
setBaseURL(baseURL) {
config.baseURL = baseURL
}
execute(requestConfig) {
let _config = {
baseURL: config.baseURL,
...requestConfig
}
_config.headers = _config.headers || {};
_config.headers[contants.HEADER_MAGIC_TOKEN] = contants.HEADER_MAGIC_TOKEN_VALUE
return this._axios.request(_config);
}
processError(error) {
if (error.response) {
modal.magicAlert({
title: `请求出错HttpStatus:(${error.response.status})`,
content: JSON.stringify(error.response.data || '') || `请求出错HttpStatus:(${error.response.status})`
})
} else {
modal.magicAlert({
title: `请求出错`,
content: error.message
})
}
console.error(error)
}
// 发送默认请求
send(url, params, newConfig) {
let requestConfig = newConfig || config || {}
requestConfig.url = url
if (requestConfig.method === 'post') {
requestConfig.data = params
} else {
requestConfig.params = params
}
requestConfig.baseURL = config.baseURL
let httpResponse = new HttpResponse()
let successed = false;
let processResult = (data, response) => {
if(data instanceof Blob){
successed = true
httpResponse.successHandle && httpResponse.successHandle(data, response)
} else if (data.code === 1) {
successed = true
httpResponse.successHandle && httpResponse.successHandle(data.data, response)
} else {
if (data.code === 401) {
bus.$emit('showLogin')
}
httpResponse.exceptionHandle && httpResponse.exceptionHandle(data.code, data.message, response)
}
}
this.execute(requestConfig)
.then(response => {
let data = response.data
let isJson = response.headers['content-type'] && response.headers['content-type'].startsWith('application/json')
if(data instanceof Blob && isJson){
let reader = new FileReader()
reader.readAsText(data)
reader.onload = function() {
try{
data = JSON.parse(this.result)
processResult(data, response)
}catch(e){
console.error(e);
processResult(data, response)
}
}
return;
}
processResult(data, response)
})
.catch((error) => {
if (typeof httpResponse.errorHandle === 'function') {
httpResponse.errorHandle(error.response.data, error.response, error)
} else {
this.processError(error)
}
})
.finally(() => {
if (typeof httpResponse.endHandle === 'function') {
httpResponse.endHandle(successed)
}
})
return httpResponse
}
}
export default new HttpRequest()
@font-face {
font-family: "magic-iconfont"; /* Project id 1928593 */
src: url('iconfont.ttf?t=1628077606269') format('truetype');
}
.ma-icon {
font-family: "magic-iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.ma-icon-lock:before {
content: "\e64f";
}
.ma-icon-unlock:before {
content: "\e783";
}
.ma-icon-collapse:before {
content: "\e609";
}
.ma-icon-expand:before {
content: "\efad";
}
.ma-icon-mysql:before {
content: "\e61e";
}
.ma-icon-postgresql:before {
content: "\e603";
}
.ma-icon-oracle:before {
content: "\e7c8";
}
.ma-icon-event:before {
content: "\e664";
}
.ma-icon-sql-server:before {
content: "\e605";
}
.ma-icon-clickhouse:before {
content: "\ec35";
}
.ma-icon-todo:before {
content: "\e602";
}
.ma-icon-push:before {
content: "\e79d";
}
.ma-icon-copy:before {
content: "\ec7a";
}
.ma-icon-move:before {
content: "\e727";
}
.ma-icon-update:before {
content: "\e7e4";
}
.ma-icon-logout:before {
content: "\e65a";
}
.ma-icon-datasource:before {
content: "\e615";
}
.ma-icon-table:before {
content: "\e619";
}
.ma-icon-upload:before {
content: "\e658";
}
.ma-icon-download:before {
content: "\e659";
}
.ma-icon-http-api:before {
content: "\e6e3";
}
.ma-icon-function:before {
content: "\e604";
}
.ma-icon-search:before {
content: "\e608";
}
.ma-icon-refresh:before {
content: "\e747";
}
.ma-icon-ascending:before {
content: "\e7a7";
}
.ma-icon-descending:before {
content: "\e7a8";
}
.ma-icon-group-add:before {
content: "\e6cd";
}
.ma-icon-folding:before {
content: "\e647";
}
.ma-icon-minus:before {
content: "\e68a";
}
.ma-icon-plus:before {
content: "\e621";
}
.ma-icon-api:before {
content: "\e700";
}
.ma-icon-settings:before {
content: "\e786";
}
.ma-icon-star:before {
content: "\e601";
}
.ma-icon-unfold:before {
content: "\e732";
}
.ma-icon-fold:before {
content: "\e66b";
}
.ma-icon-step-over:before {
content: "\e7b2";
}
.ma-icon-history:before {
content: "\e668";
}
.ma-icon-log:before {
content: "\efac";
}
.ma-icon-minimize:before {
content: "\e707";
}
.ma-icon-save:before {
content: "\e66c";
}
.ma-icon-close:before {
content: "\e652";
}
.ma-icon-skin:before {
content: "\e606";
}
.ma-icon-qq:before {
content: "\e635";
}
.ma-icon-help:before {
content: "\e60d";
}
.ma-icon-git:before {
content: "\e64a";
}
.ma-icon-gitee:before {
content: "\e6d6";
}
.ma-icon-delete:before {
content: "\e607";
}
.ma-icon-clear:before {
content: "\e673";
}
.ma-icon-continue:before {
content: "\e663";
}
.ma-icon-format:before {
content: "\e6c1";
}
.ma-icon-script:before {
content: "\e61d";
}
.ma-icon-arrow-right:before {
content: "\e600";
}
.ma-icon-arrow-bottom:before {
content: "\efa2";
}
.ma-icon-list:before {
content: "\e679";
}
.ma-icon-options:before {
content: "\e60f";
}
.ma-icon-debug-info:before {
content: "\efa1";
}
.ma-icon-run:before {
content: "\e626";
}
.ma-icon-parameter:before {
content: "\e6e9";
}
.ma-icon-position:before {
content: "\e60b";
}
\ No newline at end of file
@font-face{
font-family:JetBrainsMono;
src:url(JetBrainsMono-Regular.woff2) format("woff2");
font-weight:100;
font-style:normal
}
.ma-container {
font-size: 12px;
font-family: 'JetBrainsMono','Consolas', "Courier New",monospace, '微软雅黑';
letter-spacing: 0px;
overflow: auto;
display: flex;
flex-direction: column;
position: relative;
width: 100%;
height: 100%;
min-width: 1200px;
min-height: 600px;
--color: #000;
--empty-color: #505050;
--empty-key-color: #5263A0;
--background: #f2f2f2;
--empty-background: #B6B6B6;
--border-color: #cdcdcd;
--input-border-color: #bdbdbd;
--input-border-foucs-color: #0784DE;
--input-background: #fff;
--select-border-color: #808080;
--select-background: #e3e3e3;
--select-icon-background: #fff;
--select-option-background: #fff;
--select-hover-background: #e3f1fa;
--select-option-hover-background: #1A7DC4;
--select-option-hover-color: #fff;
--select-option-disabled-color: #c0c4cc;
--select-inputable-background: #fff;
--select-inputable-border: #bdbdbd;
--checkbox-background: #fff;
--checkbox-text-color: #fff;
--checkbox-border: #b0b0b0;
--checkbox-selected-border: #4F9EE3;
--checkbox-selected-background: #4F9EE3;
--scollbar-color: rgba(170, 170, 170, .7);
--scollbar-background: rgba(221, 221, 221, .3);
--scollbar-thumb-background: rgba(170, 170, 170, .4);
--scollbar-thumb-hover-background: rgba(170, 170, 170, .7);
--scollbar-scrollbar-corner-background: rgba(221, 221, 221, .3);
--header-title-color: #000;
--header-version-color: #333;
--header-default-color: #6e6e6e;
--dialog-button-hover-border-color: #99a0a5;
--dialog-button-hover-background: #E3F1FA;
--dialog-button-background: #E3E3E3;
--dialog-button-border: #ADADAD;
--dialog-border-color: #707070;
--dialog-shadow-color: #cfcfcf;
--middle-background: #F0F0F0;
--button-run-color: #59A869;
--button-hover-background: #d9d9d9;
--button-disabled-background: #BDBDBD;
--toolbox-border-right-color: #c0c0c0;
/* 左侧工具条边框颜色 */
--toolbox-border-color: #c9c9c9;
/* 图标颜色 */
--icon-color: #6e6e6e;
/* DEBUG图标颜色 */
--icon-debug-color: #59A869;
--icon-step-color: #389FD6;
/* 选中时背景颜色 */
--selected-background: #bdbdbd;
/* 悬浮时背景颜色 */
--hover-background: #d9d9d9;
/* 左侧工具条列表悬浮时背景颜色 */
--toolbox-list-hover-background: #d4d4d4;
--toolbox-background: #fff;
/* 左侧工具条列表选中时背景颜色 */
--toolbox-list-selected-background: #d4d4d4;
/* 左侧工具条列表中图标的颜色 */
--toolbox-list-icon-color: #aeb9c0;
/* 左侧工具条列表中span的文字颜色 */
--toolbox-list-span-color: #999;
/* 左侧工具条列表中label的文字颜色 */
--toolbox-list-label-color: #000;
/* 左侧工具条列表中箭头图标的颜色 */
--toolbox-list-arrow-color: #b3b3b3;
/* 左侧工具条列表中头部图标的颜色 */
--toolbox-list-header-icon-color: #7f7f7f;
/* 中间选项卡边框颜色 */
--tab-bar-border-color: #c9c9c9;
/* 底部状态条边框颜色 */
--footer-border-color: #919191;
/* 表格边框颜色*/
--table-col-border-color: #e5e5e5;
--table-row-border-color: #c0c0c0;
--table-even-background: #F2F5F9;
--table-hover-color: #fff;
--table-hover-background: #1A7DC4;
--breakpoints-background: #db5860;
--debug-line-background: #2154A6;
--breakpoint-line-background: #FAEAE6;
--todo-color: #008DDE;
--history-select-background: #1A7DC4;
--history-select-color: #fff;
--text-number-color: #0000FF;
--text-string-color: #008000;
--text-boolean-color: #000080;
--text-default-color: #000000;
--text-key-color: #660e7a;
--suggest-hover-background: #D6EBFF;
--suggest-hover-color: #000;
--statusbar-em-color: #007f31;
--run-log-background: #fff;
/* 日志级别颜色 */
--log-color-info: #00cd00;
--log-color-warn: #A66F00;
--log-color-debug: #00cccc;
--log-color-error: #cd0000;
--log-color-trace: #0000EE;
--log-color-cyan: #00CCCC;
--log-color-link: #006DCC;
scrollbar-color: var(--scollbar-color) var(--scollbar-color);
scrollbar-width: thin;
outline: 0;
}
.ma-container pre,
.ma-container .monaco-editor{
font-family: 'JetBrainsMono','Consolas', "Courier New",monospace, '微软雅黑';
}
.ma-container * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.ma-container label {
font-weight: normal;
}
.ma-container .ma-logo,
.ma-container .ma-dialog-wrapper .ma-dialog .ma-dialog-header {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA/1BMVEUIfPoENm0CHz8CIUMAAAAAAwYqKioaGhoQEBAsLCwQEBAJCQkrKysDAwMCAgL6+vr19fUWFhaJiYn8/PylpaWAgID8/PxmZmYSEhLk5ORJSUnLy8vk5OSgoKAFBQXk5OS7u7upqakmJiaqqqqDg4Pp6em5ubmQkJBNTU3x8fF4eHjn5+cBAQE5OTlwcHDm5uafn5/Nzc1+fn6lpaX09PTs7OwpKSk5OTn8/PwsLCz5+flHR0fV1dVsbGzY2NhwcHAMDAzu7u5gYGAMDAwJCQkAAAAHBwcGBgYHBwcEBAQHBwcGBgZtbGyvrq5ERERLSkp5eHgvLy8CIkQABAgABw6+8utZAAAAAWJLR0QEj2jZUQAAAAd0SU1FB+UIFBENHjcTIfEAAACNSURBVBjTY2DAAIxMSIAZKMDEggRYwQJs7CwcnFws3Dy8EAE+fgFBIWERFlExcYiAhLCklLSMrJy8giJEQElYWUVVTV1DU0tbByygK6ynb2BoZGxiKmwGFjC3sLSytrG1s3dwdILY4szi4urm7uHJ4uXNitUdPr5g4AcX8A8Ag0CES9G0BAUjgRBMzwMA2+sWGs+mksMAAAAuelRYdGRhdGU6Y3JlYXRlAAAImTMyMDLUNbDQNTIIMbC0MjS2MjbQNrCwMjAAAEGIBQ9X33PQAAAALnpUWHRkYXRlOm1vZGlmeQAACJkzMjAy1DWw0DUyCDGwtDI0tjI20DawsDIwAABBiAUPfuDbWAAAAABJRU5ErkJggg==);
}
.ma-container input::-webkit-input-placeholder {
/* WebKit browsers */
font-size: 12px;
}
.ma-container input::-moz-placeholder {
/* Mozilla Firefox 19+ */
font-size: 12px;
}
.ma-container input:-ms-input-placeholder {
/* Internet Explorer 10+ */
font-size: 12px;
}
.ma-container * {
scrollbar-color: var(--scollbar-thumb-background) var(--scollbar-thumb-background);
scrollbar-track-color: var(--scollbar-thumb-background);
-ms-scrollbar-track-color: var(--scollbar-thumb-background);
scrollbar-width: thin;
}
.ma-container *::-webkit-scrollbar {
width: 7px;
height: 7px;
background: var(--scollbar-background);
}
.ma-container *::-webkit-scrollbar-thumb {
border-radius: 5px;
background: var(--scollbar-thumb-background);
}
.ma-container *::-webkit-scrollbar-thumb:hover {
background: var(--scollbar-thumb-hover-background);
}
.ma-container *::-webkit-scrollbar-corner {
background: var(--scollbar-scrollbar-corner-background);
}
.ma-container .not-select {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
user-select: none;
}
.ma-container .monaco-editor .margin-view-overlays .codicon-folding-expanded,
.ma-container .monaco-editor .margin-view-overlays .codicon-folding-collapsed {
margin-left: 12px !important;
}
.ma-container ul li {
list-style: none;
}
.ma-container .breakpoints {
background: var(--breakpoints-background);
width: 10px !important;
height: 10px !important;
right: 0px !important;
margin-left: 12px;
top: 5px;
border-radius: 5px;
}
.ma-container .debug-line {
background: var(--debug-line-background);
color: #fff !important;
}
.ma-container .breakpoint-line {
background: var(--breakpoint-line-background);
}
.ma-icon.ma-icon-http-api{
color: #87CEE7 !important;
}
.ma-icon.ma-icon-function{
color: #F3C373 !important;
}
.ma-container .ma-button {
height: 22px;
line-height: 22px;
background: var(--dialog-button-background);
text-align: center;
padding: 0 15px;
border: 1px solid var(--dialog-button-border);
outline: 0;
cursor: pointer;
color: var(--color);
}
.ma-container .ma-button.active,
.ma-container .ma-button:hover {
background: var(--dialog-button-hover-background);
border-color: var(--dialog-button-hover-border-color);
}
.ma-request-wrapper {
background: var(--background);
height: 100%;
width: 100%;
position: relative;
}
.ma-api-info {
padding: 5px;
border-bottom: 1px solid var(--tab-bar-border-color);
display: flex;
}
.ma-api-info * {
display: inline-block;
}
.ma-api-info label {
width: 75px;
text-align: right;
padding: 0 5px;
height: 22px;
line-height: 22px;
}
.ma-api-info > .ma-select {
width: 80px;
}
.ma-request-wrapper > div:not(.ma-api-info) {
position: absolute;
top: 33px;
bottom: 0px;
width: 100%;
overflow: hidden;
display: inline-block;
}
.ma-request-wrapper > div > h3 {
color: var(--color);
font-size: 12px;
font-weight: inherit;
height: 24px;
line-height: 24px;
text-align: center;
border-bottom: 1px solid var(--tab-bar-border-color);
}
.ma-table-request-row {
display: flex;
}
.ma-container .monaco-list .monaco-list-row.focused{
background-color: var(--suggest-hover-background) !important;
color: var(--suggest-hover-color) !important;
}
.ma-container .monaco-list-row.focused .monaco-highlighted-label .highlight{
color: #0097fb !important
}
.ma-container .ma-status-container em,.ma-event .ma-content em{
color: var(--statusbar-em-color);
font-style: normal;
font-weight: bold;
}
.ma-log pre span.log-INFO{
color: var(--log-color-info);
}
.ma-log pre span.log-DEBUG{
color: var(--log-color-debug);
}
.ma-log pre span.log-ERROR{
color: var(--log-color-error);
}
.ma-log pre span.log-WARN{
color: var(--log-color-warn);
}
.ma-log pre span.log-TRACE{
color: var(--log-color-trace);
}
.ma-log pre span.log-cyan{
color: var(--log-color-cyan);
}
.ma-log pre a.log-link{
color: var(--log-color-link);
}
/** 旋转特效 **/
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
\ No newline at end of file
<template>
<div class="ma-bottom-content-item">
<div class="not-select">
<label>{{ title }}</label>
<span v-for="button in buttons" :key="'ma_button_' + button.name" :title="button.name"><i
:class="'ma-icon ma-icon' + button.icon"></i></span>
<span title="最小化" @click="$emit('update:selectedTab', null)"><i class="ma-icon ma-icon-minimize"></i></span>
</div>
<div class="ma-item-body">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: 'MagicBottomPanel',
props: {
title: String,
buttons: Array,
},
}
</script>
<style scoped>
.ma-bottom-content-item {
background: #fff;
overflow: auto;
height: 100%;
position: relative;
color: var(--color);
}
.ma-bottom-content-item > div:first-child {
background: var(--background);
border-bottom: 1px solid var(--tab-bar-border-color);
height: 24px;
line-height: 24px;
padding-left: 10px;
}
.ma-bottom-content-item > div:first-child > span {
color: var(--icon-color);
cursor: pointer;
padding: 0 3px;
display: inline-block;
height: 24px;
line-height: 24px;
}
.ma-bottom-content-item > div:first-child > span:last-child {
float: right;
margin-right: 12px;
}
.ma-bottom-content-item .ma-item-body {
position: absolute;
top: 24px;
bottom: 0px;
width: 100%;
overflow: auto;
}
</style>
<template>
<div class="ma-checkbox" @click.stop="e=>$emit('click',e)">
<input :id="cboId" ref="checkbox" type="checkbox" @change="onChange" :checked="value"/>
<label :for="cboId" :class="{ checkedHalf: checkedHalf&&value }"/>
</div>
</template>
<script>
export default {
name: 'MagicCheckbox',
props: {
value: {
type: [Number,Boolean],
default: ()=> false
},
checkedHalf: {
type: Boolean,
default: false
}
},
data() {
return {
cboId: new Date().getTime() + '' + Math.floor(Math.random() * 1000)
}
},
mounted() {
},
methods: {
onChange() {
this.$emit('update:value', this.$refs.checkbox.checked);
this.$emit('change', this.$refs.checkbox.checked);
}
},
}
</script>
<style scoped>
.ma-checkbox {
width: 100%;
height: 100%;
text-align: center;
}
.ma-checkbox input {
display: none;
}
.ma-checkbox input + label {
position: relative;
color: #c9c9c9;
font-size: 12px;
height: 24px;
line-height: 24px;
width: 24px;
user-select: none;
display: inline-block;
}
.ma-checkbox input + label::after {
display: inline-block;
background-color: var(--checkbox-background);
border: 1px solid var(--checkbox-border);
content: '';
width: 16px;
height: 16px;
line-height: 16px;
position: absolute;
top: 2px;
left: 3px;
text-align: center;
font-size: 12px;
color: var(--checkbox-text-color);
}
.ma-checkbox input:checked + label::after {
content: "\2714";
background-color: var(--checkbox-selected-background);
border-color: var(--checkbox-selected-border);
}
.ma-checkbox input+ label.checkedHalf::after {
content: "\2501";
}
</style>
\ No newline at end of file
export const SUBMENU_X_OFFSET = 3;
export const SUBMENU_Y_OFFSET = 0;
export const SUBMENU_OPEN_TREND_LEFT = "left";
export const SUBMENU_OPEN_TREND_RIGHT = "right";
export const COMPONENT_NAME = "magic-contextmenu-submenu";
import Vue from 'vue';
import Contextmenu from "./Contextmenu";
import Submenu from "./Submenu";
import {COMPONENT_NAME} from "./constant";
const ContextmenuConstructor = Vue.extend(Contextmenu);
Vue.component(COMPONENT_NAME, Submenu);
function install(Vue) {
let lastInstance = null;
const ContextmenuProxy = function (options) {
let instance = new ContextmenuConstructor();
instance.menus = options.menus;
instance.position.x = options.x || 0;
instance.position.y = options.y || 0;
if (options.event) {
instance.position.x = options.event.clientX;
instance.position.y = options.event.clientY;
}
instance.customClass = options.customClass;
options.minWidth && (instance.style.minWidth = options.minWidth);
options.zIndex && (instance.style.zIndex = options.zIndex);
instance.destroy = options.destroy;
ContextmenuProxy.destroy();
lastInstance = instance;
instance.$mount();
};
ContextmenuProxy.destroy = function (options) {
if (lastInstance) {
lastInstance.$destroy();
lastInstance = null;
}
};
Vue.prototype.$magicContextmenu = ContextmenuProxy;
}
export default {
install
};
export function hasClass(el, className) {
if (!className) {
return true;
}
if (!el || !el.className || typeof el.className !== 'string') {
return false;
}
for (let cn of el.className.split(/\s+/)) {
if (cn === className) {
return true;
}
}
return false;
}
export function getElementsByClassName(className) {
let els = [];
for (let el of document.getElementsByClassName(className) || []) {
els.push(el);
}
return els;
}
<template>
<div v-if="item.level > 0">
<span v-for="index in item.level - 1" :key="index">
<img :src="imgLine" v-if="indentLevel[index] != 1"/>
<img :src="imgIndent" style="width: 16px;height: 16px;" v-else/>
</span>
<img :src="index + 1 == dataLength ? imgEnd : img"/>
</div>
</template>
<script>
export default {
name: 'MagicJsonTreeFormat',
props: {
item: {
type: Object,
required: true
},
index: Number,
dataLength: Number,
indentLevel: Array
},
data() {
return {
imgEnd: require('../../assets/images/elbow-end.gif'),
imgLine: require('../../assets/images/elbow-line.gif'),
img: require('../../assets/images/elbow.gif'),
imgIndent: require('../../assets/images/s.gif')
}
},
}
</script>
<style>
</style>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册