自定义表单控件 - 扩展出一个选择框

 注:开窗功能实现,具体可看本人文章:EKP其它/EKPSQL工具_Liquid-Li的博客-CSDN博客

1、实现效果

 ​​​​​​

 

2、具体实现 - 配置xml扩展(WebContent/WEB-INF/KmssConfig/xp/xform/plugin.xml)

<?xml version="1.0" encoding="UTF-8"?>
<plugin
        xmlns="http://www.example.org/plugin-config"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.example.org/plugin-config ../../plugin.xsd ">

    <extension point="com.landray.kmss.sys.xform.base.service.controls" model="*">
        <!-- xp选择框 -->
        <item name="control">
            <param name="class" value="com.landray.kmss.xp.xform.base.service.controls.relationchoose.XpSysFormTemplateRelationChooseControl" />
            <param name="file" value="/xp/xform/designer/relation_choose/relation_choose.js" />
        </item>
</plugin>

2、具体实现 - 前端

WebContent/xp/xform/designer/relation_choose/relation_choose.js

/* 1、菜单栏配置 */
Designer_Config.operations['xpRelationChoose'] = {
	lab : "5",
	imgIndex : 58,// sys/xform/designer/style/css/editor-icon-m_z.png中从上往下数的索引位置,用于菜单的图标展示
	title : "XP选择框",// 菜单图标名称
	run : function(designer) {// 点击菜单控件图标时执行的方法,一般类似这样写即可
		designer.toolBar.selectButton('xpRelationChoose');
	},
	type : 'cmd',
	order: 1001,// 页签里面的排序顺序,小前大后,可为小数
	shortcut : '',
	select : true,
	line_splits_font: true,
	cursorImg : 'style/cursor/relationChoose.cur'// 鼠标点击菜单图标之后的鼠标手势加图标的展示
};
Designer_Config.buttons.form.push("xpRelationChoose");// 放入菜单选项卡(control为"扩展"选项卡)
Designer_Menus.tool.menu['xpRelationChoose'] = Designer_Config.operations['xpRelationChoose'];// 放入右键菜单

/* 2、控件属性配置 */
Designer_Config.controls["xpRelationChoose"] = {
	type : "xpRelationChoose",// 和service配置解析类型一致
	storeType : 'field',
	inherit    : 'base',
	container : false,
	onDraw : _Designer_Control_XpRelationChoose_OnDraw,// 拖动控件到表单中展示js方法
	drawMobile : _Designer_Control_RelationChoose_DrawMobile,
	drawXML : _Designer_Control_XpRelationChoose_DrawXML,// 控件值xml
	onInitialize : _Designer_Control_XpRelationChoose_OnInitialize,
	onInitializeDict : _Designer_Control_DisplayText_OnInitializeDict,
	initDefaultValueAfterPaste :_Designer_Control_XpRelationChoose_initDefaultValueAfterPaste,// 粘贴后初始化默认值
	implementDetailsTable : true,// 是否允许在明细表中展示
	resizeMode : 'onlyWidth', // 尺寸调整模式(onlyWidth, onlyHeight, all, no)
	info : {
		name : "XP选择框",// 双击控件开窗时上方的标题
		//preview : "mutiTab.png"
	},
	attrs : {// 双击控件弹出的属性框参数
		label : Designer_Config.attrs.label,
		required: {
			text: Designer_Lang.controlAttrRequired,
			value: "true",
			type: 'checkbox',
			checked: false,
			show: true
		},
		width : {
			text: Designer_Lang.controlAttrWidth,
			value: "120",
			type: 'text',
			show: true,
			validator: Designer_Control_Attr_Width_Validator,// 修改参数的校验方法
			checkout: Designer_Control_Attr_Width_Checkout// 提交时候的校验方法
		},
		summary: {
			text: Designer_Lang.controlAttrSummary,
			value: "true",
			type: 'checkbox',
			checked: false,
			synchronous: true,//多表单是否同步
			show: true
		},
		encrypt : Designer_Config.attrs.encrypt,

		/* 以下为新增的参数 */
		freeEdit: {
			text: "自由编辑",
			value: "true",
			type: 'checkbox',
			checked: false,
			show: true
		},
		editCallbackFunName: {
			text : "编辑回调",
			hint:"回调参数(obj, flagid, value)",
			value : 'editCallback_' + new Date().getTime(),
			type : 'text',
			required: false,
			show : true
		},
		openWinFunName: {
			text : "开窗函数",
			hint: "函数参数(obj)",
			value : 'openWinFun_' + new Date().getTime(),
			type : 'text',
			required: true,
			// lang: true,// 值是否加入多语言
			show : true,
			convertor: function (name, attr, value, values) {
				if (!value) {
					return 'openWinFun_' + new Date().getTime();
				}
				return value;
			}
		},
		clearCallbackFunName: {
			text : "清空回调",
			hint: "回调参数(obj, flagid)",
			value : 'clearCallback_' + new Date().getTime(),
			type : 'text',
			required: false,
			show : true
		},
		clearFieldId: {
			text : "联动清空",
			hint: "1、栏位ID,分号分隔 2、明细表栏位:明细表ID.栏位ID",
			value : '',
			type : 'textarea',
			show : true
		},
		notNullCheck: {
			text : "非空检查",
			hint: "1、栏位ID,分号分隔 2、明细表栏位:明细表ID.栏位ID",
			value : '',
			type : 'textarea',
			show : true
		},
	}
};

function _Designer_Control_XpRelationChoose_OnInitialize(){
	this.onInitializeDict();
}

function _Designer_Control_XpRelationChoose_initDefaultValueAfterPaste(values){
	// values.openWinFunName = "";
	return values;
}

function _Designer_Control_XpRelationChoose_OnDraw(parentNode, childNode) {
	var values = this.options.values;
	if (this.options.values.id == null){
		this.options.values.id = "fd_" + Designer.generateID();

	}
	var domElement = _CreateDesignElement('div', this, parentNode, childNode);
	domElement.className="xform_relation_choose";
	domElement.style.display="inline";
	var values = this.options.values;
	var inputDom = document.createElement('input');
	inputDom.className="inputsgl";
	inputDom.label = _Get_Designer_Control_Label(this.options.values, this);
	domElement.style._widthUnit = "px";

	if (this.options.values.width ) {
		if( this.options.values.width.toString().indexOf('%') > -1){
			inputDom.style.width = this.options.values.width;
			domElement.style._widthUnit = "%";
		}
		else{
			inputDom.style.width = this.options.values.width+"px";
		}
	}
	else{
		values.width = "120";
		inputDom.style.width=values.width+"px";
	}

	domElement.style._width = $(inputDom).width();
	domElement.appendChild(inputDom);

	if(values.required == 'true') {
		$(domElement).append('<span class=txtstrong>*</span>');
	}
	$(domElement).append('&nbsp;');

	inputDom.id = this.options.values.id;

	if (values.required == "true") {
		$(inputDom).attr("required", "true");
		$(inputDom).attr("_required", "true");
	} else {
		$(inputDom).attr("required", "false");
		$(inputDom).attr("_required", "false");
	}

	if(values.summary == "true"){
		$(inputDom).attr("summary", "true");
	}else{
		$(inputDom).attr("summary", "false");
	}

	if (values.width) {
		$(inputDom).attr("width", values.width);
	}

	/* 以下为新增的参数 */
	if (values.freeEdit) {
		$(inputDom).attr("freeEdit" , values.freeEdit);
	}
	if (values.editCallbackFunName) {
		$(inputDom).attr("editCallbackFunName" , values.editCallbackFunName);
	}
	if (values.openWinFunName) {
		$(inputDom).attr("openWinFunName" , values.openWinFunName);
	}
	if (values.clearCallbackFunName) {
		$(inputDom).attr("clearCallbackFunName" , values.clearCallbackFunName);
	}
	if (values.clearFieldId) {
		$(inputDom).attr("clearFieldId" , values.clearFieldId);
	}
	if (values.notNullCheck) {
		$(inputDom).attr("notNullCheck" , values.notNullCheck);
	}

	var a = document.createElement("a");
	a.innerText = Designer_Lang.relation_choose_button + "(xp)";
	domElement.appendChild(a);
}

function _Designer_Control_XpRelationChoose_DrawXML() {

	var values = this.options.values;

	// 如果一个控件对应多个值,需要补充这两个属性,用于流程文档提交业务处理
	var customElementProperties = {};
	customElementProperties.isMultiVal = "true";
	customElementProperties.controlId = values.id;

	buf=[];
	buf.push( '<extendSimpleProperty ');
	buf.push('name="', values.id, '" ');
	buf.push('label="', values.label, '" ');
	buf.push('type="', values.dataType ? values.dataType : 'String', '" ');
	// 控件类型
	buf.push('businessType="', this.type, '" ');

	if (values.formula != '' && values.formula != null) {
		buf.push('formula="true" ');
		buf.push('defaultValue="', values.formula, '" ');
		if (values.reCalculate == 'true') {
			buf.push('recalculateOnSave="true" ');
		}
	} else if (values.defaultValue != '' && values.defaultValue != null) {
		buf.push('defaultValue="', values.defaultValue, '" ');
	}

	buf.push('customElementProperties="', Designer.HtmlEscape(JSON.stringify(customElementProperties)), '" ');

	// 字段是否需要加密
	if (values.encrypt == 'true') {
		buf.push('encrypt="true" ');
		buf.push('encryptionMethod="AES" ');
	}
	buf.push('/>\r\n');

	buf.push(_Designer_Control_RelationChoose_DrawXML_HiddenPropertyTemp(values.id+"_text",values.label + Designer_Lang.controlDisplayValueMessage,this.type,customElementProperties,values));

	return buf.join('');
}

