detail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. <!--
  2. * @Author: wanglj 471442253@qq.com
  3. * @Date: 2023-01-10 13:41:07
  4. * @LastEditors: wanglj
  5. * @LastEditTime: 2023-01-10 18:00:55
  6. * @Description: file content
  7. * @FilePath: \opms_frontend\src\views\plat\task\detail.vue
  8. -->
  9. <template>
  10. <div class="detail">
  11. <el-row :gutter="10">
  12. <el-col :span="16">
  13. <div class="title">
  14. <p>督办详情</p>
  15. <h3>
  16. {{ theTask.taskTitle }}
  17. <span></span>
  18. </h3>
  19. </div>
  20. <header>
  21. <el-descriptions :colon="false" :column="6" direction="vertical">
  22. <el-descriptions-item content-class-name="my-content" label="督办类型" label-class-name="my-label">
  23. {{ typeMap[theTask.taskType] }}
  24. </el-descriptions-item>
  25. <el-descriptions-item content-class-name="my-content" label="状态" label-class-name="my-label">
  26. <span v-if="theTask.taskStatus === '10'">发起</span>
  27. <span v-else-if="theTask.taskStatus === '20'">进行中</span>
  28. <span v-else-if="theTask.taskStatus === '30'">流程完成</span>
  29. </el-descriptions-item>
  30. <el-descriptions-item content-class-name="my-content" label="超期" label-class-name="my-label">
  31. {{ isNotOverdue(parseTime(theTask.taskEndDate, '{y}-{m}-{d} 23:59:59')) ? '否' : '是' }}
  32. </el-descriptions-item>
  33. <el-descriptions-item content-class-name="my-content" label="发布时间" label-class-name="my-label">
  34. {{ parseTime(theTask.taskStartDate, '{y}-{m}-{d}') }}
  35. </el-descriptions-item>
  36. <el-descriptions-item content-class-name="my-content" label="要求完成时间" label-class-name="my-label">
  37. {{ parseTime(theTask.taskEndDate, '{y}-{m}-{d}') }}
  38. </el-descriptions-item>
  39. <el-descriptions-item content-class-name="my-content" label="督办内容" label-class-name="my-label">
  40. {{ theTask.taskDesc }}
  41. </el-descriptions-item>
  42. </el-descriptions>
  43. <el-descriptions :colon="false" :column="6" direction="vertical">
  44. <el-descriptions-item content-class-name="my-content" label="负责人" label-class-name="my-label">
  45. {{ userMap[theTask.mainUserId] }}
  46. </el-descriptions-item>
  47. <el-descriptions-item content-class-name="my-content" label="协办人" label-class-name="my-label">
  48. {{ teamNames }}
  49. </el-descriptions-item>
  50. <el-descriptions-item content-class-name="my-content" label="督办人" label-class-name="my-label">
  51. {{ userMap[theTask.supervisorUserId] }}
  52. </el-descriptions-item>
  53. <el-descriptions-item content-class-name="my-content" label="监办人" label-class-name="my-label">
  54. {{ userMap[theTask.watchUserId] }}
  55. </el-descriptions-item>
  56. <!-- <el-descriptions-item content-class-name="my-content" label="关联类型" label-class-name="my-label">
  57. <span v-show="theTask.targetType == '10'">客户</span>
  58. <span v-show="theTask.targetType == '20'">项目</span>
  59. <span v-show="theTask.targetType == '30'">合同</span>
  60. <span v-show="theTask.targetType == '40'">回款</span>
  61. </el-descriptions-item> -->
  62. <el-descriptions-item content-class-name="my-content" label="督办事项来源" label-class-name="my-label">
  63. {{ theTask.source }}
  64. </el-descriptions-item>
  65. </el-descriptions>
  66. </header>
  67. <el-form label-width="80px" :model="form" style="margin-top: 10px">
  68. <el-form-item label="进展情况">
  69. <el-button v-show="theTask.step === 20 && type == '1'" circle icon="el-icon-plus" @click="addProgress" />
  70. </el-form-item>
  71. <el-form-item label-width="0">
  72. <el-table border :data="progressList" max-height="300px">
  73. <el-table-column align="center" label="进展说明" prop="progDesc">
  74. <template #default="{ row }">
  75. <span v-if="row.id">{{ row.progDesc }}</span>
  76. <el-input v-else v-model="row.progDesc" placeholder="进展说明" style="width: 100%" />
  77. </template>
  78. </el-table-column>
  79. <el-table-column align="center" label="时间" prop="progDate">
  80. <template #default="{ row }">
  81. <span v-if="row.id">{{ parseTime(row.progDate) }}</span>
  82. <el-date-picker
  83. v-else
  84. v-model="row.progDate"
  85. style="width: 100%"
  86. value-format="yyyy-MM-dd HH:mm:ss" />
  87. </template>
  88. </el-table-column>
  89. <el-table-column align="center" label="附件" prop="progFile">
  90. <template #default="{ row }">
  91. <el-upload action="#" :http-request="uploadrequest" :show-file-list="false">
  92. <el-button
  93. size="mini"
  94. style="margin-left: 10px"
  95. slot="trigger"
  96. type="primary"
  97. @click="showUploadFileDialog(row)">
  98. 上传/更新
  99. </el-button>
  100. <el-button
  101. v-show="row.progFile"
  102. size="mini"
  103. style="margin-left: 10px"
  104. @click="showFile(row.progFile)">
  105. 查看附件
  106. </el-button>
  107. </el-upload>
  108. </template>
  109. </el-table-column>
  110. <el-table-column align="center" label="备注" prop="remark">
  111. <template #default="{ row }">
  112. <span v-if="row.id">{{ row.remark }}</span>
  113. <el-input v-else v-model="row.remark" placeholder="备注" style="width: 100%" />
  114. </template>
  115. </el-table-column>
  116. <el-table-column v-if="theTask.step < 30 && type == 1" align="center" label="操作">
  117. <template #default="{ row }">
  118. <el-button type="text" @click="handleDel(row.$index)">删除</el-button>
  119. </template>
  120. </el-table-column>
  121. </el-table>
  122. </el-form-item>
  123. <el-form-item v-if="theTask.approDate || theTask.step === 30" label="审批意见">
  124. <el-input v-if="theTask.step === 30" v-model="form.approDesc" placeholder="审批意见" />
  125. <span v-else>{{ theTask.approDesc }}</span>
  126. </el-form-item>
  127. <el-form-item v-if="theTask.approDate || theTask.step === 40" label="督办评价">
  128. <el-input v-if="theTask.step === 40" v-model="form.evaluateDesc" placeholder="督办评价" />
  129. <span v-else>{{ theTask.evaluateDesc }}</span>
  130. </el-form-item>
  131. </el-form>
  132. </el-col>
  133. <el-col :span="8">
  134. <div class="buttons">
  135. <el-button v-show="theTask.taskStatus === '10' && type == 1" type="primary" @click="changeStatus(10, '10')">
  136. 接收
  137. </el-button>
  138. <el-button
  139. v-show="theTask.taskStatus === '20' && theTask.step === 20 && type == 1"
  140. type="success"
  141. @click="changeStatus(15)">
  142. 暂存
  143. </el-button>
  144. <el-button
  145. v-show="theTask.taskStatus === '20' && theTask.step === 20 && type == 1"
  146. type="primary"
  147. @click="changeStatus(20, '20')">
  148. 提交
  149. </el-button>
  150. <el-button v-show="theTask.step === 30 && type == 1" type="primary" @click="changeStatus(30, '30')">
  151. 审批通过
  152. </el-button>
  153. <el-button v-show="theTask.step === 30 && type == 1" type="danger" @click="changeStatus(30, '40')">
  154. 审批退回
  155. </el-button>
  156. <el-button v-show="theTask.step === 40 && type == 1" type="primary" @click="changeStatus(40, '30')">
  157. 审批通过
  158. </el-button>
  159. <el-button v-show="theTask.step === 40 && type == 1" type="danger" @click="changeStatus(40, '40')">
  160. 审批退回
  161. </el-button>
  162. <el-button @click="$router.go(-1)">返回</el-button>
  163. </div>
  164. <ul class="records">
  165. <li v-for="log in logList" :key="log.id">
  166. <vab-icon icon="record-circle-line" />
  167. <div class="text">
  168. <p class="action">
  169. <span>{{ log.nodeName }}</span>
  170. <span>{{ log.desc }}</span>
  171. </p>
  172. <p>
  173. <span>开始处理</span>
  174. <span>完成处理</span>
  175. </p>
  176. <p>
  177. <span>{{ log.startTime }}</span>
  178. <span>{{ log.endTime }}</span>
  179. </p>
  180. </div>
  181. </li>
  182. </ul>
  183. </el-col>
  184. </el-row>
  185. </div>
  186. </template>
  187. <script>
  188. import to from 'await-to-js'
  189. import taskApi from '@/api/plat/task'
  190. import userApi from '@/api/system/user'
  191. import asyncUploadFile from '@/utils/uploadajax'
  192. import dictApi from '@/api/system/dict'
  193. import axios from 'axios'
  194. export default {
  195. name: 'TaskDetail',
  196. data() {
  197. return {
  198. detail: {},
  199. // 督办进展
  200. progressList: [],
  201. // 日志
  202. logList: [],
  203. theTask: {},
  204. typeMap: {},
  205. userMap: {},
  206. teamNames: '',
  207. form: {
  208. comment: '',
  209. },
  210. type: '',
  211. // 选中进展
  212. theProp: {},
  213. }
  214. },
  215. async mounted() {
  216. this.type = this.$route.query.type
  217. await this.initData()
  218. this.theTask = this.$store.state.task.theTask
  219. this.open()
  220. },
  221. methods: {
  222. // 判断是否没有超期
  223. isNotOverdue(date) {
  224. return new Date() <= new Date(date)
  225. },
  226. // 打开弹窗
  227. open() {
  228. // 获取数据信息
  229. this.getProgressList()
  230. this.getLogList()
  231. this.teamNames = ''
  232. if (this.theTask.ownerUserId != '') {
  233. let ids = this.theTask.ownerUserId.split(',')
  234. for (let id of ids) {
  235. if (this.teamNames == '') {
  236. this.teamNames = this.userMap[parseInt(id)]
  237. } else {
  238. this.teamNames += ',' + this.userMap[parseInt(id)]
  239. }
  240. }
  241. }
  242. },
  243. // 初始化数据
  244. async initData() {
  245. const [err, [type, user]] = await to(
  246. Promise.all([dictApi.getDictDataList({ dictType: 'task_type' }), userApi.getList()])
  247. )
  248. if (err) return
  249. this.types = type.data.list
  250. for (let type of this.types) {
  251. this.typeMap[type.dictValue] = type.dictLabel
  252. }
  253. this.users = user.data.list
  254. for (let user of this.users) {
  255. this.userMap[user.id] = user.nickName
  256. }
  257. },
  258. // 获取进展信息
  259. getProgressList() {
  260. this.progressList = []
  261. taskApi
  262. .getTaskProgressList({ taskId: this.theTask.id + '' })
  263. .then((res) => {
  264. if (res.data.list) {
  265. this.progressList = res.data.list
  266. }
  267. })
  268. .catch((err) => {
  269. console.error(err)
  270. })
  271. },
  272. // 获取日志信息
  273. getLogList() {
  274. this.logList = []
  275. taskApi
  276. .getTaskLogList({ taskId: this.theTask.id + '' })
  277. .then((res) => {
  278. if (res.data.list) {
  279. this.logList = res.data.list
  280. }
  281. })
  282. .catch((err) => {
  283. console.error(err)
  284. })
  285. },
  286. // 改变督办状态
  287. changeStatus(step, status) {
  288. if (step == 30 && !this.form.approDesc) return this.$message.warning('请填写审批意见')
  289. else if (step == 40 && !this.form.evaluateDesc) return this.$message.warning('请填写督办意见')
  290. this.$confirm('确定修改督办状态?', '提示', {
  291. confirmButtonText: '确定',
  292. cancelButtonText: '取消',
  293. type: 'warning',
  294. }).then(async () => {
  295. // eslint-disable-next-line vue/no-mutating-props
  296. this.selfVisible = false
  297. let data = {
  298. taskId: this.theTask.id,
  299. step,
  300. handleStatus: status,
  301. handleDesc: '',
  302. progressList: this.progressList,
  303. }
  304. if (this.theTask.step == 30) data.handleDesc = this.form.approDesc
  305. else if (this.theTask.step == 40) data.handleDesc = this.form.evaluateDesc
  306. const { msg } = await taskApi.handleTask(data)
  307. this.$baseMessage(msg, 'success', 'vab-hey-message-success')
  308. this.$router.go(-1)
  309. })
  310. },
  311. // 查看附件
  312. showFile(path) {
  313. window.open(path, '_system')
  314. },
  315. // 添加进展
  316. addProgress() {
  317. this.progressList.push({
  318. progDesc: '',
  319. progDate: '',
  320. progFile: '',
  321. remark: '',
  322. })
  323. },
  324. handleDel(index) {
  325. this.progressList.splice(index, 1)
  326. },
  327. // 开始上传附件
  328. showUploadFileDialog(row) {
  329. this.theProp = row
  330. },
  331. // 上传
  332. uploadrequest(option) {
  333. let _this = this
  334. let url = process.env.VUE_APP_UPLOAD_WEED
  335. axios
  336. .post(url)
  337. .then(function (res) {
  338. if (res.data && res.data.fid && res.data.fid !== '') {
  339. option.action = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}`
  340. asyncUploadFile(option).then(() => {
  341. _this.theProp.progFile = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}`
  342. })
  343. } else {
  344. _this.$message({
  345. type: 'warning',
  346. message: '未上传成功!请重新上传!',
  347. })
  348. }
  349. })
  350. .catch(function () {
  351. _this.$message({
  352. type: 'warning',
  353. message: '未上传成功!请重新上传!',
  354. })
  355. })
  356. },
  357. },
  358. }
  359. </script>
  360. <style lang="scss" scoped>
  361. $base: '.detail';
  362. #{$base} {
  363. height: calc(100vh - 60px - 12px * 2 - 40px);
  364. display: flex;
  365. padding: 20px 40px;
  366. > .el-row {
  367. flex: 1;
  368. > .el-col {
  369. height: 100%;
  370. }
  371. }
  372. .title {
  373. p,
  374. h3 {
  375. margin: 0;
  376. }
  377. p {
  378. font-size: 14px;
  379. font-weight: 400;
  380. line-height: 22px;
  381. }
  382. h3 {
  383. font-size: 24px;
  384. font-weight: 500;
  385. line-height: 36px;
  386. color: #333;
  387. display: flex;
  388. justify-content: space-between;
  389. }
  390. }
  391. header {
  392. min-height: 134px;
  393. background: rgba(196, 196, 196, 0.5);
  394. border-radius: 4px;
  395. display: flex;
  396. flex-direction: column;
  397. align-items: center;
  398. padding: 7px 20px;
  399. margin-top: 16px;
  400. ::v-deep .el-descriptions__body {
  401. background: transparent;
  402. }
  403. ::v-deep .my-label {
  404. font-size: 14px;
  405. font-weight: 600;
  406. color: #1d66dc;
  407. }
  408. ::v-deep .my-content {
  409. font-size: 14px;
  410. font-weight: 600;
  411. color: #333;
  412. overflow: hidden;
  413. text-overflow: ellipsis;
  414. white-space: nowrap;
  415. }
  416. }
  417. .buttons {
  418. height: 60px;
  419. padding-top: 28px;
  420. text-align: right;
  421. }
  422. .add-pro {
  423. text-align: right;
  424. margin: 10px 0;
  425. }
  426. .records {
  427. padding: 10px 20px;
  428. height: calc(100% - 60px);
  429. overflow-y: auto;
  430. li {
  431. display: flex;
  432. align-items: center;
  433. i {
  434. font-size: 40px;
  435. margin-right: 10px;
  436. }
  437. .text {
  438. flex: 1;
  439. }
  440. p {
  441. height: 20px;
  442. line-height: 20px;
  443. display: flex;
  444. justify-content: space-between;
  445. font-weight: 500;
  446. &:first-child {
  447. font-weight: bold;
  448. }
  449. }
  450. & + li {
  451. margin-top: 10px;
  452. }
  453. }
  454. }
  455. }
  456. </style>