Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
前,我內(nèi)置表單功能中,已有自動(dòng)關(guān)聯(lián)字典數(shù)據(jù)方式。由于,此前字典數(shù)據(jù)過于依賴表單字段,導(dǎo)致內(nèi)存占用較高,字典數(shù)據(jù)重復(fù)性較大,字典緩存清理不便捷。
故,我欲改造之,將其與表單字段,單獨(dú)剝離,并統(tǒng)一與功能對(duì)象進(jìn)行關(guān)聯(lián),從而解決如上問題。
后端,需設(shè)計(jì)“功能字典關(guān)聯(lián)表”用于查詢,獨(dú)立“字典配置”注解,改造功能管理類。
前端,需提供通用字典獲取函數(shù),改造使用字典的元素如(下拉框、單選框、多選框等)。
整體看來,此次改造還是比較簡(jiǎn)單的,接下來就直接進(jìn)入編碼環(huán)節(jié)。
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;
/**
* 功能字典關(guān)聯(lián)表
*
* @author 趙屈犇
* @version 1.0
* @date 創(chuàng)建時(shí)間: 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;
/** 數(shù)據(jù)字典編碼 */
@Column(columnName="data_dict_code", columnType=ColumnType.VARCHAR, length=SqlConstants.DB_CODE_SIZE)
private String dataDictCode;
/** 字典驅(qū)動(dòng)庫關(guān)聯(lián)配置編碼 */
@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;
}
}
此對(duì)象,用于功能與字典之間的關(guān)聯(lián)。當(dāng),用戶獲取功能時(shí),后臺(tái)通過此函數(shù),自動(dòng)裝載入字典數(shù)據(jù)。
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 創(chuàng)建時(shí)間: 2022/10/18 20:18
* @Copyright(C): 2022 by 趙屈犇
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE })
public @interface DictConfig {
/**
* 普通字典編碼
*
* @return
*/
String dictCode() default "";
/**
* 數(shù)據(jù)字典編碼
*
* @return
*/
String dataDictCode() default "";
/**
* 字典驅(qū)動(dòng)庫關(guān)聯(lián)配置
*
* @return
*/
String dbConfigCode() default "";
/**
* 是否使用配置
*
* @return
*/
boolean isUseConfig() default true;
}
此注解,暫用于表單字段與字典配置的關(guān)聯(lián)函數(shù)。
可以看到,字典數(shù)據(jù)已于功能進(jìn)行綁定。
接下來,我準(zhǔn)備將字典數(shù)據(jù),直接綁定至功能對(duì)象上,并返回給前端。
為提升查詢效率, 此處仍采用初次查詢,后續(xù)緩存的方式。
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;
/**
* 功能字典關(guān)聯(lián)工廠
*
* @author 趙屈犇
* @version 1.0
* @date 創(chuàng)建時(shí)間: 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 {
// 查詢關(guān)聯(lián)字典配置根據(jù)功能主鍵
return getResult(requestFactory, ConfigRequestConstants.QUERY_FUNC_DICT_BY_FUNC_ID, relatedId);
}
}
最后,我在獲取功能對(duì)象時(shí),動(dòng)態(tài)獲取字典配置,并綁定字典結(jié)果數(shù)據(jù)。
為兼容普通字典和數(shù)據(jù)字典主鍵可能重復(fù)問題,我根據(jù)dictCode,dataDictCode,dbConfigCode進(jìn)行拼接,生成新的key值。
/**
* 初始化功能字典數(shù)據(jù)
*
* @param functionInfo
* @return a
* @author 趙屈犇
* @date 創(chuàng)建時(shí)間: 2022/10/20 20:58
* @version 1.0
*/
private void initFuncDictData(FunctionMessage functionInfo) {
if (functionInfo !=null) {
// 獲取關(guān)聯(lián)字典數(shù)據(jù)
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());
// 數(shù)據(jù)字典
} else if (StringUtils.isNotEmpty(dictInfo.getDataDictCode())) {
dictionaryInfos=dataDictResultFactory.getResult(dictInfo.getDataDictCode());
}
dictMap.put(key, dictionaryInfos);
}
});
functionInfo.setDictDatas(dictMap);
}
}
至此,后端功能業(yè)已完成。此下,將進(jìn)行改造前端功能。
/**
* 獲取字典數(shù)據(jù)
*
* @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;
}
此函數(shù),內(nèi)置在基礎(chǔ)引擎js段中,故可以直接獲取功能對(duì)象,并通過功能對(duì)象直接獲取字典數(shù)據(jù)。
此下,我選了選擇框元素進(jìn)行改造,其他的復(fù)刻即可。
// 定義選擇框元素
let selectView=factory.byId("${fieldCode}");
// 定義過濾lay
let layFilter="${fieldCode}_" + self.containerId;
// 設(shè)置屬性
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');
}
// 監(jiān)聽選中事件
layFactory.on(LayEventConstants.SELECT, layFilter, function(data){
selectView.val(data.value);
// 回調(diào)lay事件
if (self.factory && self.factory['onLayEvent']) {
self.factory.onLayEvent('${fieldCode}', LayEventConstants.SELECT, data);
}
});
至此,其他字典組件相繼修改,既完成。
在尋找支持在Java中用編程方法處理各類格式文檔的API嗎?好巧,Java版企業(yè)級(jí)文檔管理組合套包Spire.Office 2020全新上線!Word、Excel、PPT、PDF、條形碼等格式一網(wǎng)打盡。
目前,Spire.Office for Java 迎來v3.1.0版的更新。Excel格式處理控件Spire.XLS(點(diǎn)擊下載)強(qiáng)勢(shì)加入,同時(shí)各個(gè)產(chǎn)品也增加了新功能。如Spire.PDF 支持轉(zhuǎn)換PDF到SVG時(shí)設(shè)置寬度和高度像素,并公開了PdfFileLinkAnnotationWidget類及getValue屬性;Spire.Presentation 支持轉(zhuǎn)換PPT到HMTL時(shí),可設(shè)置內(nèi)容居中;同時(shí),也修復(fù)了將PDF轉(zhuǎn)換為Word/HTML、PPT轉(zhuǎn)換為PDF/圖片、Word轉(zhuǎn)換為PDF/HTML、給PPT設(shè)置圖片背景、對(duì)Word文檔進(jìn)行加密時(shí)出現(xiàn)的問題。
新功能及問題修復(fù)詳情,請(qǐng)參閱如下內(nèi)容。(點(diǎn)擊文末“了解更多”可下載Spire.office全部產(chǎn)品)
新功能:
問題修復(fù):
新功能:
問題修復(fù):
問題修復(fù):
述
在表單的設(shè)計(jì)過程中,會(huì)有一些表單字段需要在已知的內(nèi)容中進(jìn)行選擇,這在html中會(huì)使用select組件來設(shè)計(jì)該表單字段。而在Material中,同樣有與之對(duì)應(yīng)的 <mat-select> 組件。這種組件在選項(xiàng)內(nèi)容較少的情況下使用非常方便,能很好的引導(dǎo)用戶在表單中,輸入規(guī)范的內(nèi)容:
但是在選項(xiàng)內(nèi)容過多時(shí),這種 <mat-select> 組件,對(duì)用戶來說,想要找到自己想要的選項(xiàng)就比較麻煩,需要一個(gè)個(gè)選項(xiàng)去對(duì)比,查找:
幸運(yùn)的是,在Material中提供了 mat-autocomplete ,這個(gè)組件和我們以前在JQuery時(shí)代使用的select2類似。它支持用戶鍵盤輸入功能,并根據(jù)輸入內(nèi)容在已有的數(shù)據(jù)集合中進(jìn)行過濾,選項(xiàng)內(nèi)容動(dòng)態(tài)的顯示過濾后的結(jié)果。
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'];
}
呈現(xiàn)的效果如下:
在簡(jiǎn)單用法中,我們發(fā)現(xiàn),選項(xiàng)內(nèi)容為字符串,選擇中的值和顯示的值為相同的。但在實(shí)際的應(yīng)用過程中,我們需要選中的值和顯示的內(nèi)容是不同的。
舉個(gè)例子,我們有個(gè)需要選擇企業(yè)的字段,要存儲(chǔ)到數(shù)據(jù)庫的值是企業(yè)的編碼,而在界面上顯示的需要是企業(yè)的名稱。在這個(gè)例子中,我們有個(gè)企業(yè)的數(shù)據(jù)結(jié)構(gòu):
interface Company {
code: string;
name: string;
}
companies: Company[]=[]; constructor() { this.companies.push({code: 'qiye-01', name: '企業(yè)01'}); this.companies.push({code: 'qiye-02', name: '企業(yè)02'}); this.companies.push({code: 'qiye-03', name: '企業(yè)03'}); this.companies.push({code: 'qiye-04', name: '企業(yè)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的顯示內(nèi)容修改成name,如 {{option.name}} 。這樣我們的下拉選項(xiàng)中就會(huì)顯示企業(yè)的名稱:
但是如果我們選中其中一個(gè)選項(xiàng),就會(huì)發(fā)現(xiàn)結(jié)果和我們想象的不一樣,選中后的結(jié)果顯示并不是我們想要的企業(yè)名稱
此時(shí),我們就需要使用API中提供的
在MatAutocomplete的API: displayWith。文檔已經(jīng)很明確的告訴我們 displayWith 需要我們傳入一個(gè)已定義的函數(shù)
@Input()
displayWith: ((value: any)=> string) | null
其中的函數(shù)形參value指的是 <mat-option *ngFor="let option of companies" [value]="option"> 中value對(duì)應(yīng)的option,此處option就是一個(gè)我們后臺(tái)定義的Company。這樣我們的value就擁有code和name兩個(gè)屬性。
這樣我們就可以定義一個(gè)displayWith的函數(shù):
displayFn(value: any): string { // value: Company
return value ? value.name : undefined;
}
同時(shí)修改html
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let option of companies" [value]="option">
{{option.name}} </mat-option></mat-autocomplete>
這樣就會(huì)顯示我們想要的結(jié)果
在這種方式下,我們的formControl得到的其實(shí)是一個(gè)company對(duì)象,要存儲(chǔ)code,需要在從company對(duì)象中獲取code屬性值。
那么如果我們想在formControl中直接得到code呢?
擴(kuò)展用法通過分析,我們發(fā)現(xiàn),formControl獲得值的內(nèi)容,其實(shí)和中的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>
得到如下顯示結(jié)果:
發(fā)現(xiàn)顯示的是code值,那我們還可以使用displayWith函數(shù),讓它顯示企業(yè)名稱嗎?
答案是可以的。不過我們就不能再使用上面定義的displayFn方法了。此時(shí)我們需要重新定義一個(gè)更高級(jí)的方法:
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中,實(shí)際寫法差異卻如此大呢?這個(gè)和JS的This作用于有關(guān)。
在不是用匿名函數(shù)的情況下,如:
displayFn(value) { if (value) { const arr=this.companies.filter(item=> item.code===value); if (arr.length) { return arr[0].name;
}
} return undefined;
}
此時(shí),如果將displayFn直接設(shè)置到mat-autocomplete中,此時(shí)的this表示mat-autocomplete對(duì)象,這樣this.companies.filter就會(huì)拋出異常。
在本文中,我們主要講了MatAutocomplete在實(shí)際開發(fā)過程中,在不同的業(yè)務(wù)場(chǎng)景中,同樣的組件我們用到的不同的實(shí)現(xiàn)方式。每一種語言都有它自己的特性,掌握了這些特性,會(huì)讓我們工作時(shí)更加得心應(yīng)手。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。