function _Designer_Control_RelationChoose_DrawXML_HiddenPropertyTemp(name,label,businessType,customElementProperties,values,isStore){
	var buf = [];
	customElementProperties.isShow = 'false';
	buf.push( '<extendSimpleProperty ');
	buf.push('name="', name, '" ');
	buf.push('label="',label , '" ');
	buf.push('type="', values.dataType ? values.dataType : 'String', '" ');
	buf.push('businessType="', businessType, '" ');
	if(isStore){
		buf.push('store="',isStore,'" ');
		buf.push('canDisplay="',isStore,'" ');//如果是不需要映射的值则不显示在列表中
	}
	buf.push('customElementProperties="', Designer.HtmlEscape(JSON.stringify(customElementProperties)), '" ');
	// 字段是否需要加密
	if (values.encrypt == 'true') {
		buf.push('encrypt="true" ');
		buf.push('encryptionMethod="AES" ');
	}
	if(name.indexOf('_text') > 0 && values.summary == 'true'){
		//摘要汇总
		buf.push('summary="true" ');
	}
	buf.push('/>\r\n');
	return buf.join('');
}

WebContent/xp/xform/designer/relation_choose/relation_choose_display.jsp

<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<%@ include file="/sys/ui/jsp/common.jsp" %>

<body>
<div class="relationChoose_content" style="${param.style} display:table">
	<div  class="relationChoose_textShow ${param.parentClass}" parentClass ="${param.parentClass}"></div>
	<xform:xtext property="extendDataFormInfo.value(${param.fdControlId }_text)" showStatus="edit" style="display:none;" subject="${param.subject }" htmlElementProperties="validator='true'" onValueChange="__xformDispatch(this.value, this);"></xform:xtext>
	<xform:editShow>
		<xform:xtext property="extendDataFormInfo.value(${param.fdControlId })" style="display:none;" subject="${param.subject }" required="${param.required }" htmlElementProperties="validator='true'"></xform:xtext>
	</xform:editShow>
	<xform:viewShow>
		<xform:xtext property="extendDataFormInfo.value(${param.fdControlId })" showStatus="noShow"></xform:xtext>
	</xform:viewShow>

	<%-- 编辑、选择、清空 --%>
	<xform:editShow>
		<div class="relationChoose_operation" params="${param.params}">
			<%--编辑--%>
			<c:if test="${param.params.contains('quot;freeEditquot;:quot;truequot;')}">
				<a onclick='xpRelationChoose_freeEdit(this);'>Edit</a>
			</c:if>

			<%--选择--%>
			<div style='display:inline-block;cursor:pointer;'
				 textId="${param.fdControlId }_text"
				 bindEvent='${param.bindEvent}'
				 mytype='relation_choose'
				 myid='extendDataFormInfo.value(${param.fdControlId})'
				 onclick='xpRelationChoose_sqlOpenWin(this);'>
				<bean:message bundle="sys-xform-base" key="Designer_Lang.relation_choose_button" />
			</div>

			<%--清空--%>
			<a class='com_btn_link' onclick='xpRelationChoose_clearValue(this);'>
				<bean:message bundle="sys-xform-base" key="Designer_Lang.relation_choose_clear" />
			</a>
		</div>

		<%-- 公用js --%>
		<script>Com_IncludeFile('relation_run.js','../sys/xform/designer/relation/');</script>
		<script>Com_IncludeFile('relation_event_run.js','../sys/xform/designer/relation_event/');</script>
		<script>Com_IncludeFile("xpUtil/publicMethod.js");</script>
		<script>Com_IncludeFile('designerUtil.js','../xp/xform/designer/');</script>
		<script>Com_IncludeFile('relation_choose_script.js','../xp/xform/designer/relation_choose/');</script>
	</xform:editShow>
</div>
<script>
	Com_IncludeFile('relation_choose_run.js','../sys/xform/designer/relation_choose/');
	//使用Com_IncludeCSSFiles追加到head中,不知道为什么谷歌好想也有link样式表个数的限制,具体原因未知
	var urlArr = [];
	urlArr.push('${LUI_ContextPath}/xp/xform/designer/relation_choose/style/css/relation_choose.css');
	Com_IncludeCSSFiles(urlArr);
</script>
</body>

WebContent/xp/xform/designer/relation_choose/relation_choose_script.js

/**
 * 点击"选择"时触发
 * @author liquid
 * @date 2023年4月5日
 * @param {object} obj "选择"按钮的dom
 * @return {void}
 */
function xpRelationChoose_sqlOpenWin(obj) {
    // liquid 2023年4月5日16:46:38
    let paramsStr = $(obj).closest(".relationChoose_operation").attr("params").replace(/quot;/g, "\"");
    let params = JSON.parse(paramsStr);
    let openWinFunName = params["openWinFunName"];
    let openWinFun = window[openWinFunName];
    let notNullCheckIds = params["notNullCheck"];
    if (typeof openWinFun === "function" &&
        xpDesigner_notNullCheckBeforeOpen(notNullCheckIds, obj)) {
        openWinFun(obj);
    } else if (typeof openWinFun !== "function"){
        alert(xp_lang("0022_key") + openWinFunName );
    }
}

/**
 * 点击"清空"时触发
 * @author liquid
 * @date 2023年4月5日
 * @param {object} obj "清空"按钮的dom
 * @return {void}
 */
function xpRelationChoose_clearValue(obj) {
    // liquid 2023年4月5日16:46:38
    let flagid = $(obj).closest("xformflag").attr("flagid");
    if (isNotEmpty(flagid)) {
        clearField(flagid);
        exeValueChange(flagid);// 执行自身值变化事件
        // 执行一次校验
        // var myid = $(obj).closest("xformflag").attr("property");
        // var validation = $GetFormValidation(document.forms[0]);
        // if(myid && validation && $("input[name='"+myid+"']").length > 0){
        //     validation.validateElement($("input[name='"+myid+"']")[0]);
        // }
    }
    // 联动清空
    let paramsStr = $(obj).closest(".relationChoose_operation").attr("params").replace(/quot;/g, "\"");
    let params = JSON.parse(paramsStr);
    let clearFieldId = params["clearFieldId"];
    if (isNotEmpty(clearFieldId)) {
        let relativeFlagidArr = clearFieldId.split(";");
        for (const relativeFlagid of relativeFlagidArr) {
            if (isEmpty(relativeFlagid)) {
                continue;
            }
            // 明细表中的控件
            if (relativeFlagid.includes(".")) {
                xpRelationChoose_clearInDetailList(relativeFlagid, obj);
                continue;
            }
            // 非明细表控件
            clearField(relativeFlagid);
        }
    }
    // 清空后回调
    let clearCallbackFunName = params["clearCallbackFunName"];
    let clearCallbackFun = window[clearCallbackFunName];
    if (typeof clearCallbackFun === "function") {
        clearCallbackFun(obj, flagid);
    }
}

/**
 * 联动清空明细表栏位
 * @author liquid
 * @date 2023年4月5日
 * @param {string} flagId 栏位ID(明细表ID.栏位ID)
 * @param {object} obj "清空"按钮的dom
 * @return {void}
 * @private
 */
function xpRelationChoose_clearInDetailList(flagId, obj) {
    let dotIndex = flagId.indexOf(".");
    let detailListId = flagId.substring(0, dotIndex);
    let detailObj = getDetailObj(detailListId);
    let fieldId = flagId.substring(dotIndex + 1);

    /* 选择框与flagId在同一明细表 */
    let tableId = $(obj).closest("table").attr("id");
    if (isNotEmpty(tableId) && tableId.includes(detailListId)) {
        let rowIndex = detailObj.getRowIndexByDom(obj);
        detailObj.clearSingle(rowIndex, fieldId);
        exeValueChange(detailObj.getFlagidInDetailList(rowIndex, fieldId));
        return;
    }

    /* 选择框与flagId不在同一明细表 */
    detailObj.clearColumn(fieldId);
    for (let i = 0; i < detailObj.getRowLength(); i++) {
        exeValueChange(detailObj.getFlagidInDetailList(i, fieldId));
    }
}


/**
 * 点击"编辑"时触发
 * @author liquid
 * @date 2023年4月5日
 * @param {object} obj "编辑"按钮的dom
 * @return {void}
 */
function xpRelationChoose_freeEdit(obj) {
    let flagid = $(obj).closest("xformflag").attr("flagid");
    let value = `${window.prompt()}`;
    let paramsStr = $(obj).closest(".relationChoose_operation").attr("params").replace(/quot;/g, "\"");
    let params = JSON.parse(paramsStr);
    if (isNotEmpty(value)) {
        setValueById(flagid, value);
        const editCallbackFunName = params["editCallbackFunName"];
        const editCallbackFun = window[editCallbackFunName];
        if (typeof editCallbackFun === "function") {
            editCallbackFun(obj, flagid, value);
        }
    }
}

WebContent/xp/xform/designer/designerUtil.js

/**
 * 某些栏位的非空检查
 * @author liquid
 * @date 2023年4月5日
 * @param {string} notNullCheckIds 需判空的栏位ID字符串
 * @param {object} obj 触发元素的dom
 * @return {boolean} 是否全部非空
 * @private
 */
function xpDesigner_notNullCheckBeforeOpen(notNullCheckIds, obj) {
    let notNullCheckIdArr = notNullCheckIds.split(";");
    for (const notNullCheckId of notNullCheckIdArr) {
        if (isEmpty(notNullCheckId)) {
            continue;
        }
        let xformLabel = getXFormLabel(notNullCheckId);
        let dotIndex = notNullCheckId.indexOf(".");
        /* 待判断的栏位在明细表 */
        if (dotIndex > -1) {
            let detailListId = notNullCheckId.substring(0, dotIndex);
            let detailObj = getDetailObj(detailListId);
            let fieldId = notNullCheckId.substring(dotIndex + 1);
            // 选择框与flagId在同一明细表
            let tableId = $(obj).closest("table").attr("id");
            if (isNotEmpty(tableId) && tableId.includes(detailListId)) {
                let rowIndex = detailObj.getRowIndexByDom(obj);
                let value = detailObj.getDataSingle(rowIndex, fieldId);
                if (xpDesigner_isNull(value)) {
                    alert(xp_lang("0023_key") + xformLabel);
                    return false;
                }
            } else {// 选择框与flagId不在同一明细表
                let dataArr = detailObj.getDataByColumnId(fieldId);
                if (xpDesigner_isArrNull(dataArr)) {
                    alert(xp_lang("0024_key") + xformLabel);
                    return false;
                }
            }
        }

        /* 待判断的栏位不在明细表 */
        if (dotIndex < 0) {
            let value = getValueById(notNullCheckId);
            if (xpDesigner_isNull(value)) {
                alert(xp_lang("0023_key") + xformLabel);
                return false;
            }
        }
    }
    return true;
}
function xpDesigner_isNull(var1) {
    return isEmpty(var1) || "{};[];[\"\"];[\"\",\"\"];[null];[null,null]".includes(JSON.stringify(var1));
}
function xpDesigner_isArrNull(varArr1) {
    for (const var1 of varArr1) {
        if (xpDesigner_isNull(var1)) {
            return true;
        }
    }
    return false;
}

