mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 15:35:55 +08:00
Merge branch 'master' into develop
This commit is contained in:
commit
6658f272be
|
@ -40,7 +40,7 @@ public enum DisplayType {
|
|||
BARCODE(EasyBarCode.class, "二维码", FieldType.STRING, 300, null, false, true),
|
||||
N2NREFERENCE(EasyN2NReference.class, "多引用", FieldType.REFERENCE_LIST, -1, null),
|
||||
LOCATION(EasyLocation.class, "位置", FieldType.STRING, 100, null),
|
||||
SIGN(EasySign.class, "签名", FieldType.TEXT, 32767, null, false, false),
|
||||
SIGN(EasySign.class, "签名", FieldType.TEXT, 32767, null, false, true),
|
||||
|
||||
// 内部
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ public class DataExporter extends SetUser {
|
|||
|
||||
if (cellVal.toString().equals(FieldValueHelper.NO_READ_PRIVILEGES)) {
|
||||
cellVal = Language.L("[无权限]");
|
||||
} else if (!dt.isExportable()) {
|
||||
} else if (!dt.isExportable() || (dt == DisplayType.SIGN || dt == DisplayType.BARCODE)) {
|
||||
cellVal = Language.L("[暂不支持]");
|
||||
} else if (dt == DisplayType.DECIMAL || dt == DisplayType.NUMBER) {
|
||||
cellVal = cellVal.toString().replace(",", ""); // 移除千分位
|
||||
|
|
|
@ -8,6 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.core.service.datareport;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
|
@ -21,14 +22,20 @@ import com.rebuild.core.metadata.easymeta.EasyField;
|
|||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import com.rebuild.core.support.general.BarCodeSupport;
|
||||
import com.rebuild.core.support.general.FieldValueHelper;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
@ -115,10 +122,19 @@ public class EasyExcelGenerator extends SetUser {
|
|||
TemplateExtractor templateExtractor = new TemplateExtractor(this.template, true);
|
||||
Map<String, String> varsMap = templateExtractor.transformVars(entity);
|
||||
|
||||
Map<String, String> varsMapOfMain = new HashMap<>();
|
||||
Map<String, String> varsMapOfDetail = new HashMap<>();
|
||||
|
||||
List<String> fieldsOfMain = new ArrayList<>();
|
||||
List<String> fieldsOfDetail = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, String> e : varsMap.entrySet()) {
|
||||
if (e.getKey().startsWith(TemplateExtractor.NROW_PREFIX)) {
|
||||
varsMapOfDetail.put(e.getKey(), e.getValue());
|
||||
} else {
|
||||
varsMapOfMain.put(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
String validField = e.getValue();
|
||||
// 无效字段
|
||||
if (validField == null) {
|
||||
|
@ -138,18 +154,19 @@ public class EasyExcelGenerator extends SetUser {
|
|||
}
|
||||
|
||||
final List<Map<String, Object>> datas = new ArrayList<>();
|
||||
final String baseSql = "select %s from %s where %s = ?";
|
||||
final String baseSql = "select %s,%s from %s where %s = ?";
|
||||
|
||||
if (!fieldsOfMain.isEmpty()) {
|
||||
String sql = String.format(baseSql,
|
||||
StringUtils.join(fieldsOfMain, ","), entity.getName(), entity.getPrimaryField().getName());
|
||||
StringUtils.join(fieldsOfMain, ","),
|
||||
entity.getPrimaryField().getName(), entity.getName(), entity.getPrimaryField().getName());
|
||||
|
||||
Record record = Application.createQuery(sql, this.getUser())
|
||||
.setParameter(1, this.recordId)
|
||||
.record();
|
||||
Assert.notNull(record, "No record found : " + this.recordId);
|
||||
|
||||
Map<String, Object> data = buildData(record, varsMap);
|
||||
datas.add(data);
|
||||
datas.add(buildData(record, varsMapOfMain));
|
||||
this.hasMain = true;
|
||||
}
|
||||
|
||||
|
@ -158,14 +175,16 @@ public class EasyExcelGenerator extends SetUser {
|
|||
|
||||
String sql = String.format(baseSql + " order by modifiedOn desc",
|
||||
StringUtils.join(fieldsOfDetail, ","),
|
||||
entity.getDetailEntity().getPrimaryField().getName(),
|
||||
entity.getDetailEntity().getName(),
|
||||
MetadataHelper.getDetailToMainField(entity.getDetailEntity()).getName());
|
||||
|
||||
List<Record> list = Application.createQuery(sql, this.getUser())
|
||||
.setParameter(1, this.recordId)
|
||||
.list();
|
||||
|
||||
for (Record c : list) {
|
||||
datas.add(buildData(c, varsMap));
|
||||
datas.add(buildData(c, varsMapOfDetail));
|
||||
}
|
||||
return datas;
|
||||
}
|
||||
|
@ -182,6 +201,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
final String unsupportFieldTip = Language.L("[暂不支持]");
|
||||
|
||||
final Map<String, Object> data = new HashMap<>();
|
||||
|
||||
// 无效字段填充
|
||||
for (Map.Entry<String, String> e : varsMap.entrySet()) {
|
||||
if (e.getValue() == null) {
|
||||
|
@ -193,17 +213,13 @@ public class EasyExcelGenerator extends SetUser {
|
|||
}
|
||||
}
|
||||
|
||||
for (Iterator<String> iter = record.getAvailableFieldIterator(); iter.hasNext(); ) {
|
||||
final String fieldName = iter.next();
|
||||
for (final String fieldName : varsMap.values()) {
|
||||
if (fieldName == null) continue;
|
||||
|
||||
EasyField easyField = EasyMetaFactory.valueOf(
|
||||
Objects.requireNonNull(MetadataHelper.getLastJoinField(entity, fieldName)));
|
||||
DisplayType dt = easyField.getDisplayType();
|
||||
|
||||
if (!dt.isExportable() && dt != DisplayType.SIGN) {
|
||||
data.put(fieldName, unsupportFieldTip);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 替换成变量名
|
||||
String varName = fieldName;
|
||||
for (Map.Entry<String, String> e : varsMap.entrySet()) {
|
||||
|
@ -216,14 +232,22 @@ public class EasyExcelGenerator extends SetUser {
|
|||
varName = varName.substring(1);
|
||||
}
|
||||
|
||||
if (!dt.isExportable()) {
|
||||
data.put(varName, unsupportFieldTip);
|
||||
continue;
|
||||
}
|
||||
|
||||
Object fieldValue = record.getObjectValue(fieldName);
|
||||
if (fieldValue == null) {
|
||||
|
||||
if (dt == DisplayType.BARCODE) {
|
||||
data.put(varName, buildBarcodeData(easyField.getRawMeta(), record.getPrimary()));
|
||||
} else if (fieldValue == null) {
|
||||
data.put(varName, StringUtils.EMPTY);
|
||||
} else {
|
||||
|
||||
if (dt == DisplayType.SIGN) {
|
||||
fieldValue = buildImgData((String) fieldValue);
|
||||
} else {
|
||||
fieldValue = buildSignData((String) fieldValue);
|
||||
} else {
|
||||
fieldValue = FieldValueHelper.wrapFieldValue(fieldValue, easyField, true);
|
||||
|
||||
if (FieldValueHelper.isUseDesensitized(easyField, this.getUser())) {
|
||||
|
@ -236,7 +260,23 @@ public class EasyExcelGenerator extends SetUser {
|
|||
return data;
|
||||
}
|
||||
|
||||
private byte[] buildImgData(String base64img) {
|
||||
private byte[] buildSignData(String base64img) {
|
||||
// data:image/png;base64,xxx
|
||||
return Base64Utils.decodeFromString(base64img.split("base64,")[1]);
|
||||
}
|
||||
|
||||
private byte[] buildBarcodeData(Field barcodeField, ID recordId) {
|
||||
BufferedImage bi = BarCodeSupport.getBarCodeImage(barcodeField, recordId);
|
||||
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
ImageIO.write(bi, "png", baos);
|
||||
|
||||
String base64 = Base64.encodeBase64String(baos.toByteArray());
|
||||
return buildSignData("base64," + base64);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Cannot encode image of barcode : {}", recordId, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,11 +63,10 @@ public class BarCodeSupport {
|
|||
String barcodeType = EasyMetaFactory.valueOf(field).getExtraAttr("barcodeType");
|
||||
|
||||
if (TYPE_BARCODE.equalsIgnoreCase(barcodeType)) {
|
||||
return createBarCode(content);
|
||||
}
|
||||
// 默认为二维码
|
||||
else {
|
||||
return createQRCode(content);
|
||||
return createBarCode(content, 0);
|
||||
} else {
|
||||
// 默认为二维码
|
||||
return createQRCode(content, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,10 +74,11 @@ public class BarCodeSupport {
|
|||
* QR_CODE
|
||||
*
|
||||
* @param content
|
||||
* @param w
|
||||
* @return
|
||||
*/
|
||||
public static BufferedImage createQRCode(String content) {
|
||||
BitMatrix bitMatrix = createCode(content, BarcodeFormat.QR_CODE, 320);
|
||||
public static BufferedImage createQRCode(String content, int w) {
|
||||
BitMatrix bitMatrix = createCode(content, BarcodeFormat.QR_CODE, w <= 0 ? 320 : w);
|
||||
return MatrixToImageWriter.toBufferedImage(bitMatrix);
|
||||
}
|
||||
|
||||
|
@ -86,10 +86,11 @@ public class BarCodeSupport {
|
|||
* CODE_128
|
||||
*
|
||||
* @param content
|
||||
* @param h
|
||||
* @return
|
||||
*/
|
||||
public static BufferedImage createBarCode(String content) {
|
||||
BitMatrix bitMatrix = createCode(content, BarcodeFormat.CODE_128, 320);
|
||||
public static BufferedImage createBarCode(String content, int h) {
|
||||
BitMatrix bitMatrix = createCode(content, BarcodeFormat.CODE_128, h <= 0 ? 80 : h);
|
||||
return MatrixToImageWriter.toBufferedImage(bitMatrix);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.rebuild.utils;
|
|||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.commons.runtime.MemoryInformationBean;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
import oshi.hardware.NetworkIF;
|
||||
|
@ -82,10 +83,22 @@ public class OshiUtils {
|
|||
List<NetworkIF> nets = getSI().getHardware().getNetworkIFs();
|
||||
if (nets == null || nets.isEmpty()) return "localhost";
|
||||
|
||||
String bestipv4 = null;
|
||||
for (NetworkIF net : nets) {
|
||||
for (String ip : net.getIPv4addr()) {
|
||||
if (bestipv4 == null) bestipv4 = ip;
|
||||
break;
|
||||
}
|
||||
|
||||
if (net.isKnownVmMacAddr()) continue;
|
||||
|
||||
String[] ipsv4 = net.getIPv4addr();
|
||||
if (ipsv4 != null && ipsv4.length > 0) return ipsv4[0];
|
||||
if (ipsv4.length > 0) {
|
||||
bestipv4 = ipsv4[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "127.0.0.1";
|
||||
|
||||
return StringUtils.defaultString(bestipv4, "127.0.0.1");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ public class ReportTemplateController extends BaseController {
|
|||
File template = DataReportManager.instance.getTemplateFile(entity, reportId);
|
||||
file = new EasyExcelGenerator(template, (ID) random[0]).generate();
|
||||
} catch (ConfigurationException ex) {
|
||||
response.sendError(400, Language.L("未找到可供预览的记录"));
|
||||
response.sendError(500, ex.getLocalizedMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,14 +51,15 @@ public class BarCodeGeneratorController extends BaseController {
|
|||
@GetMapping({"/commons/barcode/render-qr", "/commons/barcode/render"})
|
||||
public void render(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
String content = getParameterNotNull(request, "t");
|
||||
int w = getIntParameter(request, "w", 0);
|
||||
|
||||
// 4小时缓存
|
||||
ServletUtils.addCacheHead(response, 240);
|
||||
|
||||
if (request.getRequestURI().endsWith("render-qr")) {
|
||||
writeTo(BarCodeSupport.createQRCode(content), response);
|
||||
writeTo(BarCodeSupport.createQRCode(content, w), response);
|
||||
} else {
|
||||
writeTo(BarCodeSupport.createBarCode(content), response);
|
||||
writeTo(BarCodeSupport.createBarCode(content, w), response);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class ConfigFormDlg extends RbFormHandler {
|
|||
|
||||
componentDidMount() {
|
||||
if (this._entity) {
|
||||
$.get('/commons/metadata/entities', (res) => {
|
||||
$.get(`/commons/metadata/entities?detail=${this.hasDetail === true}`, (res) => {
|
||||
this.setState({ entities: res.data }, () => {
|
||||
this.__select2 = $(this._entity).select2({
|
||||
placeholder: $L('选择实体'),
|
||||
|
|
|
@ -68,6 +68,7 @@ class ReporEdit extends ConfigFormDlg {
|
|||
constructor(props) {
|
||||
super(props)
|
||||
this.subtitle = $L('报表模板')
|
||||
this.hasDetail = true
|
||||
}
|
||||
|
||||
renderFrom() {
|
||||
|
|
|
@ -14,8 +14,8 @@ $(document).ready(() => {
|
|||
class PreviewTable extends React.Component {
|
||||
render() {
|
||||
const rows = [] // [[]]
|
||||
let crtRow = []
|
||||
let crtSpan = 0
|
||||
let currentRow = []
|
||||
let currentSpan = 0
|
||||
|
||||
this.props.data.elements.forEach((c) => {
|
||||
let colspan = c.colspan || 2
|
||||
|
@ -23,22 +23,22 @@ class PreviewTable extends React.Component {
|
|||
// set
|
||||
c.colspan = colspan
|
||||
|
||||
if (crtSpan + colspan > 4) {
|
||||
rows.push(crtRow)
|
||||
crtRow = [c]
|
||||
crtSpan = colspan
|
||||
} else if (crtSpan + colspan === 4) {
|
||||
crtRow.push(c)
|
||||
rows.push(crtRow)
|
||||
crtRow = []
|
||||
crtSpan = 0
|
||||
if (currentSpan + colspan > 4) {
|
||||
rows.push(currentRow)
|
||||
currentRow = [c]
|
||||
currentSpan = colspan
|
||||
} else if (currentSpan + colspan === 4) {
|
||||
currentRow.push(c)
|
||||
rows.push(currentRow)
|
||||
currentRow = []
|
||||
currentSpan = 0
|
||||
} else {
|
||||
crtRow.push(c)
|
||||
crtSpan += colspan
|
||||
currentRow.push(c)
|
||||
currentSpan += colspan
|
||||
}
|
||||
})
|
||||
// last
|
||||
if (crtRow.length > 0) rows.push(crtRow)
|
||||
if (currentRow.length > 0) rows.push(currentRow)
|
||||
|
||||
return (
|
||||
<table className="table table-bordered table-sm table-fixed">
|
||||
|
@ -103,6 +103,7 @@ class PreviewTable extends React.Component {
|
|||
cells.push(<th>{c4.label}</th>)
|
||||
colSpan = 1
|
||||
cells.push(<td colSpan={colSpan}>{this.formatValue(c4)}</td>)
|
||||
return cells
|
||||
}
|
||||
|
||||
componentDidMount = () => $('.font-italic.hide').removeClass('hide')
|
||||
|
|
|
@ -1049,6 +1049,7 @@ class RbFormTime extends RbFormDateTime {
|
|||
class RbFormImage extends RbFormElement {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this._inputid = `${props.field}-input${$random()}`
|
||||
|
||||
if (props.value) this.state.value = [...props.value] // clone
|
||||
if (this.props.uploadNumber) {
|
||||
|
@ -1085,8 +1086,8 @@ class RbFormImage extends RbFormElement {
|
|||
)
|
||||
})}
|
||||
<span title={$L('上传图片。需要 %s 个', `${this.__minUpload}~${this.__maxUpload}`)} className={showUpload ? '' : 'hide'}>
|
||||
<input ref={(c) => (this._fieldValue__input = c)} type="file" className="inputfile" id={`${this.props.field}-input`} accept="image/*" />
|
||||
<label htmlFor={`${this.props.field}-input`} className="img-thumbnail img-upload">
|
||||
<input ref={(c) => (this._fieldValue__input = c)} type="file" className="inputfile" id={this._inputid} accept="image/*" />
|
||||
<label htmlFor={this._inputid} className="img-thumbnail img-upload">
|
||||
<span className="zmdi zmdi-image-alt" />
|
||||
</label>
|
||||
</span>
|
||||
|
@ -1195,8 +1196,8 @@ class RbFormFile extends RbFormImage {
|
|||
)
|
||||
})}
|
||||
<div className={`file-select ${showUpload ? '' : 'hide'}`}>
|
||||
<input type="file" className="inputfile" ref={(c) => (this._fieldValue__input = c)} id={`${this.props.field}-input`} />
|
||||
<label htmlFor={`${this.props.field}-input`} title={$L('上传文件。需要 %d 个', `${this.__minUpload}~${this.__maxUpload}`)} className="btn-secondary">
|
||||
<input type="file" className="inputfile" ref={(c) => (this._fieldValue__input = c)} id={this._inputid} />
|
||||
<label htmlFor={this._inputid} title={$L('上传文件。需要 %d 个', `${this.__minUpload}~${this.__maxUpload}`)} className="btn-secondary">
|
||||
<i className="zmdi zmdi-upload" />
|
||||
<span>{$L('上传文件')}</span>
|
||||
</label>
|
||||
|
@ -1770,22 +1771,23 @@ class RbFormBarcode extends RbFormElement {
|
|||
|
||||
renderElement() {
|
||||
if (this.state.value) return this.renderViewElement()
|
||||
else
|
||||
return (
|
||||
<div className="form-control-plaintext barcode text-muted">
|
||||
{$L('自动值')} ({this.props.barcodeType === 'QRCODE' ? $L('二维码') : $L('条形码')})
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="form-control-plaintext barcode text-muted">
|
||||
{$L('自动值')} ({this.props.barcodeType === 'BARCODE' ? $L('条形码') : $L('二维码')})
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderViewElement() {
|
||||
if (!this.state.value) return super.renderViewElement()
|
||||
|
||||
const codeUrl = `${rb.baseUrl}/commons/barcode/render${this.props.barcodeType === 'BARCODE' ? '' : '-qr'}?t=${$encode(this.state.value)}`
|
||||
const isbar = this.props.barcodeType === 'BARCODE'
|
||||
const codeUrl = `${rb.baseUrl}/commons/barcode/render${isbar ? '' : '-qr'}?t=${$encode(this.state.value)}`
|
||||
return (
|
||||
<div className="img-field barcode">
|
||||
<a className="img-thumbnail" title={this.state.value}>
|
||||
<img src={codeUrl} alt={this.state.value} />
|
||||
<img src={codeUrl} alt={this.state.value} className={isbar ? 'w-auto' : ''} />
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
@ -1795,14 +1797,15 @@ class RbFormBarcode extends RbFormElement {
|
|||
class RbFormAvatar extends RbFormElement {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this._inputid = `${props.field}-input${$random()}`
|
||||
}
|
||||
|
||||
renderElement() {
|
||||
return (
|
||||
<div className="img-field avatar">
|
||||
<span title={this.props.readonly ? null : $L('选择头像')}>
|
||||
{!this.props.readonly && <input ref={(c) => (this._fieldValue__input = c)} type="file" className="inputfile" id={`${this.props.field}-input`} accept="image/*" />}
|
||||
<label htmlFor={`${this.props.field}-input`} className="img-thumbnail img-upload" disabled={this.props.readonly}>
|
||||
{!this.props.readonly && <input ref={(c) => (this._fieldValue__input = c)} type="file" className="inputfile" id={this._inputid} accept="image/*" />}
|
||||
<label htmlFor={this._inputid} className="img-thumbnail img-upload" disabled={this.props.readonly}>
|
||||
<img src={this._formatUrl(this.state.value)} alt="Avatar" />
|
||||
</label>
|
||||
</span>
|
||||
|
@ -1980,7 +1983,6 @@ class RbFormSign extends RbFormElement {
|
|||
<div className="img-field sign sign-edit">
|
||||
<span title={this.props.readonly ? null : $L('签名')}>
|
||||
<label
|
||||
htmlFor={`${this.props.field}-input`}
|
||||
className="img-thumbnail img-upload"
|
||||
onClick={() => {
|
||||
if (!this.props.readonly) {
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
<a class="dropdown-item J_delete"><i class="icon zmdi zmdi-delete"></i> [[${bundle.L('删除')}]]</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item J_print" target="_blank" th:href="|${baseUrl}/app/${entityName}/print?id=${id}|"><i class="icon zmdi zmdi-print"></i> [[${bundle.L('打印')}]]</a>
|
||||
<a class="dropdown-item J_report"><i class="icon zmdi zmdi-map"></i> [[${bundle.L('报表')}]]</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 btn-group J_trans hide">
|
||||
|
|
Loading…
Reference in a new issue