脚本管理/执行脚本
14
pom.xml
@ -22,8 +22,9 @@
|
||||
<alibaba.druid.version>1.1.16</alibaba.druid.version>
|
||||
<alibaba.transmittable.version>2.11.2</alibaba.transmittable.version>
|
||||
<mybatis.plus.version>3.1.0</mybatis.plus.version>
|
||||
<apache.commons.text.verion>1.6</apache.commons.text.verion>
|
||||
<apache.commons.csv.verion>1.8</apache.commons.csv.verion>
|
||||
<apache.commons.text.version>1.6</apache.commons.text.version>
|
||||
<apache.commons.csv.version>1.8</apache.commons.csv.version>
|
||||
<apache.commons.exec.version>1.3</apache.commons.exec.version>
|
||||
<commons.io.version>2.6</commons.io.version>
|
||||
<guava.version>28.2-jre</guava.version>
|
||||
<jsoup.version>1.11.3</jsoup.version>
|
||||
@ -82,12 +83,17 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>${apache.commons.text.verion}</version>
|
||||
<version>${apache.commons.text.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-csv</artifactId>
|
||||
<version>${apache.commons.csv.verion}</version>
|
||||
<version>${apache.commons.csv.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-exec</artifactId>
|
||||
<version>${apache.commons.exec.version}</version>
|
||||
</dependency>
|
||||
<!-- commons包 -->
|
||||
<dependency>
|
||||
|
@ -0,0 +1,81 @@
|
||||
package org.spiderflow.core.executor.shape;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spiderflow.context.SpiderContext;
|
||||
import org.spiderflow.core.io.ScriptResponse;
|
||||
import org.spiderflow.core.service.ScriptService;
|
||||
import org.spiderflow.core.utils.ExpressionUtils;
|
||||
import org.spiderflow.executor.ShapeExecutor;
|
||||
import org.spiderflow.model.SpiderNode;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class ScriptExecutor implements ShapeExecutor {
|
||||
|
||||
public static final String RESULT_VARIABLE = "scriptVariable";
|
||||
|
||||
public static final String SCRIPT_TIMEOUT = "scriptTimeout";
|
||||
|
||||
public static final String SCRIPT_NAME = "scriptName";
|
||||
|
||||
public static final String SCRIPT_FILE = "scriptFile";
|
||||
|
||||
public static final String SCRIPT_CHARSET = "scriptCharset";
|
||||
|
||||
public static final String SCRIPT_PARAMETER = "scriptParameter";
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ScriptExecutor.class);
|
||||
|
||||
@Autowired
|
||||
private ScriptService scriptService;
|
||||
|
||||
@Override
|
||||
public String supportShape() {
|
||||
return "script";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(SpiderNode node, SpiderContext context, Map<String, Object> variables) {
|
||||
String scriptName = node.getStringJsonValue(SCRIPT_NAME);
|
||||
String scriptFile = node.getStringJsonValue(SCRIPT_FILE);
|
||||
String parameter = node.getStringJsonValue(SCRIPT_PARAMETER);
|
||||
int timeout = NumberUtils.toInt(node.getStringJsonValue(SCRIPT_TIMEOUT,"-1"),-1);
|
||||
if(StringUtils.isBlank(scriptName) || StringUtils.isBlank(scriptFile)){
|
||||
logger.info("脚本名和脚本文件名不能为空!");
|
||||
return;
|
||||
}
|
||||
File file = scriptService.getScriptFile(scriptName, scriptFile);
|
||||
if(!file.exists()){
|
||||
logger.info("在脚本{}中找不到脚本文件{}",scriptName,scriptFile);
|
||||
return;
|
||||
}
|
||||
String charset = node.getStringJsonValue(SCRIPT_CHARSET);
|
||||
String variable = node.getStringJsonValue(RESULT_VARIABLE);
|
||||
if(parameter != null){
|
||||
try {
|
||||
Object value = ExpressionUtils.execute(parameter,variables);
|
||||
logger.info("脚本参数:{}",value);
|
||||
if(value != null){
|
||||
parameter = value.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("获取脚本参数出错",e);
|
||||
}
|
||||
}
|
||||
ScriptResponse response = scriptService.execute(scriptName, scriptFile, parameter, timeout);
|
||||
if(StringUtils.isNotBlank(charset)){
|
||||
response.charset(charset);
|
||||
}
|
||||
if(StringUtils.isNotBlank(variable)){
|
||||
variables.put(variable,response);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package org.spiderflow.core.io;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class ScriptResponse {
|
||||
|
||||
private int exitValue;
|
||||
|
||||
private ByteArrayOutputStream outputStream;
|
||||
|
||||
private ByteArrayOutputStream errorStream;
|
||||
|
||||
private String defaultCharset;
|
||||
|
||||
private Throwable throwable;
|
||||
|
||||
public ScriptResponse(int exitValue, ByteArrayOutputStream outputStream, ByteArrayOutputStream errorStream) {
|
||||
this.exitValue = exitValue;
|
||||
this.outputStream = outputStream;
|
||||
this.errorStream = errorStream;
|
||||
}
|
||||
|
||||
public ScriptResponse(int exitValue, ByteArrayOutputStream outputStream, ByteArrayOutputStream errorStream,Throwable throwable) {
|
||||
this(exitValue,outputStream,errorStream);
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
public int getExitValue() {
|
||||
return exitValue;
|
||||
}
|
||||
|
||||
public ScriptResponse charset(String charset){
|
||||
this.defaultCharset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getValue() throws UnsupportedEncodingException {
|
||||
if(errorStream == null){
|
||||
return null;
|
||||
}
|
||||
return defaultCharset == null ? outputStream.toString() : outputStream.toString(defaultCharset);
|
||||
}
|
||||
|
||||
public String getError() throws UnsupportedEncodingException {
|
||||
if(errorStream == null){
|
||||
return null;
|
||||
}
|
||||
return defaultCharset == null ? errorStream.toString() : errorStream.toString(defaultCharset);
|
||||
}
|
||||
|
||||
public String getStack(){
|
||||
return this.throwable == null ? null : ExceptionUtils.getStackTrace(this.throwable);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package org.spiderflow.core.model;
|
||||
|
||||
public class ScriptFile {
|
||||
|
||||
private String name;
|
||||
|
||||
private String path;
|
||||
|
||||
private boolean directory;
|
||||
|
||||
public ScriptFile(String name, String path, boolean directory) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
public void setDirectory(boolean directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ScriptFile{" +
|
||||
"name='" + name + '\'' +
|
||||
", path='" + path + '\'' +
|
||||
", directory=" + directory +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.spiderflow.core.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TreeNode<T> {
|
||||
|
||||
private T node;
|
||||
|
||||
private List<TreeNode<T>> children;
|
||||
|
||||
public TreeNode(T node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public T getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public void setNode(T node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public List<TreeNode<T>> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<TreeNode<T>> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public void addNode(TreeNode<T> node){
|
||||
if(this.children == null){
|
||||
this.children = new ArrayList<>();
|
||||
}
|
||||
this.children.add(node);
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
package org.spiderflow.core.service;
|
||||
|
||||
import org.apache.commons.exec.CommandLine;
|
||||
import org.apache.commons.exec.DefaultExecutor;
|
||||
import org.apache.commons.exec.ExecuteWatchdog;
|
||||
import org.apache.commons.exec.PumpStreamHandler;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spiderflow.core.io.ScriptResponse;
|
||||
import org.spiderflow.core.model.ScriptFile;
|
||||
import org.spiderflow.core.model.TreeNode;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ScriptService {
|
||||
|
||||
@Value("${spider.workspace}")
|
||||
private String workspace;
|
||||
|
||||
@Autowired
|
||||
private static Logger logger = LoggerFactory.getLogger(ScriptService.class);
|
||||
|
||||
/**
|
||||
* 保存脚本
|
||||
* @param scriptName 脚本名称
|
||||
* @param filename 文件名
|
||||
* @param content 脚本内容
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean saveScript(String scriptName,String filename,String content){
|
||||
try {
|
||||
FileUtils.write(getScriptFile(scriptName,filename),content,"UTF-8");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public ScriptResponse execute(String scriptName, String filename, String parameters, int timeout) {
|
||||
String command = "";
|
||||
if(filename.endsWith(".py")){
|
||||
command = "python ";
|
||||
}else if(filename.endsWith(".js")){
|
||||
command = "node ";
|
||||
}else{
|
||||
return new ScriptResponse(-2,null,null,null);
|
||||
}
|
||||
command+= filename;
|
||||
if(parameters != null){
|
||||
command += " " + parameters;
|
||||
}
|
||||
logger.info("准备执行命令:{}",command);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
|
||||
try{
|
||||
File directory = getScriptDirectory(scriptName);
|
||||
CommandLine commandLine = CommandLine.parse(command);
|
||||
DefaultExecutor executor = new DefaultExecutor();
|
||||
ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
|
||||
executor.setWatchdog(watchdog);
|
||||
executor.setWorkingDirectory(directory);
|
||||
PumpStreamHandler handler = new PumpStreamHandler(outputStream, errorStream);
|
||||
executor.setStreamHandler(handler);
|
||||
int value = executor.execute(commandLine);
|
||||
logger.info("命令{}执行完毕,exitValue={}",command,value);
|
||||
return new ScriptResponse(value,outputStream,errorStream);
|
||||
} catch (Exception e) {
|
||||
logger.error("执行脚本失败",e);
|
||||
return new ScriptResponse(-1,outputStream,errorStream,e);
|
||||
}
|
||||
}
|
||||
|
||||
public File getScriptFile(String scriptName,String filename){
|
||||
File file = new File(getScriptDirectory(scriptName),filename);
|
||||
File dir = file.getParentFile();
|
||||
if(!dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private File getScriptDirectory(String scriptName){
|
||||
File directory = new File(new File(workspace,"scripts"),scriptName);
|
||||
if(!directory.exists()){
|
||||
directory.mkdirs();
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
|
||||
public String read(String scriptName,String filename) throws IOException {
|
||||
File file = getScriptFile(scriptName, filename);
|
||||
if(!file.exists()){
|
||||
return null;
|
||||
}
|
||||
return FileUtils.readFileToString(file,"UTF-8");
|
||||
}
|
||||
|
||||
public boolean rename(String scriptName,String oldName,String newName){
|
||||
File directory = getScriptDirectory(scriptName);
|
||||
return new File(directory, oldName).renameTo(new File(directory,newName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除脚本
|
||||
* @param scriptName 脚本名称
|
||||
* @param filename 文件名
|
||||
*/
|
||||
public boolean removeScript(String scriptName,String filename) {
|
||||
try {
|
||||
File scriptDirectory = getScriptDirectory(scriptName);
|
||||
if(StringUtils.isBlank(filename)){
|
||||
FileUtils.deleteDirectory(scriptDirectory);
|
||||
}else{
|
||||
File file = getScriptFile(scriptName, filename);
|
||||
if(!file.exists()){
|
||||
return false;
|
||||
}
|
||||
if(file.isDirectory()){
|
||||
FileUtils.deleteDirectory(file);
|
||||
return true;
|
||||
}
|
||||
return file.delete();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> listScript(){
|
||||
String[] files = new File(workspace, "scripts").list();
|
||||
if(files != null){
|
||||
Arrays.sort(files);
|
||||
return Arrays.asList(files);
|
||||
}else{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean createScript(String scriptName){
|
||||
File file = new File(new File(workspace,"scripts"),scriptName);
|
||||
if(file.exists()){
|
||||
return false;
|
||||
}
|
||||
return file.mkdirs();
|
||||
}
|
||||
|
||||
public boolean createScriptFile(String scriptName,String file,boolean directory){
|
||||
File dir = getScriptDirectory(scriptName);
|
||||
File f = new File(dir, file);
|
||||
File parentFile = f.getParentFile();
|
||||
parentFile.mkdirs();
|
||||
try {
|
||||
return directory ? f.mkdirs() : f.createNewFile();
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环遍历
|
||||
*/
|
||||
public TreeNode<ScriptFile> list(String scriptName){
|
||||
return list(getScriptDirectory(scriptName),null,"");
|
||||
}
|
||||
|
||||
private TreeNode<ScriptFile> list(File file,TreeNode<ScriptFile> node, String parentName){
|
||||
ScriptFile scriptFile = new ScriptFile(file.getName(),parentName,file.isDirectory());
|
||||
TreeNode<ScriptFile> current = new TreeNode<>(scriptFile);
|
||||
if(node != null){
|
||||
node.addNode(current);
|
||||
}
|
||||
if(file.isDirectory()){
|
||||
File[] files = file.listFiles();
|
||||
if(files != null){
|
||||
for (File child : files) {
|
||||
list(child,current,(parentName.isEmpty() ? "" : parentName + File.separator) + child.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package org.spiderflow.controller;
|
||||
|
||||
import org.spiderflow.core.io.ScriptResponse;
|
||||
import org.spiderflow.core.model.ScriptFile;
|
||||
import org.spiderflow.core.model.TreeNode;
|
||||
import org.spiderflow.core.service.ScriptService;
|
||||
import org.spiderflow.model.JsonBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/script")
|
||||
public class ScriptController {
|
||||
|
||||
@Autowired
|
||||
private ScriptService scriptService;
|
||||
|
||||
@RequestMapping("/list")
|
||||
public JsonBean<List<String>> scripts(){
|
||||
return new JsonBean<>(scriptService.listScript());
|
||||
}
|
||||
|
||||
@RequestMapping("/files")
|
||||
public JsonBean<TreeNode<ScriptFile>> files(String script){
|
||||
return new JsonBean<>(scriptService.list(script));
|
||||
}
|
||||
@RequestMapping("/create")
|
||||
public JsonBean<Boolean> create(String scriptName){
|
||||
return new JsonBean<>(scriptService.createScript(scriptName));
|
||||
}
|
||||
|
||||
@RequestMapping("/create/file")
|
||||
public JsonBean<Boolean> createFile(String scriptName,String file,String dir){
|
||||
return new JsonBean<>(scriptService.createScriptFile(scriptName,file,"1".equalsIgnoreCase(dir)));
|
||||
}
|
||||
|
||||
@RequestMapping("/remove/file")
|
||||
public JsonBean<Boolean> remove(String scriptName,String file){
|
||||
return new JsonBean<>(scriptService.removeScript(scriptName,file));
|
||||
}
|
||||
|
||||
@RequestMapping("/rename/file")
|
||||
public JsonBean<Boolean> rename(String scriptName,String file,String newFile){
|
||||
return new JsonBean<>(scriptService.rename(scriptName,file,newFile));
|
||||
}
|
||||
|
||||
@RequestMapping("/read")
|
||||
public JsonBean<String> read(String scriptName,String file) throws IOException {
|
||||
return new JsonBean<>(scriptService.read(scriptName,file));
|
||||
}
|
||||
|
||||
@RequestMapping("/save")
|
||||
public JsonBean<Boolean> save(String scriptName,String file,String content){
|
||||
return new JsonBean<>(scriptService.saveScript(scriptName,file,content));
|
||||
}
|
||||
|
||||
@RequestMapping("/test")
|
||||
public JsonBean<ScriptResponse> test(String scriptName, String file, String parameter, Integer timeout) {
|
||||
return new JsonBean<>(scriptService.execute(scriptName,file,parameter,timeout == null ? -1 : timeout));
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
<li class="layui-nav-item layui-nav-itemed"><a data-link="variables.html" title="全局变量">全局变量</a></li>
|
||||
<li class="layui-nav-item layui-nav-itemed"><a data-link="functions.html" title="自定义函数">自定义函数</a></li>
|
||||
<li class="layui-nav-item layui-nav-itemed"><a data-link="datasources.html" title="数据源管理">数据源管理</a></li>
|
||||
<li class="layui-nav-item layui-nav-itemed"><a data-link="scripts.html" title="脚本管理">脚本管理</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,399 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
function wordRegexp(words) {
|
||||
return new RegExp("^((" + words.join(")|(") + "))\\b");
|
||||
}
|
||||
|
||||
var wordOperators = wordRegexp(["and", "or", "not", "is"]);
|
||||
var commonKeywords = ["as", "assert", "break", "class", "continue",
|
||||
"def", "del", "elif", "else", "except", "finally",
|
||||
"for", "from", "global", "if", "import",
|
||||
"lambda", "pass", "raise", "return",
|
||||
"try", "while", "with", "yield", "in"];
|
||||
var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
|
||||
"classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
|
||||
"enumerate", "eval", "filter", "float", "format", "frozenset",
|
||||
"getattr", "globals", "hasattr", "hash", "help", "hex", "id",
|
||||
"input", "int", "isinstance", "issubclass", "iter", "len",
|
||||
"list", "locals", "map", "max", "memoryview", "min", "next",
|
||||
"object", "oct", "open", "ord", "pow", "property", "range",
|
||||
"repr", "reversed", "round", "set", "setattr", "slice",
|
||||
"sorted", "staticmethod", "str", "sum", "super", "tuple",
|
||||
"type", "vars", "zip", "__import__", "NotImplemented",
|
||||
"Ellipsis", "__debug__"];
|
||||
CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
|
||||
|
||||
function top(state) {
|
||||
return state.scopes[state.scopes.length - 1];
|
||||
}
|
||||
|
||||
CodeMirror.defineMode("python", function(conf, parserConf) {
|
||||
var ERRORCLASS = "error";
|
||||
|
||||
var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
|
||||
// (Backwards-compatibility with old, cumbersome config system)
|
||||
var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
|
||||
parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/]
|
||||
for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)
|
||||
|
||||
var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
|
||||
|
||||
var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
|
||||
if (parserConf.extra_keywords != undefined)
|
||||
myKeywords = myKeywords.concat(parserConf.extra_keywords);
|
||||
|
||||
if (parserConf.extra_builtins != undefined)
|
||||
myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
|
||||
|
||||
var py3 = !(parserConf.version && Number(parserConf.version) < 3)
|
||||
if (py3) {
|
||||
// since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
|
||||
var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
|
||||
myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
|
||||
myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
|
||||
var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
|
||||
} else {
|
||||
var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
|
||||
myKeywords = myKeywords.concat(["exec", "print"]);
|
||||
myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
|
||||
"file", "intern", "long", "raw_input", "reduce", "reload",
|
||||
"unichr", "unicode", "xrange", "False", "True", "None"]);
|
||||
var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
|
||||
}
|
||||
var keywords = wordRegexp(myKeywords);
|
||||
var builtins = wordRegexp(myBuiltins);
|
||||
|
||||
// tokenizers
|
||||
function tokenBase(stream, state) {
|
||||
var sol = stream.sol() && state.lastToken != "\\"
|
||||
if (sol) state.indent = stream.indentation()
|
||||
// Handle scope changes
|
||||
if (sol && top(state).type == "py") {
|
||||
var scopeOffset = top(state).offset;
|
||||
if (stream.eatSpace()) {
|
||||
var lineOffset = stream.indentation();
|
||||
if (lineOffset > scopeOffset)
|
||||
pushPyScope(state);
|
||||
else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#")
|
||||
state.errorToken = true;
|
||||
return null;
|
||||
} else {
|
||||
var style = tokenBaseInner(stream, state);
|
||||
if (scopeOffset > 0 && dedent(stream, state))
|
||||
style += " " + ERRORCLASS;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
return tokenBaseInner(stream, state);
|
||||
}
|
||||
|
||||
function tokenBaseInner(stream, state) {
|
||||
if (stream.eatSpace()) return null;
|
||||
|
||||
// Handle Comments
|
||||
if (stream.match(/^#.*/)) return "comment";
|
||||
|
||||
// Handle Number Literals
|
||||
if (stream.match(/^[0-9\.]/, false)) {
|
||||
var floatLiteral = false;
|
||||
// Floats
|
||||
if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
|
||||
if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; }
|
||||
if (stream.match(/^\.\d+/)) { floatLiteral = true; }
|
||||
if (floatLiteral) {
|
||||
// Float literals may be "imaginary"
|
||||
stream.eat(/J/i);
|
||||
return "number";
|
||||
}
|
||||
// Integers
|
||||
var intLiteral = false;
|
||||
// Hex
|
||||
if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;
|
||||
// Binary
|
||||
if (stream.match(/^0b[01_]+/i)) intLiteral = true;
|
||||
// Octal
|
||||
if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;
|
||||
// Decimal
|
||||
if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) {
|
||||
// Decimal literals may be "imaginary"
|
||||
stream.eat(/J/i);
|
||||
// TODO - Can you have imaginary longs?
|
||||
intLiteral = true;
|
||||
}
|
||||
// Zero by itself with no other piece of number.
|
||||
if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
|
||||
if (intLiteral) {
|
||||
// Integer literals may be "long"
|
||||
stream.eat(/L/i);
|
||||
return "number";
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Strings
|
||||
if (stream.match(stringPrefixes)) {
|
||||
var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
|
||||
if (!isFmtString) {
|
||||
state.tokenize = tokenStringFactory(stream.current(), state.tokenize);
|
||||
return state.tokenize(stream, state);
|
||||
} else {
|
||||
state.tokenize = formatStringFactory(stream.current(), state.tokenize);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < operators.length; i++)
|
||||
if (stream.match(operators[i])) return "operator"
|
||||
|
||||
if (stream.match(delimiters)) return "punctuation";
|
||||
|
||||
if (state.lastToken == "." && stream.match(identifiers))
|
||||
return "property";
|
||||
|
||||
if (stream.match(keywords) || stream.match(wordOperators))
|
||||
return "keyword";
|
||||
|
||||
if (stream.match(builtins))
|
||||
return "builtin";
|
||||
|
||||
if (stream.match(/^(self|cls)\b/))
|
||||
return "variable-2";
|
||||
|
||||
if (stream.match(identifiers)) {
|
||||
if (state.lastToken == "def" || state.lastToken == "class")
|
||||
return "def";
|
||||
return "variable";
|
||||
}
|
||||
|
||||
// Handle non-detected items
|
||||
stream.next();
|
||||
return ERRORCLASS;
|
||||
}
|
||||
|
||||
function formatStringFactory(delimiter, tokenOuter) {
|
||||
while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
|
||||
delimiter = delimiter.substr(1);
|
||||
|
||||
var singleline = delimiter.length == 1;
|
||||
var OUTCLASS = "string";
|
||||
|
||||
function tokenNestedExpr(depth) {
|
||||
return function(stream, state) {
|
||||
var inner = tokenBaseInner(stream, state)
|
||||
if (inner == "punctuation") {
|
||||
if (stream.current() == "{") {
|
||||
state.tokenize = tokenNestedExpr(depth + 1)
|
||||
} else if (stream.current() == "}") {
|
||||
if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)
|
||||
else state.tokenize = tokenString
|
||||
}
|
||||
}
|
||||
return inner
|
||||
}
|
||||
}
|
||||
|
||||
function tokenString(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
stream.eatWhile(/[^'"\{\}\\]/);
|
||||
if (stream.eat("\\")) {
|
||||
stream.next();
|
||||
if (singleline && stream.eol())
|
||||
return OUTCLASS;
|
||||
} else if (stream.match(delimiter)) {
|
||||
state.tokenize = tokenOuter;
|
||||
return OUTCLASS;
|
||||
} else if (stream.match('{{')) {
|
||||
// ignore {{ in f-str
|
||||
return OUTCLASS;
|
||||
} else if (stream.match('{', false)) {
|
||||
// switch to nested mode
|
||||
state.tokenize = tokenNestedExpr(0)
|
||||
if (stream.current()) return OUTCLASS;
|
||||
else return state.tokenize(stream, state)
|
||||
} else if (stream.match('}}')) {
|
||||
return OUTCLASS;
|
||||
} else if (stream.match('}')) {
|
||||
// single } in f-string is an error
|
||||
return ERRORCLASS;
|
||||
} else {
|
||||
stream.eat(/['"]/);
|
||||
}
|
||||
}
|
||||
if (singleline) {
|
||||
if (parserConf.singleLineStringErrors)
|
||||
return ERRORCLASS;
|
||||
else
|
||||
state.tokenize = tokenOuter;
|
||||
}
|
||||
return OUTCLASS;
|
||||
}
|
||||
tokenString.isString = true;
|
||||
return tokenString;
|
||||
}
|
||||
|
||||
function tokenStringFactory(delimiter, tokenOuter) {
|
||||
while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
|
||||
delimiter = delimiter.substr(1);
|
||||
|
||||
var singleline = delimiter.length == 1;
|
||||
var OUTCLASS = "string";
|
||||
|
||||
function tokenString(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
stream.eatWhile(/[^'"\\]/);
|
||||
if (stream.eat("\\")) {
|
||||
stream.next();
|
||||
if (singleline && stream.eol())
|
||||
return OUTCLASS;
|
||||
} else if (stream.match(delimiter)) {
|
||||
state.tokenize = tokenOuter;
|
||||
return OUTCLASS;
|
||||
} else {
|
||||
stream.eat(/['"]/);
|
||||
}
|
||||
}
|
||||
if (singleline) {
|
||||
if (parserConf.singleLineStringErrors)
|
||||
return ERRORCLASS;
|
||||
else
|
||||
state.tokenize = tokenOuter;
|
||||
}
|
||||
return OUTCLASS;
|
||||
}
|
||||
tokenString.isString = true;
|
||||
return tokenString;
|
||||
}
|
||||
|
||||
function pushPyScope(state) {
|
||||
while (top(state).type != "py") state.scopes.pop()
|
||||
state.scopes.push({offset: top(state).offset + conf.indentUnit,
|
||||
type: "py",
|
||||
align: null})
|
||||
}
|
||||
|
||||
function pushBracketScope(stream, state, type) {
|
||||
var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1
|
||||
state.scopes.push({offset: state.indent + hangingIndent,
|
||||
type: type,
|
||||
align: align})
|
||||
}
|
||||
|
||||
function dedent(stream, state) {
|
||||
var indented = stream.indentation();
|
||||
while (state.scopes.length > 1 && top(state).offset > indented) {
|
||||
if (top(state).type != "py") return true;
|
||||
state.scopes.pop();
|
||||
}
|
||||
return top(state).offset != indented;
|
||||
}
|
||||
|
||||
function tokenLexer(stream, state) {
|
||||
if (stream.sol()) state.beginningOfLine = true;
|
||||
|
||||
var style = state.tokenize(stream, state);
|
||||
var current = stream.current();
|
||||
|
||||
// Handle decorators
|
||||
if (state.beginningOfLine && current == "@")
|
||||
return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS;
|
||||
|
||||
if (/\S/.test(current)) state.beginningOfLine = false;
|
||||
|
||||
if ((style == "variable" || style == "builtin")
|
||||
&& state.lastToken == "meta")
|
||||
style = "meta";
|
||||
|
||||
// Handle scope changes.
|
||||
if (current == "pass" || current == "return")
|
||||
state.dedent += 1;
|
||||
|
||||
if (current == "lambda") state.lambda = true;
|
||||
if (current == ":" && !state.lambda && top(state).type == "py")
|
||||
pushPyScope(state);
|
||||
|
||||
if (current.length == 1 && !/string|comment/.test(style)) {
|
||||
var delimiter_index = "[({".indexOf(current);
|
||||
if (delimiter_index != -1)
|
||||
pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
|
||||
|
||||
delimiter_index = "])}".indexOf(current);
|
||||
if (delimiter_index != -1) {
|
||||
if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
|
||||
else return ERRORCLASS;
|
||||
}
|
||||
}
|
||||
if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
|
||||
if (state.scopes.length > 1) state.scopes.pop();
|
||||
state.dedent -= 1;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
var external = {
|
||||
startState: function(basecolumn) {
|
||||
return {
|
||||
tokenize: tokenBase,
|
||||
scopes: [{offset: basecolumn || 0, type: "py", align: null}],
|
||||
indent: basecolumn || 0,
|
||||
lastToken: null,
|
||||
lambda: false,
|
||||
dedent: 0
|
||||
};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
var addErr = state.errorToken;
|
||||
if (addErr) state.errorToken = false;
|
||||
var style = tokenLexer(stream, state);
|
||||
|
||||
if (style && style != "comment")
|
||||
state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style;
|
||||
if (style == "punctuation") style = null;
|
||||
|
||||
if (stream.eol() && state.lambda)
|
||||
state.lambda = false;
|
||||
return addErr ? style + " " + ERRORCLASS : style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
if (state.tokenize != tokenBase)
|
||||
return state.tokenize.isString ? CodeMirror.Pass : 0;
|
||||
|
||||
var scope = top(state), closing = scope.type == textAfter.charAt(0)
|
||||
if (scope.align != null)
|
||||
return scope.align - (closing ? 1 : 0)
|
||||
else
|
||||
return scope.offset - (closing ? hangingIndent : 0)
|
||||
},
|
||||
|
||||
electricInput: /^\s*[\}\]\)]$/,
|
||||
closeBrackets: {triples: "'\""},
|
||||
lineComment: "#",
|
||||
fold: "indent"
|
||||
};
|
||||
return external;
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-python", "python");
|
||||
|
||||
var words = function(str) { return str.split(" "); };
|
||||
|
||||
CodeMirror.defineMIME("text/x-cython", {
|
||||
name: "python",
|
||||
extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+
|
||||
"extern gil include nogil property public "+
|
||||
"readonly struct union DEF IF ELIF ELSE")
|
||||
});
|
||||
|
||||
});
|
@ -587,6 +587,11 @@ $(function(){
|
||||
image : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAD1ElEQVRYR7WXUXIaRxCGv4YqR2+WH2zxZukEUU5gdAKjExhfILAnMD7BohMYn8D4BEYnsHQCobfd5CHkDVIFnerZmc0sLAJjMlVUSbszPX93//13r7DnOk319JcGb0TpAOfRzyxMw0+F8WLF7SyR2T6mZdemVqrnCB+A7sZe5dE9E17X2BmhfMwSMXBb11YAzmPhg0DfnVYeFcYKkz8SGddZfJVqR6At0ClBCYP5ipttEakF4C//JnCpyt/AIE9kuCta8fuzVA34QITnCncL5aoOxAaAl6leNgS7/FSVrwvo7pvPdYDOERiJ8FZhtlKu/kzkLt5XAeA9f/CX3+SJFOH/yXWW6lCEnoFYKBexQyWAtbAf7fKAPQJRSUcJoNygfM0TsVI7+jpLdWzp8NUxKAoI8KX2YIRbwPmhOd+F2HNiasREubASDQBGCO9USdbZ3kq1TRPNfpfb9Qsc8CavWfIY17sRudnk+XzJ/bozrVQHTleUz1kiXTFUJ8JfVudZIqZwbjkjwhevePZoulSuA4tLQ36/wjDvS+IjOkF4g3KVJTKpAT41nZgrL8TEoyF8UaVCvFaqbpOVooEQ4Ver57wvv7moCN8sZVKIU8eH9X2WyKiV6i4ALuIr5VpaqZb/BIWLOHGfJ3LpvXKAsr7YGRdGM2Bn1p3YBSDstzSYMYfWDIdQBQ9RbrNE2h6A9YLzLJFBlEcX4vX9uwA4e0NVs28ASs/iXLkN1uWKPFYayrEAWErFI6kQ0IAEXbC/rQmJchMIdRQA3nEHIJCrhq0mFt2ysxXt9TgpKAFsSUElHakaiE+m5XlfXhwlAua4cl9LwjpFi8l6LACBhBtl6EUo9Wo1isUlLkMj6BJmTaHnUuWrZlcVVMqwTog8gO+BG14tv7syjHTApNuGFhMVF7X/OLK/EJVSDNOsLxdR+7wz9aukw+u3CZUrIWsq0QqTzwmMnRRXX8aaUpb+1mbkgMHQZNa3zfEc+qG5mPiY/juJLuR6YvPjEjoN6LrIrIGzAWejGfn82uT7UDex1BHy0GfxxFVpxx6E03cTnbwv14de8tS5iJxOT3xkiyN+WJj4kP5/I5lyv4B2SOXmUApuYon7+89GIxr3Niau+rHcCFWAGC+U94eOaD7nn+xDxWaHFbSfHMuDp5V0wAxhkPfk5kciYWxXoedH/ErYYztPfpqd2GdZ8V1oa2oRQZjkPbGy21he1N4CNkMU453ycQ7DH/o0iy276QisQgq1q64wJ5SzZPla+WznDv44Xb/J0vIM2o1CmEwJT4NSuq5mqYLpCsb/wGRf3vwLgODoY+vqQ1gAAAAASUVORK5CYII=',
|
||||
title : '执行SQL',
|
||||
desc : '执行sql,需配置数据源,sql执行结果存于变量rs中。<br/>语句类型为:select,返回:List<Map<String,Object>><br/>语句类型为:selectOne,返回:Map<String,Object><br/>语句类型为:selectInt,返回:Integer<br/>语句类型为:insert、update、delete,返回:int,批量操作返回int数组<br/>sql中变量必须用 # # 包裹,如:#${title}#'
|
||||
},{
|
||||
name : 'script',
|
||||
image : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADD0lEQVRYR92XTUhUURTH//8xyzYRJM2MuZCoFrk0iD4WFQiJRSsnF4GmIFHkvEdmUIEKRZjVm5EoDErcmS4rSgpzIX1ArcIWYSZt5vkBSUWM0zgn3nPe9NSx+XBeQRcGZuaej98999xzzyX+8aDl3xuUMonhcE54iBFdYX86thIAHk1aQbSko5SmzGld4c1UsksBBG2plFLOxxdC4krIz/N/kk8GsF9XOZTSyTICHk32gXiemCZ6NnjRMOJjJJmK8wDzXgchqNdVji+G+FsAgGA05kLNpJ8v7BA5BzCMewIiy+zQLIEjIYUD1rwzAEFpgaAKQOkSEEGbrrLVUYBkq08k5/8PUCV56Ofc4iisOAKeoLRTMBZS2GU37g3InZigd0LlYFFAymOCE9EIaqfP8ZtdLmuALZ2y5rvgFgR1AEbggk9v5HvDuDcoB0Xw2HTkQili6Isn3sBcBLVTzdQtiKwAim/Lpp+z6CZQDmA4KvBNqwzZjD4EUSnE5Qk/Lxa2S1HeavSR2APgVSyCY5PN/GgeTatCZpKEnoAYRWMXiAcF6+AbP86w5dytSR2JuyA+hGPYOaNyxpgrviFroy4zEocAvNQV7l4JQC+AoySe5efD9/kkvxjGSjRZH3bhNQTbRFA/ofKeBVbSLQXhr+iDmFf7fV1hddYA8ap2FcBZAGMkfCE/37qDcomCCxA80lUaKzVHoSbeVTRXvxdAh66weUU5kFAOyBkA18zfRKXh2PxKVIT8fGKCdsp2WxI26Qqv5+QUJCCCUgPBD6PTcWtywEVUhxQ2LHJSARc26n725LwOJCuvmfyX1SnIxEE6smbbBwzZGx5HbsN0YCwZRwE8AXljONIV7lgOyjEAtyabSZhVUFeY8LMYxBEAY69dwNMYMRx3+LvJFbzTVTY6tgUeTUpAfAJgNKAlSUNvuw+ciUBQOiBoWjYZUwBk/DCJAl32WzJewo0ELMs8ApmcIUtWsOQx4w1KgwgWNC+W+Fw+tk6d4uh8ZY+PlTxOk0XAjILRAwDGJzHsHfECgGwWngudX+XU9zD0knfOAAAAAElFTkSuQmCC',
|
||||
title : '执行脚本',
|
||||
desc : '单独执行脚本方法'
|
||||
},{
|
||||
name : 'function',
|
||||
image : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAgCAYAAACPb1E+AAAC9UlEQVRYR+2YP0zUUBjAf9+RKJPgQK6THptxERPiZOTUxEUj/tlMjCS62xoT3YARl5bJTXByBGcHISQmxhjQ6A6TPRg8JrnE62de6Z0Htne9XhMw4W1N3/u+X7//rwJgzeokygRQMs+HYSksBsr0liNrYrk6gTB3GMBiGNZ9W4al6OqiCOOqfFGYKkD1oIEVRkRwQw5l2FhyCWEMZdp3ZOqgARv6LU/1CDIPbxxZMg8rhmUxj5gcdHWw6khiJSh6+lVgAGXMd2S90/79H9czpOXpd+CsKk7FEe8fBab2whwCdeF6n/ISOFUPuLT1RFbSWLsnSMvTOVUmREJVM74tz1uVGov1C6tR9/rs2zJqefoL6FfYDpSy6SKdQDNDGkAI2yeqvKk4ci/GilMIk40aZ1w95OpIQVgWOKFQDZTLnUAzQbYCorz2HQlhW5flakmFVYFBVWYrjtiN9yEoLIkwkAa0a0jL0x8m4SLrxAKGGenqPMIDVbZrUNqfWE1Qk1AFqAc83HLkVZzrs0AGgKhQDwJG41xluVpGeB99SGKLtTx9i3LTJFUAC5u23MkFcsjTZwWYFjie5CrLUwNYRtnwHYkd+fbENHyr/eZG9als5AJphLSLqdZxL1BubzqyGFP3mkmXFNN74jtrMW8FRdgB7u4EfGiWHGXZd6QcA7iCcBEz1yQkXa7F3ID2wUeEY0HATJ9wRoVxo6SunI+N18gqCu8qtlzrVCNzaYtFVx8BVwJ4UYB5Ec4pfKrYciEOoOiqjXC1ptxv10pzcXccQBQC5VqdhaQkSGO5XN2dRWGWM13XySxKej1zBNmrBRvnYy25fyDIS1kWOdG49zNqseG92xPhcSRsqWuhgoXSn3jOFH3F71Kuaatha91RTpp7d0lhzYxQXQra3W66yO4AHL86vW+nNOpOofgI9JaZAzOAmt80p9tAmgFivlu5Aaw1ZoB2Nkglt/kHJNmSsf08lfBoU2pIy9W/14JuNCTs9e3olpRC1n8B+Qd0Dhp9ddQMugAAAABJRU5ErkJggg==',
|
||||
|
3
spider-flow-web/src/main/resources/static/js/ztree/jquery.ztree.all.min.js
vendored
Normal file
After Width: | Height: | Size: 601 B |
After Width: | Height: | Size: 580 B |
After Width: | Height: | Size: 570 B |
After Width: | Height: | Size: 762 B |
After Width: | Height: | Size: 399 B |
After Width: | Height: | Size: 710 B |
After Width: | Height: | Size: 432 B |
After Width: | Height: | Size: 534 B |
After Width: | Height: | Size: 529 B |
After Width: | Height: | Size: 467 B |
After Width: | Height: | Size: 45 B |
After Width: | Height: | Size: 381 B |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,97 @@
|
||||
/*-------------------------------------
|
||||
zTree Style
|
||||
|
||||
version: 3.5.19
|
||||
author: Hunter.z
|
||||
email: hunter.z@263.net
|
||||
website: http://code.google.com/p/jquerytree/
|
||||
|
||||
-------------------------------------*/
|
||||
|
||||
.ztree * {padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif}
|
||||
.ztree {margin:0; padding:5px; color:#333}
|
||||
.ztree li{padding:0; margin:0; list-style:none; line-height:14px; text-align:left; white-space:nowrap; outline:0}
|
||||
.ztree li ul{ margin:0; padding:0 0 0 18px}
|
||||
.ztree li ul.line{ background:url(./img/line_conn.gif) 0 0 repeat-y;}
|
||||
|
||||
.ztree li a {padding:1px 3px 0 0; margin:0; cursor:pointer; height:17px; color:#333; background-color: transparent;
|
||||
text-decoration:none; vertical-align:top; display: inline-block}
|
||||
.ztree li a:hover {text-decoration:underline}
|
||||
.ztree li a.curSelectedNode {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;}
|
||||
.ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;}
|
||||
.ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#316AC5; color:white; height:16px; border:1px #316AC5 solid;
|
||||
opacity:0.8; filter:alpha(opacity=80)}
|
||||
.ztree li a.tmpTargetNode_prev {}
|
||||
.ztree li a.tmpTargetNode_next {}
|
||||
.ztree li a input.rename {height:14px; width:80px; padding:0; margin:0;
|
||||
font-size:12px; border:1px #7EC4CC solid; *border:0px}
|
||||
.ztree li span {line-height:16px; margin-right:2px}
|
||||
.ztree li span.button {line-height:0; margin:0; width:16px; height:16px; display: inline-block; vertical-align:middle;
|
||||
border:0 none; cursor: pointer;outline:none;
|
||||
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
|
||||
background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")}
|
||||
|
||||
.ztree li span.button.chk {width:13px; height:13px; margin:0 3px 0 0; cursor: auto}
|
||||
.ztree li span.button.chk.checkbox_false_full {background-position:0 0}
|
||||
.ztree li span.button.chk.checkbox_false_full_focus {background-position:0 -14px}
|
||||
.ztree li span.button.chk.checkbox_false_part {background-position:0 -28px}
|
||||
.ztree li span.button.chk.checkbox_false_part_focus {background-position:0 -42px}
|
||||
.ztree li span.button.chk.checkbox_false_disable {background-position:0 -56px}
|
||||
.ztree li span.button.chk.checkbox_true_full {background-position:-14px 0}
|
||||
.ztree li span.button.chk.checkbox_true_full_focus {background-position:-14px -14px}
|
||||
.ztree li span.button.chk.checkbox_true_part {background-position:-14px -28px}
|
||||
.ztree li span.button.chk.checkbox_true_part_focus {background-position:-14px -42px}
|
||||
.ztree li span.button.chk.checkbox_true_disable {background-position:-14px -56px}
|
||||
.ztree li span.button.chk.radio_false_full {background-position:-28px 0}
|
||||
.ztree li span.button.chk.radio_false_full_focus {background-position:-28px -14px}
|
||||
.ztree li span.button.chk.radio_false_part {background-position:-28px -28px}
|
||||
.ztree li span.button.chk.radio_false_part_focus {background-position:-28px -42px}
|
||||
.ztree li span.button.chk.radio_false_disable {background-position:-28px -56px}
|
||||
.ztree li span.button.chk.radio_true_full {background-position:-42px 0}
|
||||
.ztree li span.button.chk.radio_true_full_focus {background-position:-42px -14px}
|
||||
.ztree li span.button.chk.radio_true_part {background-position:-42px -28px}
|
||||
.ztree li span.button.chk.radio_true_part_focus {background-position:-42px -42px}
|
||||
.ztree li span.button.chk.radio_true_disable {background-position:-42px -56px}
|
||||
|
||||
.ztree li span.button.switch {width:18px; height:18px}
|
||||
.ztree li span.button.root_open{background-position:-92px -54px}
|
||||
.ztree li span.button.root_close{background-position:-74px -54px}
|
||||
.ztree li span.button.roots_open{background-position:-92px 0}
|
||||
.ztree li span.button.roots_close{background-position:-74px 0}
|
||||
.ztree li span.button.center_open{background-position:-92px -18px}
|
||||
.ztree li span.button.center_close{background-position:-74px -18px}
|
||||
.ztree li span.button.bottom_open{background-position:-92px -36px}
|
||||
.ztree li span.button.bottom_close{background-position:-74px -36px}
|
||||
.ztree li span.button.noline_open{background-position:-92px -72px}
|
||||
.ztree li span.button.noline_close{background-position:-74px -72px}
|
||||
.ztree li span.button.root_docu{ background:none;}
|
||||
.ztree li span.button.roots_docu{background-position:-56px 0}
|
||||
.ztree li span.button.center_docu{background-position:-56px -18px}
|
||||
.ztree li span.button.bottom_docu{background-position:-56px -36px}
|
||||
.ztree li span.button.noline_docu{ background:none;}
|
||||
|
||||
.ztree li span.button.ico_open{margin-right:2px; background-position:-110px -16px; vertical-align:top; *vertical-align:middle}
|
||||
.ztree li span.button.ico_close{margin-right:2px; background-position:-110px 0; vertical-align:top; *vertical-align:middle}
|
||||
.ztree li span.button.ico_docu{margin-right:2px; background-position:-110px -32px; vertical-align:top; *vertical-align:middle}
|
||||
.ztree li span.button.edit {margin-right:2px; background-position:-110px -48px; vertical-align:top; *vertical-align:middle}
|
||||
.ztree li span.button.remove {margin-right:2px; background-position:-110px -64px; vertical-align:top; *vertical-align:middle}
|
||||
|
||||
.ztree li span.button.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle}
|
||||
|
||||
ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)}
|
||||
|
||||
span.tmpzTreeMove_arrow {width:16px; height:16px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute;
|
||||
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
|
||||
background-position:-110px -80px; background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")}
|
||||
|
||||
ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)}
|
||||
.zTreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}
|
||||
|
||||
/* level style*/
|
||||
/*.ztree li span.button.level0 {
|
||||
display:none;
|
||||
}
|
||||
.ztree li ul.level0 {
|
||||
padding:0;
|
||||
background:none;
|
||||
}*/
|
@ -0,0 +1,67 @@
|
||||
<div class="layui-tab layui-tab-fixed layui-tab-brief">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">配置</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<form class="layui-form editor-form-node">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md4">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">节点名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="value" placeholder="请输入节点名称" value="{{=d.value}}" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md4">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">结果变量</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="scriptVariable" placeholder="请输入结果变量" autocomplete="off" class="layui-input input-default" value="{{=d.data.object.scriptVariable}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md4">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">超时时间</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="scriptTimeout" placeholder="请输入超时时间" autocomplete="off" class="layui-input input-default" value="{{=d.data.object.scriptTimeout}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md4">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">脚本名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="scriptName" placeholder="请输入脚本名" autocomplete="off" class="layui-input input-default" value="{{=d.data.object.scriptName}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md4">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">脚本文件</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="scriptFile" placeholder="请输入脚本文件" autocomplete="off" class="layui-input input-default" value="{{=d.data.object.scriptFile}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-md4">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">结果编码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="scriptCharset" placeholder="请输入结果编码" autocomplete="off" class="layui-input input-default" value="{{=d.data.object.scriptCharset}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">执行参数</label>
|
||||
<div class="layui-input-block" codemirror="scriptParameter" placeholder="请输入执行参数" data-value="{{=d.data.object.scriptParameter}}"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
438
spider-flow-web/src/main/resources/static/scripts.html
Normal file
@ -0,0 +1,438 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>DataSource</title>
|
||||
<link rel="stylesheet" href="js/layui/css/layui.css" />
|
||||
<link rel="stylesheet" href="css/layui-blue.css" />
|
||||
<link rel="stylesheet" href="js/ztree/zTreeStyle/zTreeStyle.css">
|
||||
<link rel="stylesheet" href="js/codemirror/codemirror.css">
|
||||
<link rel="stylesheet" href="js/codemirror/dracula.css">
|
||||
<script type="text/javascript" src="js/layui/layui.all.js" ></script>
|
||||
<script type="text/javascript" src="js/codemirror/codemirror.js" ></script>
|
||||
<script type="text/javascript" src="js/codemirror/python.js" ></script>
|
||||
<script type="text/javascript" src="js/codemirror/javascript.js" ></script>
|
||||
<script>var $ = layui.$;var jQuery = $;</script>
|
||||
<script type="text/javascript" src="js/ztree/jquery.ztree.all.min.js" ></script>
|
||||
<style type="text/css">
|
||||
html,body,script-container{
|
||||
width : 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
.script-container .script-left-container{
|
||||
width : 200px;
|
||||
height : 100px;
|
||||
position: fixed;
|
||||
}
|
||||
.script-container .script-right-container{
|
||||
right : 0;
|
||||
left : 205px;
|
||||
height : 100%;
|
||||
position: fixed;
|
||||
}
|
||||
.script-tree-files{
|
||||
top : 50px;
|
||||
bottom:0px;
|
||||
position: fixed;
|
||||
width: 200px;
|
||||
border : 1px solid #eee;
|
||||
}
|
||||
.script-right-container .script-buttons{
|
||||
display: inline-block;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.script-right-container .script-buttons input{
|
||||
display: inline-block;
|
||||
}
|
||||
.script-content{
|
||||
top : 50px;
|
||||
bottom: 0px;
|
||||
position: fixed;
|
||||
left : 225px;
|
||||
right : 15px;
|
||||
border:1px solid #eee;
|
||||
}
|
||||
#codemirror-content{
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
.ztree-contextmenu{
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
border:1px solid #aaa;
|
||||
background:#f2f2f2;
|
||||
font-size : 12px;
|
||||
}
|
||||
.ztree-contextmenu li{
|
||||
border-bottom: 1px solid #cdcdcd;
|
||||
padding : 0px 10px;
|
||||
height : 24px;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ztree-contextmenu li:hover{
|
||||
background:#1a7dc4;
|
||||
color : #fff;
|
||||
}
|
||||
.ztree-contextmenu li:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="padding:5px;">
|
||||
<div class="script-container layui-form">
|
||||
<div class="script-left-container">
|
||||
<div class="script-select-container">
|
||||
<select id="script-select" lay-filter="script-select"><option>请选择脚本</option></select>
|
||||
</div>
|
||||
<div class="script-tree-files">
|
||||
<ul id="tree" class="ztree"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="script-right-container">
|
||||
<div class="script-buttons">
|
||||
<button type="button" class="layui-btn layui-btn-sm layui-btn-normal btn-create-script">创建脚本</button>
|
||||
<button type="button" class="layui-btn layui-btn-sm layui-btn-normal btn-refresh">刷新</button>
|
||||
<button type="button" class="layui-btn layui-btn-sm layui-btn-normal btn-save">保存当前文件</button>
|
||||
<input type="text" class="layui-input" id="path" value="./" readonly style="width: 200px"/>
|
||||
<input type="text" class="layui-input" id="parameter" value="" placeholder="运行参数" style="width: 400px"/>
|
||||
<button type="button" class="layui-btn layui-btn-sm layui-btn-normal btn-test">测试执行</button>
|
||||
</div>
|
||||
<div class="script-content">
|
||||
<textarea id="codemirror-content"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
String.prototype.endWith = function (str) {
|
||||
if (str == null || str == "" || this.length == 0 || str.length > this.length) {
|
||||
return false;
|
||||
}
|
||||
if (this.substring(this.length - str.length) == str) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
$.ajax({
|
||||
url : 'script/list',
|
||||
success : function(data){
|
||||
var scripts = data.data;
|
||||
for(var i=0,len = scripts.length;i<len;i++){
|
||||
$('#script-select').append('<option value="'+scripts[i]+'">'+scripts[i]+'</option>');
|
||||
}
|
||||
layui.form.render();
|
||||
}
|
||||
})
|
||||
var currentScript;
|
||||
var currentFile;
|
||||
layui.form.on('select(script-select)',function(data){
|
||||
renderScriptTree(data.value);
|
||||
})
|
||||
function renderScriptTree(scriptName,passCodemirror){
|
||||
currentScript = scriptName;
|
||||
$.ajax({
|
||||
url : 'script/files',
|
||||
type : 'post',
|
||||
data : {
|
||||
script : scriptName
|
||||
},
|
||||
success : function(data){
|
||||
var process = function(item){
|
||||
item.name = item.node.name;
|
||||
item.isParent = item.node.directory;
|
||||
if(item.children){
|
||||
for(var i=0,len = item.children.length;i<len;i++){
|
||||
process(item.children[i]);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
var tree = data.data.children || [];
|
||||
for (var i = 0; i < tree.length; i++) {
|
||||
process(tree[i])
|
||||
}
|
||||
var ztree = $.fn.zTree.init($("#tree"), {
|
||||
callback : {
|
||||
onClick : function(event,treeId,treeNode){
|
||||
if(!treeNode.isParent){
|
||||
loadScript(treeNode.node.path);
|
||||
}
|
||||
},
|
||||
onRightClick : function(event,treeId,treeNode){
|
||||
var path = treeNode&&treeNode.node&&treeNode.node.path || '';
|
||||
var name = treeNode&&treeNode.node&&treeNode.node.name || '';
|
||||
var html = '<ul class="ztree-contextmenu" data-name="'+name+'" data-path="'+path+'">';
|
||||
if(treeNode.isRoot){
|
||||
html+='<li class="ztre-create-file">添加文件</li>';
|
||||
html+='<li class="ztre-create-directory">添加文件夹</li>';
|
||||
}else if(treeNode.isParent){
|
||||
html+='<li class="ztre-create-file">添加文件</li>';
|
||||
html+='<li class="ztre-create-directory">添加文件夹</li>';
|
||||
html+='<li class="ztree-remove-file">删除文件夹</li>';
|
||||
html+='<li class="ztree-rename-file">重命名</li>';
|
||||
}else{
|
||||
html+='<li class="ztree-remove-file">删除文件</li>';
|
||||
html+='<li class="ztree-rename-file">重命名</li>';
|
||||
}
|
||||
html+='</ul>';
|
||||
var y = event.clientY + document.body.scrollTop;
|
||||
var x = event.clientX + document.body.scrollLeft;
|
||||
$('.ztree-contextmenu').remove();
|
||||
$('body').append(html);
|
||||
$('.ztree-contextmenu').css({"top":y+"px", "left":x+"px"});
|
||||
}
|
||||
}
|
||||
}, [{
|
||||
name : scriptName,
|
||||
isRoot :true,
|
||||
children : tree
|
||||
}]);
|
||||
ztree.expandAll(true);
|
||||
if(passCodemirror === undefined){
|
||||
createCodeMirror('text');
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
function getMode(filename){
|
||||
if(filename.endWith('.py')){
|
||||
return 'python';
|
||||
}else if(filename.endWith('.js')){
|
||||
return 'javascript';
|
||||
}
|
||||
return 'text';
|
||||
}
|
||||
function loadScript(filename){
|
||||
$.ajax({
|
||||
url : 'script/read',
|
||||
type : 'post',
|
||||
data : {
|
||||
file : filename,
|
||||
scriptName : currentScript
|
||||
},
|
||||
success : function(data){
|
||||
var content = data.data || '';
|
||||
currentFile = filename;
|
||||
$('#path').val(filename);
|
||||
createCodeMirror(getMode(filename),content);
|
||||
}
|
||||
})
|
||||
}
|
||||
function saveFile(filename,content){
|
||||
$.ajax({
|
||||
url : 'script/save',
|
||||
type : 'post',
|
||||
data : {
|
||||
file : filename,
|
||||
content : content,
|
||||
scriptName : currentScript
|
||||
},
|
||||
success : function(data){
|
||||
if(data.data){
|
||||
layui.layer.msg('保存成功!');
|
||||
}else{
|
||||
layui.layer.msg('保存失败!');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function removeFile(filename){
|
||||
$.ajax({
|
||||
url : 'script/remove/file',
|
||||
type : 'post',
|
||||
data : {
|
||||
file : filename,
|
||||
scriptName : currentScript
|
||||
},
|
||||
success : function(data){
|
||||
if(data.data){
|
||||
layui.layer.msg('删除成功');
|
||||
renderScriptTree(currentScript);
|
||||
}else{
|
||||
layui.layer.msg('删除失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function renameFile(oldName,newName){
|
||||
$.ajax({
|
||||
url : 'script/rename/file',
|
||||
type : 'post',
|
||||
data : {
|
||||
file : oldName,
|
||||
newFile : newName,
|
||||
scriptName : currentScript
|
||||
},
|
||||
success : function(data){
|
||||
if(data.data){
|
||||
layui.layer.msg('重命名成功');
|
||||
renderScriptTree(currentScript);
|
||||
}else{
|
||||
layui.layer.msg('重命名失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function testRun(){
|
||||
var parameter = $('#parameter').val();
|
||||
var path = $('#path').val();
|
||||
$.ajax({
|
||||
url : 'script/test',
|
||||
type : 'post',
|
||||
data : {
|
||||
file : path,
|
||||
parameter : parameter,
|
||||
scriptName : currentScript
|
||||
},
|
||||
success : function(data){
|
||||
data = data.data;
|
||||
if(data.exitValue == 0){
|
||||
layui.layer.alert(data.value,{
|
||||
title : '执行成功'
|
||||
});
|
||||
}else if(data.exitValue == -2){
|
||||
layui.layer.msg('不支持的脚本类型');
|
||||
}else {
|
||||
var message = ((data.value || '') + '\r\n' + (data.error || '')) + '\r\n' + (data.stack || '');
|
||||
message = message.replace(/\n/g,'<br>').replace(/ /g,' ').replace(/\t/g,' ');
|
||||
layui.layer.alert('<div style="font-weight: bold;font-family:Consolas;font-size:12px;">' + message + '</div>',{
|
||||
title : '执行失败,exitValue=' + data.exitValue,
|
||||
area:['800px','400px']
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
function createFile(filename,path,dir){
|
||||
$.ajax({
|
||||
url : 'script/create/file',
|
||||
type : 'post',
|
||||
data : {
|
||||
file : (path||'') + '/'+ filename,
|
||||
dir : dir,
|
||||
scriptName : currentScript
|
||||
},
|
||||
success : function(data){
|
||||
if(data.data){
|
||||
layui.layer.msg('创建文件'+(dir == '1' ? '夹' : '') +'成功');
|
||||
if(dir == '0'){
|
||||
renderScriptTree(currentScript,true);
|
||||
createCodeMirror(getMode(filename));
|
||||
loadScript((path ? path + '/' : '') + filename);
|
||||
}else{
|
||||
renderScriptTree(currentScript);
|
||||
}
|
||||
}else{
|
||||
layui.layer.msg('创建失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
var $instance;
|
||||
function createCodeMirror(mode,content){
|
||||
if($instance){
|
||||
$instance.toTextArea();
|
||||
}
|
||||
var element = document.getElementById('codemirror-content');
|
||||
$instance = CodeMirror.fromTextArea(element,{
|
||||
mode: mode,
|
||||
lineNumbers : true,
|
||||
theme: 'dracula',
|
||||
})
|
||||
$instance.setValue(content || '');
|
||||
}
|
||||
$('body').on('click','.btn-create-script',function(){
|
||||
layui.layer.prompt({
|
||||
title : '请输入脚本名称'
|
||||
},function(value,index){
|
||||
layui.layer.close(index);
|
||||
$.ajax({
|
||||
url : 'script/create',
|
||||
type : 'post',
|
||||
data : {
|
||||
scriptName : value
|
||||
},
|
||||
success : function(data){
|
||||
if(data.data){
|
||||
layui.layer.msg('创建成功');
|
||||
$('#script-select').append('<option value="'+value+'" selected>'+value+'</option>');
|
||||
layui.form.render();
|
||||
$('#path').val('');
|
||||
currentFile = '';
|
||||
renderScriptTree(value);
|
||||
}else{
|
||||
layui.layer.msg('创建失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}).on('click','.ztree-contextmenu .ztre-create-directory',function(){
|
||||
var path = $(this).parent().data('path');
|
||||
layui.layer.prompt({
|
||||
title : '请输入文件夹名称'
|
||||
},function(value,index){
|
||||
createFile(value,path,'1');
|
||||
layui.layer.close(index);
|
||||
})
|
||||
}).on('click','.ztree-contextmenu .ztre-create-file',function(){
|
||||
var path = $(this).parent().data('path');
|
||||
layui.layer.prompt({
|
||||
title : '请输入文件名称'
|
||||
},function(value,index){
|
||||
createFile(value,path,'0');
|
||||
layui.layer.close(index);
|
||||
})
|
||||
}).on('click','.ztree-contextmenu .ztree-remove-file',function(){
|
||||
var path = $(this).parent().data('path');
|
||||
layui.layer.confirm('是否删除该文件?',{
|
||||
title : '是否删除?'
|
||||
},function(index){
|
||||
removeFile(path);
|
||||
layui.layer.close(index);
|
||||
})
|
||||
}).on('click','.ztree-contextmenu .ztree-rename-file',function(){
|
||||
var path = $(this).parent().data('path');
|
||||
var oldName = $(this).parent().data('name');
|
||||
layui.layer.prompt({
|
||||
title : '重命名',
|
||||
value : oldName
|
||||
},function(newName,index){
|
||||
renameFile(path,path.substring(0,path.length - oldName.length) + newName);
|
||||
layui.layer.close(index);
|
||||
})
|
||||
}).on('click','.btn-save',function(){
|
||||
if($instance && currentFile){
|
||||
var content = $instance.getValue();
|
||||
saveFile(currentFile,content);
|
||||
}else{
|
||||
layui.layer.msg('请选择文件!');
|
||||
}
|
||||
|
||||
}).on('click','.btn-test',function(){
|
||||
if($instance && currentFile){
|
||||
testRun(currentFile);
|
||||
}else{
|
||||
layui.layer.msg('请选择文件!');
|
||||
}
|
||||
|
||||
}).on('click','.btn-refresh',function(){
|
||||
if($instance && currentFile){
|
||||
loadScript(currentFile);
|
||||
}
|
||||
}).on('click',function(){
|
||||
$('.ztree-contextmenu').remove();
|
||||
})
|
||||
createCodeMirror('text');
|
||||
</script>
|
||||
<script type="text/html" id="buttons">
|
||||
<a class="layui-btn layui-btn-sm btn-edit" data-id="{{d.id}}">编辑</a>
|
||||
<a class="layui-btn layui-btn-sm btn-remove" data-id="{{d.id}}">删除</a>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|