完善数据集表单SQL编辑器功能

This commit is contained in:
datagear 2020-01-01 18:08:26 +08:00
parent 77807fba81
commit 7757a6cda4
11 changed files with 342 additions and 97 deletions

View File

@ -169,7 +169,7 @@ CREATE TABLE DATAGEAR_SQL_DATA_SET_FACTORY
DSF_ID VARCHAR(50) NOT NULL,
DSF_NAME VARCHAR(100) NOT NULL,
DSF_SCHEMA_ID VARCHAR(50) NOT NULL,
DSF_SQL VARCHAR(50) NOT NULL,
DSF_SQL VARCHAR(1000) NOT NULL,
DSF_CREATE_USER_ID VARCHAR(50),
DSF_CREATE_TIME TIMESTAMP,
PRIMARY KEY (DSF_ID)

View File

@ -0,0 +1,161 @@
/*
* Copyright (c) 2018 datagear.org. All Rights Reserved.
*/
package org.datagear.web.controller;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.datagear.connection.ConnectionSource;
import org.datagear.dbinfo.ColumnInfo;
import org.datagear.dbinfo.DatabaseInfoResolver;
import org.datagear.dbinfo.TableInfo;
import org.datagear.management.domain.Schema;
import org.datagear.management.domain.User;
import org.datagear.management.service.SchemaService;
import org.datagear.web.convert.ClassDataConverter;
import org.datagear.web.util.KeywordMatcher;
import org.datagear.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* SQL编辑器控制器
*
* @author datagear@163.com
*
*/
@Controller
@RequestMapping("/sqlEditor")
public class SqlEditorController extends AbstractSchemaConnController
{
@Autowired
private DatabaseInfoResolver databaseInfoResolver;
public SqlEditorController()
{
super();
}
public SqlEditorController(MessageSource messageSource, ClassDataConverter classDataConverter,
SchemaService schemaService, ConnectionSource connectionSource, DatabaseInfoResolver databaseInfoResolver)
{
super(messageSource, classDataConverter, schemaService, connectionSource);
this.databaseInfoResolver = databaseInfoResolver;
}
public DatabaseInfoResolver getDatabaseInfoResolver()
{
return databaseInfoResolver;
}
public void setDatabaseInfoResolver(DatabaseInfoResolver databaseInfoResolver)
{
this.databaseInfoResolver = databaseInfoResolver;
}
@RequestMapping(value = "/{schemaId}/findTableNames", produces = CONTENT_TYPE_JSON)
@ResponseBody
public List<String> findTableNames(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @PathVariable("schemaId") String schemaId,
@RequestParam(value = "keyword", required = false) String keyword) throws Throwable
{
final User user = WebUtils.getUser(request, response);
TableInfo[] tableInfos = new ReturnSchemaConnExecutor<TableInfo[]>(request, response, springModel, schemaId,
true)
{
@Override
protected TableInfo[] execute(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, Schema schema) throws Throwable
{
checkReadTableDataPermission(schema, user);
return getDatabaseInfoResolver().getTableInfos(getConnection());
}
}.execute();
List<TableInfo> tableInfoList = SchemaController.findByKeyword(tableInfos, keyword);
Collections.sort(tableInfoList, SchemaController.TABLE_INFO_SORT_BY_NAME_COMPARATOR);
List<String> tableNames = new ArrayList<String>();
for (TableInfo tableInfo : tableInfoList)
tableNames.add(tableInfo.getName());
return tableNames;
}
@RequestMapping(value = "/{schemaId}/findColumnNames", produces = CONTENT_TYPE_JSON)
@ResponseBody
public List<String> findColumnNames(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @PathVariable("schemaId") String schemaId,
@RequestParam("table") final String table,
@RequestParam(value = "keyword", required = false) String keyword) throws Throwable
{
final User user = WebUtils.getUser(request, response);
ColumnInfo[] columnInfos = new ReturnSchemaConnExecutor<ColumnInfo[]>(request, response, springModel, schemaId,
true)
{
@Override
protected ColumnInfo[] execute(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, Schema schema) throws Throwable
{
checkReadTableDataPermission(schema, user);
return getDatabaseInfoResolver().getColumnInfos(getConnection(), table);
}
}.execute();
List<ColumnInfo> columnInfoList = findByKeyword(columnInfos, keyword);
Collections.sort(columnInfoList, COLUMNINFO_INFO_SORT_BY_NAME_COMPARATOR);
List<String> columnNames = new ArrayList<String>();
for (ColumnInfo columnInfo : columnInfoList)
columnNames.add(columnInfo.getName());
return columnNames;
}
/**
* 根据列名称关键字查询{@linkplain ColumnInfo}列表
*
* @param columnInfos
* @param columnNameKeyword
* @return
*/
public static List<ColumnInfo> findByKeyword(ColumnInfo[] columnInfos, String columnNameKeyword)
{
return KeywordMatcher.<ColumnInfo> match(columnInfos, columnNameKeyword,
new KeywordMatcher.MatchValue<ColumnInfo>()
{
@Override
public String[] get(ColumnInfo t)
{
return new String[] { t.getName() };
}
});
}
public static Comparator<ColumnInfo> COLUMNINFO_INFO_SORT_BY_NAME_COMPARATOR = new Comparator<ColumnInfo>()
{
@Override
public int compare(ColumnInfo o1, ColumnInfo o2)
{
return o1.getName().compareTo(o2.getName());
}
};
}

