import get from 'lodash/get' import set from 'lodash/set' import customForm from '@/common/customform.js' /** * 工作流表单数据处理相关方法 * (用于工作流表单页面,包含拉取、提交等函数,注意不可用于自定义应用和代码生成器页面) * * 用户新建表单的流程: * 1、拉取流程信息 * 调用 fetchProcessInfo、 getCurrentNode * 2、根据流程信息里的表单相关定义来拉取表单 scheme * 调用 fetchSchemeData * (如果是使用移动端本地表单,则无需拉取表单,且略去后续所有步骤) * 3、加载选单数据 (例如 checkbox、select 这些表单项有选单数据) * 4、赋予默认值 (例如 currentInfo 类表单项,自动把当前用户的信息填入) * 5、生成表单 * 调用 getCustomForm 即可完成步骤 3~4 * * 用户打开已经填写的表单/草稿的流程: * 1、拉取流程信息 * 调用 fetchProcessInfo、 getCurrentNode * 2、拉取表单 scheme;如果是使用移动端本地表单则无需此步骤 * 调用 fetchSchemeData * 3、加载选单数据 * 调用 fetchFormData * 4、拉取表单中已填入的值 * 5、如果打开的是草稿,可能要把部分未填的项赋默认值 * 6、生成表单 * 调用 getCustomForm 即可完成步骤 4~5 * * (以上只是简单介绍;实际使用中,如果打开子流程,需要拉取父/子两个流程信息) */ export default { mixins: [customForm], methods: { /** * 从流程信息中生成 scheme、formValue * 参数: { schemeData (必填), processId, currentNode, formData (新建时为 null), code, useDefault } * 返回: { scheme, formValue, rel } * * 参数: * schemeData: 使用 fetchSchemeData 方法拉取到的原始 scheme 数据,未经过格式化处理 * processId: 表单 GUID;如果是新建表单,可以用 this.GUID('-') 生成一个 * currentNode: 使用 getCurrentNode 方法拉取到的当前节点信息,用于权限控制 * formData: 填入表单的表单值,新建表单时此项为 null 即可 * code: 表单编号 code,会被赋值到返回的 formValue.code;重新发起流程的场合赋 null * useDefault: 如果 formData 中某一项为空,是否对这一项填入默认值;通常在编辑草稿时启用 * * 该方法返回的 scheme 项可能带有以下属性: * __valuePath__: 表单值在 formValue 中的路径,使用 lodash 的 get、set 方法即可读写 * __sourceData__: 表单值的选单数据 * __defaultItem__: 类型为 girdtable 的表单项带有此属性,表示添加一行表格时候表格项的默认值 * __schemeIndex__: (暂时用不到)表单项位于 schemeData 根级内的第几个子项中 * __dataIndex__: (暂时用不到)表单项位于 F_Scheme.data 中的第几个子项中 */ async getCustomForm(prop) { const { schemeData, formData, currentNode, code, processId, useDefault } = prop // 处理字段之间的级联、绑定关系 // 需要绑定 change 事件的: // datetime: 修改后重新计算 datetimerange // organize: 修改后重设级联到该组件的其他组件的值,user 一级无需处理 // 需要绑定某值的: // organize: 级联到某个组件,company 一级无需处理 const schemeRef = {} const refList = [] // 最终返回值:scheme、rel、formValue const scheme = [] const rel = {} const formValue = { processId, formreq: [] } if (code) { formValue.code = code } // 遍历 schemeData 中所有的 scheme const schemeList = Array.isArray(schemeData) ? schemeData : Object.values(schemeData) for (let schemeIndex = 0; schemeIndex < schemeList.length; ++schemeIndex) { const schemeItem = schemeList[schemeIndex] schemeItem.F_Scheme = JSON.parse(schemeItem.F_Scheme) // 已有表单值的时候,舍弃掉不存在表单值中的 scheme if (formData && !formData[schemeItem.F_SchemeInfoId]) { continue } // 设置 formreq 的内容,非新建模式下需要设置 keyValue const { formId, field } = get(currentNode, `wfForms.${schemeIndex}`, {}) const formreqObj = { schemeInfoId: formId, processIdName: field, formData: {} } if (formData) { if (Object.values(get(formData, `${schemeItem.F_SchemeInfoId}`, {})).some(t => t && t.length > 0)) { formreqObj.keyValue = processId } } formValue.formreq[schemeIndex] = formreqObj for (let dataIndex = 0; dataIndex < schemeItem.F_Scheme.data.length; ++dataIndex) { const { componts } = schemeItem.F_Scheme.data[dataIndex] for (const t of componts) { // 之后的 t 即表示每个 scheme 项 t.__valuePath__ = `formreq.${schemeIndex}.formData.${t.id}` // 以下两个属性暂时用不到 t.__schemeIndex__ = schemeIndex t.__dataIndex__ = dataIndex if (t.type === 'girdtable' && t.table) { // 数据项是表格的情况 // 先设置源数据,不然无法获取默认值 for (const fieldItem of t.fieldsData) { fieldItem.__sourceData__ = await this.getSourceData(fieldItem) } t.__defaultItem__ = await this.getDefaultData(t, prop) if (formData) { // 有表单值的情况,从表单值中获取数据 const val = [] for (const valueItem of get(formData, `${schemeItem.F_SchemeInfoId}.${t.table}`, [])) { const tableItemValue = {} for (const fieldItem of t.fieldsData.filter(t => t.field)) { const formDataValue = get(valueItem, fieldItem.field.toLowerCase()) tableItemValue[fieldItem.field] = await this.convertToFormValue(fieldItem, formDataValue) } val.push(tableItemValue) } // useDefault 表示在从 formData 取不到值的时候使用默认值 if ((!val || val.length <= 0) && useDefault) { set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)]) } else { set(formValue, t.__valuePath__, val) } } else { // 无表单值的情况,默认值 set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)]) } } else if (t.field) { // 数据项不是表格的情况 // 先设置源数据,不然无法获取默认值 t.__sourceData__ = await this.getSourceData(t) if (formData) { // 有表单值的情况,从表单值中获取数据 const path = `${schemeItem.F_SchemeInfoId}.${t.table}.${dataIndex}.${t.field.toLowerCase()}` const formDataValue = get(formData, path) // useDefault 表示在从 formData 取不到值的时候使用默认值 if (!formDataValue && useDefault) { set(formValue, t.__valuePath__, await this.getDefaultData(t, prop)) } else { set(formValue, t.__valuePath__, await this.convertToFormValue(t, formDataValue)) } } else { // 无表单值的情况,默认值 set(formValue, t.__valuePath__, await this.getDefaultData(t, prop)) } } // 权限控制 const authObj = get(currentNode, `wfForms.${schemeIndex}.authorize.${t.id}`, {}) t.edit = authObj.isEdit if (Number(t.isHide) !== 1 && authObj.isLook !== 0) { // 加入 scheme scheme.push(t) // organize、datetime 可能作为其他 organize 或 datetimerange 的依赖项,引用它们 if (['organize', 'datetime'].includes(t.type)) { schemeRef[t.id] = t } // datetimerange、带有 relation 级联字段的 organize,依赖其他项 if ((t.type === 'datetimerange' && t.startTime && t.endTime) || (t.type === 'organize' && t.relation)) { refList.push(t) } } } } } // 依次处理表单关联 refList.forEach(t => { if (t.type === 'organize') { // 处理组件结构级联 // 给当前组件赋上级级联的值路径 __relationPath__ const parent = schemeRef[t.relation] t.__relationPath__ = parent.__valuePath__ // 给上级级联的组件注册自动重置当前组件的 change 事件 const relItem = { type: 'organize', id: t.id, path: t.__valuePath__ } rel[parent.id] = rel[parent.id] ? rel[parent.id].concat(relItem) : [relItem] } else if (t.type === 'datetimerange') { // 处理日期区间 const start = schemeRef[t.startTime] const end = schemeRef[t.endTime] const relItem = { type: 'datetimerange', path: t.__valuePath__, id: t.id, startPath: start.__valuePath__, endPath: end.__valuePath__, } rel[start.id] = rel[start.id] ? rel[start.id].concat(relItem) : [relItem] rel[end.id] = rel[end.id] ? rel[end.id].concat(relItem) : [relItem] } }) return { scheme, formValue, rel } }, /** * 获取最终需要 POST 的数据 * 参数:formValue, scheme * 返回:用于提交的数据 * * 遍历 formValue,将其中的表单值依次使用 convertToPostData 这个方法转化为提交值 */ async getPostData(originFormValue, scheme) { const formValue = this.COPY(originFormValue) // 依次按照 scheme 项目遍历 for (const item of scheme) { if (item.field) { // 不是表格的情况 const path = item.__valuePath__ const val = get(formValue, path) const result = await this.convertToPostData(item, val, originFormValue, scheme) set(formValue, path, result) } else if (item.table && item.fieldsData) { // 是表格的情况 const tableValue = get(formValue, item.__valuePath__, []) for (let valueIndex = 0; valueIndex < tableValue.length; ++valueIndex) { for (const schemeItem of item.fieldsData) { const path = `${item.__valuePath__}.${valueIndex}.${schemeItem.field}` const val = get(formValue, path) const result = await this.convertToPostData(schemeItem, val, originFormValue, scheme) set(formValue, path, result) } } } } formValue.formreq.forEach(t => { t.formData = JSON.stringify(t.formData) }) formValue.formreq = JSON.stringify(formValue.formreq) return formValue }, /** * 获取流程信息 * 参数: { code, processId, taskId } * */ async fetchProcessInfo({ code, processId, taskId }) { const url = processId ? 'learun/adms/newwf/processinfo' : 'learun/adms/newwf/scheme' const reqObj = { processId } if (taskId) { reqObj.taskId = taskId } const data = processId ? reqObj : code const result = await this.HTTP_GET(url, data) if (!result) { return {} } if (result.info) { result.info.Scheme = JSON.parse(result.info.Scheme) } else if (result.F_Content) { result.F_Content = JSON.parse(result.F_Content) } return result }, /** * 从 processInfo 流程信息中,提取出 currentNode * 参数: processInfo * */ getCurrentNode(processInfo) { if (processInfo.info) { return processInfo.info.Scheme.nodes.find(t => t.id === processInfo.info.CurrentNodeId) } else if (processInfo.F_Content) { return processInfo.F_Content.nodes.find(t => t.type === 'startround') } return {} }, /** * 拉取表单的 schemeData * 参数: currentNode * * 从当前节点 currentNode 中提取出表单 id,然后自 API 地址 /form/scheme 中拉取表单数据并返回 */ async fetchSchemeData(currentNode, currentTask, type) { const { wfForms } = currentNode const data = wfForms.filter(t => t.formId).map(t => ({ id: t.formId, ver: '' })) const schemeData = await this.HTTP_GET('learun/adms/form/scheme', data) return schemeData || {} }, /** * 拉取表单的 formData * 参数: currentNode, keyValue * * 提取当前节点信息、表单主键信息,从 API 地址 /form/data 中拉取表单数据 */ async fetchFormData({ wfForms }, keyValue) { const reqData = wfForms .filter(t => t.formId) .map(t => ({ schemeInfoId: t.formId, processIdName: t.field, keyValue })) const formData = await this.HTTP_GET('learun/adms/form/data', reqData) return formData || {} } } }