import { ControlVOBase, FormActionType, ICodeListItem, ICtrlActionResult, IEvent, IFormAbility, IFormController, IFormControllerParams, IFormModel, IFormStore, IHttpResponse, IParam, IViewLogicInput, } from '@/core'; import { VerifyUtil, DataTypeUtil, deepCopy, verifyDynamicLogic, dateFormat, typeOf, } from '@/core/utils'; import { FormDetailController } from './form-detail-controller'; import { DECtrlController } from './de-ctrl-controller'; import FormService from '@/core/modules/ctrl-service/form-service'; /** * 表单控制器 * * @export * @class FormController * @extends {(DECtrlController)} * @implements {IFormController} * @template T * @template S * @template A */ export class FormController extends DECtrlController implements IFormController { /** * 表单模型 * * @type {IFormModel} * @memberof FormController */ protected declare readonly model: IFormModel; /** * 部件服务 * * @type {FormService} * @memberof FormController */ declare ctrlService: FormService; /** * 表单校验 * * @type {Function} * @memberof FormController */ validate!: () => Promise; /** * 表单项校验 * * @type {Function} * @memberof FormController */ validateFields!: (name: string[]) => Promise; /** * 值规则 * * @private * @type {IParam} * @memberof FormController */ protected valueRule: IParam = {}; /** * 处理部件初始化 * * @protected * @param {IFormControllerParams} params * @memberof FormController */ protected processCtrlInit( params: IFormControllerParams ) { super.processCtrlInit(params); this.validate = params.formValidate; this.validateFields = params.formValidateFields; Object.assign(this.store, { data: this.ctrlService.newControlVO({}), detailModel: {}, }); this.initValueRule(); this.initFormDetailModel(); } /** * 初始化表单成员模型 * * @memberof FormController */ private initFormDetailModel() { const { detailModel } = this.model; Object.values(detailModel).forEach((detail: IParam) => { this.store.detailModel[detail.codeName] = new FormDetailController({ model: detail, valueRule: this.valueRule[detail.name] }); }); } /** * 初始化值规则 * * @private * @memberof FormController */ private initValueRule() { const { detailModel, valueRule } = this.model; // 系统值规则和属性值规则 const staticRules: IParam = {}; valueRule.forEach((rule: IParam) => { staticRules[rule.name] = []; if (rule.valueRuleType == 'SYSVALUERULE') { // 正则校验 if (rule.ruleType == 'REG') { staticRules[rule.name].push({ trigger: ['change', 'blur'], message: rule.ruleInfo, pattern: rule.regExCode, }); } else if (rule.ruleType == 'SCRIPT') { // 脚本校验 staticRules[rule.name].push({ trigger: ['change', 'blur'], message: rule.ruleInfo, validator: (verify: any, value: any, callback: Function) => { if (this.store.data[rule.name]) { const data = this.store.data; const context = this.store.context; const viewParams = this.store.viewParams; try { eval(rule.scriptCode); } catch (error) { return Promise.reject(App.ts("widget.editform.scriptcodecheckerror", "脚本代码校验错误")); } } return Promise.resolve(); }, }); } } else if (rule.valueRuleType == 'DEFVALUERULE') { staticRules[rule.name].push({ trigger: ['change', 'blur'], validator: (verify: any, value: any, callback: Function) => { if (this.store.data[rule.name]) { const { isPast, infoMessage } = VerifyUtil.verifyDeRules( rule.name, this.store.data, rule.condition ); if (!isPast) { return Promise.reject(infoMessage || rule.ruleInfo); } } return Promise.resolve(); }, }); } }); // 非空值规则 this.valueRule = {}; Object.values(detailModel).forEach((item: any) => { let rules: IParam = []; if (staticRules[item.name]) { rules = staticRules[item.name]; } const rule = { trigger: ['change', 'blur'], required: !!item.required, message: item.caption + `${App.ts("widget.editform.mandatory")}`, validator: (rule: any, value: any, callback: Function) => { if (this.store.detailModel[item.name].required) { if (typeOf(value) == 'array') { for (let index = 0; index < value.length; index++) { if (!(value[index] || value[index] === 0)) { return Promise.reject(` ${item.captionItemName ? this.store.data[ item.captionItemName.toLowerCase() ] : item.caption } ${App.ts("widget.editform.mandatory")}! `); } } } else if (!(value || value === 0)) { return Promise.reject(` ${item.captionItemName ? this.store.data[ item.captionItemName.toLowerCase() ] : item.caption } ${App.ts("widget.editform.mandatory")}! `); } } return Promise.resolve(); }, }; rules.push(rule); if (item.editRules) { rules.push(...VerifyUtil.buildVerConditions(item.editRules)); } this.valueRule[item.name] = rules; }); } /** * 加载草稿 * * @param {IParam} [opts={}] * @return {*} {Promise} * @memberof FormController */ async loadDraft(opts: IParam = {}): Promise { return Promise.reject({ ok: false, data: this.getData(), rowData: null }); } /** * 表单执行数据行为之后 * @param {string} action 表单行为 * @param {string} data 行为数据 * @memberof FormController */ protected async afterFormAction(action: string, data: IParam) { } /** * 设置表单项启用 * * @protected * @memberof FormController */ protected setFormEnableCond() { const { data } = this.store; Object.values(this.store.detailModel).forEach((item: IParam) => { if (Object.is(item.detailType, 'FORMITEM')) { switch (item.enableCond) { case 0: // 不启用 item.disabled = true; break; case 1: // 新建 item.disabled = data.srfuf != 0; break; case 2: // 更新 item.disabled = data.srfuf != 1; break; case 3: // 启用 item.disabled = false; break; default: break; } } }); } /** * 表单动态逻辑 * * @protected * @param {string} name * @memberof FormController */ protected formDynamicLogic(name: string) { const { data } = this.store; const { detailModel } = this.model; Object.values(detailModel).forEach((item: IParam) => { item.groupLogics?.forEach((logic: IParam) => { const relatedNames = logic.relatedDetailNames || []; if (Object.is(name, '') || relatedNames.indexOf(name) != -1) { const ret = verifyDynamicLogic(data, logic); switch (logic.logicCat) { // 动态空输入,不满足则必填 case 'ITEMBLANK': this.store.detailModel[item.name].required = !ret; // 必填 - > 非必填 清除必填校验信息 ret && this.validateFields([item.name]) break; // 动态启用,满足则启用 case 'ITEMENABLE': this.store.detailModel[item.name].disabled = !ret; break; // 动态显示,满足则显示 case 'PANELVISIBLE': this.store.detailModel[item.name].visible = ret; break; } } }); }); } /** * 转化代码项文本 * * @protected * @memberof FormController */ protected async translateCodeListText() { const { data, context, viewParams } = this.store; const { detailModel } = this.model; const tempData = deepCopy(data); for (const key in tempData) { if (data.hasOwnProperty(key)) { if ( detailModel[key] && detailModel[key].convertToCodeItemText && detailModel[key].codeListTag ) { const codeListResult = await App.getAppCodeListHelper().getCodeListItems( detailModel[key].codeListTag, context, viewParams ); if (codeListResult.ok) { const result = codeListResult.data; result.forEach((item: ICodeListItem) => { if (Object.is(item.value, tempData[key])) { tempData[key] = item.text; } }); } } } } Object.assign(this.store.data, tempData); } /** * 表单项更新 * * @protected * @param {string} name 表单项名称 * @memberof FormController */ protected async formItemUpdate(name: string) { const { detailModel } = this.model; const formItemUpdate = detailModel[name]?.formItemUpdate; if (formItemUpdate) { const context = deepCopy(this.store.context); const viewParams = deepCopy(this.store.viewParams); const data = this.store.data; try { if (formItemUpdate.customCode && formItemUpdate.scriptCode) { eval(formItemUpdate.scriptCode); } else { const response: IHttpResponse = await this.ctrlService.frontLogic( formItemUpdate.appDEMethod, context, Object.assign(data, viewParams) ); if (response.success) { formItemUpdate.updateDetails?.forEach((detailsName: string) => { if (data.hasOwnProperty(detailsName.toLowerCase())) { Object.assign(data, { [detailsName.toLowerCase()]: response.data[detailsName.toLowerCase()], }); } }); await this.afterFormAction('formItemUpdate', data); this.executeFromDetailAbility({ name: name, action: 'formItemUpdate', data: this.store.data, }); } } } catch (error: any) { } } } /** * 检验是否忽略输入值 * * @protected * @param {string} name 表单项名称 * @param {("before" | "change")} [step="change"] 步骤 * @return {*} {boolean} * @memberof FormController */ protected checkIgnoreInput( name: string, step: 'before' | 'change' = 'change' ): boolean { const { detailModel } = this.store; const formDetail = detailModel[name]; if (formDetail) { switch (formDetail.ignoreInput) { case 4: if (formDetail.disabled) { return true; } break; } } return false; } /** * 重置表单项 * * @protected * @param {string} name 表单项名称 * @memberof FormController */ protected async resetFormItem(name: string) { const { detailModel } = this.model; for (const detailName in detailModel) { const detail = detailModel[detailName]; if (detail.resetItemName && Object.is(name, detail.resetItemName)) { await this.handleFormValueChange(detail.name, null); if (detail.valueItemName) { await this.handleFormValueChange(detail.valueItemName, null); } } } } /** * 设置新建默认值 * * @protected * @param {string} [name] * @param {IParam} [item] * @memberof FormController */ protected setCreateDefault(name?: string, item?: IParam) { const createDefaultItems = name ? this.store.detailModel[name]?.createDefaultItems : this.model.createDefaultItems; let data: any = name ? item : this.store.data; const { viewParams, context } = this.store; createDefaultItems?.forEach((item: IParam) => { const { createDV, createDVT, property, dataType, valueFormat } = item; if (createDV || createDVT) { switch (createDVT) { case 'CONTEXT': data[property] = viewParams[createDV]; break; case 'SESSION': data[property] = context[createDV]; break; case 'APPDATA': data[property] = context[createDV]; break; case 'OPERATORNAME': data[property] = context['srfusername']; break; case 'OPERATOR': data[property] = context['srfuserid']; break; case 'CURTIME': data[property] = dateFormat(new Date(), valueFormat); break; case 'PARAM': data[property] = this.ctrlService.getRemoteCopyData()[property] || null; break; default: data[property] = DataTypeUtil.isNumber(dataType) ? Number(createDV) : createDV; break; } } }); } /** * 处理表单值变化 * * @param {string} name 表单项名称 * @param {*} value 表单项值 * @return {*} * @memberof FormController */ protected async handleFormValueChange( name: string, value: any ): Promise { this.handleLogicScript(name, value, 'SCRIPTCODE_CHANGE'); } /** * 处理表单成员能力 * * @protected * @param {IEvent} params 参数 * @memberof FormController */ protected executeFromDetailAbility(params: IEvent) { const { detailModel } = this.store; Object.values(detailModel).forEach((detail: IParam) => { const ability = detail.getAbility(); if (ability && ability.executeFormDetail) { ability.executeFormDetail(params); } }); } /** * 处理表单界面行为 * * @param {MouseEvent} event 事件源 * @param {IViewLogicInput} logic 逻辑 * @memberof FormController */ private handleFormUIAction(event: MouseEvent, logic: IViewLogicInput): void { const { context, viewParams } = this.store; App.getViewLogicHelper().executeViewLogic( context, viewParams, this.getData(), event, this.getAbility(), logic ); } /** * 处理表单分组管理 * * @param {string} name 表单分组名称 * @param {boolean} value 值 * @memberof FormController */ private handleFormGroupManage(name: string, value: boolean) { const formGroupDetail = this.store.detailModel[name]; if ( formGroupDetail.showMoreMode == 2 && formGroupDetail.showMoreItems.length > 0 ) { const showMoreItems = formGroupDetail.showMoreItems; showMoreItems.forEach((itemName: string) => { this.store.detailModel[itemName].visible = value; }); } } /** * 设置表单成员能力 * * @param {string} name * @param {IParam} data * @memberof FormController */ private setFormDetailAbility(name: string, data: IParam) { this.store.detailModel[name].setAbility(data); } /** * 处理表单组件行为 * * @param {IEvent} actionParam 行为参数 * @memberof FormController */ public handleComponentAction(actionParam: IEvent): void { const { name, action, data } = actionParam; switch (action) { case 'valueChange': this.handleFormValueChange(name, data); break; case 'formButtonAction': this.handleFormUIAction(data.event, data.logic); break; case 'formGroupAction': this.handleFormUIAction(data.event, data.logic); break; case 'formGroupManage': this.handleFormGroupManage(name, data); break; case 'componentInit': this.setFormDetailAbility(name, data); break; case 'editorClick': this.handleEditorClick(name, data); break; case 'editorFocus': this.handleEditorFocus(name, data); break; case 'editorBlur': this.handleEditorBlur(name, data); break; } } /** * @description 表单编辑器点击事件 * @date 2023/02/03 10:02:48 * @param {string} name * @param {*} data * @memberof FormController */ public handleEditorClick(name: string, data: any) { this.handleLogicScript(name, data, 'SCRIPTCODE_CLICK'); } /** * @description 表单编辑器聚焦事件 * @date 2023/02/03 10:02:24 * @param {string} name * @param {*} data * @memberof FormController */ public handleEditorFocus(name: string, data: any) { this.handleLogicScript(name, data, 'SCRIPTCODE_FOCUS'); } /** * @description 表单编辑器失去焦点事件 * @date 2023/02/03 10:02:37 * @param {string} name * @param {*} data * @memberof FormController */ public handleEditorBlur(name: string, data: any) { this.handleLogicScript(name, data, 'SCRIPTCODE_BLUR'); } /** * @description 处理逻辑脚本 ({name, value, context, viewparams, data, details}: any) => {......} * @param {string} name * @param {string} logicCat * @param {any} data * @memberof FormController */ public handleLogicScript(name: string, value: any, logicCat: string) { const groupLogics: IParam[] = this.model.detailModel[name]?.groupLogics || []; if (groupLogics.length > 0) { const groupLogic = groupLogics.find((logic: IParam) => logic.logicCat && Object.is(logic.logicCat, logicCat)); if (groupLogic && groupLogic.childLogics && groupLogic.childLogics.length > 0) { groupLogic.childLogics.forEach((logic: IParam) => { if (logic.script && logic.script instanceof Function) { const params = { name: name, value: value, data: this.store.data, context: this.store.context, viewparams: this.store.viewParams, details: this.store.detailModel } logic.script(params); } }) } } } /** * 获取能力 * * @return {*} {IEditFormAbility} * @memberof FormController */ getAbility(): A { return { ...super.getAbility(), loadDraft: this.loadDraft.bind(this), validate: this.validate, }; } }