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

 注:开窗功能实现,具体可看本人文章: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
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Liquid-Li

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

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

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

打赏作者

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

抵扣说明:

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

余额充值