前,我内置表单功能中,已有自动关联字典数据方式。由于,此前字典数据过于依赖表单字段,导致内存占用较高,字典数据重复性较大,字典缓存清理不便捷。
故,我欲改造之,将其与表单字段,单独剥离,并统一与功能对象进行关联,从而解决如上问题。
后端,需设计“功能字典关联表”用于查询,独立“字典配置”注解,改造功能管理类。
前端,需提供通用字典获取函数,改造使用字典的元素如(下拉框、单选框、多选框等)。
整体看来,此次改造还是比较简单的,接下来就直接进入编码环节。
package com.flycoding.drivenlibrary.engine.function.entity;
import com.flycoding.dblibrary.annotation.create.Column;
import com.flycoding.dblibrary.annotation.create.PrimaryAuto;
import com.flycoding.dblibrary.annotation.create.Table;
import com.flycoding.dblibrary.enums.ColumnType;
import com.flycoding.drivenlibrary.engine.constants.SqlConstants;
import com.flycoding.drivenlibrary.engine.function.entity.base.BaseFunctionMessage;
/**
* 功能字典关联表
*
* @author 赵屈犇
* @version 1.0
* @date 创建时间: 2022/10/18 20:47
* @Copyright(C): 2022 by 赵屈犇
*/
@Table(tableName="Sy_Func_Dict")
public class FuncDictInfo extends BaseFunctionMessage {
/** 主键 */
@PrimaryAuto
private Integer id;
/** 普通字典编码 */
@Column(columnName="dict_code", columnType=ColumnType.VARCHAR, length=SqlConstants.DB_CODE_SIZE)
private String dictCode;
/** 数据字典编码 */
@Column(columnName="data_dict_code", columnType=ColumnType.VARCHAR, length=SqlConstants.DB_CODE_SIZE)
private String dataDictCode;
/** 字典驱动库关联配置编码 */
@Column(columnName="db_config_code", columnType=ColumnType.VARCHAR, length=SqlConstants.DB_CODE_SIZE)
private String dbConfigCode;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id=id;
}
public String getDictCode() {
return dictCode;
}
public void setDictCode(String dictCode) {
this.dictCode=dictCode;
}
public String getDataDictCode() {
return dataDictCode;
}
public void setDataDictCode(String dataDictCode) {
this.dataDictCode=dataDictCode;
}
public String getDbConfigCode() {
return dbConfigCode;
}
public void setDbConfigCode(String dbConfigCode) {
this.dbConfigCode=dbConfigCode;
}
}
此对象,用于功能与字典之间的关联。当,用户获取功能时,后台通过此函数,自动装载入字典数据。
package com.threeox.drivenlibrary.engine.annotation.function.form.field;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 字典配置
*
* @author 赵屈犇
* @version 1.0
* @date 创建时间: 2022/10/18 20:18
* @Copyright(C): 2022 by 赵屈犇
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE })
public @interface DictConfig {
/**
* 普通字典编码
*
* @return
*/
String dictCode() default "";
/**
* 数据字典编码
*
* @return
*/
String dataDictCode() default "";
/**
* 字典驱动库关联配置
*
* @return
*/
String dbConfigCode() default "";
/**
* 是否使用配置
*
* @return
*/
boolean isUseConfig() default true;
}
此注解,暂用于表单字段与字典配置的关联函数。
可以看到,字典数据已于功能进行绑定。
接下来,我准备将字典数据,直接绑定至功能对象上,并返回给前端。
为提升查询效率, 此处仍采用初次查询,后续缓存的方式。
package com.threeox.drivenlibrary.engine.function.factory.config;
import com.threeox.drivenlibrary.cache.impl.function.FuncDictCacheManager;
import com.threeox.drivenlibrary.engine.constants.config.request.ConfigRequestConstants;
import com.threeox.drivenlibrary.engine.function.entity.FuncDictInfo;
import com.threeox.drivenlibrary.engine.function.factory.base.BaseCustomFactory;
import com.threeox.drivenlibrary.engine.request.execute.ExecuteRequestFactory;
import java.util.List;
/**
* 功能字典关联工厂
*
* @author 赵屈犇
* @version 1.0
* @date 创建时间: 2022/10/20 20:42
* @Copyright(C): 2022 by 赵屈犇
*/
public class FuncDictFactory extends BaseCustomFactory<List<FuncDictInfo>> {
private static FuncDictFactory inst=null;
public static FuncDictFactory getInstance() {
if (inst==null) {
synchronized (FuncDictFactory.class) {
if (inst==null) {
inst=new FuncDictFactory();
}
}
}
return inst;
}
private FuncDictFactory() {
}
@Override
protected void init() {
}
@Override
protected void initCacheManager() {
cache=FuncDictCacheManager.getInstance();
}
@Override
protected List<FuncDictInfo> initResult(ExecuteRequestFactory requestFactory, String dbConfigCode, Object key, Integer relatedId, Object... extendParams) throws Exception {
// 查询关联字典配置根据功能主键
return getResult(requestFactory, ConfigRequestConstants.QUERY_FUNC_DICT_BY_FUNC_ID, relatedId);
}
}
最后,我在获取功能对象时,动态获取字典配置,并绑定字典结果数据。
为兼容普通字典和数据字典主键可能重复问题,我根据dictCode,dataDictCode,dbConfigCode进行拼接,生成新的key值。
/**
* 初始化功能字典数据
*
* @param functionInfo
* @return a
* @author 赵屈犇
* @date 创建时间: 2022/10/20 20:58
* @version 1.0
*/
private void initFuncDictData(FunctionMessage functionInfo) {
if (functionInfo !=null) {
// 获取关联字典数据
List<FuncDictInfo> dictInfos=funcDictFactory.getResult(functionInfo.getDbConfigCode(), functionInfo.getFuncCode(), functionInfo.getFuncId());
if (ArrayUtils.isNotEmpty(dictInfos)) {
Map <String, List<DictDataInfo>> dictMap=new HashMap<>();
dictInfos.forEach(dictInfo - > {
// key
String key=StringUtils.appendStrings(dictInfo.getDictCode(), dictInfo.getDataDictCode(), dictInfo.getDbConfigCode());
if (!dictMap.containsKey(key)) {
List<DictDataInfo> dictionaryInfos=null;
// 普通字典
if (StringUtils.isNotEmpty(dictInfo.getDictCode())) {
// 普通字典类型
dictionaryInfos=dictFactory.getResult(dictInfo.getDbConfigCode(), dictInfo.getDictCode());
// 数据字典
} else if (StringUtils.isNotEmpty(dictInfo.getDataDictCode())) {
dictionaryInfos=dataDictResultFactory.getResult(dictInfo.getDataDictCode());
}
dictMap.put(key, dictionaryInfos);
}
});
functionInfo.setDictDatas(dictMap);
}
}
至此,后端功能业已完成。此下,将进行改造前端功能。
/**
* 获取字典数据
*
* @param elementConfig
* @returns {null|*}
*/
engine.getDictData=function (elementConfig) {
if (self.funcMessage) {
let key=[elementConfig['dictCode'], elementConfig['dataDictCode'], elementConfig['dictDBConfigCode']].join(
"");
return self.funcMessage.dictDatas[key];
}
return null;
}
此函数,内置在基础引擎js段中,故可以直接获取功能对象,并通过功能对象直接获取字典数据。
此下,我选了选择框元素进行改造,其他的复刻即可。
// 定义选择框元素
let selectView=factory.byId("${fieldCode}");
// 定义过滤lay
let layFilter="${fieldCode}_" + self.containerId;
// 设置属性
selectView.attr("lay-filter", layFilter);
// 获取字典
let dicts=factory.getDictData(elementConfig);
if (!engineCommon.isListEmpty(dicts)) {
for (let i=0, length=dicts.length; i < length; i++) {
try {
let dict=dicts[i];
let dictionaryValue=dict["dictionaryValue"];
let optionHtml="<option name='" + layFilter + "' id='${fieldCode}_" + dictionaryValue + "' value='" + dictionaryValue + "'>" + dictionary["dictionaryName"] + "</option>";
selectView.append(optionHtml);
} catch(e) {
console.log(e);
}
}
layFactory.render('select');
}
// 监听选中事件
layFactory.on(LayEventConstants.SELECT, layFilter, function(data){
selectView.val(data.value);
// 回调lay事件
if (self.factory && self.factory['onLayEvent']) {
self.factory.onLayEvent('${fieldCode}', LayEventConstants.SELECT, data);
}
});
至此,其他字典组件相继修改,既完成。
在寻找支持在Java中用编程方法处理各类格式文档的API吗?好巧,Java版企业级文档管理组合套包Spire.Office 2020全新上线!Word、Excel、PPT、PDF、条形码等格式一网打尽。
目前,Spire.Office for Java 迎来v3.1.0版的更新。Excel格式处理控件Spire.XLS(点击下载)强势加入,同时各个产品也增加了新功能。如Spire.PDF 支持转换PDF到SVG时设置宽度和高度像素,并公开了PdfFileLinkAnnotationWidget类及getValue属性;Spire.Presentation 支持转换PPT到HMTL时,可设置内容居中;同时,也修复了将PDF转换为Word/HTML、PPT转换为PDF/图片、Word转换为PDF/HTML、给PPT设置图片背景、对Word文档进行加密时出现的问题。
新功能及问题修复详情,请参阅如下内容。(点击文末“了解更多”可下载Spire.office全部产品)
新功能:
问题修复:
新功能:
问题修复:
问题修复:
述
在表单的设计过程中,会有一些表单字段需要在已知的内容中进行选择,这在html中会使用select组件来设计该表单字段。而在Material中,同样有与之对应的 <mat-select> 组件。这种组件在选项内容较少的情况下使用非常方便,能很好的引导用户在表单中,输入规范的内容:
但是在选项内容过多时,这种 <mat-select> 组件,对用户来说,想要找到自己想要的选项就比较麻烦,需要一个个选项去对比,查找:
幸运的是,在Material中提供了 mat-autocomplete ,这个组件和我们以前在JQuery时代使用的select2类似。它支持用户键盘输入功能,并根据输入内容在已有的数据集合中进行过滤,选项内容动态的显示过滤后的结果。
html:
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of options" [value]="option">
{{option}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
TypeScript:
import {Component} from '@angular/core';import {FormControl} from '@angular/forms';/**
* @title Simple autocomplete
*/@Component({
selector: 'autocomplete-simple-example',
templateUrl: 'autocomplete-simple-example.html',
styleUrls: ['autocomplete-simple-example.css'],
})export class AutocompleteSimpleExample {
myControl=new FormControl();
options: string[]=['One', 'Two', 'Three'];
}
呈现的效果如下:
在简单用法中,我们发现,选项内容为字符串,选择中的值和显示的值为相同的。但在实际的应用过程中,我们需要选中的值和显示的内容是不同的。
举个例子,我们有个需要选择企业的字段,要存储到数据库的值是企业的编码,而在界面上显示的需要是企业的名称。在这个例子中,我们有个企业的数据结构:
interface Company {
code: string;
name: string;
}
companies: Company[]=[]; constructor() { this.companies.push({code: 'qiye-01', name: '企业01'}); this.companies.push({code: 'qiye-02', name: '企业02'}); this.companies.push({code: 'qiye-03', name: '企业03'}); this.companies.push({code: 'qiye-04', name: '企业04'});
}
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of companies" [value]="option">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
在html中,我们将mat-option的显示内容修改成name,如 {{option.name}} 。这样我们的下拉选项中就会显示企业的名称:
但是如果我们选中其中一个选项,就会发现结果和我们想象的不一样,选中后的结果显示并不是我们想要的企业名称
此时,我们就需要使用API中提供的
在MatAutocomplete的API: displayWith。文档已经很明确的告诉我们 displayWith 需要我们传入一个已定义的函数
@Input()
displayWith: ((value: any)=> string) | null
其中的函数形参value指的是 <mat-option *ngFor="let option of companies" [value]="option"> 中value对应的option,此处option就是一个我们后台定义的Company。这样我们的value就拥有code和name两个属性。
这样我们就可以定义一个displayWith的函数:
displayFn(value: any): string { // value: Company
return value ? value.name : undefined;
}
同时修改html
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let option of companies" [value]="option">
{{option.name}} </mat-option></mat-autocomplete>
这样就会显示我们想要的结果
在这种方式下,我们的formControl得到的其实是一个company对象,要存储code,需要在从company对象中获取code属性值。
那么如果我们想在formControl中直接得到code呢?
扩展用法通过分析,我们发现,formControl获得值的内容,其实和中的value是一致的。
因此,我们可以做如下修改, 将value改成option.code
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of companies" [value]="option.code">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
得到如下显示结果:
发现显示的是code值,那我们还可以使用displayWith函数,让它显示企业名称吗?
答案是可以的。不过我们就不能再使用上面定义的displayFn方法了。此时我们需要重新定义一个更高级的方法:
displayWith() { return (value)=> { if (value) { const arr=that.companies.filter(item=> item.code===value); if (arr.length) { return arr[0].name;
}
} return undefined;
}
}
在html中的使用方法如下:
<form class="example-form">
<mat-form-field class="example-full-width">
<input type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayWith()">
<mat-option *ngFor="let option of companies" [value]="option.code">
{{option.name}} </mat-option>
</mat-autocomplete>
</mat-form-field></form>
需要[displayWith]=”displayWithThis(this)” 这样特殊的使用方法。
为什么同样是用displayWith,不同的value中,实际写法差异却如此大呢?这个和JS的This作用于有关。
在不是用匿名函数的情况下,如:
displayFn(value) { if (value) { const arr=this.companies.filter(item=> item.code===value); if (arr.length) { return arr[0].name;
}
} return undefined;
}
此时,如果将displayFn直接设置到mat-autocomplete中,此时的this表示mat-autocomplete对象,这样this.companies.filter就会抛出异常。
在本文中,我们主要讲了MatAutocomplete在实际开发过程中,在不同的业务场景中,同样的组件我们用到的不同的实现方式。每一种语言都有它自己的特性,掌握了这些特性,会让我们工作时更加得心应手。
*请认真填写需求信息,我们会在24小时内与您取得联系。