2、具体实现 - 前端(开窗)

<!doctype html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>资料选择器 Select Data</title>
    <link rel="stylesheet" href="sql_openwin.css">
    <script src="/xpwebframe/node_modules/_jquery@3.6.0@jquery/dist/jquery.min.js"></script>
    <script src="/xpwebframe/node_modules/_vue@2.6.14@vue/dist/vue.min.js"></script>
    <script src="/resource/js/xpUtil/array.js"></script>
    <script src="/resource/js/xpUtil/ajaxUtil.js"></script>
    <script src="/resource/js/xpUtil/page.js"></script>
    <script src="/resource/js/xpUtil/string.js"></script>
    <script src="/resource/js/xpUtil/errorHandle.js"></script>
    <script src="/resource/js/xpUtil/date.js"></script>
    <script src="/resource/js/xpUtil/download.js"></script>
    <script src="/resource/js/xpUtil/excel.js"></script>
    <script src="/resource/js/xpUtil/kmReviewMainUtil.js"></script>
    <script src="sql_openwin_selfCallback.js"></script>
    <script src="sql_openwin_rightMenuCallback.js"></script>
    <script src="sql_openwin_inParams.js"></script>
</head>
<body>
<div id="outer_div" v-on:keydown="outerDivKeydown($event)">
    <!---------------------------查询区---------------------------->
    <div id="query_outer_div">
        <!--查询输入框-->
        <table>
            <tr v-for="(i_value, i) in search">
                <td class="query_td" v-for="(j_value, j) in i_value">
                    <span style="display: flex;flex-wrap: nowrap">
                        <div class="query_label_div">
                            <label class="query_label"
                                   v-if="!isEmpty(j_value)" v-html="j_value.title">
                            </label>
                        </div>
                        <div class="query_input_div">
                            <!--单行输入框-->
                            <input type="text" class="query_input"
                                   v-bind:placeholder="j_value.placeholder"
                                   v-bind:value="j_value.default"
                                   v-if="!isEmpty(j_value) && j_value.type === 'text'"
                                   v-bind:name="j_value.name">
                            <!--日期选择-->
                            <input type="date" class="query_input" style="height: 20px;"
                                   v-if="!isEmpty(j_value) && j_value.type === 'date'"
                                   v-bind:value="getTodayStr(j_value.default)"
                                   v-bind:name="j_value.name">
                            <!--下拉菜单-->
                            <select type="dropdown" class="query_input" style="height: 24px;"
                                    v-if="!isEmpty(j_value) && j_value.type === 'dropdown'"
                                    v-bind:value="j_value.default"
                                    v-bind:name="j_value.name"
                                    v-on:change="dropdownChange($event)">
                                <option v-for="(k_value, k) in j_value.items"
                                        v-bind:value="getSelectTrueValue(k_value)">
                                    {{getSelectDisplayValue(k_value)}}
                                </option>
                            </select>
                            <!--单选按钮-->
                            <span v-if="!isEmpty(j_value) && j_value.type==='radio'">
                                <span v-for="(p_value, p) in j_value.items">
                                    <input class="radio_input" v-bind:value="getSelectTrueValue(p_value)"
                                           v-bind:id="j_value.name + '_' + p"
                                           v-bind:name="j_value.name"
                                           v-bind:type="j_value.type"
                                           v-bind:checked="j_value.default===getSelectTrueValue(p_value)"
                                           v-bind:data-index="p"
                                           v-on:click="radioClick($event)">
                                    {{getSelectDisplayValue(p_value)}}
                                </span>
                            </span>
                            <!--多选按钮-->
                            <span v-if="!isEmpty(j_value) && j_value.type==='checkbox'">
                                <span v-for="(p_value, p) in j_value.items">
                                    <input class="checkbox_input" v-bind:value="getSelectTrueValue(p_value)"
                                           v-bind:id="j_value.name + '_' + p"
                                           v-bind:name="j_value.name"
                                           v-bind:type="j_value.type"
                                           v-bind:checked="j_value.default.includes(getSelectTrueValue(p_value))"
                                           v-on:click="checkboxClick($event)">
                                    {{getSelectDisplayValue(p_value)}}
                                </span>
                            </span>
                        </div>
                    </span>
                </td>
            </tr>
        </table>
        <!--查询按钮-->
        <div id="right_button_div">
            <button class="query_reset" v-on:click="queryClick()">{{lang === 'ZH-CN' ? '查询' : 'QUERY'}}</button>
            <button class="query_reset" v-on:click="resetClick()">{{lang === 'ZH-CN' ? '重置' : 'RESET'}}</button>
            <button class="query_reset" v-if="winType==='multiple'" v-on:click="confirmClick()">
                {{lang === 'ZH-CN' ? '确定' : 'CONFIRM'}}
            </button>
        </div>
    </div>
    <!------------------------------------------------------------>

    <!---------------------------数据区---------------------------->
    <div id="data_div" v-bind:style="{height: data_div_height + 'px'}">
        <!--无数据提示-->
        <div class="request_no_data_tip" style="margin-top:25px" v-if="nowData.length <= 0 && noDataShow">
            <div class="no_data_tip_icon"></div>
            <div class="no_data_tip_text">
                <span>查无数据</span>
            </div>
        </div>
        <table id="tab" v-if="nowData.length > 0" v-bind:style="totalWidth()">
            <!--标题-->
            <tr style="position:sticky;top:0">
                <td id="multiple_choose_all" v-if="winType==='multiple'">
                    <input type="checkbox" v-bind:checked="isAllSelected()"
                           v-on:click="multipleChooseAll()"/>
                </td>
                <td class="title_td"
                    v-for="(i_value, i) in titles"
                    v-if="!hideColumn(i)"
                    v-bind:style="(i_value.width.includes('%') ? 'width:' : 'min-width:') + i_value.width"
                    v-html="i_value.label">
                </td>
            </tr>

            <!--数据-->
            <tr class="data_tr" v-bind:title="showDetail(i_value)" v-for="(i_value, i) in nowData"
                v-bind:class="dataRowClass(i)">
                <td class="multiple_choose_single" v-if="winType==='multiple'"
                    v-on:click="dataRowClick(i_value, i, $event)">
                    <input type="checkbox"
                           v-bind:checked="isSelected(i_value)"
                           v-on:click="multipleChooseSingleClick(i)"/>
                </td>
                <td class="data_td" v-if="!hideColumn(j)" v-for="(j_value, j) in i_value"
                    v-on:click="dataRowClick(i_value, i, $event)"
                    v-on:contextmenu="dataRowRightClick(i_value, i, $event)"
                    v-html="j_value">
                </td>
                <!---------------------------右键菜单------------------------->
                <div class="rightMenu_outer_div"
                     v-if="rightMenu_itemArr.length!==0"
                     v-show="rightMenu.visible"
                     v-bind:style="{left: rightMenu.left + 'px', top: rightMenu.top + 'px'}">
                    <div class="rightMenu_item_outer">
                        <div class="rightMenu_item" v-for="(k_value, k) in rightMenu_itemArr"
                             v-on:click="rightMenu_callback(k)"
                             v-html="k_value.itemName"></div>
                    </div>
                </div>
                <!------------------------------------------------------------>
            </tr>

            <!---------------------------已选数据区------------------------->
            <tr class="data_tr" style="background-color: #dcdcdc;color: #ff0000" v-bind:title="showDetail(i_value)"
                v-for="(i_value, i) in multipleSelectedData">
                <td class="selected_data_cancel"
                    v-if="winType==='multiple'"
                    v-on:click="cancelSelectedData(i_value, i, $event)">×
                </td>
                <td class="data_td" v-if="!hideColumn(j)" v-for="(j_value, j) in i_value"
                    v-html="j_value" v-on:click="cancelSelectedData(i_value, i, $event)"
                    v-on:contextmenu="dataRowRightClick(i_value, i, $event)">
                </td>
                <!---------------------------右键菜单------------------------->
                <div class="rightMenu_outer_div"
                     v-if="rightMenu_itemArr.length!==0"
                     v-show="rightMenu.visible"
                     v-bind:style="{left: rightMenu.left + 'px', top: rightMenu.top + 'px'}">
                    <div class="rightMenu_item_outer">
                        <div class="rightMenu_item" v-for="(k_value, k) in rightMenu_itemArr"
                             v-on:click="rightMenu_callback(k)"
                             v-html="k_value.itemName"></div>
                    </div>
                </div>
                <!------------------------------------------------------------>
            </tr>
            <!------------------------------------------------------------>
        </table>
    </div>
    <!------------------------------------------------------------>

    <!---------------------------分页区---------------------------->
    <div id="paging_outer_div">
        <!--数据数量-->
        <div id="count_div">
            <label v-if="lang==='ZH-CN'">共 {{total}} 条记录 | 第 {{nowPage}} 页</label>
            <label v-if="lang==='EN-US'">Record Quantity: {{total}} | Page No: {{nowPage}} </label>
        </div>
        <!-转开窗-->
        <img class="page_win_btn" src="/xpicon/static/openwin01.png" v-on:click="changeToWin()">
        <!--分页操作-->
        <div id="page_operation">
            <button class="page_move_btn" v-on:click="exportData()">{{lang === 'ZH-CN' ? '导出' : 'Export'}}</button>
            <button class="page_move_btn" v-if="total===0" v-on:click="firstPageClick()">
                {{lang === 'ZH-CN' ? '最前页(0)' : 'First Page(0)'}}
            </button>
            <button class="page_move_btn" v-else v-on:click="firstPageClick()">
                {{lang === 'ZH-CN' ? '最前页(1)' : 'First Page(1)'}}
            </button>
            <button class="page_move_btn" v-on:click="pageUpClick()">{{lang === 'ZH-CN' ? '上一页' : 'Page Up'}}
            </button>
            <button class="page_move_btn" v-on:click="pageDownCLick()">{{lang === 'ZH-CN' ? '下一页' : 'Page Down'}}
            </button>
            <button class="page_move_btn" v-on:click="lastPageClick()">
                {{lang === 'ZH-CN' ? '最后页(' + lastPageIndex + ')' : 'Last Page(' + lastPageIndex + ')'}}
            </button>
        </div>
    </div>
    <!------------------------------------------------------------>

    <div id="white_cover" v-if="nowData===undefined"></div>
    <div id="goto_sqlDefinition" v-on:click="gotoSqlDefinition()"></div>
