approveTasks.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <template>
  2. <div>
  3. <el-dialog :title="dialog.title" v-model="dialog.isShowDialog" width="90%">
  4. <el-collapse v-model="activeNames">
  5. <el-collapse-item name="2" disabled>
  6. <template #title>
  7. <el-icon class="header-icon"><info-filled /></el-icon>
  8. <span>审批意见</span>
  9. </template>
  10. <el-form ref="formRef" :rules="rules" :model="state.form" label-position="top" label-width="150px" style="padding: 10px 0">
  11. <el-form-item label="审批意见" prop="approveOpinion">
  12. <el-input
  13. v-model="state.form.approveOpinion"
  14. type="textarea"
  15. :rows="3"
  16. resize="none"
  17. placeholder="请输入审批意见"
  18. clearable
  19. style="width: 100%"
  20. ></el-input>
  21. </el-form-item>
  22. <el-form-item label="审批结果" prop="approveResult">
  23. <el-radio-group v-model="state.form.approveResult">
  24. <el-radio label="pass">通过</el-radio>
  25. <el-radio label="rejection">拒绝</el-radio>
  26. </el-radio-group>
  27. </el-form-item>
  28. <el-form-item v-show="state.taskDetail.isFile && state.form.approveResult === 'pass'" label="附件" prop="fileList">
  29. <el-upload
  30. :accept="state.SUPPORT_FILE_UPLOAD_TYPE_MAX"
  31. ref="upload"
  32. :file-list="state.form.fileList"
  33. :action="uploadUrl"
  34. :before-upload="beforeAvatarFileUpload"
  35. :on-success="handleSuccess"
  36. :limit="1"
  37. :on-exceed="handleExceed"
  38. style="width: 100%"
  39. >
  40. <template #trigger>
  41. <el-button type="primary" size="default">点击上传</el-button>
  42. </template>
  43. </el-upload>
  44. </el-form-item>
  45. </el-form>
  46. </el-collapse-item>
  47. </el-collapse>
  48. <template #footer>
  49. <el-button @click="dialog.isShowDialog = false">取 消</el-button>
  50. <el-button type="primary" @click="onSave">提 交</el-button>
  51. </template>
  52. </el-dialog>
  53. </div>
  54. </template>
  55. <script setup lang="ts" name="approvalForm">
  56. import to from 'await-to-js'
  57. import { nextTick, reactive, ref } from 'vue'
  58. import { ElMessage } from 'element-plus'
  59. import { useExecutionApi } from '/@/api/platform/execution'
  60. import { useDictApi } from '/@/api/system/dict'
  61. import type { UploadFile, UploadUserFile, UploadProps, UploadRawFile } from 'element-plus'
  62. import { genFileId } from 'element-plus'
  63. import { Session } from '/@/utils/storage'
  64. import { SUPPORT_FILE_UPLOAD_TYPE_MAX } from '/@/constants/pageConstants'
  65. // 定义变量内容
  66. const uploadUrl = import.meta.env.VITE_UPLOAD
  67. const upload = ref()
  68. const dictApi = useDictApi()
  69. const executionApi = useExecutionApi()
  70. const riskLevelOption = ref<RowDicDataType[]>([])
  71. const activeNames = ref(['1', '2', '3'])
  72. const formRef = ref()
  73. const dialog = reactive({
  74. type: '',
  75. isShowDialog: false,
  76. title: '',
  77. submitTxt: ''
  78. })
  79. const idList = ref<number[]>([])
  80. const state = reactive<any>({
  81. SUPPORT_FILE_UPLOAD_TYPE_MAX,
  82. taskDetail: {},
  83. detail: {},
  84. form: {
  85. taskId: 0,
  86. approveResult: 'pass',
  87. approveOpinion: '',
  88. fileList: [] as UploadUserFile[],
  89. fileUrl: ''
  90. },
  91. tableData: {
  92. data: [],
  93. loading: false,
  94. param: {
  95. id: 0
  96. }
  97. }
  98. })
  99. const checkFileUrl = (rule: any, value: any, callback: any) => {
  100. if (state.form.isFile && state.form.approveResult === 'pass') {
  101. if (!state.form.fileUrl) {
  102. callback(new Error('请上传附件'))
  103. }
  104. } else {
  105. callback()
  106. }
  107. }
  108. const rules = {
  109. approveResult: [{ required: true, message: '请选择审批结果', trigger: 'blur' }],
  110. approveOpinion: [{ required: true, message: '请输入审批意见', trigger: 'blur' }],
  111. fileList: [{ validator: checkFileUrl }]
  112. }
  113. const beforeAvatarFileUpload = (file: any) => {
  114. let isLt10m = file.size / 1024 / 1024 / 20 < 1
  115. if (!isLt10m) {
  116. ElMessage.error('上传文件大小不能超过 20MB!')
  117. return false
  118. }
  119. return true
  120. }
  121. const handleExceed: UploadProps['onExceed'] = (files) => {
  122. upload.value!.clearFiles()
  123. const file = files[0] as UploadRawFile
  124. file.uid = genFileId()
  125. upload.value!.handleStart(file)
  126. }
  127. const handleSuccess = (res: any, uploadFile: UploadFile) => {
  128. state.form.fileUrl = res.Data
  129. state.form.fileList = [
  130. {
  131. name: uploadFile?.name,
  132. url: res.Data,
  133. uid: uploadFile?.uid
  134. }
  135. ]
  136. }
  137. // 初始化表格数据
  138. const getTableData = async (id: number) => {
  139. state.tableData.loading = true
  140. state.tableData.param.id = id
  141. const [err, res]: ToResponse = await to(executionApi.getParticipantByProcInstID({ id }))
  142. if (err) return
  143. const arr = res?.data || []
  144. state.tableData.data = arr
  145. setTimeout(() => {
  146. state.tableData.loading = false
  147. }, 500)
  148. }
  149. // 刷新上级列表
  150. const emit = defineEmits(['getParentTableDate'])
  151. // 保存
  152. const onSave = async () => {
  153. formRef.value.validate(async (valid: any) => {
  154. if (valid) {
  155. let params = {
  156. paramIds: idList.value,
  157. result: state.form.approveResult,
  158. opinion: state.form.approveOpinion
  159. }
  160. const [err]: ToResponse = await to(executionApi.batchApprove(params))
  161. if (err) return
  162. ElMessage({ type: 'success', message: '提交成功' })
  163. dialog.isShowDialog = false
  164. emit('getParentTableDate')
  165. }
  166. })
  167. }
  168. const getDicts = () => {
  169. Promise.all([dictApi.getDictDataByType('RiskLevel')]).then(([riskLevel]) => {
  170. riskLevelOption.value = riskLevel.data.values
  171. })
  172. }
  173. // 打开弹窗
  174. const openDialog = (ids: number[]) => {
  175. getDicts()
  176. idList.value = ids
  177. activeNames.value = ['1', '2', '3']
  178. dialog.isShowDialog = true
  179. nextTick(() => {
  180. formRef.value.resetFields()
  181. })
  182. }
  183. // 暴露变量
  184. defineExpose({
  185. openDialog
  186. })
  187. </script>
  188. <style scoped lang="scss">
  189. .system-user-container {
  190. :deep(.el-card__body) {
  191. display: flex;
  192. flex-direction: column;
  193. flex: 1;
  194. overflow: auto;
  195. .vxe-table {
  196. flex: 1;
  197. }
  198. }
  199. }
  200. .card-header {
  201. display: flex;
  202. justify-content: space-between;
  203. align-items: center;
  204. span {
  205. font-size: 18px;
  206. font-weight: bold;
  207. color: #2c405e;
  208. }
  209. }
  210. :deep(.el-descriptions__label) {
  211. width: 10%;
  212. }
  213. :deep(.el-descriptions__content) {
  214. width: 23%;
  215. }
  216. </style>