import _ from 'lodash' import {Lib, lookupScope} from './lib' import {ajax} from "./config"; import {Model} from 'src/types'; import {msg, showErrorDialog} from "../message"; export const SIMPLE_RE = /^(?:\{(?:(\d+)|([a-z_][\w\.]*))\})$/i /** * 对某个表达式进行求值 * a:{query.a},b:{query.b} -> a:aValue,b:bValue * * @example * calcExpress(cc.viewModel.data, "WH_ID:{query.WH_ID},C:{theGrid.selection.data.WH_ID}") * 计算出来的值是: "WH_ID:queryWhId,C:JH000000001" * * @param data 数据环境对象 * @param express 表达式对象 */ export function calcExpress(data, express) { let result = express if (SIMPLE_RE.test(express)) { // '{foo}' 简单表达式 const path = express.substring(1, express.length - 1); return _.get(data, path) } while (true) { const mlist = result.match(/{(.*?)}/) if (!mlist) { break } const pathC = mlist[0] // {query.a} const path = mlist[1] // query.a let value = _.get(data, path) result = result.replaceAll(pathC, value || '') } return result } /** * 对个对象进行表达式求值,不用回调 * @example * calcObjectFlat({query:{a:'aValue',b1:'b1Value',b2:'b2Value',d1:1,d2:2}}, { a:'{query.a}', b:{b1:'{query.b1}', b2:'{query.b2}'},c:'aa',d:['{query.d1}','{query.d2}'] }) * * { * a: '{query.a}', * b: { * b1: '{query.b1}', * b2: '{query.b2}', * }, * c: 'aa', * d: [ * '{query.d1}', * '{query.d2}' * ] * } * * 计算结果为 * { * a: 'aValue', * b: { * b1: 'b1Value', * b2: 'b2Value' * }, * c: 'aa' * d: [ * '1', * '2' * ] * } * * @param data * @param paramObject */ export function calcObjectFlat(data, paramObject) { const result = _.cloneDeep(paramObject) const trav = (param) => { _.forOwn(param, (value, key) => { if (_.isPlainObject(value)) { // 深度递归,对子对象进行求解 trav(value) } else if (_.isString(value)) { // 字符串直接用 calcExpress 表达式求解 param[key] = calcExpress(data, param[key]) } else if (_.isArray(value)) { // 数组求解 _.each(value, (v, idx) => { value[idx] = calcExpress(data, v) }) } }) } trav(result) return result } /** * 根据表达式进入写值 * express="{query.a}" 写值就是 viewModel.set('query.a', value) * express="test-{query.a}" 写值就会失败 * * @example * tryWriteByExpress(cc.viewModel, "{query.WH_ID}", "111") * 写值成功 * * tryWriteByExpress(cc.viewModel, "test-{query.WH_ID}", "111") * 写值失败 * * @param viewModel VM对象 * @param express 表达式对象 * @param value 目标值 */ export function tryWriteByExpress(viewModel, express, value) { if (SIMPLE_RE.test(express)) { // '{foo}' 简单表达式 express = express.substring(1, express.length - 1); viewModel.set(express, value) } } /** * 尝试根据含表达式的对象回写, calcObjectFlat 的逆向方法 * @example * tryWriteObject({ a:'{query.a}', b:{b1:'{query.b1}', b2:'{query.b2}'},c:'aa',d:['{query.d1}','{query.d2}']}, {a:'aValue', b:{b1:'b1Value', b2:'b2Value'}, c:'aa', d:[1,2]}) * * expressObject: * { * a: '{query.a}', * b: { * b1: '{query.b1}', * b2: '{query.b2}', * }, * c: 'aa', * d: [ * '{query.a}', * '{query.b2}' * ] * } * * valueObject: * { * a: 'aValue', * b: { * b1: 'b1Value', * b2: 'b2Value' * }, * c: 'aa' * c: [ * 'aValue', * 'b2Value' * ] * } * * 系统会尝试回写 * viewModel.set('query.a', 'aValue') * viewModel.set('query.b1', 'b1Value') * viewModel.set('query.b2', 'b2Value') * * @param expressObject 含表达式的对象 * @param valueObject 表达式计算完成之后的结果对象 * @param writeFn 写入的方法 (path, value)=>void */ export function tryWriteObject(expressObject, valueObject, writeFn) { const trav = (pathPrefix) => { let parent = expressObject if (_.size(pathPrefix) > 1) { parent = _.get(parent, pathPrefix.substring(1)) } _.forOwn(parent, (value, key) => { if (_.isPlainObject(value)) { // 深度递归,对子对象进行求解 trav(pathPrefix + "." + key) } else if (_.isString(value)) { // 字符串直接用 calcExpress 表达式求解 if (SIMPLE_RE.test(value)) { // If we have '{foo}' alone it is a literal 简单表达式 const targetPath = value.substring(1, value.length - 1); const targetValue = _.get(valueObject, (pathPrefix + "." + key).substr(1)) if (!writeFn) { console.log(`viewModel.set('${targetPath}', '${targetValue}')`) } else { writeFn(targetPath, targetValue) } } } else if (_.isArray(value)) { _.each(value, (v, idx) => { if (SIMPLE_RE.test(v)) { const targetPath = (pathPrefix + "." + key).substr(1) + "[" + idx + "]"; const targetValue = _.get(valueObject, (pathPrefix + "." + key).substr(1) + "[" + idx + "]") if (!writeFn) { console.log(`viewModel.set('${targetPath}', '${targetValue}')`) } else { writeFn(targetPath, targetValue) } } }) } }) } trav("") } /** * 对多个表达式进行求值. 异步回调的方式返回 * { * a: 1, * b: '{someBind}', * c: ['a', 'b', 'c'], * d: ['a', 'b', '{someBind}'], * e: { * y: 1, * z: 2 * }, * f: { * y: 1, * z: '{someBind}' * } * } * * // Will produce * { * b: value, * d: ['a', 'b', value], * f: { * y: 1, * z: value * } * } * @param viewModel scope.viewModel对象 * @param paramObject 求值对象 */ export function calcObject(viewModel, paramObject) { // new Ext.app.bind.Multi({a:'1',b:'ddd{query.WH_ID}'},currentScope.viewModel,function(v){console.log(v)},currentScope, {single: true}) return new Promise(resolve => { const schedule = new Ext.app.bind.Multi( paramObject, viewModel, (ret) => { schedule.destroy() // 从 Ext.data.Model 对象转换为 js-object 对象 ret = toPlainObject(ret) resolve(ret) }, viewModel, {single: true}) }) } /** * 用于任意组件 Ext.Component 构造时,获取当前组件对应的表格(如果不是 grid.columns 对象就会返回 undefined) * @param config 组件构造函数传入的 config 配置文件 */ export function getParentGrid(config) { return config.$initParent?.grid } /** * 动态的为 combo 或 columns.combo 设置下拉框的值 * @param sender 目标对象 * @param config 目标对象的配置(在构造函数之前也可以) * @param getDictFn 获取字典的方法 * @param bizKey 传入字典的参数 */ export function setComboStore(sender, config, getDictFn, bizKey) { if (sender.$className === 'Ext.form.field.ComboBox') { getDictFn(bizKey, (r) => { if (sender.el?.dom) { // 异步回传 sender.setStore(new Ext.data.Store(r)) } else { // 同步回传 config.store = new Ext.data.Store(r) } }) return } else if (sender.xtype === 'gridcolumn') { const grid = getParentGrid(config) const {editor, renderer} = config getDictFn(bizKey, (r) => { if (sender.rendered) { // 已经渲染出来了, 用方法进行修改 const editor = sender.getEditor() if (editor && editor.xtype === 'combo') { const valueField = r.field[0] const displayField = r.field[1] editor.valueField = valueField editor.setDisplayField(displayField) editor.setStore(new Ext.data.Store({ field: ['id', 'text'], data: [ {id: "Y", text: "启用"}, {id: "N", text: "禁用"}, {id: "D", text: "删除"}, ] })) } } else { // 没有渲染之前,修改 config 即可 if (editor && editor.xtype === 'combo') { // 带编辑模式 editor.store = new Ext.data.Store(r) } } const renderer = (value, metaData) => { const valueField = r.field[0] const displayField = r.field[1] _.each(r.data, row => { // 从 valueField 找到要显示的 displayField if (row[valueField] == value) { value = row[displayField] return false } }) return value } if (sender.rendered) { // 已经渲染出来了, 对列进行渲染 sender.renderer = renderer sender.getView().refresh() } else { config.renderer = renderer } }) return } throw new TypeError("无法识别的组件类型") } /** * 调用服务器 Ajax */ export function invokeServer(url: string, ...args: any[]) { // @ts-ignore return ajax.func({ url: url, method: 'invoke', args: args }) } export function clearViewModelValues(viewModel, propertyName) { const dd = _.get(viewModel.getData(), propertyName) _.forOwn(dd, (value, key) => { viewModel.set(propertyName + '.' + key, '') }) } export function reloadGrid(scope, gridRefName) { scope.refs[gridRefName]?.reload() } /** * 将 Ext.data.Model 对象 (及子属性) 转换为 js.object 对象 */ export function toPlainObject(obj) { if (obj.isModel) { obj = obj.data } _.forOwn(obj, (v, k) => { // Ext.data.Model.constructor if (!v) { return } if (v.isModel) { v = v.data } if (typeof v === 'object') { obj[k] = toPlainObject(v) } else { obj[k] = v } }) return obj } class SystemEventFu { @Lib({ title: '提交表单', author: '罗一帆', createAt: '2021-07-02', updateAt: '2021-07-02', type: 'system', category: '表单', args: [ { type: 'string', title: 'groovy 服务路径', name: 'groovyUrl', }, { type: 'object', title: '参数的 lookup 表达式, 如果不填 默认提交所有的 viewModel.data', name: 'arg0', allowEmpty: true, } ] }) formCommit(groovyUrl: string, arg0: any) { return function (sender) { const scope = lookupScope(sender) const valid = scope.down('form').isValid() if (!valid) { return } scope.setLoading(true) let data = scope.viewModel.data if (arg0) { data = calcObjectFlat(data, arg0) } invokeServer(groovyUrl, data).then(res => { if (res.success) { scope.dialogSuccess(res) } else { showErrorDialog(res.msg || '未知错误', sender) } }).catch((e) => { const msg = e.response?.data?.msg showErrorDialog(msg || e.toString(), sender) }).finally(() => { scope.setLoading(false) }) } } @Lib({ title: '对话框成功回调', author: '罗一帆', createAt: '2021-07-02', updateAt: '2021-07-02', type: 'system', category: '对话框', args: [] }) dialogSuccess() { debugger } @Lib({ title: '清空 viewModel 某个属性', author: '罗一帆', createAt: '2021-07-02', updateAt: '2021-07-02', type: 'system', category: '表单', args: [ { type: 'viewModel', title: 'propertyName 属性路径', name: 'propertyName', } ] }) clearViewModelValues(propertyName: string) { return function (sender) { const scope = lookupScope(sender) clearViewModelValues(scope.viewModel, propertyName) } } @Lib({ title: '清空 viewModel 某个属性,并刷新表格', author: '罗一帆', createAt: '2021-07-02', updateAt: '2021-07-02', type: 'system', category: '表单', args: [ { type: 'viewModel', title: 'propertyName 属性路径', name: 'propertyName', }, { type: 'refs', title: 'gridRef 表格引用名', allowEmpty: true, name: 'gridRefName', } ] }) clearViewModelReloadGrid(propertyName: string, gridRefName?: string) { return function (sender) { const scope = lookupScope(sender) clearViewModelValues(scope.viewModel, propertyName) if (!gridRefName) { scope.down('grid')?.reload() } else { scope.refs[gridRefName]?.reload() } } } @Lib({ title: '刷新表格', author: '罗一帆', createAt: '2021-07-02', updateAt: '2021-07-02', type: 'system', category: '表单', args: [ { type: 'refs', title: 'gridRef 表格引用名, 不填写的情况下刷新所有', allowEmpty: true, name: 'gridRefName', } ] }) reloadGrid(gridRefName: string) { return function (sender) { const scope = lookupScope(sender) if (!gridRefName) { scope.down('grid')?.reload() } else { scope.refs[gridRefName]?.reload() } } } @Lib({ title: '显示对话框', author: '罗一帆', createAt: '2021-07-02', updateAt: '2021-07-02', type: 'system', category: '对话框', args: [ { type: 'module', title: '业务模块名', name: 'url', }, { type: 'object', title: '参数数据 lookup 表达式', name: 'lookupForData', allowEmpty: true, }, { type: 'event', title: '成功回调', name: 'successCallback', allowEmpty: true, } ] }) showDialog(url: string, lookupForData: any, successCallback) { return function (sender) { const scope = lookupScope(sender) const data = scope.viewModel.data const param = calcObjectFlat(data, lookupForData) // @ts-ignore require([url], (module) => { const ScopeClass = module.default const scope = new ScopeClass() scope.success = successCallback scope.showDialog(sender, {}, {data: param}) }) } } @Lib({ title: '弹出查找框(不借助 search)', author: '罗一帆', createAt: '2021-07-02', updateAt: '2021-07-02', type: 'system', category: '对话框', args: [ { type: 'module', title: '模块名 (WidgetDialog)', name: 'widgetUrl', }, { type: 'object', title: 'lookup 映射关系', name: 'lookupSetting', allowEmpty: true, } ] }) showWidget(widgetUrl, lookup, params) { return function (sender, queryValue) { showWidget(widgetUrl, lookup, params, sender, queryValue) } } @Lib({ title: '根据 lookup 清空 viewModel', author: '罗一帆', createAt: '2021-07-05', updateAt: '2021-07-05', type: 'system', category: '表单', args: [ { type: 'viewModel', title: 'lookup 设值', name: 'lookup', }, ] }) clearViewModelByLookup(lookup) { return function (sender) { clearViewModelByLookup(sender, lookup) } } @Lib({ title: '关闭对话框', author: '罗一帆', createAt: '2021-07-05', updateAt: '2021-07-05', type: 'system', category: '对话框', args: [ { type: 'event', title: '对话框的返回值回调', name: 'callBack', }, ] }) closeMe(callBack) { return function (sender) { const scope = lookupScope(sender) scope.close() if (callBack) { callBack.call(sender) } } } } export function clearViewModelByLookup(sender, lookup) { if (_.isPlainObject(lookup)) { const parentScope = lookupScope(sender) _.forOwn(lookup, (value, key) => { if (SIMPLE_RE.test(value)) { // '{foo}' 简单表达式 const path = value.substring(1, value.length - 1); if (path !== 'queryValue') { parentScope.viewModel.set(path, '') } } }) } } export function showWidget(widgetUrl, lookup, params, sender, queryValue, vjson = {}) { const parentScope = lookupScope(sender) const me = sender // @ts-ignore require([widgetUrl], (widgetScope) => { const WidgetScopeClass = widgetScope.default widgetScope = new WidgetScopeClass() // 传递进 widget.model 的数据 const widgetDialogData = calcObjectFlat({ queryValue: queryValue, ...parentScope.viewModel.data }, lookup) _.map(params, (value, key) => { widgetDialogData[key] = value }) widgetScope.parentScope = parentScope widgetScope.searchWidgetSuccess = (data) => { if (typeof lookup === 'string') { // lookup 是字符串的情况下,就是取某个列作为 value 值 me.setValue(data[lookup]) return } /** * lookup: { * // 扩展到 viewModel 的值做更改 * WH_CODE: "{queryValue}", * WH_NAME: "{query.WH_NAME}", * } */ if (_.isPlainObject(lookup)) { const parentScope = lookupScope(sender) tryWriteObject(lookup, data, (path, value) => { if (path === 'queryValue') { me.setValue(value) } else { parentScope.viewModel.set(path, value) } } ) } return true } widgetScope.showDialog(sender, vjson, {data: widgetDialogData}) }) } /** * 停止事件的默认行为 * @param e */ export function stopEvent(e) { e.preventDefault() e.stopPropagation() // @ts-ignore window.event.cancelBubble = true e.returnValue = false; e.cancelBubble = true; }