</div>
<div id="loading_div">
    <img src="/xpicon/gif/loading1.gif" style="height:70px;width:70px;"/>
</div>
</body>
<script src="sql_openwin.js"></script>
</html>

 

window.Com_Parameter = pageParent()["Com_Parameter"];
let vue_outer_div;
const pageParams = getPageParams();
console.log({pageParams});
let firstResult = 0;
let maxResults = Number(pageParams["maxResults"] || 20);// 默认每页20条记录
let lang = pageParams["lang"] || getLang();
let firstQuery = pageParams["firstQuery"] || "1";// 窗口打开后是否自动执行查询
let mainCallbackName = pageParams["mainCallbackName"] || "";
let inParamsWithSearch = pageParent(window)[pageParams["inParamsWithSearchKey"]] || {};// 搜索框传入动态默认参数
pageParent(window)[pageParams["inParamsWithSearchKey"]] = undefined;
let inParamsNotWithSearch = pageParent(window)[pageParams["inParamsNotWithSearchKey"]] || {};// 搜索框传入动态默认参数
pageParent(window)[pageParams["inParamsNotWithSearchKey"]] = undefined;
const parentCallback = pageParams["parentCallback"];// 待回调的父页面函数名(点击行、点击确定)
lang = lang.toUpperCase();
let sqlDefinitionFdId = "";
let sqlKey = pageParams["sqlKey"];
let winType = pageParams["winType"];// 窗口类型(single单、multiple多选)
let selectedData = pageParent(window)[pageParams["selectedDataKey"]] || [];// 已选择的数据
pageParent(window)[pageParams["selectedDataKey"]] = undefined;
let selectedData_copy = JSON.parse(JSON.stringify(selectedData));
let searchValue;
let search;
let search_copy;
let titles;
let winTitle;
let rightMenu_itemArr;
let historyData = {ekp: {}}
let rowData4RightMenu;

/* 对传入参数进行处理 */
let inParamsFunc = inParams[sqlKey];
if (inParamsFunc) {
    if (!inParamsWithSearch || JSON.stringify(inParamsWithSearch) === "{}") {
        let func = inParamsFunc["withSearch"];
        if (typeof func === "function") {
            inParamsWithSearch = func() || {};
        }
    }
    if (!inParamsNotWithSearch || JSON.stringify(inParamsNotWithSearch) === "{}") {
        let func = inParamsFunc["notWithSearch"];
        if (typeof func === "function") {
            inParamsNotWithSearch = func() || {};
        }
    }
}

/* 页面初始化 */
$(() => {
    initConfig();// 设置开窗配置
    vue_outer_div = new Vue({
        el: '#outer_div',
        data: {
            lang: lang,
            search: search[lang],
            titles: titles[lang],
            nowData: [],
            total: 999,
            nowPage: 1,
            lastPageIndex: 0,
            winType: winType,
            multipleSelectedData: selectedData,// 多选状态下:已选数据
            rightMenu: {// 右键菜单数据对象
                visible: false,
                top: 0,
                left: 0,
            },
            rightMenu_itemArr: rightMenu_itemArr[lang],
            data_div_height: 0,
            noDataShow: false,
        },
        methods: {
            isEmpty: isEmpty,
            isNotEmpty: isNotEmpty,
            dataRowClass: dataRowClass,
            getSelectDisplayValue: getSelectDisplayValue,
            getSelectTrueValue: getSelectTrueValue,
            getTodayStr: getTodayStr,
            hideColumn: hideColumn,
            totalWidth : totalWidth
        },
        computed: {},
        watch: {
            rightMenu(value) {
                if (value.visible) {
                    document.body.addEventListener('click', closeRightMenu)
                } else {
                    document.body.removeEventListener('click', closeRightMenu)
                }
            }
        }
    });

    // 执行查询
    if (firstQuery === "1") {
        searchData(true, 0);
    }

    // 设置数据区高度
    let interval_setDivHeight = setInterval(() => {
        let pagingOuterDiv_top = $("#paging_outer_div")[0].offsetTop;
        let dataDiv_top = $("#data_div")[0].offsetTop;
        if (pagingOuterDiv_top !== 0) {
            vue_outer_div.$data.data_div_height = pagingOuterDiv_top - dataDiv_top - 5;
            clearInterval(interval_setDivHeight);
        }
    }, 200)
})

/**
 * 初始化配置
 * @author liquid
 * @date 2022年11月15日
 */
function initConfig() {
    const _path_in = "xp/modeling/app/it/actions/XpEkpSqlDefinitionAction.do?method=getOpenWinConfigJson";
    const _data_in = {key: sqlKey}
    let openWinConfigJson;
    ajaxAction(_path_in, _data_in, false, {
        success: (res)=>{
            if (res.status !== "success") {
                errorHandler(res); return;
            }
            // 业务逻辑
            sqlDefinitionFdId = res["fdId"];
            openWinConfigJson = JSON.parse(res["openWinConfigJson"]);
            console.log("openWinConfigJson",openWinConfigJson);
        },
        error: (error)=>{
            errorHandler(error);
        }
    })

    search = setDefaultParams(openWinConfigJson.search || {});
    search_copy = JSON.parse(JSON.stringify(search));

    // 列标题
    titles = openWinConfigJson.titles || {"ZH-CN":[], "EN-US":[]};

    // 窗口标题 - 移动端
    winTitle = openWinConfigJson.winTitle;
    document.title = winTitle[lang];
    // 窗口标题 - pc端
    let setTitle = window.parent["setWinCenterWithMaskTitle"];
    if (typeof setTitle === "function") {
        setTitle(winTitle[lang]);
    }

    // 右键菜单
    rightMenu_itemArr = openWinConfigJson.rightMenu_itemArr || {"ZH-CN":[], "EN-US":[]};

    // search = {
    //     "ZH-CN": [
    //         [
    //             {
    //                 "name": "id",
    //                 "title": "ID",
    //                 "type": "text",
    //                 "items": [],
    //                 "default": "",
    //                 "placeholder": "组织架构对象ID",
    //                 "fuzzy": "Y"
    //             },
    //             {
    //                 "name": "org_type",
    //                 "title": "类型",
    //                 "type": "dropdown",
    //                 "items": [
    //                     "机构|1",
    //                     "部门|2",
    //                     "岗位|4",
    //                     "人员|8",
    //                     "群组|16",
    //                     "通用岗位|32"
    //                 ],
    //                 "default": "8",
    //                 "placeholder": "",
    //                 "fuzzy": "N"
    //             }
    //         ],
    //         [
    //             {
    //                 "name": "name",
    //                 "title": "名称",
    //                 "type": "text",
    //                 "items": [],
    //                 "default": "",
    //                 "placeholder": "",
    //                 "fuzzy": "Y"
    //             },
    //             {
    //                 "name": "name_pinyin",
    //                 "title": "名称拼音",
    //                 "type": "text",
    //                 "items": [],
    //                 "default": "",
    //                 "placeholder": "",
    //                 "fuzzy": "Y"
    //             }
    //         ]
    //     ],
    //     "EN-US": [
    //         [
    //             {
    //                 "name": "id",
    //                 "title": "ID",
    //                 "type": "text",
    //                 "items": [],
    //                 "default": "",
    //                 "placeholder": "Organization ID",
    //                 "fuzzy": "Y"
    //             },
    //             {
    //                 "name": "org_type",
    //                 "title": "Type",
    //                 "type": "dropdown",
    //                 "items": [
    //                     "Company|1",
    //                     "Dept.|2",
    //                     "Post|4",
    //                     "Personnel|8",
    //                     "Group|16",
    //                     "Universal Post|32"
    //                 ],
    //                 "default": "8",
    //                 "placeholder": "",
    //                 "fuzzy": "N"
    //             }
    //         ],
    //         [
    //             {
    //                 "name": "name",
    //                 "title": "Name",
    //                 "type": "text",
    //                 "items": [],
    //                 "default": "",
    //                 "placeholder": "",
    //                 "fuzzy": "Y"
    //             },
    //             {
    //                 "name": "name_pinyin",
    //                 "title": "Name Pinyin",
    //                 "type": "text",
    //                 "items": [],
    //                 "default": "",
    //                 "placeholder": "",
    //                 "fuzzy": "Y"
    //             }
    //         ]
    //     ]
    // };
    // search_copy = JSON.parse(JSON.stringify(search));
    // titles = {
    //     "ZH-CN": [
    //         {
    //             "label": "ID",
    //             "width": "25%"
    //         },
    //         {
    //             "label": "类型",
    //             "width": "5%"
    //         },
    //         {
    //             "label": "名称",
    //             "width": "25%"
    //         },
    //         {
    //             "label": "名称拼音",
    //             "width": "45%"
    //         }
    //     ],
    //     "EN-US": [
    //         {
    //             "label": "ID",
    //             "width": "25%"
    //         },
    //         {
    //             "label": "Type",
    //             "width": "5%"
    //         },
    //         {
    //             "label": "Name",
    //             "width": "25%"
    //         },
    //         {
    //             "label": "Name Pinyin",
    //             "width": "45%"
    //         }
    //     ]
    // }

    // rightMenu_itemArr = {
    //     "ZH-CN": [
    //         {
    //             title: "选项1"
    //         },
    //         {
    //             title: "选项2"
    //         },
    //         {
    //             title: "选项3"
    //         }
    //     ],
    //     "EN-US": [
    //         {
    //             title: "item-1"
    //         },
    //         {
    //             title: "item-2"
    //         },
    //         {
    //             title: "item-3"
    //         }
    //     ],
    // };
}

