背景:项目运行过程中,时不时要加一些查询报表供用户使用,但是每次开发一个新报表,客户端页面和后端功能就要重写一套,90%的工作都是重复的,所以想着做一套基于sql配置的动态查询报表,新增新报表的时候只需要在后台加一条配置,前端就可以动态解析相关字段,执行sql查询并展示给用户。
报表配置模块
服务端就是一个普通的springMVC程序,新增一个报表配置模块
- 报表配置模块实体类
public class ReportConfigDto implements Serializable{
/**
* 报表编号
*/
private String code;
/**
* 报表名称
*/
private String name;
/**
* 表头字段
*/
private String header;
/**
* 报表业务分类(相当于分组)
*/
private String businessType;
/**
* 报表说明和介绍
*/
private String remark;
/**
* 表头字典
*/
private String headerCategory;
/**
* 表头展示名称
*/
private String displayName;
/**
* 查询条件
*/
private String queryCondition;
/**
* 查询条件类型
*/
private String queryType;
/**
* 查询时该条件是否必须不为空
*/
private String queryRequired;
/**
* 查询模式 (=、<=、>=、allLike、leftLike、rightLike等)
*/
private String queryPattern;
/**
* 查询sql,如果多表联查,可以在查询语句外面套一层select语句来对应表头别名
*/
private String querySql;
- 报表配置模块表
CREATE TABLE `report_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`code` varchar(50) NOT NULL COMMENT '报表编码',
`name` varchar(100) NOT NULL COMMENT '报表名称',
`header` varchar(255) NOT NULL COMMENT '表头',
`header_category` varchar(1000) DEFAULT NULL COMMENT '表头字典(字段名1{key1:value1,key2:value2};字段名2{key1:value1,key2:value2})',
`display_name` varchar(255) NOT NULL COMMENT '表头名称',
`query_condition` varchar(1000) DEFAULT NULL COMMENT '查询条件(地图@SelectMapControl;文本框@TextBox;下拉框@ComboBox;日期@Date)',
`query_required` varchar(1000) DEFAULT NULL COMMENT '查询条件是否必填验证(字段名1@true;字段名2@false)',
`query_type` varchar(1000) DEFAULT NULL COMMENT '查询条件字段类型(字段名1@number;@字段名2@string)',
`query_pattern` varchar(1000) DEFAULT NULL COMMENT '查询模式(字段名1@=;字段名2@>=;字段名3@allLike;字段名4@leftLike;字段名5@rightLike)',
`query_sql` varchar(5000) DEFAULT NULL COMMENT '查询sql(开头不带select)',
`is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
`create_user` varchar(50) NOT NULL COMMENT '创建人',
`update_user` varchar(50) NOT NULL COMMENT '更新人',
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '时间戳',
`load_search` tinyint(1) NOT NULL COMMENT '打开报表时是否立即查询(0:否,1:是)',
`remark` varchar(500) NOT NULL COMMENT '报表说明',
`business_type` varchar(100) NOT NULL COMMENT '业务分类',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_updateTime` (`update_time`) USING BTREE,
KEY `idx_code` (`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='自定义报表配置';
-- 参考示例
INSERT INTO ws_report_config (code, name, header, header_category, display_name, query_condition, query_required, query_type, query_pattern, query_sql, area_no, is_delete, create_time, update_time, create_user, update_user, ts, load_search, remark, business_type) VALUES('workTotalNum', '近30天生产量统计', 'dates;tp;totalqty', 'tp{Pick:出库,Putaway:入库,Review:盘点}', '日期;业务类型;数量', 'tp@ComboBox', NULL, 'tp@string', 'tp@=', 'date_format(t.create_time ,''%Y-%m-%d'' ) dates,t.tp,sum(t.real_qty) totalqty
from (select r.create_time,m.milestone_type tp,r.real_qty
from ws_job_r r left join ws_job_m m
on r.milestone_no = m.milestone_no
where m.milestone_type in(''Pick'',''Review'',''Putaway'',''Tally'')
and r.create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)) t
where 1 = 1 <conditions>
group by dates,t.tp order by dates desc', 0, '2021-11-17 00:00:00.0', '2021-11-17 00:00:00.0', 'admin', 'admin', '2021-11-18 11:50:07.0', 1, '', '工作量统计');
- 前端界面(每个报表配置,操作列提供一个按钮,点击按钮即执行报表查询,渲染新的报表页面)
动态报表查询模块
- 检索按钮点击事件,根据报表配置id查询到报表配置对象,跳转到动态报表界面
- 动态报表界面主要代码如下
public partial class SqlReport : UserControl {
private int loadNum = 0; //防止重复加载
public ReportConfigDto reportConfig; //报表配置信息,前个页面传入
public Dictionary<string, string> headerDic = new Dictionary<string, string>(); //字段名和显示名称的字典
public Dictionary<string, string> queryConditionDic = new Dictionary<string, string>(); //查询条件的字典
public Dictionary<string, string> queryTypeDic = new Dictionary<string, string>(); //查询条件字段类型字典
public Dictionary<string, string> queryPatternDic = new Dictionary<string, string>(); //查询条件模式字典(全等、左等、右等...)
public Dictionary<string, bool> queryRequiedDic = new Dictionary<string, bool>(); //查询条件是否必填校验字典
public Dictionary<string, Dictionary<string, string>> headerCategoryDic =
new Dictionary<string, Dictionary<string, string>>(); //枚举值的字典
public Dictionary<string, SqlReportMeta> metaDic = new Dictionary<string, SqlReportMeta>(); //全部查询条件集合
public DataTable tableData = new DataTable();
private List<PageColumns> pcs = new List<PageColumns>();
private BackgroundWorker queryMonitorWorker;
/// <summary>
/// 查询条件
/// </summary>
public static string queryCondition = "";
public SqlReport() {
InitializeComponent();
AddHandler(PageControl.PageRoutedEvent, new RoutedEventHandler(PageRoutedHandler));
queryMonitorWorker = new BackgroundWorker();
queryMonitorWorker.WorkerSupportsCancellation =
true; // 表示BackgroundWorker是否支持异步取消。当该属性设置为true时,可以调用异步方法CancelAsync来取消异步操作
queryMonitorWorker.DoWork += QueryMonitor_AsyDoWork;
queryMonitorWorker.RunWorkerCompleted += QueryMonitor_Completed;
this.Loaded += (o, e) => {
if (loadNum == 0) {
SqlReport_Load(o, e);
}
};
}
/// <summary>
/// 页面加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SqlReport_Load(object sender, RoutedEventArgs e) {
loadNum = 1;
try {
if (User.CurrentUser == null) {
throw new UserMessage("当前用户不能为空!请重新登录");
}
//查询所有的日期选择框,设置默认值
foreach (var datePicker in this.queryConditionsPanel.FindChildren<DatePicker>()) {
string date = datePicker.Text.Trim();
if (string.Equals(date, Constant.INITTIME)) {
datePicker.Text = DateTime.Now.ToString("yyyy-MM-dd");
}
}
initReportMeta(); //构建查询条件
initColumn(); //初始化表头
this.pcCon.SetColumnHeader(pcs);
this.pcCon.IsShowLoading = true; //加载
this.pcCon.LoadingMsg = "加载中……"; //加载信息 默认:正在加载…
this.pcCon.SelectionUnitType(DataGridSelectionUnit.Cell);
if (reportConfig.loadSearch == 1) {
btnSearch_Click(null, null);
}
} catch (UserMessage um) {
MsgHelper.ShowMessage(this.txtMsg, um.Message, 0);
}
}
private void initColumn() {
// foreach (var colNo in headerDic.Keys)
// {
// DataColumn column = new DataColumn(colNo, typeof(string));
// tableData.Columns.Add(column);
// }
foreach (var header in headerDic) {
pcs.Add(new PageColumns(header.Value, header.Key));
DataColumn column = new DataColumn(header.Key, typeof(string));
tableData.Columns.Add(column);
}
}
/// <summary>
/// 初始化报表元数据
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private void initReportMeta() {
if (string.IsNullOrEmpty(reportConfig.header) || string.IsNullOrEmpty(reportConfig.displayName)) {
throw new UserMessage("报表表头信息配置有误,请联系研发处理!");
}
String[] headers = reportConfig.header.Split(';');
String[] headerCategorys =
reportConfig.headerCategory != null ? reportConfig.headerCategory.Split(';') : null;
String[] displayNames = reportConfig.displayName != null ? reportConfig.displayName.Split(';') : null;
String[] queryConditions =
reportConfig.queryCondition != null ? reportConfig.queryCondition.Split(';') : null;
String[] queryTypes = reportConfig.queryType != null ? reportConfig.queryType.Split(';') : null;
String[] queryPatterns = reportConfig.queryPattern != null ? reportConfig.queryPattern.Split(';') : null;
String[] queryRequireds = reportConfig.queryRequired != null ? reportConfig.queryRequired.Split(';') : null;
if (headers.Length != displayNames.Length) {
throw new UserMessage("报表配置有误,请联系研发处理!");
}
for (int i = 0; i < headers.Length; i++) {
headerDic.Add(headers[i], displayNames[i]);
}
if (queryConditions != null && queryConditions.Length > 0) {
foreach (var condition in queryConditions) {
if (!string.IsNullOrEmpty(condition)) {
string[] conditionKV = condition.Split('@');
if (string.IsNullOrEmpty(conditionKV[0]) || string.IsNullOrEmpty(conditionKV[1])) {
throw new UserMessage("queryCondition字段键值对配置有误");
}
queryConditionDic.Add(conditionKV[0], conditionKV[1]);
}
}
}
//展示元素类型
if (queryTypes != null && queryTypes.Length > 0) {
foreach (var type in queryTypes) {
if (!string.IsNullOrEmpty(type)) {
string[] typeKV = type.Split('@');
if (string.IsNullOrEmpty(typeKV[0]) || string.IsNullOrEmpty(typeKV[1])) {
throw new UserMessage("queryType字段键值对配置有误");
}
queryTypeDic.Add(typeKV[0], typeKV[1]);
}
}
}
//必填项
if (queryRequireds != null && queryRequireds.Length > 0) {
foreach (var req in queryRequireds) {
if (!string.IsNullOrEmpty(req)) {
string[] reqKV = req.Split('@');
if (string.IsNullOrEmpty(reqKV[0]) || string.IsNullOrEmpty(reqKV[1])) {
throw new UserMessage("queryRequired字段键值对配置有误");
}
queryRequiedDic.Add(reqKV[0], string.Equals(reqKV[1], "true"));
}
}
}
//查询模式
if (queryPatterns != null && queryPatterns.Length > 0) {
foreach (var pattern in queryPatterns) {
if (!string.IsNullOrEmpty(pattern)) {
string[] patternKV = pattern.Split('@');
if (string.IsNullOrEmpty(patternKV[0]) || string.IsNullOrEmpty(patternKV[1])) {
throw new UserMessage("queryPattern字段键值对配置有误");
}
queryPatternDic.Add(patternKV[0], patternKV[1]);
}
}
}
if (headerCategorys != null && headerCategorys.Length > 0) {
foreach (var category in headerCategorys) {
if (!string.IsNullOrEmpty(category)) {
string key = category.Substring(0, category.IndexOf('{'));
int startIndex = category.IndexOf('{') + 1;
int endIndex = category.IndexOf('}');
string categoryString = category.Substring(startIndex, endIndex - startIndex);
string[] cateMap = categoryString.Split(',');
Dictionary<string, string> cateKVDic = new Dictionary<string, string>();
foreach (var cate in cateMap) {
string[] cateDetail = cate.Split(':');
cateKVDic.Add(cateDetail[0], cateDetail[1]);
}
headerCategoryDic.Add(key, cateKVDic);
}
}
}
//创建查询条件
if (queryConditionDic != null && queryConditionDic.Count > 0) {
foreach (var query in queryConditionDic.Keys) {
//string code, string name, string type,string filedType,string pattern,bool? required, Dictionary<string, string> category
SqlReportMeta meta = new SqlReportMeta(query,
headerDic.ContainsKey(query) ? headerDic[query] : null,
queryConditionDic.ContainsKey(query) ? queryConditionDic[query] : null,
queryTypeDic.ContainsKey(query) ? queryTypeDic[query] : null,
queryPatternDic.ContainsKey(query) ? queryPatternDic[query] : null,
queryRequiedDic.ContainsKey(query) && queryRequiedDic[query],
headerCategoryDic.ContainsKey(query) ? headerCategoryDic[query] : null);
metaDic.Add(query, meta);
buildReportQueryConditions(meta);
}
}
}
/// <summary>
/// 构建报表查询条件
/// </summary>
/// <param name="meta"></param>
/// <exception cref="NotImplementedException"></exception>
private void buildReportQueryConditions(SqlReportMeta meta) {
if (string.Equals(meta.type, SqlReportMetaType.TextBox)) {
buildLabel(meta);
TextBox textBox = new TextBox();
textBox.Name = meta.code;
textBox.Margin = new Thickness(0, 8, 0, 8);
textBox.Style = this.FindResource("PageTBStyle") as Style;
this.queryConditionsPanel.Children.Add(textBox);
} else if (string.Equals(meta.type, SqlReportMetaType.ComboBox)) {
buildLabel(meta);
List<ComboBoxKVItem> items = new List<ComboBoxKVItem>();
foreach (var cate in meta.category) {
items.Add(new ComboBoxKVItem(cate.Value, cate.Key));
}
ComboBox comboBox = new ComboBox();
comboBox.ItemsSource = items;
comboBox.Name = meta.code;
comboBox.Margin = new Thickness(0, 10, 0, 10);
comboBox.Width = 150;
this.queryConditionsPanel.Children.Add(comboBox);
} else if (string.Equals(meta.type, SqlReportMetaType.SelectMapControl)) {
SelectMapControl mapControl = new SelectMapControl();
mapControl.Name = meta.code;
this.queryConditionsPanel.Children.Add(mapControl);
} else if (string.Equals(meta.type, SqlReportMetaType.DatePicker)) {
buildLabel(meta);
//<DatePicker Style="{StaticResource PageDpStyle}" SelectedDateFormat="Short" x:Name="dpStartTime"/>
DatePicker datePicker = new DatePicker();
datePicker.Name = meta.code;
datePicker.Style = this.FindResource("PageDpStyle") as Style;
datePicker.SelectedDateFormat = DatePickerFormat.Short;
this.queryConditionsPanel.Children.Add(datePicker);
}
}
private void buildLabel(SqlReportMeta meta) {
Label label = new Label();
label.Content = meta.name + ":";
label.Style = this.FindResource("PageLbStyle") as Style;
label.Margin = new Thickness(8, 8, 0, 8);
this.queryConditionsPanel.Children.Add(label);
}
/// <summary>
/// 查询异步worker执行
/// </summary>
/// <param name="user"></param>
private void QueryMonitor_AsyDoWork(object sender, DoWorkEventArgs e) {
e.Result = SmartReportAssemble.querySqlReportCommonResult((ReportCommonQuery) e.Argument);
}
/// <summary>
/// 报表数据返回
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void QueryMonitor_Completed(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
MsgHelper.ShowMessage(this.txtMsg, e.Error.Message, 0);
} else if (e.Cancelled) {
MsgHelper.ShowMessage(this.txtMsg, "请求操作已取消", 0);
} else {
PageInfo<dynamic> result = (PageInfo<dynamic>) e.Result;
if (result != null) {
if (result.list == null || result.list.Count == 0) {
MsgHelper.ShowMessage(this.txtMsg, "查询结果为空", 1);
}
//清空数据表
clearTableData();
List<ReportCommonDto> resultList =
JsonConvert.DeserializeObject<List<ReportCommonDto>>(JSON.ToJSON(result.list));
foreach (var dto in resultList) {
DataRow row = tableData.NewRow();
foreach (var colNo in headerDic.Keys) {
object value = dto.ContainsKey(colNo) ? dto[colNo] : null;
if (headerCategoryDic.ContainsKey(colNo) && value != null) {
Dictionary<String, String> curCateDic = headerCategoryDic[colNo];
value = curCateDic.ContainsKey(value.ToString())
? curCateDic[value.ToString()]
: value;
}
row[colNo] = value;
}
tableData.Rows.Add(row);
}
PageData page = new PageData();
page.Totals = result.total;
page.tableDatas = tableData;
this.pcCon.SetTableDatas(page);
}
}
}
private void clearTableData() {
tableData.Clear();
PageData page = new PageData();
page.Totals = 0;
page.tableDatas = null;
this.pcCon.SetTableDatas(page);
}
/// <summary>
/// 搜索
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSearch_Click(object sender, RoutedEventArgs e) {
try {
MsgHelper.HideMessage(this.txtMsg);
QueryDone(1, this.pcCon.PageNum);
} catch (UserMessage um) {
MsgHelper.ShowMessage(this.txtMsg, um.Message, 0);
} catch (Exception ex) {
MsgHelper.ShowMessage(this.txtMsg, ex.Message, 0);
}
}
/// <summary>
/// 查询按钮触发操作
/// </summary>
/// <param name="pageNo"></param>
/// <param name="pageNum"></param>
private void QueryDone(int pageNo, int pageNum) {
try {
ReportCommonQuery query = new ReportCommonQuery();
query.orgNo = User.CurrentUser.orgNo;
query.distributeNo = User.CurrentUser.distributeNo;
query.warehouseNo = User.CurrentUser.warehouseNo;
query.mapAreaId = GetMapAreaNo();
query.reportCode = reportConfig.code; //报表编号
query.pageSize = pageNum;
query.pageNum = pageNo;
buildQueryParams(query);
if (!queryMonitorWorker.IsBusy) {
queryMonitorWorker.RunWorkerAsync(query);
}
} catch (UserMessage um) {
throw um;
} catch (Exception ex) {
throw ex;
}
}
/// <summary>
///构建查询参数
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
private void buildQueryParams(ReportCommonQuery query) {
foreach (var element in this.queryConditionsPanel.Children) {
if (string.Equals(element.GetType().Name, SqlReportMetaType.TextBox)) {
TextBox t = element as TextBox;
if (queryConditionDic.ContainsKey(t.Name)) {
if (t.Text.Contains(";")) {
throw new UserMessage("查询条件中不允许包含特殊字符");
}
string param = t.Name;
if (queryRequiedDic.ContainsKey(t.Name) &&
queryRequiedDic[t.Name] &&
(string.IsNullOrEmpty(t.Text) || string.IsNullOrWhiteSpace(t.Text))) {
throw new UserMessage("[" + headerDic[t.Name] + "]不能为空");
}
//字符串查询条件
if (queryTypeDic.ContainsKey(t.Name) &&
string.Equals(queryTypeDic[t.Name], SqlReportMetaType.META_TYPE_STRING)) {
if (queryPatternDic.ContainsKey(t.Name)) {
switch (queryPatternDic[t.Name]) {
case SqlReportMetaType.PATTERN_ALL_LIKE:
param += " like '%" + t.Text.Trim() + "%'";
break;
case SqlReportMetaType.PATTERN_LEFT_LIKE:
param += " like '%" + t.Text.Trim() + "'";
break;
case SqlReportMetaType.PATTERN_RIGHT_LIKE:
param += " like '" + t.Text.Trim() + "%'";
break;
default:
param += " = '" + t.Text.Trim() + "'";
break;
}
} else {
param += " = '" + t.Text.Trim() + "'";
}
} else {
param += " " + queryPatternDic[t.Name] + t.Text + " ";
}
if (!string.IsNullOrEmpty(t.Text) && !string.IsNullOrWhiteSpace(t.Text)) {
query.queryParams += param + ";";
}
}
} else if (string.Equals(element.GetType().Name, SqlReportMetaType.ComboBox)) {
ComboBox c = element as ComboBox;
if (queryConditionDic.ContainsKey(c.Name)) {
//字典查询条件只支持全等查询
ComboBoxKVItem item = c.SelectionBoxItem as ComboBoxKVItem;
if (item != null) {
string param = c.Name + " = '" + item.Value + "';";
query.queryParams += param;
}
}
} else if (string.Equals(element.GetType().Name, SqlReportMetaType.SelectMapControl)) {
SelectMapControl m = element as SelectMapControl;
long? mapAreaNo = m.GetMapAreaNo();
if (mapAreaNo != null) {
string param = m.Name + " = " + mapAreaNo + ";";
query.queryParams += param;
}
} else if (string.Equals(element.GetType().Name, SqlReportMetaType.DatePicker)) {
DatePicker d = element as DatePicker;
string param = d.Name;
if (queryRequiedDic.ContainsKey(d.Name) &&
queryRequiedDic[d.Name] &&
(string.IsNullOrEmpty(d.Text) || string.IsNullOrWhiteSpace(d.Text))) {
throw new UserMessage("[" + headerDic[d.Name] + "]不能为空");
}
if (queryPatternDic.ContainsKey(d.Name)) {
param += " " + queryPatternDic[d.Name] + " ";
} else {
param += " = ";
}
param += "'" + d.Text.Trim() + "'";
query.queryParams += param;
}
}
}
/// <summary>
/// 分页回调
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PageRoutedHandler(object sender, RoutedEventArgs e) {
try {
PageRouteEventArgs args = e as PageRouteEventArgs;
if (args.EventType == PageRouteEventArgs.EventEnum.manager) {
QueryDone(args.PageNo, args.PageNum);
}
} catch (UserMessage um) {
MsgHelper.ShowMessage(this.txtMsg, um.Message, 0);
} catch (Exception ex) {
MsgHelper.ShowMessage(this.txtMsg, ex.Message, 0);
}
}
}
- 报表查询后台主要代码如下:
public BaseResponse querySqlReportCommonResult(ReportCommonRequest query) {
String executeSql = null;
try {
AssertHelper.notNull(query);
AssertHelper.notNull(query.reportCode);
ReportConfigRequest configRequest = new ReportConfigRequest();
BeanHelper.copyProperties(query,configRequest);
configRequest.setCode(query.reportCode);
List<ReportConfigDto> configDtos = reportConfigManager.listReportConfig(configRequest);
PageHelper.startPage(query.getPageNum(), query.getPageSize());
PageInfo<ReportCommonDto> pageInfo = new PageInfo<>();
if (CollectionUtils.isNotEmpty(configDtos)) {
String querySql = configDtos.get(0).getQuerySql();
//如果前台传入的查询条件不为空,就开始构建查询条件拼接到sql后面
if (StringUtils.isNotBlank(query.queryParams)) {
String appendConditions = "";
String[] params = query.queryParams.split(";");
for (String paramExp : params) {
if (StringUtils.isNotBlank(paramExp)) {
appendConditions += " and " + paramExp;
}
}
querySql = StringUtils.replace(querySql,"<conditions>",appendConditions);
} else {
querySql = StringUtils.replace(querySql,"<conditions>","");
}
executeSql = querySql;
query.setQuerySql(executeSql);
List<ReportCommonDto> result = reportConfigManager.querySqlReportCommonResult(query);
pageInfo = new PageInfo<>(result);
} else if (CollectionUtils.size(configDtos) > 1) {
return RpServiceAssist.fail(BaseResponse.class, MessageConst.SERVICE_ERROR_CODE, "查询到编号为" + query.reportCode + "的多个报表,请联系管理员处理");
}
return RpServiceAssist.ok(BaseResponse.class, pageInfo);
} catch (Exception ex) {
logger.error("查询sql报表配置:{}异常,执行SQL为:{},详细错误信息:{}" , JsonHelper.toJson(query),executeSql,ex);
return RpServiceAssist.fail(BaseResponse.class, MessageConst.SERVICE_ERROR_CODE, "报表查询异常,请联系管理员处理");
}
}
- 枚举类(界面元素渲染支持文本框,下拉框,日期选择框)
public class SqlReportMetaType
{
// 元素渲染类型
public const string TextBox = "TextBox";
public const string ComboBox = "ComboBox";
public const string DatePicker = "DatePicker";
//元数据类型
public const string META_TYPE_STRING = "string";
public const string META_TYPE_NUMBER = "number";
//模糊查询匹配模式
public const string PATTERN_ALL_LIKE = "allLike";
public const string PATTERN_LEFT_LIKE = "leftLike";
public const string PATTERN_RIGHT_LIKE = "rightLike";
}
- 查询效果如下
总结
以上代码不是完整代码,但是核心代码已经全部贴出来了,主要关键点在于点检索按钮时对报表的配置项的解析,以及查询结果表格的动态列生成。以上仅供参考。
注:这个SQL报表没有过多考虑针对SQL注入的防护问题,因为前端触发sql查询的时候是从后台取的SQL语句,如果对安全规范要求很高的话,需要针对最终执行的SQL做一些关键词的过滤。