field data wrap

This commit is contained in:
devezhao-corp 2018-09-23 22:33:36 +08:00
parent 68d8e8f9b5
commit 06cb381662
27 changed files with 441 additions and 318 deletions

View file

@ -48,14 +48,17 @@ public class DataListManager extends LayoutManager {
* @param entity
* @return
*/
public static JSON getListColumnConfig(String entity) {
Object[] lcr = getLayoutConfigRaw(entity, TYPE_DATALIST);
public static JSON getColumnLayout(String entity) {
Object[] raw = getLayoutConfigRaw(entity, TYPE_DATALIST);
List<Map<String, Object>> columnList = new ArrayList<>();
Entity entityMeta = MetadataHelper.getEntity(entity);
// 默认配置
if (lcr == null) {
Field nameField = entityMeta.getNameField();
nameField = nameField == null ? entityMeta.getPrimaryField() : nameField;
// 默认配置
if (raw == null) {
if (nameField != null) {
columnList.add(warpColumn(nameField));
}
@ -63,7 +66,7 @@ public class DataListManager extends LayoutManager {
columnList.add(warpColumn(entityMeta.getField(EntityHelper.createdOn)));
}
} else {
JSONArray config = (JSONArray) lcr[1];
JSONArray config = (JSONArray) raw[1];
for (Object o : config) {
JSONObject jo = (JSONObject) o;
String field = jo.getString("field");
@ -82,6 +85,7 @@ public class DataListManager extends LayoutManager {
Map<String, Object> ret = new HashMap<>();
ret.put("entity", entity);
ret.put("nameField", nameField.getName());
ret.put("fields", columnList);
return (JSON) JSON.parse(JSON.toJSONString(ret));
}

View file

@ -0,0 +1,140 @@
/*
Copyright 2018 DEVEZHAO(zhaofang123@gmail.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cn.devezhao.rebuild.server.service.base;
import java.text.DecimalFormat;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;
import cn.devezhao.commons.CalendarUtils;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.engine.ID;
import cn.devezhao.rebuild.server.metadata.MetadataHelper;
import cn.devezhao.rebuild.server.service.entitymanage.DisplayType;
import cn.devezhao.rebuild.server.service.entitymanage.EasyMeta;
/**
* 字段值包装
*
* @author zhaofang123@gmail.com
* @since 09/23/2018
*/
public class FieldValueWrapper {
/**
* @param date
* @param field
* @return
*/
public static Object wrapFieldValue(Object value, EasyMeta field) {
if (value == null || StringUtils.isBlank(value.toString())) {
return StringUtils.EMPTY;
}
DisplayType dt = field.getDisplayType();
if (dt == DisplayType.DATE) {
return wrapDate(value, field);
} else if (dt == DisplayType.DATETIME) {
return wrapDatetime(value, field);
} else if (dt == DisplayType.NUMBER) {
return wrapNumber(value, field);
} else if (dt == DisplayType.DECIMAL) {
return wrapDecimal(value, field);
} else if (dt == DisplayType.REFERENCE) {
return wrapReference(value, field);
} else if (/*dt == DisplayType.ID ||*/ dt == DisplayType.PICKLIST
|| dt == DisplayType.IMAGE || dt == DisplayType.FILE || dt == DisplayType.LOCATION) {
// 无需处理
return value;
} else {
return wrapSimple(value, field);
}
}
/**
* @param date
* @param field
* @return
*/
public static String wrapDate(Object date, EasyMeta field) {
String format = field.getFieldExtConfig().getString("dateFormat");
format = StringUtils.defaultIfEmpty(format, field.getDisplayType().getDefaultFormat());
Assert.notNull(format, "No format : " + field.getBaseMeta());
return CalendarUtils.getDateFormat(format).format(date);
}
/**
* @param date
* @param field
* @return
*/
public static String wrapDatetime(Object date, EasyMeta field) {
String format = field.getFieldExtConfig().getString("datetimeFormat");
format = StringUtils.defaultIfEmpty(format, field.getDisplayType().getDefaultFormat());
Assert.notNull(format, "No format : " + field.getBaseMeta());
return CalendarUtils.getDateFormat(format).format(date);
}
/**
* @param number
* @param field
* @return
*/
public static String wrapNumber(Object number, EasyMeta field) {
String format = field.getFieldExtConfig().getString("numberFormat");
format = StringUtils.defaultIfEmpty(format, field.getDisplayType().getDefaultFormat());
Assert.notNull(format, "No format : " + field.getBaseMeta());
return new DecimalFormat(format).format(number);
}
/**
* @param decimal
* @param field
* @return
*/
public static String wrapDecimal(Object decimal, EasyMeta field) {
String format = field.getFieldExtConfig().getString("decimalFormat");
format = StringUtils.defaultIfEmpty(format, field.getDisplayType().getDefaultFormat());
Assert.notNull(format, "No format : " + field.getBaseMeta());
return new DecimalFormat(format).format(decimal);
}
/**
* @param reference
* @param field
* @return
*/
public static Object wrapReference(Object reference, EasyMeta field) {
Object[] referenceValue = (Object[]) reference;
Object[] idNamed = new Object[3];
Entity idEntity = MetadataHelper.getEntity(((ID) referenceValue[0]).getEntityCode());
idNamed[2] = idEntity.getName();
idNamed[1] = referenceValue[1] == null ? StringUtils.EMPTY : referenceValue[1].toString();
idNamed[0] = referenceValue[0].toString();
return idNamed;
}
/**
* @param simple
* @param field
* @return
*/
public static String wrapSimple(Object simple, EasyMeta field) {
return simple.toString().trim();
}
}

View file

@ -108,8 +108,8 @@ public class FormManager extends LayoutManager {
el.put("options", picklist);
}
else if (DisplayType.DATETIME.name().equals(dt)) {
if (!el.containsKey("dateFormat")) {
el.put("dateFormat", "yyyy-MM-dd HH:mm:ss");
if (!el.containsKey("datetimeFormat")) {
el.put("datetimeFormat", "yyyy-MM-dd HH:mm:ss");
}
}
else if (DisplayType.DATE.name().equals(dt)) {
@ -167,7 +167,10 @@ public class FormManager extends LayoutManager {
JSONObject config = (JSONObject) getViewLayout(entity);
JSONArray elements = config.getJSONArray("elements");
Record record = record(recordId, elements);
Record record = null;
if (!elements.isEmpty()) {
record = record(recordId, elements);
}
for (Object element : elements) {
JSONObject el = (JSONObject) element;

View file

@ -25,29 +25,38 @@ import cn.devezhao.persist4j.dialect.Type;
*/
public enum DisplayType {
NUMBER("数字", FieldType.LONG),
DECIMAL("货币", FieldType.DECIMAL),
DATE("日期", FieldType.DATE),
DATETIME("日期时间", FieldType.TIMESTAMP),
TEXT("文本", FieldType.STRING),
EMAIL("邮箱", FieldType.STRING),
URL("链接", FieldType.STRING),
PHONE("电话", FieldType.STRING),
IMAGE("图片", FieldType.STRING),
FILE("附件", FieldType.STRING),
PICKLIST("列表", FieldType.REFERENCE),
REFERENCE("引用", FieldType.REFERENCE),
NUMBER("数字", FieldType.LONG, "##,###"),
DECIMAL("货币", FieldType.DECIMAL, "##,##0.00"),
ID("主键", FieldType.PRIMARY);
DATE("日期", FieldType.DATE, "yyyy-MM-dd"),
DATETIME("日期时间", FieldType.TIMESTAMP, "yyyy-MM-dd HH:mm"),
TEXT("文本", FieldType.STRING, null),
EMAIL("邮箱", FieldType.STRING, null),
URL("链接", FieldType.STRING, null),
PHONE("电话", FieldType.STRING, null),
IMAGE("图片", FieldType.STRING, null),
FILE("附件", FieldType.STRING, null),
PICKLIST("列表", FieldType.REFERENCE, null),
REFERENCE("引用", FieldType.REFERENCE, null),
ID("主键", FieldType.PRIMARY, null),
LOCATION("位置", FieldType.STRING, null),
_BOOL("布尔", FieldType.BOOL, null);
// --
private String displayName;
private Type fieldType;
private String defaultFormat;
private DisplayType(String displayName, Type fieldType) {
private DisplayType(String displayName, Type fieldType, String defaultFormat) {
this.displayName = displayName;
this.fieldType = fieldType;
this.defaultFormat = defaultFormat;
}
public String getDisplayName() {
@ -58,6 +67,10 @@ public enum DisplayType {
return fieldType;
}
public String getDefaultFormat() {
return defaultFormat;
}
@Override
public String toString() {
return getDisplayName() + " (" + name().toUpperCase() + ")";

View file

@ -29,8 +29,10 @@ import com.alibaba.fastjson.JSONObject;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.dialect.FieldType;
import cn.devezhao.persist4j.dialect.Type;
import cn.devezhao.persist4j.engine.ID;
import cn.devezhao.persist4j.metadata.BaseMeta;
import cn.devezhao.rebuild.server.RebuildException;
import cn.devezhao.rebuild.server.metadata.EntityHelper;
import cn.devezhao.rebuild.server.metadata.MetadataHelper;
@ -112,20 +114,44 @@ public class EasyMeta implements BaseMeta {
* @return
*/
public String getDisplayType(boolean fullName) {
if (baseMeta instanceof Field) {
Object[] ext = getMetaExt();
if (ext != null) {
DisplayType dt = (DisplayType) ext[2];
if (isField()) {
DisplayType dt = getDisplayType();
if (fullName) {
return dt.getDisplayName() + " (" + dt.name() + ")";
} else {
return dt.name();
}
} else {
return ((Field) baseMeta).getType().getName().toUpperCase();
}
throw new UnsupportedOperationException("Non `field` entry");
}
/**
* @return
*/
public DisplayType getDisplayType() {
if (isField()) {
Object[] ext = getMetaExt();
if (ext != null) {
DisplayType dt = (DisplayType) ext[2];
return dt;
}
Type ft = ((Field) baseMeta).getType();
if (ft == FieldType.PRIMARY) {
return DisplayType.ID;
} else if (ft == FieldType.REFERENCE) {
return DisplayType.REFERENCE;
} else if (ft == FieldType.TIMESTAMP) {
return DisplayType.DATETIME;
} else if (ft == FieldType.DATE) {
return DisplayType.DATE;
} else if (ft == FieldType.STRING) {
return DisplayType.TEXT;
} else if (ft == FieldType.BOOL) {
return DisplayType._BOOL;
}
}
return null;
throw new RebuildException("Unsupported field type : " + this.baseMeta);
}
/**
@ -239,12 +265,7 @@ public class EasyMeta implements BaseMeta {
* @return
*/
public static DisplayType getDisplayType(Field field) {
Object[] ext = MetadataHelper.getFieldExtmeta(field);
if (ext == null) {
return null;
} else {
return (DisplayType) ext[2];
}
return new EasyMeta(field).getDisplayType();
}
/**

View file

@ -48,7 +48,7 @@ public class GeneralListControll extends BaseControll {
public ModelAndView pageList(@PathVariable String entity,
HttpServletRequest request) throws IOException {
ModelAndView mv = createModelAndView("/general-entity/record-list.jsp", entity);
JSON cfg = DataListManager.getListColumnConfig(entity);
JSON cfg = DataListManager.getColumnLayout(entity);
mv.getModel().put("DataListConfig", JSON.toJSONString(cfg));
return mv;
}

View file

@ -13,24 +13,27 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cn.devezhao.rebuild.web.base.entity.datalist;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.alibaba.fastjson.JSON;
import cn.devezhao.commons.CalendarUtils;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.dialect.FieldType;
import cn.devezhao.persist4j.dialect.Type;
import cn.devezhao.persist4j.engine.ID;
import cn.devezhao.persist4j.query.compiler.SelectItem;
import cn.devezhao.rebuild.server.Application;
import cn.devezhao.rebuild.server.metadata.MetadataHelper;
import cn.devezhao.rebuild.server.service.base.FieldValueWrapper;
import cn.devezhao.rebuild.server.service.entitymanage.EasyMeta;
/**
* 数据包装
@ -39,43 +42,53 @@ import cn.devezhao.rebuild.server.metadata.MetadataHelper;
* @version $Id: DataWrapper.java 1 2014-11-26 17:20:23Z zhaoff@qidapp.com $
* @since 1.0, 2013-6-20
*/
public class DataWrapper {
public class DataWrapper extends FieldValueWrapper {
private static final Log LOG = LogFactory.getLog(DataWrapper.class);
private int total;
private Object[][] data;
private SelectItem[] selectFields;
private Entity entity;
/**
* @param total
* @param data
* @param fields
* @param selectFields
* @param entity
*/
public DataWrapper(int total, Object[][] data, SelectItem[] fields) {
public DataWrapper(int total, Object[][] data, SelectItem[] selectFields, Entity entity) {
this.total = total;
this.data = data;
this.selectFields = fields;
this.selectFields = selectFields;
this.entity = entity;
}
/**
* @return
*/
public String toJson() {
for (Object[] o : data) {
final Field namedFiled = entity.getNameField();
for (Object[] row : data) {
Object namedVal = null;
for (int i = 0; i < selectFields.length; i++) {
if (row[i] == null) {
row[i] = StringUtils.EMPTY;
continue;
}
Field field = selectFields[i].getField();
Type cType = field.getType();
if (cType == FieldType.DATE) {
o[i] = wrapDate(o[i], field);
} else if (cType == FieldType.TIMESTAMP) {
o[i] = wrapDatetime(o[i], field);
} else if (cType == FieldType.INT || cType == FieldType.LONG) {
o[i] = wrapNumber(o[i], field);
} else if (cType == FieldType.DOUBLE || cType == FieldType.DECIMAL) {
o[i] = wrapDecimal(o[i], field);
} else if (cType == FieldType.BOOL) {
o[i] = wrapBool(o[i], field);
if (field.equals(namedFiled)) {
namedVal = row[i];
}
if (field.getType() == FieldType.REFERENCE) {
row[i] = readReferenceNamed((ID) row[i], null);
} else if (field.getType() == FieldType.PRIMARY) { // Last index always
row[i] = readReferenceNamed((ID) row[i], namedVal);
} else {
o[i] = wrapSimple(o[i], field);
row[i] = wrapFieldValue(row[i], new EasyMeta(field));
}
}
}
@ -87,84 +100,29 @@ public class DataWrapper {
}
/**
* @param date
* @param field
* 读取 ID 型字段
*
* @param idVal
* @param namedVal
* @return
*/
protected Object wrapDate(Object date, Field field) {
if (date == null) {
return StringUtils.EMPTY;
protected Object[] readReferenceNamed(ID idVal, Object namedVal) {
Entity entity = MetadataHelper.getEntity(idVal.getEntityCode());
Field nameField = entity.getNameField();
if (namedVal == null) {
String sql = String.format("select %s from %s where %s = ?",
nameField.getName(), entity.getName(), entity.getPrimaryField().getName());
Object[] named = Application.createQuery(sql).setParameter(1, idVal).unique();
if (named == null) {
LOG.debug("Reference is deleted : " + idVal);
return null;
}
return CalendarUtils.getUTCDateFormat().format(date);
namedVal = named[0];
}
/**
* @param date
* @param field
* @return
*/
protected Object wrapDatetime(Object date, Field field) {
if (date == null) {
return StringUtils.EMPTY;
}
return CalendarUtils.getUTCDateTimeFormat().format(date);
}
/**
* @param number
* @param field
* @return
*/
protected Object wrapNumber(Object number, Field field) {
if (number == null) {
return StringUtils.EMPTY;
}
String fv = new DecimalFormat("##,###").format(number);
return StringUtils.isEmpty(fv) ? "0" : fv;
}
/**
* @param number
* @param field
* @return
*/
protected Object wrapDecimal(Object number, Field field) {
if (number == null) {
return StringUtils.EMPTY;
}
String fv = new DecimalFormat("##,##0.00").format(number);
return StringUtils.equals(fv, "0.00") ? "0" : fv;
}
/**
* @param bool
* @param field
* @return
*/
protected Object wrapBool(Object bool, Field field) {
return bool == null ? StringUtils.EMPTY : ((Boolean) bool ? "" : "");
}
/**
* @param value
* @param field
* @return
*/
protected Object wrapSimple(Object value, Field field) {
if (value == null) {
return StringUtils.EMPTY;
}
if (value instanceof Object[]) {
Object[] idNamed = (Object[]) value;
Object[] idNamed2 = new Object[3];
Entity idEntity = MetadataHelper.getEntity(((ID) idNamed[0]).getEntityCode());
idNamed2[2] = idEntity.getName();
idNamed2[1] = idNamed[1] == null ? StringUtils.EMPTY : idNamed[1].toString();
idNamed2[0] = idNamed[0].toString();
return idNamed2;
} else {
return value.toString();
}
namedVal = wrapFieldValue(namedVal, new EasyMeta(nameField));
String[] meta = new String[] { entity.getName(), new EasyMeta(entity).getIcon() };
return new Object[] { idVal.toLiteral(), namedVal, meta };
}
}

View file

@ -15,21 +15,10 @@ limitations under the License.
*/
package cn.devezhao.rebuild.web.base.entity.datalist;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.Query;
import cn.devezhao.persist4j.engine.ID;
import cn.devezhao.persist4j.query.compiler.SelectItem;
import cn.devezhao.rebuild.server.Application;
import cn.devezhao.rebuild.server.metadata.MetadataHelper;
import cn.devezhao.rebuild.server.service.base.PickListManager;
import cn.devezhao.rebuild.server.service.entitymanage.DisplayType;
import cn.devezhao.rebuild.server.service.entitymanage.EasyMeta;
/**
* 数据列表控制器
@ -40,7 +29,9 @@ import cn.devezhao.rebuild.server.service.entitymanage.EasyMeta;
*/
public class DefaultDataListControl implements DataListControl {
protected JSONQueryParser queryParser;
protected static final int READ_TIMEOUT = 15 * 1000;
protected JsonQueryParser queryParser;
/**
*/
@ -51,13 +42,13 @@ public class DefaultDataListControl implements DataListControl {
* @param queryElement
*/
public DefaultDataListControl(JSONObject queryElement) {
this.queryParser = new JSONQueryParser(queryElement, this);
this.queryParser = new JsonQueryParser(queryElement, this);
}
/**
* @return
*/
public JSONQueryParser getQueryParser() {
public JsonQueryParser getQueryParser() {
return queryParser;
}
@ -68,79 +59,18 @@ public class DefaultDataListControl implements DataListControl {
@Override
public String getResult() {
int timeout = 10 * 1000;
int total = 0;
if (queryParser.isNeedReload()) {
String countSql = queryParser.toSqlCount();
total = ((Long) Application.createQuery(countSql).unique()[0]).intValue();
}
Query query = Application.createQuery(queryParser.toSql()).setTimeout(timeout);
int[] limit = queryParser.getSqlLimit();
Object[][] array = query.setLimit(limit[0], limit[1]).array();
Query query = Application.createQuery(queryParser.toSql()).setTimeout(READ_TIMEOUT);
int[] limits = queryParser.getSqlLimit();
Object[][] array = query.setLimit(limits[0], limits[1]).array();
// 补充引用字段的 NameField
Field[] fields = queryParser.getFieldList();
for (int i = 0; i < fields.length; i++) {
DisplayType dt = EasyMeta.getDisplayType(fields[i]);
if (dt == DisplayType.REFERENCE) {
for (Object o[] : array) {
o[i] = readReferenceNamed(o[i]);
}
} else if (dt == DisplayType.PICKLIST) {
for (Object o[] : array) {
o[i] = readPickListLabel(o[i], fields[i]);
}
}
}
DataWrapper wrapper = getDataWrapper(total, array, query.getSelectItems());
DataWrapper wrapper = new DataWrapper(
total, array, query.getSelectItems(), query.getRootEntity());
return wrapper.toJson();
}
/**
* @param total
* @param data
* @param fields
* @return
*/
protected DataWrapper getDataWrapper(int total, Object[][] data, SelectItem[] fields) {
return new DataWrapper(total, data, fields);
}
/**
* @param idVal
* @return
*/
protected Object[] readReferenceNamed(Object idVal) {
if (idVal == null) {
return null;
}
ID id = (ID) idVal;
Entity entity = MetadataHelper.getEntity(id.getEntityCode());
String sql = String.format("select %s from %s where %s = ?",
entity.getNameField().getName(), entity.getName(), entity.getPrimaryField().getName());
Object[] named = Application.createQuery(sql).setParameter(1, id).unique();
return new Object[] { id, named == null ? "" : named[0] };
}
/**
* @param itemId
* @param field
* @return
*/
protected String readPickListLabel(Object itemId, Field field) {
if (itemId == null) {
return null;
}
List<Map<String, Object>> list = PickListManager.getPickListRaw(field.getOwnEntity().getName(), field.getName(), true, false);
for (Map<String, Object> e : list) {
if (itemId.toString().equals(e.get("id"))) {
return (String) e.get("text");
}
}
return "!!!删除!!!";
}
}

View file

@ -30,6 +30,8 @@ import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import cn.devezhao.rebuild.server.metadata.EntityHelper;
import cn.devezhao.rebuild.server.metadata.MetadataHelper;
import cn.devezhao.rebuild.server.service.entitymanage.DisplayType;
import cn.devezhao.rebuild.server.service.entitymanage.EasyMeta;
/**
* 列表查询解析
@ -38,7 +40,7 @@ import cn.devezhao.rebuild.server.metadata.MetadataHelper;
* @version $Id: QueryParser.java 1430 2015-04-16 11:29:21Z zhaoff@qidapp.com $
* @since 1.0, 2013-6-20
*/
public class JSONQueryParser {
public class JsonQueryParser {
protected JSONObject queryElement;
@ -56,7 +58,7 @@ public class JSONQueryParser {
* @param queryElement
* @param dataListControl
*/
public JSONQueryParser(JSONObject queryElement, DataListControl dataListControl) {
public JsonQueryParser(JSONObject queryElement, DataListControl dataListControl) {
this.queryElement = queryElement;
this.dataListControl = dataListControl;
@ -107,6 +109,9 @@ public class JSONQueryParser {
StringBuffer sqlBase = new StringBuffer("select ");
for (Field field : fieldList) {
if (EasyMeta.getDisplayType(field) == DisplayType.PICKLIST) {
sqlBase.append('&');
}
sqlBase.append(field.getName()).append(',');
}
// 最后增加一个主键列
@ -195,20 +200,8 @@ public class JSONQueryParser {
*/
protected String parseSort(String sort) {
String[] sort_s = sort.split(":");
return sort_s[0] + ("desc".equalsIgnoreCase(sort_s[1]) ? " desc" : " asc");
// StringBuffer sb = new StringBuffer();
// int index = 0;
// for (Object o : sortNode) {
// JSONObject el = (JSONObject) o;
// String field = el.getString("field");
// String type = BooleanUtils.toBoolean(el.getString("ascending")) ? " asc" : " desc";
//
// sb.append(index++ == 0 ? ' ' : ',')
// .append(field)
// .append(type);
// }
// return sb.toString();
String sortField = sort_s[0];
return sortField + ("desc".equalsIgnoreCase(sort_s[1]) ? " desc" : " asc");
}
/**

View file

@ -87,7 +87,7 @@
<index type="unique" field-list="belongEntity,physicalName"/>
</entity>
<entity name="PickList" type-code="012" description="下拉列表配置选项">
<entity name="PickList" type-code="012" name-field="text" description="下拉列表配置选项">
<field name="itemId" type="primary"/>
<field name="belongEntity" type="string" max-length="100" nullable="false" updatable="false" />
<field name="belongField" type="string" max-length="100" nullable="false" updatable="false" />

View file

@ -52,7 +52,7 @@
<servlet>
<servlet-name>FileUploader</servlet-name>
<servlet-class>cn.devezhao.rebuild.web.commons.FileUploader</servlet-class>
<servlet-class>cn.devezhao.rebuild.web.common.FileUploader</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileUploader</servlet-name>
@ -60,7 +60,7 @@
</servlet-mapping>
<servlet>
<servlet-name>FileDownload</servlet-name>
<servlet-class>cn.devezhao.rebuild.web.commons.FileDownload</servlet-class>
<servlet-class>cn.devezhao.rebuild.web.common.FileDownload</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileDownload</servlet-name>

View file

@ -1,6 +1,6 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" type="text/css" href="${baseUrl}/assets/lib/material-design-iconic-font.min.css">
<link rel="stylesheet" type="text/css" href="${baseUrl}/assets/lib/perfect-scrollbar.min.css">
<link rel="stylesheet" type="text/css" href="${baseUrl}/assets/lib/bootstrap-datetimepicker.min.css">

View file

@ -5,7 +5,7 @@
<%@ include file="/_include/Head.jsp"%>
<title>实体管理</title>
<style type="text/css">
a#entityIcon{display:inline-block;width:36px;height:36px;background-color:rgb(213, 216, 222);text-align:center;border-radius:2px;}
a#entityIcon{display:inline-block;width:36px;height:36px;background-color:#e3e3e3;text-align:center;border-radius:2px;}
a#entityIcon .icon{font-size:26px;color:#555;line-height:36px;}
a#entityIcon:hover{opacity:0.8}
</style>

View file

@ -7,10 +7,11 @@
<style type="text/css">
.card.entity:hover{background-color:rgba(255,255,255,.7)}
.card.entity .card-body{padding:14px 20px;color:#333;}
.card.entity .icon{font-size:32px;margin-right:12px;color:#4285f4;}
.card.entity .icon{font-size:32px;color:#4285f4;}
.card.entity .card-body .float-left{width:30px;text-align:center;}
.card.entity span{margin-top:2px;display:block;}
.card.entity p{margin:0}
.title{margin-left:43px}
.title{margin-left:44px}
</style>
</head>
<body>

View file

@ -71,13 +71,13 @@
<div class="form-group row J_for-DECIMAL hide">
<label class="col-sm-2 col-form-label text-sm-right">小数位长度</label>
<div class="col-lg-4 col-sm-10">
<select class="form-control form-control-sm" id="decimalPrecision">
<option value="1">1位</option>
<option value="2" selected="selected">2位</option>
<option value="3">3位</option>
<option value="4">4位</option>
<option value="5">5位</option>
<option value="6">6位</option>
<select class="form-control form-control-sm" id="decimalFormat">
<option value="##,##0.0">1位</option>
<option value="##,##0.00" selected="selected">2位</option>
<option value="##,##0.000">3位</option>
<option value="##,##0.0000">4位</option>
<option value="##,##0.00000">5位</option>
<option value="##,##0.000000">6位</option>
</select>
</div>
</div>
@ -87,11 +87,20 @@
<div class="form-control-plaintext"><a href="../../${fieldRefentity}/base">${fieldRefentityLabel} (${fieldRefentity})</a></div>
</div>
</div>
<div class="form-group row J_for-DATETIME hide">
<div class="form-group row J_for-DATE hide">
<label class="col-sm-2 col-form-label text-sm-right">格式</label>
<div class="col-lg-4 col-sm-10">
<select class="form-control form-control-sm" id="dateFormat">
<option value="yyyy">YYYY</option>
<option value="yyyy-MM">YYYY-MM</option>
<option value="yyyy-MM-dd" selected="selected">YYYY-MM-DD</option>
</select>
</div>
</div>
<div class="form-group row J_for-DATETIME hide">
<label class="col-sm-2 col-form-label text-sm-right">格式</label>
<div class="col-lg-4 col-sm-10">
<select class="form-control form-control-sm" id="datetimeFormat">
<option value="yyyy-MM-dd">YYYY-MM-DD</option>
<option value="yyyy-MM-dd HH:mm" selected="selected">YYYY-MM-DD HH:MM</option>
<option value="yyyy-MM-dd HH:mm:ss">YYYY-MM-DD HH:MM:SS</option>
@ -134,7 +143,7 @@
</div>
<div class="form-group row footer">
<div class="col-lg-4 col-sm-10 offset-sm-2">
<button class="btn btn-primary" type="button">保存</button>
<button class="btn btn-primary J_save" type="button">保存</button>
<div class="alert alert-warning alert-icon hide">
<div class="icon"><span class="zmdi zmdi-alert-triangle"></span></div>
<div class="message">系统内建字段,不允许修改</div>
@ -163,7 +172,7 @@ $(document).ready(function(){
if (dt.indexOf('(') > -1) dt = dt.match('\\((.+?)\\)')[1];
const extConfigOld = JSON.parse('${fieldExtConfig}' || '{}');
const btn = $('.btn-primary').click(function(){
const btn = $('.J_save').click(function(){
if (!!!metaId) return;
let label = $val('#fieldLabel'),
comments = $val('#comments'),

View file

@ -21,6 +21,7 @@
<select class="form-control form-control-sm" id="type">
<option value="NUMBER">整数</option>
<option value="DECIMAL">货币</option>
<option value="DATE">日期</option>
<option value="DATETIME">日期时间</option>
<option value="TEXT">文本</option>
<option value="PHONE">电话</option>
@ -83,7 +84,7 @@ $(document).ready(function(){
let referenceLoaded = false;
$('#type').change(function(){
if (parent && parent.newFieldModal) parent.newFieldModal.loaded()
if (parent && parent.newFieldModal) parent.newFieldModal.resize()
if ($(this).val() == 'REFERENCE'){
$('.J_dt-REFERENCE').removeClass('hide');
if (referenceLoaded == false){

View file

@ -94,9 +94,9 @@ $(document).ready(function(){
$(res.data).each(function(){
let tr = $('<tr data-id="' + (this.fieldId || '') + '"></tr>').appendTo(tbody);
$('<td><a href="field/' + this.fieldName + '" class="column-main">' + this.fieldLabel + '</a></td>').appendTo(tr);
$('<td>' + this.fieldName + '</td>').appendTo(tr);
$('<td><div class="text-muted">' + this.fieldName + '</div></td>').appendTo(tr);
$('<td>' + this.displayType + '</td>').appendTo(tr);
$('<td><div style="max-width:300px">' + (this.comments || '--') + '</div></td>').appendTo(tr);
$('<td><div>' + (this.comments || '') + '</div></td>').appendTo(tr);
let actions = $('<td class="actions"><a class="icon J_edit" href="field/' + this.fieldName + '"><i class="zmdi zmdi-settings"></i></a><a class="icon J_del"><i class="zmdi zmdi-delete"></i></a></td>').appendTo(tr);
actions.find('.J_del').click(function(){
if (!!!tr.data('id')){

View file

@ -57,7 +57,7 @@
<div class="card">
<div class="card-header">
字段列表
<div class="float-right"><span class="not-nullable">必填</span><span class="readonly">只读</span></div>
<div class="float-right"><span class="not-nullable">必填字段</span><span class="readonly">只读字段</span></div>
</div>
<div class="card-body" style="padding-top:6px">
<div class="field-list dd-list connectedSortable">

View file

@ -120,7 +120,7 @@ const itemRender = function(data, append){
item.find('.dd3-action .J_del').off('click').click(function(){
$(this).parent().parent().remove()
});
if (data['default'] === true) item.find('.dd3-action .J_default').trigger('click')
if (data['default'] === true && append == '.J_showbox') item.find('.dd3-action .J_default').trigger('click')
};
</script>
</body>

View file

@ -5844,6 +5844,16 @@ button.close {
margin: -1rem -1rem -1rem auto
}
.modal-header > .icon {
font-size: 2.1rem;
color: #4285f4;
width: 24px;
overflow: hidden;
text-align: center;
margin-right: 9px;
margin-top: 2px;
}
.modal-title {
margin-bottom: 0;
line-height: 1.428571
@ -18873,7 +18883,7 @@ pre code {
width: 1.51rem;
height: 1.51rem;
border: 1px solid #ccc;
border-radius: 3px;
border-radius: 2px;
margin: 0 0
}
@ -18885,6 +18895,7 @@ pre code {
font-weight: 400;
text-align: center;
line-height: 1.1;
margin-left: -1px;
}
.custom-radio .custom-control-label:before {

View file

@ -288,7 +288,7 @@ a {
width: 12px;
height: 32px;
cursor: pointer;
padding-top: 10px;
padding-top: 12px;
}
.col-form-label em {
@ -308,7 +308,7 @@ a {
}
.rbnotice {
position: absolute;
position: fixed;
width: 360px;
top: 6px;
left: 50%;
@ -405,6 +405,11 @@ i.split.ui-draggable-dragging {
text-overflow: ellipsis;
overflow: hidden;
clear: both;
max-width: 300px;
}
.data-list .table td, .data-list .table td>div.nmw {
max-width: 1000px;
}
.data-list td .img-zoom {
@ -436,12 +441,26 @@ i.split.ui-draggable-dragging {
padding-left: 0px;
}
.column-checkbox .custom-control-label::after {
margin-left: 0 !important;
}
.column-main {
border-bottom: 1px dotted;
border-bottom: 1px solid;
}
.column-main:hover {
border-bottom: 0 solid;
}
.column-url {
}
.column-url:hover {
text-decoration: underline;
}
.column-empty {
}
.text-3dot {
@ -573,6 +592,7 @@ h5.sortable-box-title {
}
.rbview-form {
}
.rbview-form .card-header-divider {
@ -589,3 +609,7 @@ h5.sortable-box-title {
.rbview-form .form-control-plaintext:hover {
background-color: #f8f8f8
}
.main-content .card.card-table {
margin-bottom: 0;
}

View file

@ -134,7 +134,7 @@ const $regex = {
_Number:/^[-+]?[0-9]+$/, // 数字
_Decimal:/^[-+]?\d*\.?\d+$/, // 包括小数点的数字
_Mobile:/^(13[0-9]|15[0-9]|18[0-9]|17[0-9])\d{8}$/,
_Tel:/^[0-9-]{4,20}$/,
_Tel:/^[0-9-]{4,18}$/,
_Text:/^[a-z\d\u4E00-\u9FA5]+$/i, // 不含特殊字符和标点
isDate:function(val){
return this._Date.test(val);

View file

@ -63,14 +63,16 @@ class RbView extends React.Component {
this.state = { ...props, inLoad: true, isShow: false }
}
render() {
let adminUrl = rb.baseUrl + '/admin/entity/' + this.props.entity + '/view-design'
return (
<div className={'modal rbview ' + (this.state.isShow == true && 'show')} ref="rbview">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h3 className="modal-title">{this.state.title || 'RbView'}</h3>
<a className="close admin-settings" href={adminUrl} title="配置布局" target="_blank"><span className="zmdi zmdi-settings"></span></a>
{this.state.icon ? (<span className={'icon zmdi zmdi-' + this.state.icon}></span>) : '' }
<h3 className="modal-title">
{this.state.title || '视图'}
</h3>
<a className="close admin-settings" href={rb.baseUrl + '/admin/entity/' + this.state.entity + '/view-design'} title="配置布局" target="_blank"><span className="zmdi zmdi-settings"></span></a>
<button className="close" type="button" onClick={()=>this.hide()}><span className="zmdi zmdi-close"></span></button>
</div>
<div className={'modal-body iframe rb-loading ' + (this.state.inLoad == true && 'rb-loading-active')}>
@ -102,11 +104,12 @@ class RbView extends React.Component {
this.setState({ inLoad: false })
}
}
show(url) {
this.state.url = url || this.state.url
show(url, ext) {
ext = ext || {}
url = url || this.state.url
let root = $(this.refs['rbview'])
let that = this
this.setState({ isShow: true }, function(){
this.setState({ ...ext, isShow: true, url: url }, function(){
root.find('.modal-content').animate({ 'margin-right': 0 }, 400)
setTimeout(function(){
that.setState({ showAfterUrl: that.state.url })
@ -237,8 +240,8 @@ rb.modal = function(url, title, ext) {
//
var viewModal
rb.recordView = function(id, entity) {
rb.recordView = function(id, title, entity, icon) {
let viewUrl = rb.baseUrl + '/app/' + entity + '/view/' + id
if (viewModal) viewModal.show(viewUrl)
else viewModal = renderRbcomp(<RbView url={viewUrl} entity={entity} />)
if (viewModal) viewModal.show(viewUrl, { entity: entity, title: title, icon: icon })
else viewModal = renderRbcomp(<RbView url={viewUrl} entity={entity} title={title} icon={icon} />)
}

View file

@ -77,7 +77,7 @@ const __detectElement = function(item){
return <RbFormImage {...item} />
} else if (item.type == 'FILE'){
return <RbFormFile {...item} />
} else if (item.type == 'DATETIME'){
} else if (item.type == 'DATETIME' || item.type == 'DATE'){
return <RbFormDateTime {...item} />
} else if (item.type == 'PICKLIST'){
return <RbFormPickList {...item} />
@ -123,7 +123,7 @@ class RbForm extends React.Component {
__renderFormError(message) {
let fdUrl = rb.baseUrl + '/admin/entity/' + this.props.entity + '/form-design'
message = message || `表单尚未配置,请 <a href="${fdUrl}">配置</a> 后使用`
message = { __html: '<strong>Opps! </strong> ' + message }
message = { __html: '<strong>错误! </strong> ' + message }
return <div class="alert alert-contrast alert-warning">
<div class="icon"><span class="zmdi zmdi-alert-triangle"></span></div>
<div class="message" dangerouslySetInnerHTML={message}></div>
@ -331,7 +331,7 @@ class RbFormDateTime extends RbFormElement {
}
componentDidMount() {
super.componentDidMount()
let format = (this.props.dateFormat || 'yyyy-MM-dd HH:mm:ss').replace('mm', 'ii').toLowerCase()
let format = (this.props.datetimeFormat || this.props.dateFormat).replace('mm', 'ii').toLowerCase()
let minView = 0
switch (format.length) {
case 7:

View file

@ -11,7 +11,7 @@ class RbList extends React.Component {
this.setPageSize = this.setPageSize.bind(this)
this.__defaultColumnWidth = $('#react-list').width() / 10
this.__defaultColumnWidth = 130;
if (this.__defaultColumnWidth < 130) this.__defaultColumnWidth = 130
}
render() {
let that = this;
@ -39,12 +39,12 @@ class RbList extends React.Component {
<tbody>
{this.state.rowData.map((item, index) => {
let lastGhost = item[lastIndex];
return (<tr className={lastGhost[1] && 'table-active'} data-id={lastGhost[0]} onClick={this.clickRow.bind(this, index, false)}>
return (<tr className={lastGhost[3] && 'table-active'} data-id={lastGhost[0]} onClick={this.clickRow.bind(this, index, false)}>
<td className="column-checkbox">
<div><label className="custom-control custom-control-sm custom-checkbox"><input className="custom-control-input" type="checkbox" checked={lastGhost[1]} onClick={this.clickRow.bind(this, index, true)} /><span className="custom-control-label"></span></label></div>
<div><label className="custom-control custom-control-sm custom-checkbox"><input className="custom-control-input" type="checkbox" checked={lastGhost[3]} onClick={this.clickRow.bind(this, index, true)} /><span className="custom-control-label"></span></label></div>
</td>
{item.map((cell, index) => {
return that.__renderCell(cell, index)
return that.__renderCell(cell, index, lastGhost)
})}
<td className="column-empty"></td>
</tr>)
@ -78,15 +78,16 @@ class RbList extends React.Component {
}
componentDidUpdate() {
let that = this
let selectedRows = []
this.__selectedRows = []
this.state.rowData.forEach((item) => {
let lastGhost = item[that.state.fields.length];
if (lastGhost[1] == true) selectedRows.push(lastGhost[0])
if (lastGhost[3] == true) that.__selectedRows.push(lastGhost)
})
$('.J_delete, .J_view').attr('disabled', true)
if (selectedRows.length > 0) $('.J_delete').attr('disabled', false)
if (selectedRows.length == 1) $('.J_view').attr('disabled', false)
this.__selectedRows = selectedRows
let sLen = this.__selectedRows.length
if (sLen > 0) $('.J_delete').attr('disabled', false)
if (sLen == 1) $('.J_view').attr('disabled', false)
}
fetchList(filter) {
@ -114,13 +115,13 @@ class RbList extends React.Component {
if (_rowData.length == 0) {
that.setState({ noData: true });
} else {
let lastIndex = _rowData[0].length - 1;
let lastIndex = _rowData[0].length - 1
_rowData = _rowData.map((item) => {
item[lastIndex] = [item[lastIndex], false] // [ID, Checked?]
item[lastIndex][3] = false // Checked?
return item;
})
}
that.setState({ rowData: _rowData, rowTotal: res.data.total });
that.setState({ rowData: _rowData, rowTotal: res.data.total })
}else{
rb.notice(res.error_msg || '数据加载失败,请稍后重试', 'error')
}
@ -130,63 +131,71 @@ class RbList extends React.Component {
//
__renderCell(cellVal, index) {
if (this.state.fields.length == index) return null;
if (!!!cellVal) return <td><div></div></td>;
__renderCell(cellVal, index, lastGhost) {
if (this.state.fields.length == index) return null
if (!!!cellVal) return <td><div></div></td>
let ft = this.state.fields[index].type;
const field = this.state.fields[index]
let styles = { width: (this.state.fields[index].width || this.__defaultColumnWidth) + 'px' }
if (ft == 'IMAGE') {
cellVal = JSON.parse(cellVal)
if (field.type == 'IMAGE') {
cellVal = JSON.parse(cellVal || '[]')
return <td><div style={styles}>{cellVal.map((item)=>{
return <a href={'#!/Preview/' + item} className="img-thumbnail img-zoom"><img src={rb.storageUrl + item} /></a>
})}<div className="clearfix" /></div></td>;
} else if (ft == 'FILE') {
cellVal = JSON.parse(cellVal);
})}<div className="clearfix" /></div></td>
} else if (field.type == 'FILE') {
cellVal = JSON.parse(cellVal || '[]')
return <td><div style={styles}>{cellVal.map((item)=>{
let fileName = item.split('/');
let fileName = item.split('/')
if (fileName.length > 1) fileName = fileName[fileName.length - 1];
fileName = fileName.substr(15);
return <a href={'#!/Preview/' + item}>{fileName}</a>
})}</div></td>;
} else if ($.type(cellVal) == 'array'){
return <td><div style={styles}><a href={'#!/View/' + cellVal[2] + '/' + cellVal[0]} onClick={() => this.clickView(cellVal)}>{cellVal[1]}</a></div></td>;
} else if (field.type == 'REFERENCE'){
return <td><div style={styles}><a href={'#!/View/' + cellVal[2][0] + '/' + cellVal[0]} onClick={() => this.clickView(cellVal)}>{cellVal[1]}</a></div></td>
} else if (field.field == this.props.config.nameField){
cellVal = lastGhost
return <td><div style={styles}><a href={'#!/View/' + cellVal[2][0] + '/' + cellVal[0]} onClick={() => this.clickView(cellVal)} className="column-main">{cellVal[1]}</a></div></td>
} else if (field.type == 'URL') {
return <td><div style={styles}><a href={rb.baseUrl + '/common/url-safe?url=' + encodeURIComponent(cellVal)} className="column-url" target="_blank">{cellVal}</a></div></td>
} else if (field.type == 'EMAIL') {
return <td><div style={styles}><a href={'mailto:' + cellVal} className="column-url">{cellVal}</a></div></td>
} else {
return <td><div style={styles}>{cellVal || ''}</div></td>;
return <td><div style={styles}>{cellVal}</div></td>
}
}
toggleAllRow(e) {
let checked = this.state.checkedAll == false;
let _rowData = this.state.rowData;
let checked = this.state.checkedAll == false
let _rowData = this.state.rowData
_rowData = _rowData.map((item) => {
item[item.length - 1][1] = checked; // Checked?
item[item.length - 1][3] = checked // Checked?
return item;
});
this.setState({ checkedAll: checked, rowData: _rowData });
this.setState({ checkedAll: checked, rowData: _rowData })
return false;
}
clickRow(rowIndex, holdOthers, e) {
if (e.target.tagName == 'SPAN') return false;
if (e.target.tagName == 'SPAN') return false
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
let _rowData = this.state.rowData;
let lastIndex = _rowData[0].length - 1;
let _rowData = this.state.rowData
let lastIndex = _rowData[0].length - 1
if (holdOthers == true){
let item = _rowData[rowIndex];
item[lastIndex][1] = item[lastIndex][1] == false; // Checked?
_rowData[rowIndex] = item;
item[lastIndex][3] = item[lastIndex][3] == false // Checked?
_rowData[rowIndex] = item
} else {
_rowData = _rowData.map((item, index) => {
item[lastIndex][1] = index == rowIndex;
return item;
item[lastIndex][3] = index == rowIndex
return item
})
}
this.setState({ rowData: _rowData });
return false;
this.setState({ rowData: _rowData })
return false
}
clickView(cellVal) {
console.log(cellVal)
rb.recordView(cellVal[0], cellVal[1], cellVal[2][0], cellVal[2][1])
return false;
}
@ -209,7 +218,7 @@ class RbList extends React.Component {
//
getSelectedIds() {
getSelectedRows() {
return this.__selectedRows
}

View file

@ -75,15 +75,18 @@ $(document).ready(function(){
else rbFormModal = renderRbcomp(<RbFormModal title="新建${entityLabel}" entity="${entityName}" />, 'react-forms')
});
$('.J_delete').click(function(){
let ids = rbList.getSelectedIds()
if (ids.length < 1) return
rb.alter('确认删除选中的 ' + ids.length + ' 条记录吗?', '删除确认', { type: 'danger', confirm: function(){
console.log('TODO delete ... ')
let s = rbList.getSelectedRows()
if (s.length < 1) return
rb.alter('确认删除选中的 ' + s.length + ' 条记录吗?', '删除确认', { type: 'danger', confirm: function(){
console.log('TODO delete ... ' + JSON.stringify(s))
} })
});
$('.J_view').click(function(){
let ids = rbList.getSelectedIds()
if (ids.length == 1) rb.recordView(ids[0], '${entityName}')
let s = rbList.getSelectedRows()
if (s.length == 1) {
s = s[0]
rb.recordView(s[0], s[1], s[2][0], s[2][1])
}
});
$('.J_columnset').click(function(){

View file

@ -24,10 +24,10 @@
<input class="form-control" id="passwd" type="password" placeholder="密码">
</div>
<div class="form-group row login-tools">
<div class="col-6 login-remember">
<label class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox">
<span class="custom-control-label">记住登录</span>
<label class="custom-control custom-checkbox custom-control-inline" style="margin-bottom:0">
<input class="custom-control-input" type="checkbox"><span class="custom-control-label"> 记住登录</span>
</label>
</div>
<div class="col-6 login-forgot-password">