index.vue 13 KB

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