/**
 * 计算数据区外层宽度
 * @author zengwenfu
 * @date 2022年12月14日
 * @returns {string}
 */
function totalWidth(){
    let mWidth = 0;

    if (titles["ZH-CN"][0].width.indexOf("px") > -1) {

        for (var i = 0; i < titles["ZH-CN"].length; i++) {
            mWidth += parseInt(titles["ZH-CN"][i].width);
        }
        return "width:"+mWidth+"px";
    }

    return "width:100%";
}

/**
 * 斑马线
 * @author liquid
 * @date 2022年11月18日
 * @param {number}index 行下标
 * @returns {string}
 */
function dataRowClass(index){
    return index % 2 === 0 ? "data_tr_0" : "data_tr_1";
}

/**
 * 行点击事件
 * @author liquid
 * @date 2022年11月18日
 * @param {array}rowData 触发行的数据
 * @param {number}rowIndex 触发行的行下标
 * @param event 触发事件对象
 */
function dataRowClick(rowData, rowIndex, event) {
    let selectTxt = window.getSelection().toString();
    if (isNotEmpty(selectTxt)) {// 避免选择文本时也触发点击事件的逻辑
        return;
    }

    const targetClassName= event.target.className;
    if (isEmpty(targetClassName) ||
        !"multiple_choose_single;data_td".includes(targetClassName)) {
        // 只有点击行的时候,才执行以下代码,避免在多选状态下点击checkbox时也触发
        return;
    }
    const rtnData = makeRowData(rowData);
	
    /* 单选 */
    if (winType === "single") {
        let isCloseWin = rowConfirm_callback(rtnData, "rowClick");
        if (!isCloseWin) {// 不关闭开窗,以下回调不执行
            return;
        }

        if(window.opener){
	        window.opener.postMessage(rtnData);
		}
        let mainCallBack = window.parent[mainCallbackName];
        if (mainCallBack) {
            mainCallBack(rtnData.sqlData);
        }
        closeWin();
    }

    /* 多选 */
    if (winType === "multiple") {
        const check = $(".multiple_choose_single > input")[rowIndex];
        setChecked([rowIndex], !check.checked);
        // 回调
        let isCloseWin = rowConfirm_callback(rtnData, "rowClick");
        if (isCloseWin) {
            closeWin();
        }
    }
}

/**
 * 构造被点击行的数据
 * @author liquid
 * @date 2022年12月14日
 * @param rowData
 */
function makeRowData(rowData) {
    const json = {};
    for (let i = 0; i < rowData.length; i++) {
        json[titles["ZH-CN"][i].valueKey] = rowData[i];
    }
    return {
        "sqlData": {
            arr: rowData,
            json: json
        },
    }
}

/**
 * 行鼠标右键事件
 * @author liquid
 * @date 2022年12月14日
 * @param {array}rowData 触发行的数据
 * @param {number}rowIndex 触发行的行下标
 * @param event 触发事件对象
 */
function dataRowRightClick(rowData, rowIndex, event) {
    rowData4RightMenu = makeRowData(rowData);
    event.preventDefault();
    openRightMenu(event);
}

/**
 * 显示数据细节
 * @author zengwenfu
 * @param rowData
 * @returns {string}
 */
function showDetail(rowData){
	let str = "";
	for(let i = 0;i < rowData.length; i++){
        let data = rowData[i] ? rowData[i] : "";
		str += titles[lang][i].label + " :" + data + "\n";
	}
	return str;
}

/**
 * 打开自定义右键菜单
 * @author liquid
 * @date 2022年12月14日
 * @param event 触发事件对象
 */
function openRightMenu(event) {
    vue_outer_div.$data.rightMenu = {
        left: event.clientX,
        top: event.clientY,
        visible: true
    };
}

/**
 * 关闭自定义右键菜单
 * @author liquid
 * @date 2022年12月14日
 */
function closeRightMenu() {
    vue_outer_div.$data.rightMenu = {
        visible: false
    }
}

/**
 * [多选]选中或取消选中之后执行一些操作
 * @author liquid
 * @date 2022年11月22日
 * @param indexes 选中或取消选中的下标数组
 * @param checked 选中(true),取消选中(false)
 */
function setChecked(indexes, checked) {
    for (const index of indexes) {
        const checkbox = $(".multiple_choose_single > input")[index];
        $(checkbox).prop("checked",  checked);

        const addData = JSON.parse(JSON.stringify(vue_outer_div.nowData[index]));
        if (checked && !JSON.stringify(vue_outer_div.multipleSelectedData).includes(JSON.stringify(addData))) {// 是选中
            vue_outer_div.multipleSelectedData.push(addData);
        }
        else if (!checked){// 取消选中
            vue_outer_div.multipleSelectedData = delElement(vue_outer_div.multipleSelectedData, addData);
        }
    }
}

/**
 * 获取下拉菜单、单选按钮、多选按钮的显示值
 * @author liquid
 * @date 2022年11月18日
 * @param {string}str
 * @returns {*}
 */
function getSelectDisplayValue(str) {
    return str.includes("|") ? str.substring(0, str.indexOf('|')) : str;
}

/**
 * 获取下拉菜单、单选按钮、多选按钮的实际值
 * @author liquid
 * @date 2022年11月18日
 * @param {string}str
 * @returns {*}
 */
function getSelectTrueValue(str) {
    return str.includes("|") ? str.substring(str.indexOf('|') + 1) : str;
}

/**
 * 获取今日日期字符串
 * @author liquid
 * @date 2022年11月18日
 * @param {string}date 传入"today"获取的是今日的格式化日期字符串
 * @returns {null|string|*}
 */
function getTodayStr(date) {
    if (isEmpty(date)) {
        return null;
    }
    return date === "today" ? xp_formatDateTime(new Date(), "Y-M-D") : date;
}

/**
 * 下拉菜单值变化事件
 * @author liquid
 * @date 2023年2月19日
 * @param event
 */
function dropdownChange(event) {
    queryClick();
}

/**
 * 单选按钮点击事件
 * @author liquid
 * @date 2022年11月18日
 * @param event
 */
function radioClick(event) {
    const elements = $("[name=" + event.target.name + "]");
    for (let i = 0; i < elements.length; i++) {
        if (i !==  Number(event.target.dataset.index)) {
            $(elements[i]).attr("checked", false);
        }
    }
    queryClick();
}

/**
 * 多选按钮点击事件
 * @author liquid
 * @date 2023年2月19日
 * @param event
 */
function checkboxClick(event) {
    queryClick();
}

/**
 * 若url传入搜索框默认值,则重设搜索框默认值
 * @author liquid
 * @date 2022年11月14日
 */
function setDefaultParams(search) {
    const searchTemp = JSON.parse(JSON.stringify(search));
    for (const e1 of searchTemp[lang]) {
        for (const e2 of e1) {
            const value = inParamsWithSearch[e2.name];
            e2.default = isNotEmpty(value) ? value : e2.default;
        }
    }
    return searchTemp;
}

/**
 * 获取搜索框中的输入值(包括隐藏的搜索参数)
 * @author liquid
 * @date 2022年11月14日
 */
function getSearchValue() {
    const searchConfig = search[lang];
    const rtn = {};
    for(const i_value of searchConfig) {
        for (const j_value of i_value) {
            if ("text;dropdown;date".includes(j_value.type)) {
                let value = $("[name='"+ j_value.name +"']").val();
                value = isEmpty(value) ? "" : value;
                rtn[j_value.name] = j_value.fuzzy === "Y" ? "%" + value + "%" : value;
                j_value.default = value;
            }
            else if (j_value.type === "radio") {
                let value = $("input:radio[name='" + j_value.name + "']:checked").val();
                value = isEmpty(value) ? "" : value;
                rtn[j_value.name] = j_value.fuzzy === "Y" ? "%" + value + "%" : value;
                j_value.default = value;
            }
            else if (j_value.type === "checkbox") {
                const values = [undefined];// 放一个空值,防止传入空数组导致接口调用失败
                const checkboxes = $("input:checkbox[name='"+ j_value.name +"']:checked");
                for (const e of checkboxes) {
                    values.push($(e).val())
                }
                rtn[j_value.name] = values;
                j_value.default = values.join(";");
            }
        }
    }
    for (const key in inParamsNotWithSearch) {
        rtn[key] = inParamsNotWithSearch[key];
    }
    return rtn;
}

/**
 * 搜索区域键入事件
 * @author liquid
 * @date 2022年11月18日
 * @param event
 */
function outerDivKeydown(event) {
    if (event.key === "Enter") {
        searchData(true, 0);
    }
}

/**
 * 查询点击事件
 * @author liquid
 * @date 2022年11月18日
 */
function queryClick() {
    searchData(true, 0);
}

/**
 * 重置点击事件
 * @author liquid
 * @date 2022年11月18日
 */
function resetClick() {
    // 清空本次的历史数据
    for (const key in historyData) {
        historyData[key] = {};
    }

    search = JSON.parse(JSON.stringify(search_copy));
    vue_outer_div.search = search[lang];
    vue_outer_div.multipleSelectedData = JSON.parse(JSON.stringify(selectedData_copy));
    setTimeout(function() {// 延迟搜索,避免数据还没渲染完就去拿值
        searchData(true, 0);
    }, 200)
}

