You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

415 lines
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 {
  58. schemeData,
  59. formData,
  60. currentNode,
  61. code,
  62. processId,
  63. useDefault
  64. } = prop
  65. // 处理字段之间的级联、绑定关系
  66. // 需要绑定 change 事件的:
  67. // datetime: 修改后重新计算 datetimerange
  68. // organize: 修改后重设级联到该组件的其他组件的值,user 一级无需处理
  69. // 需要绑定某值的:
  70. // organize: 级联到某个组件,company 一级无需处理
  71. const schemeRef = {}
  72. const refList = []
  73. // 最终返回值:scheme、rel、formValue
  74. const scheme = []
  75. const rel = {}
  76. const formValue = {
  77. processId,
  78. formreq: []
  79. }
  80. if (code) {
  81. formValue.code = code
  82. }
  83. // 遍历 schemeData 中所有的 scheme
  84. const schemeList = Array.isArray(schemeData) ? schemeData : Object.values(schemeData)
  85. for (let schemeIndex = 0; schemeIndex < schemeList.length; ++schemeIndex) {
  86. const schemeItem = schemeList[schemeIndex]
  87. schemeItem.F_Scheme = JSON.parse(schemeItem.F_Scheme)
  88. // 已有表单值的时候,舍弃掉不存在表单值中的 scheme
  89. if (formData && !formData[schemeItem.F_SchemeInfoId]) {
  90. continue
  91. }
  92. // 设置 formreq 的内容,非新建模式下需要设置 keyValue
  93. const {
  94. formId,
  95. field
  96. } = get(currentNode, `wfForms.${schemeIndex}`, {})
  97. const formreqObj = {
  98. schemeInfoId: formId,
  99. processIdName: field,
  100. formData: {}
  101. }
  102. if (formData) {
  103. if (Object.values(get(formData, `${schemeItem.F_SchemeInfoId}`, {})).some(t => t && t.length >
  104. 0)) {
  105. formreqObj.keyValue = processId
  106. }
  107. }
  108. formValue.formreq[schemeIndex] = formreqObj
  109. for (let dataIndex = 0; dataIndex < schemeItem.F_Scheme.data.length; ++dataIndex) {
  110. const {
  111. componts
  112. } = schemeItem.F_Scheme.data[dataIndex]
  113. for (const t of componts) {
  114. // 之后的 t 即表示每个 scheme 项
  115. t.__valuePath__ = `formreq.${schemeIndex}.formData.${t.id}`
  116. // 以下两个属性暂时用不到
  117. t.__schemeIndex__ = schemeIndex
  118. t.__dataIndex__ = dataIndex
  119. if (t.type === 'girdtable' && t.table) {
  120. // 数据项是表格的情况
  121. // 先设置源数据,不然无法获取默认值
  122. for (const fieldItem of t.fieldsData) {
  123. fieldItem.__sourceData__ = await this.getSourceData(fieldItem)
  124. }
  125. t.__defaultItem__ = await this.getDefaultData(t, prop)
  126. if (formData) {
  127. // 有表单值的情况,从表单值中获取数据
  128. const val = []
  129. for (const valueItem of get(formData, `${schemeItem.F_SchemeInfoId}.${t.table}`,
  130. [])) {
  131. const tableItemValue = {}
  132. for (const fieldItem of t.fieldsData.filter(t => t.field)) {
  133. const formDataValue = get(valueItem, fieldItem.field.toLowerCase())
  134. tableItemValue[fieldItem.field] = await this.convertToFormValue(fieldItem,
  135. formDataValue)
  136. }
  137. val.push(tableItemValue)
  138. }
  139. // useDefault 表示在从 formData 取不到值的时候使用默认值
  140. if ((!val || val.length <= 0) && useDefault) {
  141. set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)])
  142. } else {
  143. set(formValue, t.__valuePath__, val)
  144. }
  145. } else {
  146. // 无表单值的情况,默认值
  147. set(formValue, t.__valuePath__, [this.COPY(t.__defaultItem__)])
  148. }
  149. } else if (t.field) {
  150. // 数据项不是表格的情况
  151. // 先设置源数据,不然无法获取默认值
  152. t.__sourceData__ = await this.getSourceData(t)
  153. if (formData) {
  154. // 有表单值的情况,从表单值中获取数据
  155. const path =
  156. `${schemeItem.F_SchemeInfoId}.${t.table}.${dataIndex}.${t.field.toLowerCase()}`
  157. const formDataValue = get(formData, path)
  158. // useDefault 表示在从 formData 取不到值的时候使用默认值
  159. if (!formDataValue && useDefault) {
  160. set(formValue, t.__valuePath__, await this.getDefaultData(t, prop))
  161. } else {
  162. set(formValue, t.__valuePath__, await this.convertToFormValue(t, formDataValue))
  163. }
  164. } else {
  165. // 无表单值的情况,默认值
  166. set(formValue, t.__valuePath__, await this.getDefaultData(t, prop))
  167. }
  168. }
  169. // 权限控制
  170. const authObj = get(currentNode, `wfForms.${schemeIndex}.authorize.${t.id}`, {})
  171. t.edit = authObj.isEdit
  172. if (Number(t.isHide) !== 1 && authObj.isLook !== 0) {
  173. // 加入 scheme
  174. scheme.push(t)
  175. // organize、datetime 可能作为其他 organize 或 datetimerange 的依赖项,引用它们
  176. if (['organize', 'datetime'].includes(t.type)) {
  177. schemeRef[t.id] = t
  178. }
  179. // datetimerange、带有 relation 级联字段的 organize,依赖其他项
  180. if ((t.type === 'datetimerange' && t.startTime && t.endTime) || (t.type ===
  181. 'organize' && t.relation)) {
  182. refList.push(t)
  183. }
  184. }
  185. }
  186. }
  187. }
  188. // 依次处理表单关联
  189. refList.forEach(t => {
  190. if (t.type === 'organize') {
  191. // 处理组件结构级联
  192. // 给当前组件赋上级级联的值路径 __relationPath__
  193. const parent = schemeRef[t.relation]
  194. t.__relationPath__ = parent.__valuePath__
  195. // 给上级级联的组件注册自动重置当前组件的 change 事件
  196. const relItem = {
  197. type: 'organize',
  198. id: t.id,
  199. path: t.__valuePath__
  200. }
  201. rel[parent.id] = rel[parent.id] ? rel[parent.id].concat(relItem) : [relItem]
  202. } else if (t.type === 'datetimerange') {
  203. // 处理日期区间
  204. const start = schemeRef[t.startTime]
  205. const end = schemeRef[t.endTime]
  206. const relItem = {
  207. type: 'datetimerange',
  208. path: t.__valuePath__,
  209. id: t.id,
  210. startPath: start.__valuePath__,
  211. endPath: end.__valuePath__,
  212. }
  213. rel[start.id] = rel[start.id] ? rel[start.id].concat(relItem) : [relItem]
  214. rel[end.id] = rel[end.id] ? rel[end.id].concat(relItem) : [relItem]
  215. }
  216. })
  217. return {
  218. scheme,
  219. formValue,
  220. rel
  221. }
  222. },
  223. newguid() {
  224. return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  225. var r = Math.random() * 16 | 0,
  226. v = c == 'x' ? r : (r & 0x3 | 0x8);
  227. return v.toString(16);
  228. });
  229. },
  230. /**
  231. * 获取最终需要 POST 的数据
  232. * 参数:formValue, scheme
  233. * 返回:用于提交的数据
  234. *
  235. * 遍历 formValue,将其中的表单值依次使用 convertToPostData 这个方法转化为提交值
  236. */
  237. async getPostData(originFormValue, scheme) {
  238. const formValue = this.COPY(originFormValue)
  239. // 依次按照 scheme 项目遍历
  240. for (const item of scheme) {
  241. if (item.field) {
  242. // 不是表格的情况
  243. const path = item.__valuePath__
  244. const val = get(formValue, path)
  245. var guid = undefined
  246. if (item.type == 'upload') {
  247. // 先生成一个guid
  248. guid = this.newguid();
  249. // 取出当前列对应的labelId
  250. var labeId = item.id
  251. // 从缓存取出当前审批流程所有的guid
  252. var guids = JSON.parse(uni.getStorageSync('guids'))
  253. if (guids&&JSON.stringify(guids)!=='{}'&&guids[labeId]) {
  254. guid = guids[labeId]
  255. }
  256. }
  257. const result = await this.convertToPostData(item, val, originFormValue, scheme, guid)
  258. set(formValue, path, result)
  259. } else if (item.table && item.fieldsData) {
  260. // 是表格的情况
  261. const tableValue = get(formValue, item.__valuePath__, [])
  262. for (let valueIndex = 0; valueIndex < tableValue.length; ++valueIndex) {
  263. for (const schemeItem of item.fieldsData) {
  264. const path = `${item.__valuePath__}.${valueIndex}.${schemeItem.field}`
  265. const val = get(formValue, path)
  266. const result = await this.convertToPostData(schemeItem, val, originFormValue, scheme,
  267. guid)
  268. set(formValue, path, result)
  269. }
  270. }
  271. }
  272. }
  273. formValue.formreq.forEach(t => {
  274. t.formData = JSON.stringify(t.formData)
  275. })
  276. formValue.formreq = JSON.stringify(formValue.formreq)
  277. return formValue
  278. },
  279. /**
  280. * 获取流程信息
  281. * 参数: { code, processId, taskId }
  282. *
  283. */
  284. async fetchProcessInfo({
  285. code,
  286. processId,
  287. taskId
  288. }) {
  289. const url = processId ? 'learun/adms/newwf/processinfo' : 'learun/adms/newwf/scheme'
  290. const reqObj = {
  291. processId
  292. }
  293. if (taskId) {
  294. reqObj.taskId = taskId
  295. }
  296. const data = processId ? reqObj : code
  297. const result = await this.HTTP_GET(url, data)
  298. if (!result) {
  299. return {}
  300. }
  301. if (result.info) {
  302. result.info.Scheme = JSON.parse(result.info.Scheme)
  303. } else if (result.F_Content) {
  304. result.F_Content = JSON.parse(result.F_Content)
  305. }
  306. return result
  307. },
  308. /**
  309. * 从 processInfo 流程信息中,提取出 currentNode
  310. * 参数: processInfo
  311. *
  312. */
  313. getCurrentNode(processInfo) {
  314. if (processInfo.info) {
  315. return processInfo.info.Scheme.nodes.find(t => t.id === processInfo.info.CurrentNodeId)
  316. } else if (processInfo.F_Content) {
  317. return processInfo.F_Content.nodes.find(t => t.type === 'startround')
  318. }
  319. return {}
  320. },
  321. /**
  322. * 拉取表单的 schemeData
  323. * 参数: currentNode
  324. *
  325. * 从当前节点 currentNode 中提取出表单 id,然后自 API 地址 /form/scheme 中拉取表单数据并返回
  326. */
  327. async fetchSchemeData(currentNode, currentTask, type) {
  328. const {
  329. wfForms
  330. } = currentNode
  331. const data = wfForms.filter(t => t.formId).map(t => ({
  332. id: t.formId,
  333. ver: ''
  334. }))
  335. const schemeData = await this.HTTP_GET('learun/adms/form/scheme', data)
  336. return schemeData || {}
  337. },
  338. /**
  339. * 拉取表单的 formData
  340. * 参数: currentNode, keyValue
  341. *
  342. * 提取当前节点信息、表单主键信息,从 API 地址 /form/data 中拉取表单数据
  343. */
  344. async fetchFormData({
  345. wfForms
  346. }, keyValue) {
  347. const reqData = wfForms
  348. .filter(t => t.formId)
  349. .map(t => ({
  350. schemeInfoId: t.formId,
  351. processIdName: t.field,
  352. keyValue
  353. }))
  354. const formData = await this.HTTP_GET('learun/adms/form/data', reqData)
  355. return formData || {}
  356. },
  357. async fetchFolderkeyData(currentNode, keyValue) {
  358. const {
  359. wfForms
  360. } = currentNode
  361. const reqData = wfForms
  362. .filter(t => t.formId)
  363. .map(t => ({
  364. id: t.formId,
  365. ver: '',
  366. schemeInfoId: t.formId,
  367. processIdName: t.field,
  368. keyValue
  369. }))
  370. const folderkeyData = await this.HTTP_GET('learun/adms/form/folderkey', reqData)
  371. return folderkeyData || {}
  372. },
  373. }
  374. }