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.
 
 
 
 
 
 

304 line
11 KiB

  1. import get from 'lodash/get'
  2. import moment from 'moment'
  3. /**
  4. * 表单数据处理相关方法
  5. * (配合 <l-customform> 使用,注意本工具类不包含拉取表单数据的代码)
  6. *
  7. * 提供以下工具方法:
  8. *
  9. * 【新建表单时使用】:
  10. * async getDefaultData(schemeItem, { processId })
  11. * 获取单个表单项的默认数据
  12. *
  13. * 【打开一个表单时使用】:
  14. * async getSourceData(schemeItem)
  15. * 获取表单中的选单数据 / 获取单条表单项的选单数据
  16. *
  17. * async convertToFormValue(schemeItem, val)
  18. * 将从 API 拉取的表单数据格式化规范化
  19. *
  20. * 【提交表单时使用】:
  21. * async convertToPostData(schemeItem, val, formValue, scheme)
  22. * 将本地表单数据格式化为提交时的格式
  23. */
  24. export default {
  25. methods: {
  26. /**
  27. * 获取一个 scheme 表单项的源数据 (加载表单时使用)
  28. * 参数: 单个 schemeItem
  29. *
  30. * radio、select、checkbox、layer 这四种表单项,需要加载额外的选单数据
  31. * 选单数据有两种获取方式:
  32. * 1、来自数据字典:
  33. * 数据字典在 this.GET_GLOBAL('dataDictionary')
  34. * 表单使用的字段在 schemeItem.itemCode
  35. * 选单数据中的 text 字段作为显示, value 字段作为值
  36. *
  37. * 2、来自数据源:
  38. * 将 schemeItem.dataSourceId 按符号「,」逗号分割为数组,分割为: [code, displayField, valueField]
  39. * 数据源需要请求 API 来获取,请求需要带上数据源的编号 code
  40. * displayField、valueField 分别为展示字段和值绑定字段
  41. *
  42. * 选单数据有两种格式:
  43. * 1、对于 radio、select、checkbox 来说:
  44. * 只需要一个数组,数组形如: [{ text: '选项文字', value: '选项值' }, ...]
  45. * 将获取的数据绑定到组件的 range 属性上即可
  46. * 全局数据中默认是对象形式,使用 Object.values() 转化即可
  47. *
  48. * 2、对于 layer 来说:
  49. * 返回一个对象,形如 { source, layerData, selfField }
  50. * source: 为弹层中列出的数据,是一个数组
  51. * layerData: 需要在弹层窗口中展示的字段及标题文字,形如: [{ name:'要展示的字段名', label:'标题文字' }]
  52. * selfField: 该表单值绑定哪个字段,默认为绑定到自身的字段
  53. */
  54. async getSourceData(schemeItem) {
  55. if (['radio', 'select', 'checkbox'].includes(schemeItem.type)) {
  56. // radio select checkbox 三种情况
  57. if (!schemeItem.dataSource || Number(schemeItem.dataSource) === 0) {
  58. // dataSource 为 0,使用 clientData
  59. return Object
  60. .values(this.GET_GLOBAL('dataDictionary')[schemeItem.itemCode])
  61. .map(t => ({ value: t.value, text: t.text }))
  62. } else {
  63. // dataSource 不为 0,使用数据源,需要请求接口,并且提取出显示字段和实际字段
  64. const [code, displayField = schemeItem.showField, valueField = schemeItem.saveField] = schemeItem.dataSourceId
  65. .split(',')
  66. const sourceData = await this.FETCH_DATASOURCE(code)
  67. if (!sourceData) { return [] }
  68. return sourceData.data.map(t => ({ text: t[displayField], value: t[valueField] }))
  69. }
  70. } else if (['layer'].includes(schemeItem.type)) {
  71. // layer 需要更多属性
  72. if (!schemeItem.dataSource || Number(schemeItem.dataSource) === 0) {
  73. // dataSource 为 0,使用 clientData
  74. // clientData 对象转数组后,隐含 key:item.text 和 value:item.value 的关系
  75. const [keyItem, valueItem] = schemeItem.layerData
  76. const source = Object
  77. .values(this.GET_GLOBAL('dataDictionary')[schemeItem.itemCode])
  78. .map(t => ({ value: t.value, text: t.text }))
  79. return {
  80. source,
  81. layerData: [
  82. { name: 'text', label: keyItem.label || '', value: keyItem.value || '' },
  83. { name: 'value', label: valueItem.label || '', value: valueItem.value || '' }
  84. ]
  85. }
  86. } else {
  87. // dataSource 不为 0,使用数据源,需要请求接口,并且提取出显示字段和实际字段
  88. const [code] = schemeItem.dataSourceId.split(',')
  89. const sourceData = await this.FETCH_DATASOURCE(code)
  90. if (!sourceData) { return [] }
  91. const source = sourceData.data
  92. return { source, layerData: schemeItem.layerData.filter(t => (!t.hide) && (t.value || t.label)) }
  93. }
  94. }
  95. return []
  96. },
  97. /**
  98. * 获取一个 scheme 表单项的默认值 (用户新建表单时使用,或是编辑草稿)
  99. * 参数: 单个 schemeItem , { processId }
  100. *
  101. * 每种类别的表单项分别获取的默认值:
  102. *
  103. * currentInfo: 根据类别取当前用户/部门/公司/时间日期
  104. * datetime: 根据 dfValue 字段表示昨天/今天/明天,格式化为字符串
  105. * radio、select: 有 dfValue 则使用,否则取第一条
  106. * checkbox: 有 dfValue 则使用,否则为空数组
  107. * encode: 根据 rulecode 请求表单编码
  108. * upload: 空数组
  109. * guid: 赋值第二个参数中的 processId,但是如果在子表格中,赋空字符串
  110. * girdtable: 递归所有表格项 scheme 依次为它们生成默认值
  111. * datetimerange: 字符串 0
  112. */
  113. async getDefaultData(item, prop) {
  114. const { processId } = prop
  115. switch (item.type) {
  116. case 'currentInfo':
  117. switch (item.dataType) {
  118. case 'user':
  119. return this.GET_GLOBAL('loginUser').userId
  120. case 'department':
  121. return this.GET_GLOBAL('loginUser').departmentId
  122. case 'company':
  123. return this.GET_GLOBAL('loginUser').companyId
  124. case 'time':
  125. return moment().format('YYYY-MM-DD HH:mm:ss')
  126. default:
  127. return ''
  128. }
  129. case 'datetime':
  130. const datetimeFormat = item.table ?
  131. (Number(item.dateformat) === 0 ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss') :
  132. (item.datetime === 'datetime' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')
  133. const today = moment()
  134. const dfDatetime = [
  135. today.subtract(1, 'day'),
  136. today,
  137. today.add(1, 'day')
  138. ][Number(item.dfvalue)] || today
  139. return dfDatetime.format(datetimeFormat) || ''
  140. case 'radio':
  141. case 'select':
  142. const radioItem = item.__sourceData__.find(t => t.value === item.dfvalue) || item.__sourceData__[0]
  143. return item.type === 'radio' ? radioItem.value : ''
  144. case 'checkbox':
  145. if (!item.dfvalue) {
  146. return []
  147. }
  148. return item.dfvalue.split(',').filter(t => item.__sourceData__.find(s => s.value === t))
  149. case 'encode':
  150. const result = await this.FETCH_ENCODE(item.rulecode)
  151. return result
  152. case 'upload':
  153. return []
  154. case 'guid':
  155. return item.table ? processId : ''
  156. case 'girdtable':
  157. const tableItemObj = {}
  158. for (const fieldItem of item.fieldsData) {
  159. tableItemObj[fieldItem.field] = await this.getDefaultData(fieldItem, prop)
  160. }
  161. return this.COPY(tableItemObj)
  162. case 'datetimerange':
  163. return '0'
  164. default:
  165. return item.dfvalue || ''
  166. }
  167. },
  168. /**
  169. * 将单条 formData 值转化为 formValue 值 (拉取表单数据时使用)
  170. * 参数: 单个 schemeItem , 数据值
  171. *
  172. * 具体执行逻辑:
  173. * radio、select: 剔除无效值
  174. * checkbox: 分割成数组并剔除无效值
  175. * upload: 分割成数组,拉取其中所有文件的信息
  176. * datetime: 按照时间日期格式进行格式化字符串
  177. * 其他类型: 保留原值
  178. */
  179. async convertToFormValue(item, val) {
  180. switch (item.type) {
  181. case 'upload':
  182. if (!val) { return [] }
  183. const uidList = val.split(',')
  184. const fileList = []
  185. for (const uid of uidList || []) {
  186. const fileInfo = await this.FETCH_FILEINFO(uid)
  187. if (!fileInfo) { continue }
  188. const fileType = fileInfo.F_FileType
  189. const fileSize = fileInfo.F_FileSize
  190. const fileName = fileInfo.F_FileName
  191. const path = this.API + 'learun/adms/annexes/wxdown?' + this.URL_QUERY(uid, true)
  192. fileList.push({ path, type: fileType, uid, size: fileSize, name:fileName })
  193. }
  194. return fileList
  195. case 'select':
  196. case 'radio':
  197. if (!val || !item.__sourceData__.map(t => t.value).includes(val)) {
  198. return ''
  199. }
  200. return val
  201. case 'checkbox':
  202. const validValue = item.__sourceData__.map(t => t.value)
  203. const checkboxVal = val.split(',') || []
  204. return checkboxVal.filter(t => validValue.includes(t))
  205. case 'datetime':
  206. if (!val) {
  207. return ''
  208. }
  209. return moment(val).format(
  210. Number(item.dateformat) === 0 || item.datetime === 'date' ?
  211. 'YYYY-MM-DD' :
  212. 'YYYY-MM-DD HH:mm:ss'
  213. )
  214. default:
  215. return val || ''
  216. }
  217. },
  218. /**
  219. * 将一个 formValue 值转化为 post 提交值(提交表单数据时使用)
  220. * 参数: 单个 schemeItem , 表单项值 , 所有 formValue , scheme
  221. *
  222. * 具体执行逻辑:
  223. * checkbox: 将数组使用符号「,」逗号拼接成字符串
  224. * datetimerange: 获取开始日期、结束日期,计算差值天数并保留整数
  225. * datetime: 格式化为完整时间日期字符串
  226. * upload: 依次上传文件,将返回的文件 ID 使用符号「,」逗号拼接成字符串
  227. * 其他类型: 保留原值
  228. */
  229. async convertToPostData(item, val, formValue, scheme) {
  230. switch (item.type) {
  231. case 'checkbox':
  232. return val ? val.join(',') : ''
  233. case 'datetimerange':
  234. const startTime = get(formValue, scheme.find(t => t.id === item.startTime).__valuePath__, null)
  235. const endTime = get(formValue, scheme.find(t => t.id === item.endTime).__valuePath__, null)
  236. if (!startTime || !endTime || moment(endTime).isBefore(startTime)) {
  237. return ''
  238. } else {
  239. return moment.duration(moment(endTime).diff(moment(startTime))).asDays().toFixed(0)
  240. }
  241. case 'datetime':
  242. return val ? moment(val).format('YYYY-MM-DD HH:mm:ss') : ''
  243. case 'upload':
  244. const valArray = val.map(item=>{
  245. return {
  246. uid:item.uid,
  247. path:item.path===undefined?item:item.path
  248. }
  249. })
  250. const uploadUid = []
  251. for (const { path, uid } of valArray) {
  252. if (uid) {
  253. uploadUid.push(uid)
  254. continue
  255. }
  256. const fileId = await this.HTTP_UPLOAD(path)
  257. if (fileId) {
  258. uploadUid.push(fileId)
  259. }
  260. }
  261. return uploadUid.join(',')
  262. default:
  263. return val || ''
  264. }
  265. }
  266. }
  267. }