diff --git a/src/main/java/com/rebuild/server/business/datareport/ReportGenerator.java b/src/main/java/com/rebuild/server/business/datareport/ReportGenerator.java index 379b0c3be..6a5a2ad85 100644 --- a/src/main/java/com/rebuild/server/business/datareport/ReportGenerator.java +++ b/src/main/java/com/rebuild/server/business/datareport/ReportGenerator.java @@ -20,6 +20,7 @@ package com.rebuild.server.business.datareport; import cn.devezhao.persist4j.Entity; import cn.devezhao.persist4j.Field; +import cn.devezhao.persist4j.Query; import cn.devezhao.persist4j.Record; import cn.devezhao.persist4j.engine.ID; import com.rebuild.server.Application; @@ -63,7 +64,7 @@ public class ReportGenerator { * @param record */ public ReportGenerator(ID reportId, ID record) { - this(DataReportManager.instance.getTemplate(MetadataHelper.getEntity(record.getEntityCode()), reportId), record); + this(DataReportManager.instance.getTemplateFile(MetadataHelper.getEntity(record.getEntityCode()), reportId), record); } /** @@ -129,7 +130,10 @@ public class ReportGenerator { String sql = String.format("select %s from %s where %s = ?", StringUtils.join(validFields, ","), entity.getName(), entity.getPrimaryField().getName()); - Record record = Application.getQueryFactory().createQuery(sql, this.user).setParameter(1, this.record).record(); + + Query query = this.user == null ? Application.createQuery(sql) + : Application.getQueryFactory().createQuery(sql, this.user); + Record record = query.setParameter(1, this.record).record(); for (Iterator iter = record.getAvailableFieldIterator(); iter.hasNext(); ) { String name = iter.next(); diff --git a/src/main/java/com/rebuild/server/business/datareport/TemplateExtractor.java b/src/main/java/com/rebuild/server/business/datareport/TemplateExtractor.java index 0022eb5f1..21e6f1cfb 100644 --- a/src/main/java/com/rebuild/server/business/datareport/TemplateExtractor.java +++ b/src/main/java/com/rebuild/server/business/datareport/TemplateExtractor.java @@ -88,6 +88,8 @@ public class TemplateExtractor { } /** + * 转换模板中的变量 + * * @param entity * @return */ @@ -107,6 +109,8 @@ public class TemplateExtractor { } /** + * 转换模板中的变量字段 + * * @param entity * @param fieldPath * @return diff --git a/src/main/java/com/rebuild/server/configuration/DataReportManager.java b/src/main/java/com/rebuild/server/configuration/DataReportManager.java index 4c8a18640..2afb06edf 100644 --- a/src/main/java/com/rebuild/server/configuration/DataReportManager.java +++ b/src/main/java/com/rebuild/server/configuration/DataReportManager.java @@ -24,6 +24,7 @@ import com.alibaba.fastjson.JSONArray; import com.rebuild.server.Application; import com.rebuild.server.helper.ConfigurationException; import com.rebuild.server.helper.SysConfiguration; +import com.rebuild.server.metadata.MetadataHelper; import java.io.File; import java.util.ArrayList; @@ -94,7 +95,7 @@ public class DataReportManager implements ConfigManager { * @param reportId * @return */ - public File getTemplate(Entity entity, ID reportId) { + public File getTemplateFile(Entity entity, ID reportId) { String template = null; for (ConfigEntry e : getReportsRaw(entity)) { if (e.getID("id").equals(reportId)) { @@ -114,6 +115,24 @@ public class DataReportManager implements ConfigManager { return file; } + /** + * @param reportId + * @return + * @see #getTemplateFile(Entity, ID) 性能好 + */ + @Deprecated + public File getTemplateFile(ID reportId) { + Object[] report = Application.createQueryNoFilter( + "select belongEntity from DataReportConfig where configId = ?") + .setParameter(1, reportId) + .unique(); + if (report == null || !MetadataHelper.containsEntity((String) report[0])) { + throw new ConfigurationException("No config of report found : " + reportId); + } + + return getTemplateFile(MetadataHelper.getEntity((String) report[0]), reportId); + } + @Override public void clean(Entity cacheKey) { final String cKey = "DataReportManager-" + cacheKey.getName(); diff --git a/src/main/java/com/rebuild/web/admin/entityhub/DataReportControll.java b/src/main/java/com/rebuild/web/admin/entityhub/DataReportControll.java index f8d2b843f..b88b41017 100644 --- a/src/main/java/com/rebuild/web/admin/entityhub/DataReportControll.java +++ b/src/main/java/com/rebuild/web/admin/entityhub/DataReportControll.java @@ -19,10 +19,19 @@ along with this program. If not, see . package com.rebuild.web.admin.entityhub; import cn.devezhao.commons.CalendarUtils; +import cn.devezhao.persist4j.Entity; +import cn.devezhao.persist4j.engine.ID; +import com.alibaba.fastjson.JSON; import com.rebuild.server.Application; +import com.rebuild.server.business.datareport.ReportGenerator; +import com.rebuild.server.business.datareport.TemplateExtractor; +import com.rebuild.server.configuration.DataReportManager; +import com.rebuild.server.helper.SysConfiguration; import com.rebuild.server.metadata.MetadataHelper; import com.rebuild.server.metadata.entity.EasyMeta; +import com.rebuild.utils.JSONUtils; import com.rebuild.web.BasePageControll; +import com.rebuild.web.common.FileDownloader; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Controller; @@ -31,9 +40,13 @@ import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; /** * TODO @@ -50,10 +63,6 @@ public class DataReportControll extends BasePageControll { return createModelAndView("/admin/entityhub/data-reports.jsp"); } - @RequestMapping("/data-reports/check-template") - public void checkTemplate(HttpServletRequest request, HttpServletResponse response) throws IOException { - } - @RequestMapping("/data-reports/list") public void reportList(HttpServletRequest request, HttpServletResponse response) throws IOException { String belongEntity = getParameter(request, "entity"); @@ -66,6 +75,68 @@ public class DataReportControll extends BasePageControll { writeSuccess(response, array); } + @RequestMapping("/data-reports/check-template") + public void checkTemplate(HttpServletRequest request, HttpServletResponse response) throws IOException { + String file = getParameterNotNull(request, "file"); + String entity = getParameterNotNull(request, "entity"); + + File template = SysConfiguration.getFileOfData(file); + Entity entityMeta = MetadataHelper.getEntity(entity); + + Map vars = new TemplateExtractor(template).transformVars(entityMeta); + if (vars.isEmpty()) { + writeFailure(response, "无效模板文件 (缺少字段)"); + return; + } + + int totalVars = vars.size(); + Set invalidVars = new HashSet<>(); + for (Map.Entry e : vars.entrySet()) { + if (e.getValue() == null) { + invalidVars.add(e.getKey()); + } + } + + if (invalidVars.size() >= vars.size()) { + writeFailure(response, "无效模板文件 (无效字段)"); + return; + } + + JSON ret = JSONUtils.toJSONObject("invalidVars", invalidVars); + writeSuccess(response, ret); + } + + @RequestMapping("/data-reports/preview") + public void preview(HttpServletRequest request, HttpServletResponse response) throws IOException { + ID reportId = getIdParameterNotNull(request, "id"); + Object[] report = Application.createQueryNoFilter( + "select belongEntity from DataReportConfig where configId = ?") + .setParameter(1, reportId) + .unique(); + if (report == null || !MetadataHelper.containsEntity((String) report[0])) { + response.sendError(410, "报表模板不存在"); + return; + } + + Entity entity = MetadataHelper.getEntity((String) report[0]); + + String sql = String.format("select %s from %s order by modifiedOn desc", + entity.getPrimaryField().getName(), entity.getName()); + Object random[] = Application.createQueryNoFilter(sql).unique(); + if (random == null) { + response.sendError(410, "未找到任何记录"); + return; + } + + File template = DataReportManager.instance.getTemplateFile(entity, reportId); + File file = new ReportGenerator(template, (ID) random[0]).generate(); + + FileDownloader.setDownloadHeaders(response, file.getName()); + FileDownloader.writeLocalFile(file, response); + } + + // -- + /** * @param sql * @param belongEntity diff --git a/src/main/java/com/rebuild/web/base/general/ReportsControll.java b/src/main/java/com/rebuild/web/base/general/ReportsControll.java index 06d6aa68f..47652b2a3 100644 --- a/src/main/java/com/rebuild/web/base/general/ReportsControll.java +++ b/src/main/java/com/rebuild/web/base/general/ReportsControll.java @@ -27,10 +27,8 @@ import com.rebuild.server.business.datareport.ReportGenerator; import com.rebuild.server.configuration.DataReportManager; import com.rebuild.server.configuration.portals.FormsBuilder; import com.rebuild.server.metadata.MetadataHelper; -import com.rebuild.utils.JSONUtils; import com.rebuild.web.BasePageControll; import com.rebuild.web.common.FileDownloader; -import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @@ -68,41 +66,26 @@ public class ReportsControll extends BasePageControll { @RequestMapping("available-reports") public void availableReports(HttpServletRequest request, HttpServletResponse response) throws IOException { String entity = getParameterNotNull(request, "entity"); - JSONArray reports = DataReportManager.instance.getReports(MetadataHelper.getEntity(entity)); + Entity entityMeta = MetadataHelper.getEntity(entity); + + JSONArray reports = DataReportManager.instance.getReports(entityMeta); writeSuccess(response, reports); } @RequestMapping("report-generate") - public void generateReport(HttpServletRequest request, HttpServletResponse response) throws IOException { - File report = generateReport(request); - writeSuccess(response, JSONUtils.toJSONObject("file", report.getName())); - } + public void reportGenerate(HttpServletRequest request, HttpServletResponse response) throws IOException { + ID user = getRequestUser(request); + ID reportId = getIdParameterNotNull(request, "report"); + ID recordId = getIdParameterNotNull(request, "record"); + + File report = new ReportGenerator(reportId, recordId).generate(); - @RequestMapping("report-export") - public void exportReport(HttpServletRequest request, HttpServletResponse response) throws IOException { - File report = generateReport(request); String attname = request.getParameter("attname"); if (attname == null) { attname = report.getName(); } - response.setHeader("Content-Disposition", "attachment;filename=" + attname); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - FileDownloader.writeLocalFile(report.getName(), true, response); - } - - /** - * @param request - * @return - */ - private File generateReport(HttpServletRequest request) { - ID user = getRequestUser(request); - ID reportId = getIdParameterNotNull(request, "report"); - ID recordId = getIdParameterNotNull(request, "record"); - - ReportGenerator generator = new ReportGenerator(reportId, recordId); - generator.setUser(user); - File file = generator.generate(); - return file; + FileDownloader.setDownloadHeaders(response, attname); + FileDownloader.writeLocalFile(report, response); } } diff --git a/src/main/java/com/rebuild/web/common/FileDownloader.java b/src/main/java/com/rebuild/web/common/FileDownloader.java index da30fd00a..4346c86ef 100644 --- a/src/main/java/com/rebuild/web/common/FileDownloader.java +++ b/src/main/java/com/rebuild/web/common/FileDownloader.java @@ -23,6 +23,7 @@ import cn.devezhao.commons.web.ServletUtils; import com.rebuild.server.helper.QiniuCloud; import com.rebuild.server.helper.SysConfiguration; import com.rebuild.web.BaseControll; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.BooleanUtils; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; @@ -88,9 +89,7 @@ public class FileDownloader extends BaseControll { // Local storage || temp if (!QiniuCloud.instance().available() || temp) { - response.setHeader("Content-Disposition", "attachment;filename=" + fileName); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - + setDownloadHeaders(response, fileName); writeLocalFile(filePath, temp, response); } else { String privateUrl = QiniuCloud.instance().url(filePath); @@ -116,20 +115,41 @@ public class FileDownloader extends BaseControll { return false; } -// long size = FileUtils.sizeOf(file); -// response.setHeader("Content-Length", String.valueOf(size)); + return writeLocalFile(file, response); + } + + /** + * 文件下载 + * + * @param file + * @param response + * @return + * @throws IOException + */ + public static boolean writeLocalFile(File file, HttpServletResponse response) throws IOException { + long size = FileUtils.sizeOf(file); + response.setHeader("Content-Length", String.valueOf(size)); try (InputStream fis = new FileInputStream(file)) { response.setContentLength(fis.available()); - + OutputStream os = response.getOutputStream(); int count = 0; byte[] buffer = new byte[1024 * 1024]; while ((count = fis.read(buffer)) != -1) { os.write(buffer, 0, count); } -// os.flush(); + os.flush(); return true; } } + + /** + * @param response + * @param attname + */ + public static void setDownloadHeaders(HttpServletResponse response, String attname) { + response.setHeader("Content-Disposition", "attachment;filename=" + attname); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + } } diff --git a/src/main/webapp/admin/entityhub/data-reports.jsp b/src/main/webapp/admin/entityhub/data-reports.jsp index ed9cf9211..9393fe97a 100644 --- a/src/main/webapp/admin/entityhub/data-reports.jsp +++ b/src/main/webapp/admin/entityhub/data-reports.jsp @@ -51,7 +51,7 @@ 应用实体 启用 更新时间 - + diff --git a/src/main/webapp/assets/css/dashboard.css b/src/main/webapp/assets/css/dashboard.css index 2c7796617..7000913e2 100644 --- a/src/main/webapp/assets/css/dashboard.css +++ b/src/main/webapp/assets/css/dashboard.css @@ -126,7 +126,7 @@ .dlg-dash-select .modal-dialog ul li a { display: block; border: 1px solid #4285f4; - padding: 10px 12px; + padding: 9px 12px; } .dlg-dash-select .modal-dialog ul li a>.icon { @@ -233,5 +233,5 @@ } .J_dash-select:hover { - color: #4285f4; + color: #4285f4; } \ No newline at end of file diff --git a/src/main/webapp/assets/css/rb-page.css b/src/main/webapp/assets/css/rb-page.css index 6c6edabbe..5f1296f54 100644 --- a/src/main/webapp/assets/css/rb-page.css +++ b/src/main/webapp/assets/css/rb-page.css @@ -172,7 +172,7 @@ html.admin .admin-show.row { } .alert.min .close { - padding: 1.03rem 0; + padding: 0.95rem 0; } .input-search, @@ -239,6 +239,10 @@ html.admin .admin-show.row { line-height: 1.45; } +.badge+.badge { + margin-left: 3px; +} + .table tbody tr.muted td:first-child { border-left: 3px solid #bbb; padding-left: 17px diff --git a/src/main/webapp/assets/css/view-page.css b/src/main/webapp/assets/css/view-page.css index b0b3358ee..b5497415f 100644 --- a/src/main/webapp/assets/css/view-page.css +++ b/src/main/webapp/assets/css/view-page.css @@ -264,16 +264,15 @@ body { } .reports-select .modal-content { - max-width: 380px; + max-width: 400px; } .reports-select ul li a { display: block; - border: 1px solid #ccc; - padding: 8px 12px; - border-radius: 2px; + border: 1px solid #d5d8de; + padding: 8px 10px; position: relative; - color: #444; + color: #555; } .reports-select ul li a .zmdi { @@ -281,17 +280,20 @@ body { right: 12px; top: 11px; font-size: 1.3rem; - color: #999; + color: #fff; + display: none; } .reports-select ul li a:hover { - background-color: #eee; + color: #fff; + background-color: #4285f4; + border-color: #4285f4; } .reports-select ul li a:hover .zmdi { - color: #4285f4; + display: inline-block; } .reports-select ul li+li { - margin-top: 5px; + margin-top: 6px; } \ No newline at end of file diff --git a/src/main/webapp/assets/js/entityhub/data-reports.jsx b/src/main/webapp/assets/js/entityhub/data-reports.jsx index a3f450291..fb3a9702d 100644 --- a/src/main/webapp/assets/js/entityhub/data-reports.jsx +++ b/src/main/webapp/assets/js/entityhub/data-reports.jsx @@ -1,4 +1,4 @@ - +/* eslint-disable react/jsx-no-target-blank */ $(document).ready(function () { $('.J_add').click(() => { renderRbcomp() }) renderRbcomp(, 'dataList') @@ -18,8 +18,9 @@ class ReportList extends ConfigList { {item[4] ? : } {item[5]} - this.handleEdit(item)}> - this.handleDelete(item[0])}> + + this.handleEdit(item)}> + this.handleDelete(item[0])}> })} @@ -69,12 +70,20 @@ class ReporEdit extends ConfigFormDlg {
-
-
- this.__upload = c} /> - +
+
+
+ this.__upload = c} /> + +
- {this.state.uploadFileName &&
{this.state.uploadFileName}
} +
+ {this.state.uploadFileName &&
{this.state.uploadFileName}
} +
+
+ {(this.state.invalidVars || []).length > 0 &&
+ 存在无效字段 {'${'}{this.state.invalidVars.join('} ${')}{'}'},建议修改 +
}
@@ -93,9 +102,12 @@ class ReporEdit extends ConfigFormDlg { } componentDidMount() { super.componentDidMount() + setTimeout(() => { + if (this.__select2) this.__select2.on('change', () => this.checkTemplate()) + }, 500) + const that = this if (this.__upload) { - let that = this $(this.__upload).html5Uploader({ postUrl: rb.baseUrl + '/filex/upload', onSelectError: function (field, error) { @@ -105,20 +117,40 @@ class ReporEdit extends ConfigFormDlg { onSuccess: function (d) { d = JSON.parse(d.currentTarget.response) if (d.error_code === 0) { - let name = $fileCutName(d.data) - that.setState({ - templateFile: d.data, - uploadFileName: name - }) - if (!that.state.name) { - that.setState({ name: name }) - } + that.__lastFile = d.data + that.checkTemplate() } else RbHighbar.error('上传失败,请稍后重试') } }) } } + // 检查模板 + checkTemplate() { + let file = this.__lastFile + let entity = this.__select2.val() + if (!file || !entity) return + + $.get(`${rb.baseUrl}/admin/datas/data-reports/check-template?file=${file}&entity=${entity}`, (res) => { + if (res.error_code === 0) { + let fileName = $fileCutName(file) + this.setState({ + templateFile: file, + uploadFileName: fileName, + name: this.state.name || fileName, + invalidVars: res.data.invalidVars + }) + } else { + this.setState({ + templateFile: null, + uploadFileName: null, + invalidVars: null + }) + RbHighbar.error(res.error_msg) + } + }) + } + confirm = () => { let post = { name: this.state['name'] } if (!post.name) { RbHighbar.create('请输入报表名称'); return } diff --git a/src/main/webapp/assets/js/entityhub/print-preview.jsx b/src/main/webapp/assets/js/entityhub/print-preview.jsx index 64c650d02..45c3151d2 100644 --- a/src/main/webapp/assets/js/entityhub/print-preview.jsx +++ b/src/main/webapp/assets/js/entityhub/print-preview.jsx @@ -53,6 +53,10 @@ class PreviewTable extends React.Component { ) } + componentDidMount() { + $('.font-italic.hide').removeClass('hide') + } + formatValue(item) { if (!item || !item.value) return null diff --git a/src/main/webapp/assets/js/rb-forms.jsx b/src/main/webapp/assets/js/rb-forms.jsx index 046300d84..d3e373479 100644 --- a/src/main/webapp/assets/js/rb-forms.jsx +++ b/src/main/webapp/assets/js/rb-forms.jsx @@ -979,7 +979,7 @@ class ClassificationSelector extends React.Component { } render() { return ( -
this._dlg = c}> +
this._dlg = c} tabIndex="-1">
diff --git a/src/main/webapp/assets/js/rb-view.jsx b/src/main/webapp/assets/js/rb-view.jsx index 0474b3990..d35cf96fd 100644 --- a/src/main/webapp/assets/js/rb-view.jsx +++ b/src/main/webapp/assets/js/rb-view.jsx @@ -153,7 +153,7 @@ const detectViewElement = function (item) { // } // } -// 选择默认面板 +// 选择报表 class SelectReport extends React.Component { constructor(props) { super(props) @@ -165,16 +165,16 @@ class SelectReport extends React.Component {
- +
-
this._scrollbar = s}> -
    - {(this.state.reports || []).map((item) => { - return
  • {item.name}
  • - })} -
-
+
选择报表
+
    + {(this.state.reports || []).map((item) => { + let reportUrl = `${rb.baseUrl}/app/entity/report-generate?report=${item.id}&record=${this.props.id}` + return
  • {item.name}
  • + })} +
@@ -194,16 +194,19 @@ class SelectReport extends React.Component { $(this._dlg).modal({ show: true, keyboard: true }) } - generate() { - - } - /** * @param {*} entity * @param {*} id */ static create(entity, id) { - renderRbcomp() + if (this.__cached) { + this.__cached.show() + return + } + let that = this + renderRbcomp(, null, function () { + that.__cached = this + }) } } diff --git a/src/main/webapp/general-entity/print-preview.jsp b/src/main/webapp/general-entity/print-preview.jsp index 8d9217a88..b4952e2cc 100644 --- a/src/main/webapp/general-entity/print-preview.jsp +++ b/src/main/webapp/general-entity/print-preview.jsp @@ -41,7 +41,7 @@ html, body{
-
+
打印时间 ${printTime} · 编号 <%=CodecUtils.base64Encode(request.getAttribute("recordId").toString())%>
${appName} 技术支持