Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

330 linhas
13 KiB

  1. import get from 'lodash/get'
  2. import set from 'lodash/set'
  3. import customForm from '@/common/customform.js'
  4. /**
  5. * 工作流表单数据处理相关方法
  6. * (用于工作流表单页面,包含拉取、提交等函数,注意不可用于自定义应用和代码生成器页面)
  7. *
  8. * 用户新建表单的流程:
  9. * 1、拉取流程信息
  10. * 调用 fetchProcessInfo、 getCurrentNode
  11. * 2、根据流程信息里的表单相关定义来拉取表单 scheme
  12. * 调用 fetchSchemeData
  13. * (如果是使用移动端本地表单,则无需拉取表单,且略去后续所有步骤)
  14. * 3、加载选单数据 (例如 checkbox、select 这些表单项有选单数据)
  15. * 4、赋予默认值 (例如 currentInfo 类表单项,自动把当前用户的信息填入)
  16. * 5、生成表单
  17. * 调用 getCustomForm 即可完成步骤 3~4
  18. *
  19. * 用户打开已经填写的表单/草稿的流程:
  20. * 1、拉取流程信息
  21. * 调用 fetchProcessInfo、 getCurrentNode
  22. * 2、拉取表单 scheme;如果是使用移动端本地表单则无需此步骤
  23. * 调用 fetchSchemeData
  24. * 3、加载选单数据
  25. * 调用 fetchFormData
  26. * 4、拉取表单中已填入的值
  27. * 5、如果打开的是草稿,可能要把部分未填的项赋默认值
  28. * 6、生成表单
  29. * 调用 getCustomForm 即可完成步骤 4~5
  30. *
  31. * (以上只是简单介绍;实际使用中,如果打开子流程,需要拉取父/子两个流程信息)
  32. */
  33. export default {
  34. mixins: [customForm],
  35. methods: {
  36. /**
  37. * 从流程信息中生成 scheme、formValue
  38. * 参数: { schemeData (必填), processId, currentNode, formData (新建时为 null), code, useDefault }
  39. * 返回: { scheme, formValue, rel }
  40. *
  41. * 参数:
  42. * schemeData: 使用 fetchSchemeData 方法拉取到的原始 scheme 数据,未经过格式化处理
  43. * processId: 表单 GUID;如果是新建表单,可以用 this.GUID('-') 生成一个
  44. * currentNode: 使用 getCurrentNode 方法拉取到的当前节点信息,用于权限控制
  45. * formData: 填入表单的表单值,新建表单时此项为 null 即可
  46. * code: 表单编号 code,会被赋值到返回的 formValue.code;重新发起流程的场合赋 null
  47. * useDefault: 如果 formData 中某一项为空,是否对这一项填入默认值;通常在编辑草稿时启用
  48. *
  49. * 该方法返回的 scheme 项可能带有以下属性:
  50. * __valuePath__: 表单值在 formValue 中的路径,使用 lodash 的 get、set 方法即可读写
  51. * __sourceData__: 表单值的选单数据
  52. * __defaultItem__: 类型为 girdtable 的表单项带有此属性,表示添加一行表格时候表格项的默认值
  53. * __schemeIndex__: (暂时用不到)表单项位于 schemeData 根级内的第几个子项中
  54. * __dataIndex__: (暂时用不到)表单项位于 F_Scheme.data 中的第几个子项中
  55. */
  56. async getCustomForm(prop) {
  57. const { schemeData, formData, currentNode, code, processId, useDefault } = prop
  58. // 处理字段之间的级联、绑定关系
  59. // 需要绑定 change 事件的:
  60. // datetime: 修改后重新计算 datetimerange
  61. // organize: 修改后重设级联到该组件的其他组件的值,user 一级无需处理
  62. // 需要绑定某值的:
  63. // organize: 级联到某个组件,company 一级无需处理
  64. const schemeRef = {}
  65. const refList = []
  66. // 最终返回值:scheme、rel、formValue
  67. const scheme = []
  68. const rel = {}
  69. const formValue = { processId, formreq: [] }
  70. if (code) {
  71. formValue.code = code
  72. }
  73. // 遍历 schemeData 中所有的 scheme
  74. const schemeList = Array.isArray(schemeData) ? schemeData : Object.values(schemeData)
  75. for (let schemeIndex = 0; schemeIndex < schemeList.length; ++schemeIndex) {
  76. const schemeItem = schemeList[schemeIndex]
  77. schemeItem.F_Scheme = JSON.parse(schemeItem.F_Scheme)
  78. // 已有表单值的时候,舍弃掉不存在表单值中的 scheme
  79. if (formData && !formData[schemeItem.F_SchemeInfoId]) {
  80. continue
  81. }
  82. // 设置 formreq 的内容,非新建模式下需要设置 keyValue
  83. const { formId, field } = get(currentNode, `wfForms.${schemeIndex}`, {})
  84. const formreqObj = { schemeInfoId: formId, processIdName: field, formData: {} }
  85. if (formData) {
  86. if (Object.values(get(formData, `${schemeItem.F_SchemeInfoId}`, {})).some(t => t && t.length > 0)) {
  87. formreqObj.keyValue = processId
  88. }
  89. }
  90. formValue.formreq[schemeIndex] = formreqObj
  91. for (let dataIndex = 0; dataIndex < schemeItem.F_Scheme.data.length; ++dataIndex) {
  92. const { componts } = schemeItem.F_Scheme.data[dataIndex]
  93. for (const t of componts) {
  94. // 之后的 t 即表示每个 scheme 项
  95. t.__valuePath__ = `formreq.${schemeIndex}.formData.${t.id}`
  96. // 以下两个属性暂时用不到
  97. t.__schemeIndex__ = schemeIndex
  98. t.__dataIndex__ = dataIndex
  99. if (t.type === 'girdtable' && t.table) {
  100. // 数据项是表格的情况
  101. // 先设置源数据,不然无法获取默认值
  102. for (const fieldItem of t.fieldsData) {
  103. fieldItem.__sourceData__ = await this.getSourceData(fieldItem)
  104. }
  105. t.__defaultItem__ = await this.getDefaultData(t, prop)
  106. if (formData) {
  107. // 有表单值的情况,从表单值中获取数据
  108. const val = []
  109. for (const valueItem of get(formData, `${schemeItem.F_SchemeInfoId}.${t.table}`, [])) {
  110. const tableItemValue = {}
  111. for (const fieldItem of t.fieldsData.filter(t => t.field)) {
  112. const formDataValue = get(valueItem, fieldItem.field.toLowerCase())
  113. tableItemValue[fieldItem.field] = await this.convertToFormValue(fieldItem, formDataValue)
  114. }
  115. val.push(tableItemValue)
  116. }
  117. // useDefault 表示在从 formData 取不到值的时候使用默认值
  118. if ((!val || val.length <= 0) && useDefault) {
  119. set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)])
  120. } else {
  121. set(formValue, t.__valuePath__, val)
  122. }
  123. } else {
  124. // 无表单值的情况,默认值
  125. set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)])
  126. }
  127. } else if (t.field) {
  128. // 数据项不是表格的情况
  129. // 先设置源数据,不然无法获取默认值
  130. t.__sourceData__ = await this.getSourceData(t)
  131. if (formData) {
  132. // 有表单值的情况,从表单值中获取数据
  133. const path = `${schemeItem.F_SchemeInfoId}.${t.table}.${dataIndex}.${t.field.toLowerCase()}`
  134. const formDataValue = get(formData, path)
  135. // useDefault 表示在从 formData 取不到值的时候使用默认值
  136. if (!formDataValue && useDefault) {
  137. set(formValue, t.__valuePath__, await this.getDefaultData(t, prop))
  138. } else {
  139. set(formValue, t.__valuePath__, await this.convertToFormValue(t, formDataValue))
  140. }
  141. } else {
  142. // 无表单值的情况,默认值
  143. set(formValue, t.__valuePath__, await this.getDefaultData(t, prop))
  144. }
  145. }
  146. // 权限控制
  147. const authObj = get(currentNode, `wfForms.${schemeIndex}.authorize.${t.id}`, {})
  148. t.edit = authObj.isEdit
  149. if (Number(t.isHide) !== 1 && authObj.isLook !== 0) {
  150. // 加入 scheme
  151. scheme.push(t)
  152. // organize、datetime 可能作为其他 organize 或 datetimerange 的依赖项,引用它们
  153. if (['organize', 'datetime'].includes(t.type)) {
  154. schemeRef[t.id] = t
  155. }
  156. // datetimerange、带有 relation 级联字段的 organize,依赖其他项
  157. if ((t.type === 'datetimerange' && t.startTime && t.endTime) || (t.type === 'organize' && t.relation)) {
  158. refList.push(t)
  159. }
  160. }
  161. }
  162. }
  163. }
  164. // 依次处理表单关联
  165. refList.forEach(t => {
  166. if (t.type === 'organize') {
  167. // 处理组件结构级联
  168. // 给当前组件赋上级级联的值路径 __relationPath__
  169. const parent = schemeRef[t.relation]
  170. t.__relationPath__ = parent.__valuePath__
  171. // 给上级级联的组件注册自动重置当前组件的 change 事件
  172. const relItem = { type: 'organize', id: t.id, path: t.__valuePath__ }
  173. rel[parent.id] = rel[parent.id] ? rel[parent.id].concat(relItem) : [relItem]
  174. } else if (t.type === 'datetimerange') {
  175. // 处理日期区间
  176. const start = schemeRef[t.startTime]
  177. const end = schemeRef[t.endTime]
  178. const relItem = {
  179. type: 'datetimerange',
  180. path: t.__valuePath__,
  181. id: t.id,
  182. startPath: start.__valuePath__,
  183. endPath: end.__valuePath__,
  184. }
  185. rel[start.id] = rel[start.id] ? rel[start.id].concat(relItem) : [relItem]
  186. rel[end.id] = rel[end.id] ? rel[end.id].concat(relItem) : [relItem]
  187. }
  188. })
  189. return { scheme, formValue, rel }
  190. },
  191. /**
  192. * 获取最终需要 POST 的数据
  193. * 参数:formValue, scheme
  194. * 返回:用于提交的数据
  195. *
  196. * 遍历 formValue,将其中的表单值依次使用 convertToPostData 这个方法转化为提交值
  197. */
  198. async getPostData(originFormValue, scheme) {
  199. const formValue = this.COPY(originFormValue)
  200. // 依次按照 scheme 项目遍历
  201. for (const item of scheme) {
  202. if (item.field) {
  203. // 不是表格的情况
  204. const path = item.__valuePath__
  205. const val = get(formValue, path)
  206. const result = await this.convertToPostData(item, val, originFormValue, scheme)
  207. set(formValue, path, result)
  208. } else if (item.table && item.fieldsData) {
  209. // 是表格的情况
  210. const tableValue = get(formValue, item.__valuePath__, [])
  211. for (let valueIndex = 0; valueIndex < tableValue.length; ++valueIndex) {
  212. for (const schemeItem of item.fieldsData) {
  213. const path = `${item.__valuePath__}.${valueIndex}.${schemeItem.field}`
  214. const val = get(formValue, path)
  215. const result = await this.convertToPostData(schemeItem, val, originFormValue, scheme)
  216. set(formValue, path, result)
  217. }
  218. }
  219. }
  220. }
  221. formValue.formreq.forEach(t => { t.formData = JSON.stringify(t.formData) })
  222. formValue.formreq = JSON.stringify(formValue.formreq)
  223. return formValue
  224. },
  225. /**
  226. * 获取流程信息
  227. * 参数: { code, processId, taskId }
  228. *
  229. */
  230. async fetchProcessInfo({ code, processId, taskId }) {
  231. const url = processId ? 'learun/adms/newwf/processinfo' : 'learun/adms/newwf/scheme'
  232. const reqObj = { processId }
  233. if (taskId) {
  234. reqObj.taskId = taskId
  235. }
  236. const data = processId ? reqObj : code
  237. const result = await this.HTTP_GET(url, data)
  238. if (!result) { return {} }
  239. if (result.info) {
  240. result.info.Scheme = JSON.parse(result.info.Scheme)
  241. } else if (result.F_Content) {
  242. result.F_Content = JSON.parse(result.F_Content)
  243. }
  244. return result
  245. },
  246. /**
  247. * 从 processInfo 流程信息中,提取出 currentNode
  248. * 参数: processInfo
  249. *
  250. */
  251. getCurrentNode(processInfo) {
  252. if (processInfo.info) {
  253. return processInfo.info.Scheme.nodes.find(t => t.id === processInfo.info.CurrentNodeId)
  254. } else if (processInfo.F_Content) {
  255. return processInfo.F_Content.nodes.find(t => t.type === 'startround')
  256. }
  257. return {}
  258. },
  259. /**
  260. * 拉取表单的 schemeData
  261. * 参数: currentNode
  262. *
  263. * 从当前节点 currentNode 中提取出表单 id,然后自 API 地址 /form/scheme 中拉取表单数据并返回
  264. */
  265. async fetchSchemeData(currentNode, currentTask, type) {
  266. const { wfForms } = currentNode
  267. const data = wfForms.filter(t => t.formId).map(t => ({ id: t.formId, ver: '' }))
  268. const schemeData = await this.HTTP_GET('learun/adms/form/scheme', data)
  269. return schemeData || {}
  270. },
  271. /**
  272. * 拉取表单的 formData
  273. * 参数: currentNode, keyValue
  274. *
  275. * 提取当前节点信息、表单主键信息,从 API 地址 /form/data 中拉取表单数据
  276. */
  277. async fetchFormData({ wfForms }, keyValue) {
  278. const reqData = wfForms
  279. .filter(t => t.formId)
  280. .map(t => ({
  281. schemeInfoId: t.formId,
  282. processIdName: t.field,
  283. keyValue
  284. }))
  285. const formData = await this.HTTP_GET('learun/adms/form/data', reqData)
  286. return formData || {}
  287. }
  288. }
  289. }