/**
 * 确定点击事件
 * @author liquid
 * @date 2022年11月18日
 */
function confirmClick() {
    /* 非多选,不执行 */
    if (winType !== "multiple") {
        return;
    }
    const sqlData = {arr: vue_outer_div.multipleSelectedData, json: {}, arr_str: [], json_str: {}, json_arr: []};
    /* sqlData.json & sqlData.json_str & json_arr */
    const titlesArr = titles["ZH-CN"];
    for (let i = 0; i < sqlData.arr.length; i++) {
        for (let j = 0; j < titlesArr.length; j++) {
            const valueKey = titlesArr[j].valueKey;
            const arr = sqlData.json[valueKey] || [];
            arr.push(sqlData.arr[i][j]);
            sqlData.json[valueKey] = arr;
            sqlData.json_str[valueKey] = arr.join(";");
        }
    }
    // sqlData.arr_str
    for (let i = 0; i < sqlData.arr.length; i++) {
        for (let j = 0; j < sqlData.arr[i].length; j++) {
            sqlData.arr_str[j] = (sqlData.arr_str[j] || "") + sqlData.arr[i][j] + ";";
        }
    }
    for (let i = 0; i < sqlData.arr.length; i++) {
        sqlData.arr_str[i] = delLastChar(sqlData.arr_str[i], 1);
    }
    // json_arr
    let json_arr = [];
    for (let i = 0; i < sqlData.arr.length; i++) {
        let tempArr = sqlData.arr[i];
        let tempJson = {};
        for (let j = 0; j < tempArr.length; j++) {
            const valueKey = titlesArr[j].valueKey;
            tempJson[valueKey] = tempArr[j];
        }
        json_arr.push(tempJson);
    }
    sqlData.json_arr = json_arr;

    /* 回传数据 */
    const rtnData = {
        sqlData: sqlData
    }

    /* 回调 */
    let isCloseWin = rowConfirm_callback(rtnData, "confirmClick");
    if (!isCloseWin) {// 不关闭开窗,以下回调不执行
        return;
    }
    if(window.opener){
        window.opener.postMessage(rtnData);
    }
    let mainCallBack = window.parent[mainCallbackName];
    if (mainCallBack) {
        mainCallBack(rtnData.sqlData);
    }
    closeWin();
}

/**
 * 集中处理各类型回调函数
 * @author liquid
 * @date 2022年12月9日
 * @param rtnData 回传数据
 * @param clickType 点击类型(rowClick点击行\confirmClick点击确定)
 * @return {boolean} 开窗是否可关闭
 */
function rowConfirm_callback(rtnData, clickType) {
    const func1 = window.opener ? window.opener[parentCallback] || "" : "";
    let func2 = selfCallback[sqlKey] || "";
    func2 = func2 ? func2[clickType] : "";

    const canBeClosed = (winType === "single" && clickType === "rowClick") ||
        (winType === "multiple" && clickType === "confirmClick");

    // 回调(函数在父页面)
    if (typeof func1 == "function") {
        const rtn1 = func1(rtnData);
        if (rtn1.code === 0) {
            if (isNotEmpty(rtn1.msg)) alert(rtn1.msg);
        }
        else if (canBeClosed){
            return true;
        }
    }
    else if (typeof func2 == "function") {// 回调(函数在开窗页面)
        const bool = func2(rtnData);
        if (bool && canBeClosed) {
            return true;
        }
    }
    else if (canBeClosed) {
        return true;
    }

    return false;
}

/**
 * 右键菜单回调
 * @author liquid
 * @date 2022年12月14日
 */
function rightMenu_callback(callbackIndex) {
    const funcArr = rightMenuCallback[sqlKey] || [];
    let callbackRtn = false;
    if (funcArr[callbackIndex]) {
        callbackRtn = funcArr[callbackIndex](rowData4RightMenu || []);
    }
    else {
        alert("未配置该选项的回调函数");
    }
    if (callbackRtn) {
        closeWin();
    }
}

/**
 * 多选取消已选
 * @param data
 * @param rowIndex
 * @param event
 */
function cancelSelectedData(data, rowIndex, event) {
    let selectTxt = window.getSelection().toString();
    if (isNotEmpty(selectTxt)) {// 避免选择文本时也触发点击事件的逻辑
        return;
    }
    vue_outer_div.multipleSelectedData = delElement(vue_outer_div.multipleSelectedData, data);
}

/**
 * 关闭开窗
 * @author liquid
 * @date 2023年2月19日
 */
function closeWin() {
    // 移动端
    window.close();

    // pc端
    let closeFunc = window.parent.closeWinCenterWithMask;
    if (typeof closeFunc === "function") {
        closeFunc();
    }
}

function changeToWin() {
    window.open(document.location.href);
    window.onmessage = function(e) {
        const sqlData = e.data.sqlData;
        console.log(sqlData);
        if (sqlData !== null && sqlData !== undefined) {
            vue_outer_div.$data.multipleSelectedData = sqlData.arr;
        }
    }
}

/**
 * 导出开窗数据(查询条件下的全部数据)
 * @author zengwenfu
 * @date 2022年12月13日
 */
function exportData() {
    let searchStr = getSearchValue();
    let exportTitle = [];
    for (let i = 0; i < titles[lang].length; i++) {
        exportTitle.push(titles[lang][i].label);
    }
    // 调接口拿数据
    loading(true);
    ajaxActionPms("xp/modeling/app/it/actions/XpEkpSqlAction.do?method=executeQuery", {
            key: sqlKey,
            params: searchStr
    }).then((res)=>{
        if (res.status !== "success") { errorHandler(res); return; }
        // 成功回调
        export2Excel(res.queryData, exportTitle, winTitle[lang]);
        loading(false);
    }).catch((error)=>{errorHandler(error);})
}

/**
 * 获取第一页数据
 * @author liquid
 * @date 2022年11月14日
 */
function firstPageClick() {
    searchData(true, 0);
}

/**
 * 获取上一页数据
 * @author liquid
 * @date 2022年11月14日
 */
function pageUpClick() {
    const index = firstResult - maxResults;
    if (index >= 0) {
        searchData(false, index);
    }
}

/**
 * 获取下一页数据
 * @author liquid
 * @date 2022年11月14日
 */
function pageDownCLick() {
    const index = firstResult + maxResults;
    if (index < vue_outer_div.total) {
        searchData(false, index);
    }
}

/**
 * 获取最后一页数据
 * @author liquid
 * @date 2022年11月14日
 */
function lastPageClick() {
    const lastLeft = vue_outer_div.total % maxResults;
    let index = vue_outer_div.total - (lastLeft === 0 ? maxResults : lastLeft);
    searchData(false, index);
}

/**
 * 多选时点击[全选]触发事件
 * @author liquid
 * @date 2022年11月21日
 */
function multipleChooseAll() {
    const multipleCheck = $("#multiple_choose_all > input:checked");
    setChecked(new Array(maxResults).keys(), multipleCheck.length > 0);
}

/**
 * 判断是否需要隐藏列
 * @author liquid
 * @date 2022年11月18日
 */
function hideColumn(index) {
    const width = titles["ZH-CN"][index].width;
    return "0;0px;0PX;0px;0Px;0pX;0vw;0VW;0Vw;0vW;0%".includes(width);
}

/**
 * 判断是否已全选
 * @author liquid
 * @date 2022年11月22日
 * @returns {boolean} 是否已全选
 */
function isAllSelected() {
    let isAllSelected = true;
    const selectedDataStr = JSON.stringify(vue_outer_div.multipleSelectedData);
    for (let i = 0; i < vue_outer_div.nowData.length && isAllSelected; i++) {
        const nowDataRowStr = JSON.stringify(vue_outer_div.nowData[i]);
        isAllSelected = selectedDataStr.includes(nowDataRowStr);
    }
    return vue_outer_div.multipleSelectedData.length !== 0 && isAllSelected;
}

/**
 * 判断本行是否已选
 * @author liquid
 * @date 2022年11月22日
 * @param value 本行数据
 * @returns {boolean} 是否已选
 */
function isSelected(value) {
    return JSON.stringify(vue_outer_div.multipleSelectedData).includes(JSON.stringify(value));
}

/**
 * (多选)左边checkbox的点击事件
 * @author liquid
 * @date 2022年11月22日
 * @param rowIndex
 */
function multipleChooseSingleClick(rowIndex) {
    const checked = $(".multiple_choose_single > input")[rowIndex].checked;
    setChecked([rowIndex], checked);
}

/**
 * 执行搜索
 * @author liquid
 * @date 2022年11月15日
 * @param renewSearch 是否重新以搜索框值作为条件值
 * @param index 从第index个记录开始返回
 */
function searchData(renewSearch = true, index = 0) {
    let queryData;// 查询结果集
    let queryMaxCount;// 查询结果最大记录数

    // 重新以搜索框值作为条件值
    if (renewSearch) {
        index = 0;
        searchValue = getSearchValue();
    }
    firstResult = index;
    searchValue._fixed = {
        firstResult: firstResult,// 从第firstResult条记录开始查询
        maxResults: maxResults// 限制查询pageRowCount个结果
    }
    console.log("查询参数", searchValue);

    // 执行查询
    // 若存在历史记录则直接返回
    const paramsStr = JSON.stringify(searchValue);
    if (historyData.ekp[paramsStr]) {
        const data = historyData.ekp[paramsStr];
        renewData(data.queryData, data.queryMaxCount);
        return;
    }
    // 无历史记录则向后台查询
    loading(true);
    ajaxActionPms("xp/modeling/app/it/actions/XpEkpSqlAction.do?method=executeQuery", {
        key: sqlKey,
        params: searchValue
    }).then((res)=>{
        console.log({res})
        if (res.status !== "success") { errorHandler(res); return; }
        // 成功回调
        queryData = res.queryData;
        queryMaxCount = res.queryMaxCount;
        renewData(queryData, queryMaxCount);
        historyData.ekp[JSON.stringify(searchValue)] = res;// 保存历史记录
        loading(false);
    }).catch((error)=>{errorHandler(error);})
}

