detail.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. <!-- todo 无用页面 可以删除了-->
  2. <template>
  3. <div>
  4. <el-drawer v-model="visibleDialog" class="custom-adrawer" direction="rtl" size="900" @close="handleCancel">
  5. <template #header>
  6. <div class="flow-header-box">
  7. <div class="flow-no">编号:{{ taskId }}</div>
  8. </div>
  9. </template>
  10. <div class="flow-detail-container">
  11. <div class="flow-status-stamp">
  12. <FlowStatusStamp :status="0" />
  13. </div>
  14. <div class="flow-detail-box">
  15. <!--头部-->
  16. <div class="header-box">
  17. <div class="summary-info">
  18. <div class="title">{{ taskObj.processName }}</div>
  19. <FlowStatusTag :status="0" />
  20. </div>
  21. <div class="initiator-info">
  22. <FlowNodeAvatar id="1" />
  23. <div class="begin-time">{{ taskObj.createTime }} 提交</div>
  24. </div>
  25. </div>
  26. <div class="area-divider"></div>
  27. <!-- 表单 -->
  28. <div v-loading="validateForm.loading" class="form-wrap">
  29. <FormCreate v-show="validateForm.rule.length" v-model:api="validateForm.api" :option="validateForm.option" :rule="validateForm.rule" />
  30. <LeNoData v-if="!validateForm.rule.length" message="表单无数据" />
  31. </div>
  32. <div class="area-divider"></div>
  33. <!--审批流-->
  34. <el-timeline style="margin-left: 50px">
  35. <el-timeline-item v-for="active in activeData" :key="active.id" hollow :timestamp="active.taskName" placement="top">
  36. <template #dot>
  37. <FlowTypeDot :status="active.taskState" :type="active.taskType" />
  38. </template>
  39. <div class="timeline-box flex-1">
  40. <div class="flex flex-align-center">
  41. <div class="timeline-box-user flex-1">
  42. <FlowNodeAvatar id="1" />
  43. <div class="comment">
  44. <div class="comment-content">{{ active.duration }}</div>
  45. </div>
  46. </div>
  47. <span class="timeline-box-date">{{ formatTimestamp(active.finishTime) }}</span>
  48. </div>
  49. </div>
  50. </el-timeline-item>
  51. </el-timeline>
  52. </div>
  53. </div>
  54. <template #footer>
  55. <div class="flow-actions">
  56. <el-button :icon="ChatLineSquare" @click="openComment('reviewVisible')">评论</el-button>
  57. <el-button :icon="Check" type="success" @click="openComment('consentOrRefuseVisible', 'agree')">同意</el-button>
  58. <el-button :icon="Close" type="danger" @click="openComment('consentOrRefuseVisible', 'reject')">拒绝</el-button>
  59. <el-dropdown style="margin-left: 12px">
  60. <el-button :icon="More">更多</el-button>
  61. <template #dropdown>
  62. <el-dropdown-menu>
  63. <el-dropdown-item @click.native="openComment('deliverToReviewVisible')">
  64. <el-icon><DArrowLeft /></el-icon>
  65. 转交
  66. </el-dropdown-item>
  67. <el-dropdown-item @click.native="openComment('rollbackVisible')">
  68. <el-icon><Switch /></el-icon>
  69. 回退
  70. </el-dropdown-item>
  71. <el-dropdown-item @click.native="openComment('addSignVisible')">
  72. <el-icon><Plus /></el-icon>
  73. 加签
  74. </el-dropdown-item>
  75. <el-dropdown-item @click.native="openComment('loseSignVisible')">
  76. <el-icon><Minus /></el-icon>
  77. 减签
  78. </el-dropdown-item>
  79. </el-dropdown-menu>
  80. </template>
  81. </el-dropdown>
  82. </div>
  83. </template>
  84. </el-drawer>
  85. <!-- 评论弹窗-->
  86. <review-dialog v-if="reviewVisible" v-model="reviewVisible" :task-id="taskId"></review-dialog>
  87. <!-- 加签弹窗 -->
  88. <add-sign-dialog v-if="addSignVisible" v-model="addSignVisible" :task-id="taskId"></add-sign-dialog>
  89. <!-- 同意或拒绝弹窗 -->
  90. <consent-or-refuse-dialog
  91. v-if="consentOrRefuseVisible"
  92. v-model="consentOrRefuseVisible"
  93. :task-id="taskId"
  94. :current-type="currentDialog"
  95. ></consent-or-refuse-dialog>
  96. <!-- 转交审批弹窗 -->
  97. <deliver-to-review-dialog v-if="deliverToReviewVisible" v-model="deliverToReviewVisible" :task-id="taskId"></deliver-to-review-dialog>
  98. <!-- 减签弹窗 -->
  99. <lose-sign-dialog v-if="loseSignVisible" v-model="loseSignVisible" :task-id="taskId"></lose-sign-dialog>
  100. <!-- 回退弹窗 -->
  101. <rollback-dialog v-if="rollbackVisible" v-model="rollbackVisible" :task-id="taskId"></rollback-dialog>
  102. </div>
  103. </template>
  104. <script setup>
  105. import { computed, ref, onMounted, nextTick } from 'vue'
  106. import FlowStatusStamp from '@/components/Flow/FlowStatusStamp.vue'
  107. import FlowStatusTag from '@/components/Flow/FlowStatusTag.vue'
  108. import FlowNodeAvatar from '@/components/Flow/FlowNodeAvatar.vue'
  109. import FlowTypeDot from '@/components/Flow/FlowTypeDot.vue'
  110. import { ChatLineSquare, Check, Close, Switch, DArrowLeft, Plus, Minus, More } from '@element-plus/icons-vue'
  111. import { processApprovalInfoApi, processTaskApprovalInfo } from '@/api/flow/processTask'
  112. import { formatTimestamp } from '@/utils/datetime'
  113. import ReviewDialog from '../components/reviewDialog'
  114. import AddSignDialog from '../components/addSignDialog'
  115. import ConsentOrRefuseDialog from '../components/consentOrRefuseDialog'
  116. import DeliverToReviewDialog from '../components/deliverToReviewDialog'
  117. import LoseSignDialog from '../components/loseSignDialog'
  118. import RollbackDialog from '../components/rollbackDialog'
  119. import viewForm from '@/utils/form'
  120. const props = defineProps({
  121. modelValue: {
  122. type: Boolean,
  123. default: false
  124. },
  125. taskObj: {
  126. type: Object,
  127. default: () => ({})
  128. }
  129. })
  130. const emit = defineEmits(['update:modelValue', 'successFn'])
  131. // 各种操作弹窗显示隐藏 start
  132. const reviewVisible = ref(false)
  133. const addSignVisible = ref(false)
  134. const consentOrRefuseVisible = ref(false)
  135. const deliverToReviewVisible = ref(false)
  136. const loseSignVisible = ref(false)
  137. const rollbackVisible = ref(false)
  138. // 各种操作弹窗显示隐藏 end
  139. const activeData = ref([])
  140. const currentDialog = ref(null)
  141. const visibleDialog = computed({
  142. get() {
  143. return props.modelValue
  144. },
  145. set(val) {
  146. emit('update:modelValue', val)
  147. }
  148. })
  149. const taskId = computed(() => {
  150. return props.taskObj.taskId || ''
  151. })
  152. const FormCreate = viewForm.$form()
  153. const validateForm = ref({
  154. api: {},
  155. option: {
  156. submitBtn: false
  157. },
  158. rule: [],
  159. loading: false
  160. })
  161. // 关闭按钮
  162. const closeDrawer = () => {
  163. emit('successFn')
  164. emit('update:modelValue', false)
  165. }
  166. const handleCancel = () => {
  167. closeDrawer()
  168. }
  169. // 操作按钮
  170. const openComment = (type, item) => {
  171. switch (type) {
  172. case 'reviewVisible':
  173. reviewVisible.value = !reviewVisible.value
  174. break
  175. case 'addSignVisible':
  176. addSignVisible.value = !addSignVisible.value
  177. break
  178. case 'consentOrRefuseVisible':
  179. // 点击同意
  180. if (item === 'agree') {
  181. debugger
  182. // 验证表单 todo...
  183. const api = validateForm.value.api
  184. api.validate((valid, fail) => {
  185. if (valid) {
  186. const values = api.formData()
  187. console.warn(values, 'values')
  188. //todo 表单验证通过
  189. } else {
  190. //todo 表单验证未通过
  191. }
  192. })
  193. // return
  194. }
  195. currentDialog.value = item
  196. consentOrRefuseVisible.value = !consentOrRefuseVisible.value
  197. break
  198. case 'deliverToReviewVisible':
  199. deliverToReviewVisible.value = !deliverToReviewVisible.value
  200. break
  201. case 'loseSignVisible':
  202. loseSignVisible.value = !loseSignVisible.value
  203. break
  204. case 'rollbackVisible':
  205. rollbackVisible.value = !rollbackVisible.value
  206. break
  207. }
  208. }
  209. nextTick(() => {
  210. const cur = props.taskObj || {}
  211. processApprovalInfoApi(cur.taskId).then(data => {
  212. // console.log(JSON.stringify(data))
  213. activeData.value = data
  214. })
  215. // 提交的表单 数据展示
  216. validateForm.value.loading = true
  217. processTaskApprovalInfo(cur.taskId)
  218. .then(data => {
  219. console.log(data, 'data.......')
  220. // validateForm.value.origin = data
  221. try {
  222. /*descItemsData.value.list = JSON.parse(data.formContent).map(item => {
  223. const showLabel = item.title
  224. let showValue = item.local_value
  225. const options = item.options
  226. if (Array.isArray(options) && showValue !== undefined) {
  227. if (Array.isArray(showValue)) {
  228. showValue = showValue.reduce(val => {
  229. const cur = options.find(option => option.value === val)
  230. return cur?.label || val
  231. }, [])
  232. } else {
  233. const cur = options.find(option => option.value === showValue)
  234. showValue = cur?.label || showValue
  235. }
  236. }
  237. return {
  238. showLabel,
  239. showValue
  240. }
  241. })*/
  242. const forms = JSON.parse(data.formContent)
  243. if (Array.isArray(forms)) {
  244. validateForm.value.rule = forms
  245. const api = validateForm.value.api
  246. api.setValue(
  247. forms.reduce((obj, item) => {
  248. obj[item.field] = item.local_value
  249. return obj
  250. }, {})
  251. )
  252. api.nextTick(() => {
  253. // 是否有编辑权限 操作
  254. api.disabled(false /*true*/)
  255. })
  256. }
  257. } catch (e) {
  258. console.error('解析 descItems 数据出现问题', e)
  259. // descItemsData.value.list = []
  260. validateForm.value.rule = []
  261. }
  262. })
  263. .finally(() => {
  264. validateForm.value.loading = false
  265. })
  266. })
  267. </script>
  268. <style scoped lang="scss">
  269. .flow-detail-container {
  270. height: 100%;
  271. }
  272. .flow-status-stamp {
  273. position: absolute;
  274. right: 50px;
  275. top: 30px;
  276. }
  277. .flow-header-box {
  278. font-weight: 400;
  279. font-size: 14px;
  280. border-bottom: 1px solid #e5e6ec;
  281. padding: 0 16px;
  282. height: 39px;
  283. display: flex;
  284. align-items: center;
  285. justify-content: space-between;
  286. color: #86909c;
  287. }
  288. .flow-detail-box {
  289. height: calc(100% - 92px);
  290. overflow: hidden;
  291. overflow-y: auto;
  292. padding: 0 20px;
  293. .header-box {
  294. display: flex;
  295. flex-direction: column;
  296. justify-content: center;
  297. // padding-top: 20px;
  298. .summary-info {
  299. display: flex;
  300. align-items: center;
  301. .title {
  302. font-size: 24px;
  303. font-family: PingFangSC-Semibold, PingFang SC;
  304. color: #1d2129;
  305. }
  306. }
  307. .initiator-info {
  308. display: flex;
  309. align-items: center;
  310. margin-top: 16px;
  311. .begin-time {
  312. margin-left: 16px;
  313. font-weight: 350;
  314. color: #86909c;
  315. font-size: 13px;
  316. -webkit-user-select: none;
  317. user-select: none;
  318. }
  319. }
  320. }
  321. .area-divider {
  322. border-bottom: 1px solid rgba(229, 230, 235, 1);
  323. margin: 20px 0;
  324. position: relative;
  325. }
  326. }
  327. .flow-actions {
  328. display: flex;
  329. align-items: center;
  330. justify-content: flex-end;
  331. }
  332. // 时间线样式
  333. .timeline-box {
  334. padding-left: 8px;
  335. &-user {
  336. .name {
  337. color: #1d2129;
  338. font-weight: 400;
  339. }
  340. .comment {
  341. width: 100%;
  342. -webkit-user-select: none;
  343. user-select: none;
  344. margin: 4px 0 16px;
  345. padding: 8px 16px;
  346. border-radius: 4px;
  347. background-color: #f8f8fa;
  348. .comment-content {
  349. font-weight: 500;
  350. color: #1d2129;
  351. }
  352. }
  353. }
  354. &-date {
  355. margin-left: 10px;
  356. left: 0;
  357. padding-right: 32px;
  358. text-align: right;
  359. transform: translate(-100%);
  360. position: absolute;
  361. top: 0;
  362. box-sizing: border-box;
  363. max-width: 100px;
  364. color: #86909c;
  365. font-size: 12px;
  366. line-height: 1.667;
  367. }
  368. }
  369. :deep(.el-drawer__header) {
  370. padding: 0px;
  371. margin-bottom: 0 !important;
  372. }
  373. :deep(.el-drawer__footer) {
  374. padding-top: 20px;
  375. border-top: 1px solid rgba(229, 230, 235, 1);
  376. }
  377. :deep(.el-timeline .el-timeline-item__timestamp) {
  378. padding-left: 8px;
  379. }
  380. </style>