View File

@ -158,6 +158,7 @@ selectonly 是否选择操作允许为null
var tableSettings = po.buildDataTableSettingsAjax(tableColumns, po.url("pagingQueryData"));
po.initDataTable(tableSettings);
po.bindResizeDataTable();
})
(${pageId});
</script>

View File

@ -122,6 +122,7 @@ selectonly 是否选择操作允许为null
var tableSettings = po.buildDataTableSettingsAjax(tableColumns, po.url("queryData"));
po.initDataTable(tableSettings);
po.bindResizeDataTable();
})
(${pageId});
</script>

View File

@ -166,6 +166,7 @@ selectonly 是否选择操作允许为null
var tableSettings = po.buildDataTableSettingsAjax(tableColumns, po.url("pagingQueryData"));
po.initDataTable(tableSettings);
po.bindResizeDataTable();
})
(${pageId});
</script>

View File

@ -44,7 +44,10 @@ readonly 是否只读操作允许为null
<label><@spring.message code='dataSet.sql' /></label>
</div>
<div class="form-item-value">
<textarea name="sql" class="ui-widget ui-widget-content">${(dataSet.sql)!''?html}</textarea>
<textarea name="sql" class="ui-widget ui-widget-content" style="display:none;">${(dataSet.sql)!''?html}</textarea>
<div class="sql-editor-wrapper ui-widget ui-widget-content">
<div id="${pageId}-sql-editor" class="sql-editor"></div>
</div>
</div>
</div>
</div>
@ -59,16 +62,23 @@ readonly 是否只读操作允许为null
</div>
<#include "../../include/page_js_obj.ftl" >
<#include "../../include/page_obj_form.ftl">
<#include "../../include/page_obj_sqlEditor.ftl">
<script type="text/javascript">
(function(po)
{
$.initButtons(po.element());
po.element(".sql-editor-wrapper").height($(window).height()/5*2);
po.url = function(action)
{
return "${contextPath}/analysis/dataSet/" + action;
};
po.getSqlEditorSchemaId = function(){ return po.element("input[name='schemaConnectionFactory.schema.id']").val(); };
po.initSqlEditor();
var cursor = po.sqlEditor.getCursorPosition();
po.sqlEditor.session.insert(cursor, po.element("textarea[name='sql']").val());
<#if !readonly>
po.element(".select-schema-button").click(function()
{
@ -89,13 +99,20 @@ readonly 是否只读操作允许为null
po.open("${contextPath}/schema/select", options);
});
$.validator.addMethod("dataSetSqlRequired", function(value, element)
{
var sql = po.sqlEditor.getValue();
return sql.length > 0;
});
po.form().validate(
{
ignore : "",
rules :
{
"name" : "required",
"schemaConnectionFactory.schema.title" : "required",
"sql" : "required",
"sql" : "dataSetSqlRequired",
},
messages :
{
@ -105,6 +122,8 @@ readonly 是否只读操作允许为null
},
submitHandler : function(form)
{
po.element("textarea[name='sql']").val(po.sqlEditor.getValue());
$(form).ajaxSubmit(
{
success : function()

View File

@ -174,6 +174,7 @@ selectonly 是否选择操作允许为null
var tableSettings = po.buildDataTableSettingsAjax(tableColumns, po.url("pagingQueryData"));
po.initDataTable(tableSettings);
po.bindResizeDataTable();
})
(${pageId});
</script>

View File

@ -0,0 +1,139 @@
<#--
SQL编辑器JS片段。
依赖:
page_js_obj.ftl
变量:
//数据源ID不允许为null
po.getSqlEditorSchemaId
-->
<script type="text/javascript">
(function(po)
{
//SQL编辑器调用po.initSqlEditor()后初始化
po.sqlEditor = undefined;
po.getSqlEditorSchemaId = function(){ return undefined; };
po.getSqlEditorElementId = function()
{
return "${pageId}-sql-editor";
};
po.getSqlEditorAutocompleteAjaxOptions = function(autocompleteInfo)
{
var url = "${contextPath}/sqlEditor/"+po.getSqlEditorSchemaId()+"/";
var data = { "keyword" : "" };
if(autocompleteInfo.type == "table")
url += "findTableNames";
else if(autocompleteInfo.type == "column")
{
url += "findColumnNames";
data.table = autocompleteInfo.table;
}
else
url += "findUnknownNames";
return { "url" : url, "data" : data };
};
po.sqlEditorCompleters =
[
{
identifierRegexps : [/[a-zA-Z_0-9\.\$]/],
getCompletions: function(editor, session, pos, prefix, callback)
{
po.getSqlAutocompleteCompletions(editor, session, pos, prefix, callback);
}
}
];
po.getSqlAutocompleteCompletions = function(editor, session, pos, prefix, callback)
{
if(!po.getSqlEditorSchemaId())
{
callback(null, []);
return;
}
var info = $.sqlAutocomplete.resolveAutocompleteInfo(editor, session, pos, prefix, ";");
if(info && info.type == "table" && po.sqlAutocompleteTableCompletions)
{
callback(null, po.sqlAutocompleteTableCompletions);
return;
}
var tableAlias = $.sqlAutocomplete.resolveTableAlias(prefix);
if(info && info.type == "column" && info.table && po.sqlAutocompleteColumnCompletions)
{
var columns = po.sqlAutocompleteColumnCompletions[info.table];
if(columns != null)
{
var completions = $.sqlAutocomplete.buildCompletions(columns, (tableAlias ? tableAlias+"." : ""));
callback(null, completions);
return;
}
}
if(info && (info.type == "table" || (info.type == "column" && info.table)))
{
var ajaxOptions =
{
type : "POST",
success : function(names)
{
var completions;
if(info.type == "table")
{
completions = $.sqlAutocomplete.buildCompletions(names);
po.sqlAutocompleteTableCompletions = completions;
}
else if(info.type == "column")
{
completions = $.sqlAutocomplete.buildCompletions(names, (tableAlias ? tableAlias+"." : ""));
if(!po.sqlAutocompleteColumnCompletions)
po.sqlAutocompleteColumnCompletions = {};
if(names && names.length > 0)
po.sqlAutocompleteColumnCompletions[info.table] = names;
}
callback(null, completions);
},
error : function(){}
};
$.extend(ajaxOptions, po.getSqlEditorAutocompleteAjaxOptions(info));
$.ajax(ajaxOptions);
}
else
callback(null, []);
};
po.initSqlEditor = function()
{
var languageTools = ace.require("ace/ext/language_tools");
var SqlMode = ace.require("ace/mode/sql").Mode;
po.sqlEditor = ace.edit(po.getSqlEditorElementId());
po.sqlEditor.session.setMode(new SqlMode());
po.sqlEditor.setShowPrintMargin(false);
po.sqlEditor.setOptions(
{
enableBasicAutocompletion: po.sqlEditorCompleters,
enableLiveAutocompletion: po.sqlEditorCompleters
});
return po.sqlEditor;
};
})
(${pageId});
</script>

View File

@ -173,6 +173,7 @@ Schema schema 数据库不允许为null
<#include "../include/page_obj_format_time.ftl" >
<#include "../include/page_obj_data_permission.ftl">
<#include "../include/page_obj_data_permission_ds_table.ftl">
<#include "../include/page_obj_sqlEditor.ftl">
<script type="text/javascript">
(function(po)
{
@ -190,26 +191,8 @@ Schema schema 数据库不允许为null
po.cometdInitIfNot();
po.sqlEditorCompleters =
[
{
identifierRegexps : [/[a-zA-Z_0-9\.\$]/],
getCompletions: function(editor, session, pos, prefix, callback)
{
po.getSqlAutocompleteCompletions(editor, session, pos, prefix, callback);
}
}
];
var languageTools = ace.require("ace/ext/language_tools");
var SqlMode = ace.require("ace/mode/sql").Mode;
po.sqlEditor = ace.edit("${pageId}-sql-editor");
po.sqlEditor.session.setMode(new SqlMode());
po.sqlEditor.setShowPrintMargin(false);
po.sqlEditor.setOptions(
{
enableBasicAutocompletion: po.sqlEditorCompleters,
enableLiveAutocompletion: po.sqlEditorCompleters
});
po.getSqlEditorSchemaId = function(){ return po.schemaId; };
po.initSqlEditor();
po.sqlEditor.focus();
po.sqlEditor.navigateFileEnd();
@ -283,80 +266,6 @@ Schema schema 数据库不允许为null
}
});
po.getSqlAutocompleteCompletions = function(editor, session, pos, prefix, callback)
{
var info = $.sqlAutocomplete.resolveAutocompleteInfo(editor, session, pos, prefix, ";");
if(info && info.type == "table" && po.sqlAutocompleteTableCompletions)
{
callback(null, po.sqlAutocompleteTableCompletions);
return;
}
var tableAlias = $.sqlAutocomplete.resolveTableAlias(prefix);
if(info && info.type == "column" && info.table && po.sqlAutocompleteColumnCompletions)
{
var columns = po.sqlAutocompleteColumnCompletions[info.table];
if(columns != null)
{
var completions = $.sqlAutocomplete.buildCompletions(columns, (tableAlias ? tableAlias+"." : ""));
callback(null, completions);
return;
}
}
if(info && (info.type == "table" || (info.type == "column" && info.table)))
{
var url = "${contextPath}/sqlpad/"+po.schemaId+"/";
var data = { "sqlpadId" : po.sqlpadId, "keyword" : "" };
if(info.type == "table")
url += "findTableNames";
else if(info.type == "column")
{
url += "findColumnNames";
data.table = info.table;
}
else
url += "findUnknownNames";
$.ajax(
{
type : "POST",
url : url,
data : data,
success : function(names)
{
var completions;
if(info.type == "table")
{
completions = $.sqlAutocomplete.buildCompletions(names);
po.sqlAutocompleteTableCompletions = completions;
}
else if(info.type == "column")
{
completions = $.sqlAutocomplete.buildCompletions(names, (tableAlias ? tableAlias+"." : ""));
if(!po.sqlAutocompleteColumnCompletions)
po.sqlAutocompleteColumnCompletions = {};
if(names && names.length > 0)
po.sqlAutocompleteColumnCompletions[info.table] = names;
}
callback(null, completions);
},
error : function(){}
});
}
else
callback(null, []);
};
po.getSqlDelimiter = function()
{
var delimiter = po.element("#sqlDelimiterInput").val();

View File

@ -89,6 +89,7 @@
<url-pattern>/globalSetting/*</url-pattern>
<url-pattern>/notification/*</url-pattern>
<url-pattern>/analysis/*</url-pattern>
<url-pattern>/sqlEditor/*</url-pattern>
</servlet-mapping>
<servlet>

View File

@ -1923,6 +1923,18 @@ table.dataTable tbody tr .column-check .row-data-state .ui-icon{
padding-right: 0.5em;
}
.page-form-dataSet{}
.page-form-dataSet .sql-editor-wrapper{
display: inline-block;
width: 60%;
height: 10em;
}
.page-form-dataSet .sql-editor-wrapper .sql-editor{
width: 100%;
height: 100%;
}
.page-form-chart .chart-plugin-list{
margin: 0 0;
padding: 0 0;