index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <template>
  2. <view class="process_container">
  3. <uni-card margin="10px" spacing="0">
  4. <uni-forms label-position="left" :label-width="125" :border="true">
  5. <uni-forms-item v-for="(item, index) in formElements" :label="item.elementName" :key="index">
  6. <uni-easyinput v-if="'多少小时' == item.elementName" :disabled="true" placeholder=""
  7. v-model="diffHours"></uni-easyinput>
  8. <!-- 输入框 -->
  9. <uni-easyinput v-else-if="'0' == item.type" :disabled="'0' == item.canEdit"
  10. :placeholder="'0' == item.canEdit ? '' : '请输入内容'" v-model="item.defaultValue"></uni-easyinput>
  11. <!-- 富文本输入框 -->
  12. <uni-easyinput v-else-if="'1' == item.type" :disabled="'0' == item.canEdit"
  13. :placeholder="'0' == item.canEdit ? '' : '请输入内容'" v-model="item.defaultValue"
  14. type="textarea"></uni-easyinput>
  15. <!-- 下拉框 -->
  16. <picker class="picker_container" v-else-if="'2' == item.type"
  17. @change="bindPickerChange($event, item)" :value="item.defaultValue"
  18. :range="formatDict(item.typeDetail.enum)">
  19. <view class="uni-input input_text">
  20. <!-- 设置默认值为第一个选项 -->
  21. {{ item.defaultValue ? item.defaultValue : item.defaultValue =
  22. item.typeDetail.enum[0].enumVname }}
  23. </view>
  24. </picker>
  25. <!-- 开始时间选择器 -->
  26. <uni-datetime-picker :end="endTime" @change="setTimeRange(item)"
  27. v-else-if="timeFlag && '9' == item.type && '开始时间' == item.elementName"
  28. v-model="item.defaultValue" :clear-icon="false" type="datetime" />
  29. <!-- 结束时间选择器 -->
  30. <uni-datetime-picker :start="startTime" @change="setTimeRange(item)" :disabled="disableEndTime"
  31. :placeholder="disableEndTime ? '请先确认开始时间' : '确认结束时间'"
  32. v-else-if="timeFlag && '9' == item.type && '结束时间' == item.elementName"
  33. v-model="item.defaultValue" :clear-icon="false" type="datetime" />
  34. <!-- 其他时间选择器 -->
  35. <!-- 年月日 时分秒 -->
  36. <uni-datetime-picker v-else-if="'9' == item.type" v-model="item.defaultValue" type="datetime" />
  37. <!-- 年月日 -->
  38. <uni-datetime-picker v-else-if="'3' == item.type" v-model="item.defaultValue" type="date" />
  39. </uni-forms-item>
  40. </uni-forms>
  41. </uni-card>
  42. <view v-if="repeatingForm.elementItem.length > 0" class="repeating_table">
  43. <uni-card v-for="(table, tableIndex) in repeatingForm.elements" margin="10px" spacing="0"
  44. :key="tableIndex">
  45. <uni-forms label-position="left" :label-width="125" :border="true">
  46. <uni-forms-item v-for="(item, itemIndex) in table"
  47. :label="repeatingForm.elementItem[itemIndex].elementName.slice(3)" :key="itemIndex">
  48. <uni-easyinput v-if="repeatingForm.elementItem[itemIndex].bddzText" :disabled="true"
  49. :placeholder="''" :value="computedrepeatingFormItemValue(item, table)"></uni-easyinput>
  50. <uni-easyinput v-else placeholder="请输入内容" v-model="item.value"></uni-easyinput>
  51. </uni-forms-item>
  52. </uni-forms>
  53. <view class="repeating_table_button_container">
  54. <uni-row>
  55. <uni-col :span="10" :offset="1">
  56. <button @click="addrepeatingFormItem(tableIndex)" type="primary">新增</button>
  57. </uni-col>
  58. <uni-col :span="10" :offset="2">
  59. <button @click="delrepeatingFormItem(tableIndex)"
  60. :disabled="repeatingForm.elements.length <= 1" type="warn">删除</button>
  61. </uni-col>
  62. </uni-row>
  63. </view>
  64. </uni-card>
  65. </view>
  66. <uni-card title="上传附件" :extra="`${fileList.length}/15`" margin="10px" spacing="0">
  67. <uni-file-picker ref="filePicker" v-model="fileList" :auto-upload="true" mode="list" limit="10"
  68. file-mediatype="all" @select="handleFileSelect" @progress="handleFileProgress"
  69. @success="handleFileSuccess" @fail="handleFileFail" @delete="handleFileDelete" />
  70. <button :loading="!button_state" type="default" class="submit_button" @click="submitProcess">提交</button>
  71. </uni-card>
  72. </view>
  73. </template>
  74. <script setup lang="ts">
  75. import { onMounted, reactive, ref, computed } from 'vue'
  76. import { onLoad } from '@dcloudio/uni-app'
  77. import { useUserStore } from '@/store/user.js'
  78. import $modal from '@/plugins/modal.js'
  79. import $tab from '@/plugins/tab.js'
  80. import { convertToChineseCurrency } from '@/utils/ygoa.js'
  81. import { getProcessInfo, getProcessForm, submitProcessForm, uploadFile } from '@/api/work.js'
  82. let processInfo = reactive({
  83. modelName: '流程申请',
  84. control: '',
  85. formId: '',
  86. modelId: '',
  87. tmodelId: '',
  88. isMoreIns: '',
  89. pathJudgeType: '',
  90. form: []
  91. })
  92. const userStore = useUserStore()
  93. const title = ref('')
  94. onLoad((options) => {
  95. const { modelName, modelId, control } = options
  96. processInfo.modelName = modelName
  97. processInfo.modelId = modelId
  98. processInfo.control = control
  99. title.value = userStore.user.name + '的' + processInfo.modelName
  100. // 设置导航栏标题
  101. uni.setNavigationBarTitle({
  102. title: title.value
  103. });
  104. })
  105. onMounted(() => {
  106. initProcessInfo().then(() => {
  107. initProcessForm()
  108. })
  109. })
  110. function initProcessInfo() {
  111. return new Promise<void>((resolve, reject) => {
  112. getProcessInfo(processInfo)
  113. .then(({ returnParams }) => {
  114. const { formId, tmodelId, isMoreIns, pathJudgeType } = returnParams.flow[0]
  115. processInfo.formId = formId
  116. processInfo.tmodelId = tmodelId
  117. processInfo.isMoreIns = isMoreIns
  118. processInfo.pathJudgeType = pathJudgeType
  119. resolve()
  120. })
  121. })
  122. }
  123. function initProcessForm() {
  124. getProcessForm(userStore.user, processInfo).then(({ returnParams }) => {
  125. formElements.value = returnParams.formElements
  126. repeatingForm.value = returnParams.repeatingForm
  127. // 生成第一个重复表数据
  128. if (repeatingForm.value) {
  129. addrepeatingFormItem(0)
  130. }
  131. startEleIndex.value = formElements.value.findIndex((item) => '开始时间' == item.elementName)
  132. endEleIndex.value = formElements.value.findIndex((item) => '结束时间' == item.elementName)
  133. // console.log('startEle', startEleIndex.value);
  134. // console.log('endEle', endEleIndex.value);
  135. if (startEleIndex.value != -1 && endEleIndex.value != -1) { // 判断是否需要计算时间差
  136. // console.log('if (startEleIndex && endEleIndex)');
  137. timeFlag.value = true
  138. disableEndTime.value = true // 计算时间差时默认禁用结束时间选择器
  139. formElements.value[startEleIndex.value].defaultValue = new Date()
  140. }
  141. })
  142. }
  143. const formElements = ref([])
  144. const repeatingForm = ref({
  145. elementItem: [],
  146. elements: []
  147. })
  148. const startEleIndex = ref(0) // 开始时间
  149. const endEleIndex = ref(0) // 结束时间
  150. function addrepeatingFormItem(index) {
  151. const table = repeatingForm.value.elementItem.map(({ tableField, bddzText }) => {
  152. const item = {
  153. name: tableField,
  154. value: ""
  155. }
  156. if (bddzText != '') {
  157. const mulItem = bddzText.split('*')
  158. item['bddzText'] = mulItem.map(item => {
  159. return repeatingForm.value.elementItem.findIndex(({ elementName }) => item == elementName)
  160. })
  161. }
  162. return item
  163. })
  164. repeatingForm.value.elements.splice(index + 1, 0, table)
  165. }
  166. function delrepeatingFormItem(index) {
  167. console.log("delrepeatingFormItem: " + index);
  168. $modal.confirm('', '确认删除该重复表数据')
  169. .then(() => {
  170. repeatingForm.value.elements.splice(index, 1)
  171. })
  172. .catch(err => {
  173. })
  174. }
  175. function computedrepeatingFormItemValue(item, table) {
  176. let res = 1
  177. item.bddzText.forEach(itemIndex => {
  178. res = res * table[itemIndex].value
  179. })
  180. item.value = res
  181. console.log('convertToChineseCurrency', convertToChineseCurrency(res));
  182. return res
  183. }
  184. // 下拉框
  185. function bindPickerChange(e, item) {
  186. const index = e.detail.value;
  187. item.defaultValue = item.typeDetail.enum[index].enumVname
  188. }
  189. function formatDict(dict) {
  190. return dict.map(({ enumVname }) => enumVname)
  191. }
  192. const startTime = ref(0) // 开始时间
  193. const endTime = ref(0) // 结束时间
  194. const diffHours = ref(' ') // 时间差
  195. const timeFlag = ref(false) // 是否计算时间差
  196. const disableEndTime = ref(false) // 禁用 结束时间选择器
  197. function setTimeRange(e) {
  198. if (timeFlag.value && '开始时间' == e.elementName) {
  199. startTime.value = e.defaultValue // 设置 开始时间
  200. disableEndTime.value = false // 解除 结束时间选择器 禁用
  201. }
  202. if (timeFlag.value && '结束时间' == e.elementName) {
  203. endTime.value = e.defaultValue // 设置 结束时间
  204. }
  205. if (timeFlag.value && startTime.value && endTime.value) {
  206. const start = new Date(startTime.value).getTime() // 获取 开始时间 时间戳
  207. const end = new Date(endTime.value).getTime() // 获取 结束时间 时间戳
  208. const diffSec = Math.abs(end - start) // 获取 时间戳 差值
  209. diffHours.value = (diffSec / 1000 / 60 / 60).toFixed(2) // 计算 时间差值 并格式化 为只有2位小数
  210. }
  211. }
  212. const fileList = ref([]) // 文件列表
  213. const fileSeqs = ref([]) // 文件上传返回 seq 列表
  214. const filePicker = ref(null)
  215. // TODO 中间数组缓存将要上传的文件列表 保存上传顺序 上传成功后保存seq后再添加入 实际文件列表渲染
  216. async function handleFileSelect(files) { // 新增文件
  217. console.log('handleFileSelect', files.tempFiles)
  218. files.tempFiles.forEach(file => {
  219. const data = {
  220. name: file.name,
  221. filePath: file.path,
  222. }
  223. uploadFile(data) // TODO 改为同步
  224. .then(res => {
  225. fileSeqs.value.push(res.returnParams)
  226. file.seq = res.returnParams
  227. fileList.value.push(file)
  228. // file.seq = res.returnParams
  229. })
  230. .catch(err => {
  231. $modal.msgError('文件' + data.name + '上传失败,请重新上传')
  232. switch (err) {
  233. case -201:
  234. console.log('文件上传失败 未找到该文件');
  235. break;
  236. case -20201:
  237. console.log('文件上传失败 返回值不是JSON字符串');
  238. break;
  239. }
  240. // files.tempFiles.filter(item => item.path == file.path)
  241. })
  242. })
  243. console.log('UploadFiles', files.tempFiles);
  244. }
  245. function handleFileProgress(file, progress) {
  246. // console.log('handleFileProgress', file, progress);
  247. }
  248. function handleFileSuccess(file, res) {
  249. // console.log('handleFileSuccess', file, res);
  250. }
  251. function handleFileFail(file, err) {
  252. // console.log('handleFileFail', file, err);
  253. }
  254. function handleFileDelete(file, index) { // 移除文件
  255. console.log('handleDelete', file, index)
  256. console.log('index', file)
  257. fileList.value.splice(fileList.value.indexOf(file.tempFiles), 1)
  258. fileSeqs.value.splice(index, 1)
  259. }
  260. const button_state = ref(true)
  261. function submitProcess() { // 提交表单
  262. // 禁用提交按钮
  263. button_state.value = false
  264. // 保存表单数据
  265. processInfo.form = formElements.value.map(({ tableField, defaultValue }) => {
  266. return { name: tableField, value: defaultValue }
  267. })
  268. repeatingForm.value.elements.forEach((item, index) => {
  269. const newItem = item.map(({ name, value }) => {
  270. return {
  271. name: name + '_' + (index + 1),
  272. value
  273. }
  274. })
  275. processInfo.form.push(...newItem)
  276. })
  277. // console.log('submitProcessForm', processInfo.form);
  278. // console.log('repeatingForm', repeatingForm.value);
  279. // console.log('formElements', formElements.value);
  280. // console.log('fileList', fileList.value);
  281. // console.log('fileSeqs', fileSeqs.value);
  282. // console.log('filePicker', filePicker.value);
  283. button_state.value = true
  284. // return
  285. submitProcessForm(userStore.user, processInfo, fileSeqs.value).then(res => {
  286. console.log('submitProcessForm', res);
  287. if (res.returnMsg.includes('提交失败')) {
  288. // 启用提交按钮
  289. button_state.value = true
  290. $modal.msgError(res.returnMsg)
  291. } else {
  292. $modal.msgSuccess(res.returnMsg)
  293. // 返回上一页
  294. // $tab.navigateBack();
  295. }
  296. })
  297. }
  298. </script>
  299. <style lang="scss">
  300. .process_container {
  301. .picker_container {
  302. border: 1px solid #e5e5e5;
  303. background-color: #fff;
  304. border-radius: 4px;
  305. // box-sizing: border-box;
  306. .input_text {
  307. color: #333;
  308. font-size: 14px;
  309. line-height: 35px;
  310. height: 35px;
  311. padding: 0 0 0 10px;
  312. }
  313. }
  314. .repeating_table {
  315. .uni-forms-item--border {
  316. padding: 5px 0 !important;
  317. }
  318. .repeating_table_button_container {
  319. margin-top: 5px;
  320. button {
  321. height: 30px;
  322. line-height: 30px;
  323. color: #fff;
  324. }
  325. .add {
  326. background-color: #1ca035;
  327. }
  328. .del {
  329. background-color: #e64340;
  330. }
  331. }
  332. }
  333. .is-disabled {
  334. color: #666 !important;
  335. }
  336. .submit_button {
  337. background-color: #007aff !important;
  338. color: #fff !important;
  339. }
  340. }
  341. </style>