/**
 * 重新选择页面数据
 * @author liquid
 * @date 2022年11月18日
 * @param queryData
 * @param queryMaxCount
 */
function renewData(queryData, queryMaxCount) {
    // 重新渲染页面数据
    vue_outer_div.nowData = queryData;
    vue_outer_div.total = queryMaxCount;
    vue_outer_div.nowPage = queryMaxCount === 0 ? 0 : Math.ceil((firstResult + 1) / maxResults);
    vue_outer_div.lastPageIndex = Math.ceil(queryMaxCount / maxResults);
}

/**
 * 转圈圈
 * @author liquid
 * @date 2022年11月18日
 * @param {boolean}open 是否转圈圈
 * @return {void}
 */
function loading(open) {
    if (open) {
        vue_outer_div.noDataShow = false;
        $("#loading_div").css("display", "flex")
    } else {
        vue_outer_div.noDataShow = true;
        $("#loading_div").hide();
    }
}

/**
 * 前往sql定义
 * @author liquid
 * @date 2023年4月17日
 * @return {void}
 */
function gotoSqlDefinition() {
    if (isEmpty(sqlDefinitionFdId)) {
        return;
    }
    let url = Com_Parameter.ContextPath +
        "sys/modeling/main/modelingAppSimpleMain.do?method=edit" +
        "&fdId=" + sqlDefinitionFdId;
    window.open(url);
}

function getLang(){
	let langStr = window.opener == null ? parent.window.Com_Parameter.Lang : window.opener.Com_Parameter.Lang;
	return langStr.toUpperCase();
}

3、具体实现 - 后端

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.landray.kmss.xp.xform.base.service.controls.relationchoose;

import com.landray.kmss.sys.xform.base.service.ISysFormTemplateControl;
import com.landray.kmss.sys.xform.base.service.ISysFormTemplateDetailsTableControl;
import com.landray.kmss.sys.xform.base.service.controls.FilterAction;
import com.landray.kmss.sys.xform.base.service.controls.LoopAction;
import com.landray.kmss.sys.xform.base.service.controls.TagNodeUtils;
import com.landray.kmss.util.StringUtil;
import org.htmlparser.Node;
import org.htmlparser.lexer.Lexer;
import org.htmlparser.nodes.TagNode;
import org.htmlparser.tags.InputTag;
import org.htmlparser.util.ParserException;

import java.util.List;

public class XpSysFormTemplateRelationChooseControl implements ISysFormTemplateControl, FilterAction, ISysFormTemplateDetailsTableControl {
    private String parentClass;

    public XpSysFormTemplateRelationChooseControl() {
    }

    public boolean parse(Node node, Lexer lexer, StringBuilder jsp, List<ISysFormTemplateControl> controls) throws Exception {
        if (node instanceof TagNode) {
            TagNode tagNode = (TagNode)node;
            if (TagNodeUtils.isType("xpRelationChoose", tagNode)) {
                TagNodeUtils.fixRequired(tagNode);
                TagNodeUtils.filterNode(tagNode, lexer, jsp, this);
                return true;
            }
        }

        return false;
    }

    private void doParse(Node node, StringBuilder jsp) throws ParserException {
        if (node instanceof InputTag) {
            TagNode tagNode = (TagNode)node;
            String id = tagNode.getAttribute("id");
            String bindEvent = "click";
            String required = tagNode.getAttribute("_required");
            String style = tagNode.getAttribute("style");
            String freeEdit = tagNode.getAttribute("freeEdit");
            String editCallbackFunName = tagNode.getAttribute("editCallbackFunName");
            String openWinFunName = tagNode.getAttribute("openWinFunName");
            String notNullCheck = tagNode.getAttribute("notNullCheck");
            String clearCallbackFunName = tagNode.getAttribute("clearCallbackFunName");
            String clearFieldId = tagNode.getAttribute("clearFieldId");

            String params = "{" +
                        "'freeEdit':'" + freeEdit + "'," +
                        "'editCallbackFunName':'" + editCallbackFunName + "'," +
                        "'openWinFunName':'" + openWinFunName + "'," +
                        "'clearCallbackFunName':'" + clearCallbackFunName + "'," +
                        "'clearFieldId':'" + clearFieldId + "'," +
                        "'notNullCheck':'" + notNullCheck + "'" +
                    "}";
            params = params.replaceAll("'", "quot;");
            jsp.append("\r\n").append(TagNodeUtils.getElementTab()).append(TagNodeUtils.buildControlWrapHTMLBegin(tagNode.getAttribute("id"), "xform_relation_choose", "relationChoose"));
            jsp.append("\r\n").append(TagNodeUtils.getElementTab()).append("<div ");
            String canApplyClass = tagNode.getAttribute("parent_class");
            if (StringUtil.isNull(canApplyClass)) {
                canApplyClass = this.parentClass;
            }

            TagNodeUtils.setAttribute(jsp, "class", canApplyClass);
            jsp.append(">");
            jsp.append("<c:import url='/xp/xform/designer/relation_choose/relation_choose_display.jsp' charEncoding='UTF-8'>").
                    append("<c:param name='fdControlId' value='").append(id).append("'></c:param>").
                    append("<c:param name='required' value='").append(required).append("'></c:param>").
                    append("<c:param name='style' value='").append(style).append("'></c:param>").
                    append("<c:param name='bindEvent' value='").append(bindEvent).append("'></c:param>").
                    append("<c:param name='subject' value='${xform:subject(\"").append(id).append("\",\"label\")}'></c:param>").
                    append("<c:param name='parentClass' value='").append(canApplyClass).append("'></c:param>").
                    append("<c:param name='params' value='").append(params).append("'></c:param>");
            jsp.append("</c:import>");
            jsp.append("</div>");
            jsp.append(TagNodeUtils.buildControlWrapHTMLEnd());
            jsp.append(TagNodeUtils.getElementTab());
        }
    }

    public void end(Node node, StringBuilder jsp) throws ParserException {
        this.doParse(node, jsp);
    }

    public void filter(Node node, StringBuilder jsp) throws ParserException {
        this.doParse(node, jsp);
    }

    public void start(Node node, StringBuilder jsp) throws ParserException {
        if (node instanceof TagNode) {
            TagNode tempNode = (TagNode)node;
            this.parentClass = tempNode.getAttribute("class");
        }

        this.doParse(node, jsp);
    }

    public boolean parseDetailsTable(Node node, Lexer lexer, StringBuilder templateJsp, final String idPrefix, List<ISysFormTemplateControl> controls) throws Exception {
        if (node instanceof TagNode) {
            TagNode tagNode = (TagNode)node;
            if (TagNodeUtils.isType("xpRelationChoose", tagNode)) {
                TagNodeUtils.loopForDetailsTable(this, tagNode, lexer, templateJsp, idPrefix, controls, new LoopAction() {
                    public boolean action(Node aTagNode, Lexer lexer, StringBuilder jsp, List<ISysFormTemplateControl> controls) throws Exception {
                        if (aTagNode instanceof TagNode) {
                            TagNode tagNode = (TagNode)aTagNode;
                            TagNodeUtils.setDetailsTableId(idPrefix, tagNode);
                            if (!(tagNode instanceof InputTag)) {
                                return false;
                            } else {
                                XpSysFormTemplateRelationChooseControl.this.doParse(tagNode, jsp);
                                return true;
                            }
                        } else {
                            return false;
                        }
                    }
                });
                return true;
            }
        }

        return false;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
底层架构优化 Maven多项目依赖,模块及插件分项目,尽量松耦合,方便模块升级、增减模块。 模块化数据库自动升级程序,当模块升级代码需要更新时,自动对应版本执行SQL更新。 授权模块,支持CAS单点登录,简单properties配置即可,不用再写很多的xml。 支持多数据源,简单properties配置即可实现,为了安全性吧,暂不提供界面维护数据源,不存数据库。 数据表主键优化,如分类科目表,采用有意义的主键方式,让客户去自定义,减少后期运维维护成本。 实体@Table注解配置 a. 自动生成增删改通用SQL,不需要在mapper.xml里写重复又费时的SQL了,减少开发和后期维护成本。b. 这里所有定义@Column均放到类头,而不是分布到各个属性或方法上,这是如下原因:c. 一是,放到表头的好处是,可一览熟知该实体类对应的物理表结构是什么样,开发者思维从物理表结构到对象的映射转换,都是基于物理表结构的,@Column指定物理字段名,而不是指定类上的属性名,也是这个原因;d. 二是,生成的SQL和查询条件,列的排列顺序,可方便核查定义,优化查询;e. 三是,方便@JoinTable关联表和其它扩展信息的设置,如果分布到类的属性上就不太好管理列。 用户数据权限优化,不仅仅是角色,颗粒度细化到每个人员的身上,可自定义第三方数据权限,不仅仅是部门和公司,你可以扩展,如:地区,栏目分类,商品分类。 支持SAAS多租户模式,每个租户数据通过corp_code字段进行分离,数据互不干扰。 缓存EhCache统一管理,支持快速切换为Redis缓存,集群Session缓存共享。 安全方面优化 原有JeeSite1.2安全选项及安全考虑保留。 配置文件数据库密码及其它安全密钥自动加密。 所有请求参数获取,均通过XSS跨站脚本过滤方法。 乐观锁简单实现,必要情况下使用,提高数据安全性。 通过properties简单配置,限制是否允许JS跨域操作。 身份认证 a. 登录失败多少次后显示验证码b. 登录失败多少次后锁定账号及锁定账号时间c. 登录账号密码可加密后再提交后台d. 同设备是否允许账号多地登录 密码策略 a. 初始密码修改策略,提醒或强制用户修改初始密码。b. 账号密码修改策略,多长时间内未修改,则提醒或强制修改密码。c. 账号密码安全等级限制策略,很弱密码,弱密码,安全密码,很安全密码 安全审计(后期) a. 查询未修改初始密码的账号,使用简单密码的,定期未修改的,长期未登录的。b. 权限审计,按登录账号查询菜单和权限,按菜单和权限查询登录账号 用户界面优化 平面化界面设计,精细,更加美观、高端、大气、上档次。 支持手机或平板访问,响应式样式,根据屏幕分辨率自适应控件布局。 无刷新设计,除了进入功能页面和新页面,其它情况下全部采用Ajax交互,优化体验和性能。 支持一件换肤,只需在properties里修改下主题名称即可快速切换整个UI的风格,不仅仅是色调和样式,布局也可改变。支持自定义扩展项目独有的主题样式风格。 优化用户功能操作,大众思维模式,功能清晰,更加贴切和友好。 前端开发优化 采用当前比较流行的Beetl模板引擎,它的优点很多,大家可自行查阅看看,这里不多说了。 封装Beetl UI通用组件,简单实现基本表单控件、树选择,列表选择,文件上传,等等很多,总之是简化开发。 数据表格jqGrid组件封装,自动完成分页、排序、列宽、多表头、子表、编辑表、等。 功能及组件优化 工具类Utils封装优化,应有尽有,包分类层次分明,独立工具类项目。 强大的Excel导导入工具封装,支持大数据量,注解定义,简单配置即可实现。 Job作业调度,界面化在线管理,可新增,编辑、删除、暂停、恢复、运行一次等操作 在线查询在线人员,强踢在线账号。 代码生成工具操作简化及优化。
FineUI(开源版) 基于 ExtJS 的开源 ASP.NET 控件库。 FineUI的使命 创建 No JavaScript,No CSS,No UpdatePanel,No ViewState,No WebServices 的网站应用程序。 支持的浏览器 IE 8.0 、Chrome、Firefox、Opera、Safari bulid2014-09-09 v4.1.3 母版页支持。 增加新控件CPHConnector(ContentPlaceHolder)。 增加三个示例,演示布局、接口以及动态加载在母版页中的运用: 杂项->母版页->树(布局)。 杂项->母版页->表格(布局、接口与动态加载)。 杂项->母版页->表格与表单(布局、接口与动态加载)。 修正UMEditor在从全屏恢复时滚动条消失的问题(Gnid-6187)。 窗体控件增强。 增加子窗体关闭后调用父页面JS函数的方法(txw999-6022)。分别为ActiveWindow和Window增加GetHideExecuteScriptReference方法。 增加示例:内联架->新窗口中选择省份(GetHideExecuteScriptReference)。 窗体的Width和Height属性更新为AJAX属性,可以在后台改变 修正在父页面弹窗体时无法通过设置Title属性来改变标题的问题(舞柯庶-6031)。 更新示例:内联架->回发父页面(改变属性或者注册脚本)。 HyperLinkField的DataNavigateUrlFieldsEncode属性已废除,请替换为UrlEncode属性。 为ControlBase增加Listeners属性,方便设置控件的客户端事件处理函数,同时更新如下示例: 表格控件->复选列->全选复选列(客户端实现)。 表格控件->杂项->右键菜单(全选复选)。 杂项->自定义回发(__doPostBack)。 杂项->自定义回发(F.customEvent)。 杂项->文本改变客户端事件。 修正行扩展列无法显示隐藏的问题(zy32002-6195)。 为Grid增加ShowPagingMessage,并更新示例:表格控件->分页与排序->数据库分页。 增加示例:导航控件->工具栏与菜单->内联菜单(HideOnClick)。 表格的PageIndex有效性检查,更新示例:表格控件->分页工具条->改变每页记录数。
JEECG(J2EE Code Generation)是一款基于代码生成器的智能开发平台。引领新的开发模式(Online Coding->代码生成器->手工MERGE智能开发),可以帮助解决Java项目90%的重复工作,让开发更多关注业务逻辑。既能快速提高开发效率,帮助公司节省人力成本,同时又不失灵活性。 JEECG宗旨是:简单功能由代Online Coding配置功能;复杂功能由代码生成器生成进行手工Merge;复杂流程业务采用表单自定义,业务流程使用工作流来实现、扩展任务接口,供开发编写业务逻辑。实现了流程任务节点和任务接口的灵活配置,既保证了公司流程的保密行,又减少了开发人员的工作量。 JEECG功能特点: 1、采用SpringMVC+Hibernate+Minidao(类Mybatis)+Easyui(UI库)+Jquery+Boostrap+Ehcache+Redis+Ztree+Vue+Boostrap-table+ElementUI等基础架构 2、采用面向声明的开发模式,基于泛型编写极少代码即可实现复杂的数据展示、数据编辑、表单处理等功能,再配合Online Coding在线开发与代码生成器的使用,将J2EE的开发效率提高8倍以上,可以将代码减少90%以上。 3、JEECGV4.0,经过了专业压力测试,性能测试,保证后台数据的准确性和页面访问速度 4、支持多种浏览器:IE,火狐,Google等 5、支持数据库:Mysql,Oracle,Postgre,SqlServer等 6、基础权限:用户,角色,菜单权限,按钮权限,数据权限 7、智能报表集成:简易的图像报表工具和Excel导入导 8、Web容器测试通过的有Jetty和Tomcat,Weblogic 9、亮点功能:分布式部署,云平台,移动平台开发,规则引擎 10、要求JDK1.6+ JEECG技术点总结: 技术点一:代码生成器SPA单页面应用快速生成,采用VUE+ElementUI打造酷炫效果 技术点二:新一代代码生成器更灵活的代码生成器工厂,可灵活自定义生成的代码文件名称、路径等;根据模板结构生成代码文件 技术点三:新一代代码生成器支持Vue+ElementUI风格,Bootstrap表单+EasyUI原生态列表风格,ElementUI表单+EasyUI原生态列表风格 技术点四:Dategrid标签多列表风格快速切换,给用户提供多种选择 技术点五:Online Coding在线开发(通过在线配置实现一个表模型的增删改查功能,无需一行代码,支持用户自定义表单布局) 技术点六:代码生成器,支持多种数据模型,根据表生成对应的Entity,Service,Dao,Action,JSP等,增删改查功能生成直接使用 技术点七:UI快速开发库,针对WEBUI进行标准封装,页面统一采用UI标签实现功能:数据datagrid,表单校验,Popup,Tab等,实现JSP页面零JS,开发维护非常高效 技术点八:在线流程定义,采用开源Activiti流程引擎,实现在线画流程,自定义表单,表单挂接,业务流转,流程监控,流程跟踪,流程委托等 技术点九:自定义表单,支持用户自定义表单布局,支持单表、列表、Select\Radio\Checkbox\PopUP\Date等特殊控件 技术点十:查询过滤器:查询功能自动生成,后台动态拼SQL追加查询条件;支持多种匹配方式(全匹配/模糊查询/包含查询/不匹配查询) 技术点十一:移动平台支持,对Bootstrap(兼容Html5)进行标准封装 技术点十二:动态报表功能(用户输入一个sql,系统自动解析生成报表) 技术点十三:数据权限(精细化数据权限控制,控制到行级,列表级,表单字段级,实现不同人看不同数据,不同人对同一个页面操作不同字段) 技术点十四:国际化(支持多语言,国际化的封装为多语言做了便捷支持) 技术点十五:多数据源(在线配置数据源,数据源工作类封装) 技术点十六:多种首页风格切换,支持自定义首页风格。(经典风格、Shortcut风格、ACEbootstrap风格、云桌面风格) 技术点十七:在线配置报表(无需编码,通过在线配置方式,实现曲线图,柱状图,数据等报表) 技术点十八:简易Excel导入导,支持单表导和一对多表模式导,生成的代码自带导入导功能 技术点十九:移动OA,移动OA审批功能,采用H5技术,实现手机移动办公,无缝对接微信、钉钉、微信企业号、也可以做APP 技术点二十:移动图表,在线配置移动报表,采用H5技术,可以手机端查看 技术点二十一:插件开发,业务功能组件以插件方式集成平台,也可以单独部署发发布,有力支撑了SAAS云应用系统需求 技术点二十二:专业接口对接机制,统一采用restful接口方式,集成
好的,下面是一个基本的el-form表单,包含了常用的表单控件,每个控件都可以自定义事件和大小: ``` <template> <el-form :model="form" ref="form" label-width="100px"> <el-form-item label="Input"> <el-input v-model="form.input" @change="handleInput" :style="{width: inputWidth + 'px'}"></el-input> </el-form-item> <el-form-item label="Select"> <el-select v-model="form.select" @change="handleSelect" :style="{width: selectWidth + 'px'}"> <el-option label="Option 1" value="1"></el-option> <el-option label="Option 2" value="2"></el-option> <el-option label="Option 3" value="3"></el-option> </el-select> </el-form-item> <el-form-item label="DatePicker"> <el-date-picker v-model="form.date" @change="handleDate" :style="{width: datePickerWidth + 'px'}"></el-date-picker> </el-form-item> <el-form-item label="Checkbox"> <el-checkbox v-model="form.checkbox" @change="handleCheckbox"></el-checkbox> </el-form-item> <el-form-item label="Radio"> <el-radio-group v-model="form.radio" @change="handleRadio"> <el-radio label="Radio 1"></el-radio> <el-radio label="Radio 2"></el-radio> <el-radio label="Radio 3"></el-radio> </el-radio-group> </el-form-item> </el-form> </template> <script> export default { data() { return { form: { input: '', select: '', date: '', checkbox: false, radio: '' }, inputWidth: 200, selectWidth: 200, datePickerWidth: 200 } }, methods: { handleInput() { // 自定义input控件的change事件 }, handleSelect() { // 自定义select控件的change事件 }, handleDate() { // 自定义datePicker控件的change事件 }, handleCheckbox() { // 自定义checkbox控件的change事件 }, handleRadio() { // 自定义radio控件的change事件 } } } </script> ``` 在这个表单中,每一个控件都可以自定义事件,并且通过设置控件的style属性,可以自定义控件的大小。您可以在这个基础上进行修改,满足您的具体需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liquid-Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值