task.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. <template>
  2. <el-dialog
  3. v-model="showDialog"
  4. width="800"
  5. @close="close"
  6. :close-on-click-modal="false"
  7. >
  8. <template #header>
  9. <div class="full-width flex flex-center flex-justify-between">
  10. <h4>任务详情</h4>
  11. </div>
  12. </template>
  13. <div>
  14. <el-input
  15. v-model="form.title"
  16. maxlength="20"
  17. clearable
  18. :disabled="!canEdit"
  19. style="height: 50px"
  20. class="font-16 bold"
  21. placeholder="请输入任务名称(最大20个字符)"
  22. ></el-input>
  23. <div
  24. class="full-width flex flex-justify-between flex-center padding-top mt-10"
  25. @click="goProject"
  26. v-if="task && task.projectId"
  27. >
  28. <span class="flex-center flex-justify-start title"
  29. >所属项目:{{ task.projectName }}</span
  30. >
  31. <el-icon>
  32. <ArrowRight />
  33. </el-icon>
  34. </div>
  35. <div class="mt-20">
  36. <div class="flex flex-center flex-justify-start">
  37. <span class="mr-10 title flex flex-justify-start">状态:</span>
  38. <wt-tag
  39. :data="status"
  40. :status="form.taskStatus"
  41. @change="changeStatus($event, 1)"
  42. :disabled="isFinish"
  43. />
  44. </div>
  45. <div class="mt-20 flex flex-center flex-justify-start">
  46. <span class="mr-10 title flex flex-justify-start">优先级:</span>
  47. <wt-tag
  48. :data="level"
  49. :disabled="!canEdit"
  50. :status="form.level"
  51. @change="changeStatus($event, 2)"
  52. />
  53. </div>
  54. <div class="mt-20 flex flex-center flex-justify-start">
  55. <span class="mr-10 title flex flex-justify-start">时间:</span>
  56. <div class="flex flex-center">
  57. <el-date-picker
  58. v-model="form.startTime"
  59. type="date"
  60. :disabled="!canEdit"
  61. placeholder="设置开始日期"
  62. format="YYYY-MM-DD"
  63. value-format="YYYY-MM-DD"
  64. >
  65. </el-date-picker>
  66. <div class="ml-5 mr-5">至</div>
  67. <el-date-picker
  68. v-model="form.endTime"
  69. type="date"
  70. :disabled="!canEdit"
  71. placeholder="设置截止日期"
  72. format="YYYY-MM-DD"
  73. value-format="YYYY-MM-DD"
  74. >
  75. </el-date-picker>
  76. </div>
  77. </div>
  78. <div class="mt-20 flex lex-align-start flex-justify-start">
  79. <span class="mr-10 title flex flex-justify-start">标签:</span>
  80. <div>
  81. <wt-label
  82. @submit="handleTags"
  83. :ids="[form.tags, form.category]"
  84. :disabled="!canEdit"
  85. />
  86. </div>
  87. </div>
  88. <div
  89. class="mt-20 flex lex-align-start flex-justify-start"
  90. v-if="form && form.createUserName && form.createUserName.length > 0"
  91. >
  92. <span class="mr-10 title flex flex-justify-start">创建人:</span>
  93. <div class="tag">
  94. <div class="ml-5">{{ form.createUserName }}</div>
  95. </div>
  96. </div>
  97. <div class="mt-20 flex lex-align-start flex-justify-start">
  98. <span class="mr-10 title flex flex-justify-start">执行者:</span>
  99. <div>
  100. <tasker
  101. :data="executeUser === null ? [] : executeUser"
  102. :disabled="!canEdit"
  103. ref="tasker"
  104. @success="selected"
  105. />
  106. </div>
  107. </div>
  108. <div class="mt-20 flex flex-align-start flex-justify-start">
  109. <span class="mr-10 title flex flex-justify-start">备注:</span>
  110. <el-input
  111. type="textarea"
  112. :rows="5"
  113. v-model="form.remark"
  114. :disabled="!canEdit"
  115. ></el-input>
  116. </div>
  117. <div class="mt-20 flex flex-align-start flex-justify-start flex-col">
  118. <div class="flex flex-center flex-justify-start">
  119. <span class="mr-10 title flex flex-justify-start">关联附件:</span>
  120. <filepicker
  121. :project-id="projectId"
  122. @submit="selection"
  123. v-if="canEdit"
  124. />
  125. </div>
  126. <div class="flex flex-center flex-justify-start full-width mt-10">
  127. <div class="title mr-10"></div>
  128. <div>
  129. <div v-for="item in fileList" :key="item.id">
  130. <div class="flex flex-center flex-justify-start">
  131. {{ item.title }}
  132. <preview :id="item.fileId" :show-action="true" />
  133. </div>
  134. </div>
  135. </div>
  136. </div>
  137. </div>
  138. <div class="flex flex-justify-start full-width flex-col">
  139. <div class="mt-10 flex flex-align-start flex-justify-start">
  140. <span class="mr-10 title flex flex-justify-start">任务进展:</span>
  141. <el-input
  142. type="textarea"
  143. :rows="5"
  144. v-model="form.taskProcess"
  145. :disabled="isFinish"
  146. ></el-input>
  147. </div>
  148. <div class="flex flex-justify-start mt-10 full-width">
  149. <div class="title mr-10">成果文件:</div>
  150. <upload-office
  151. @success="uploadResult"
  152. :max="1"
  153. v-if="isFinish === false"
  154. />
  155. </div>
  156. <div class="full-width flex flex-justify-start flex-center">
  157. <div class="title mr-10" />
  158. <div>
  159. <div v-for="item in resultFiles" :key="item.id">
  160. <div class="flex flex-justify-start flex-center">
  161. {{ item.fileVO.originalFileName }}
  162. ({{ item.createUserName }})
  163. <preview :id="item.fileVO.id" :show-action="true"></preview>
  164. <el-button
  165. text
  166. type="primary"
  167. v-if="isFinish === false"
  168. @click="removeFile(item)"
  169. >删除</el-button
  170. >
  171. </div>
  172. </div>
  173. </div>
  174. </div>
  175. </div>
  176. </div>
  177. <div class="flex flex-justify-end full-width">
  178. <el-button type="primary" plain @click="showDialog = false"
  179. >取 消
  180. </el-button>
  181. <el-button type="primary" @click="submit">确 定</el-button>
  182. </div>
  183. <move
  184. ref="move"
  185. :file-id="resultFiles.map(ele => ele.fileId).join(',')"
  186. :project-id="form.projectId"
  187. @on-success="moveSucc"
  188. />
  189. </div>
  190. </el-dialog>
  191. </template>
  192. <script>
  193. import WtTag from '@/views/task/component/wt-tag.vue'
  194. import Tasker from '@/views/task/component/tasker.vue'
  195. import filepicker from '@/components/filepicker/index.vue'
  196. import WtLabel from '@/views/task/component/wt-label.vue'
  197. import { useStore } from '@/store/user.js'
  198. import Preview from '@/views/resource/component/preview.vue'
  199. import api from '@/api/index.js'
  200. import uploadOffice from '@/components/upload-office/index.vue'
  201. import move from '@/views/task/component/move.vue'
  202. export default {
  203. /**
  204. * 任务添加、查看
  205. */
  206. computed: {
  207. api() {
  208. return api
  209. }
  210. },
  211. components: {
  212. Preview,
  213. WtLabel,
  214. Tasker,
  215. WtTag,
  216. filepicker,
  217. uploadOffice,
  218. move
  219. },
  220. props: {
  221. projectId: {
  222. type: String,
  223. default: ''
  224. },
  225. task: {
  226. type: Object,
  227. default: () => {
  228. return null
  229. }
  230. }
  231. },
  232. setup() {
  233. const user = useStore()
  234. return { user }
  235. },
  236. data() {
  237. return {
  238. isFinish: false,
  239. showDialog: false,
  240. fileList: [],
  241. loading: false,
  242. canEdit: true,
  243. executeUser: [],
  244. resultFiles: [],
  245. editResult: false,
  246. isMove: false,
  247. form: {
  248. title: '',
  249. taskStatus: 1,
  250. level: 1,
  251. remark: '',
  252. startTime: '',
  253. endTime: '',
  254. executeUser: '',
  255. tags: '',
  256. taskProcess: ''
  257. },
  258. status: [
  259. {
  260. title: '待确认',
  261. value: 1,
  262. color: '#D7D7D7',
  263. checked: true
  264. },
  265. {
  266. title: '进行中',
  267. value: 2,
  268. color: '#47A6EA',
  269. checked: false
  270. },
  271. {
  272. title: '已提交',
  273. value: 3,
  274. color: '#ECAB56',
  275. checked: false
  276. },
  277. {
  278. title: '已完成',
  279. value: 4,
  280. color: '#80B336',
  281. checked: false
  282. },
  283. {
  284. title: '已取消',
  285. value: 5,
  286. color: '#C72A29',
  287. checked: false
  288. }
  289. ],
  290. level: [
  291. {
  292. title: 'P1',
  293. value: 1,
  294. color: '#C72A29',
  295. checked: true
  296. },
  297. {
  298. title: 'P2',
  299. value: 2,
  300. color: '#E89D42',
  301. checked: false
  302. },
  303. {
  304. title: 'P3',
  305. value: 3,
  306. color: '#47A6EA',
  307. checked: false
  308. },
  309. {
  310. title: 'P4',
  311. value: 4,
  312. color: '#A0A0A0',
  313. checked: false
  314. }
  315. ]
  316. }
  317. },
  318. methods: {
  319. init() {
  320. this.form = this.task
  321. if (this.task.executeUsers !== undefined) {
  322. this.executeUser = [...this.task.executeUsers]
  323. }
  324. this.canEdit = this.form.createUser === this.user.info.userId
  325. if (this.task.taskStatus > 2) {
  326. this.canEdit = false
  327. }
  328. if (this.task.files !== undefined && this.task.files.length > 0) {
  329. this.fileList = this.task.files
  330. }
  331. this.resultFileInfo()
  332. },
  333. fetchIndex(list, key) {
  334. const index = list.findIndex(ele => ele.value === key)
  335. if (index > -1) {
  336. list.map(ele => {
  337. ele.checked = false
  338. return ele
  339. })
  340. list[index].checked = true
  341. }
  342. return list
  343. },
  344. /**
  345. *
  346. * @param type = 1 新增 2 查看
  347. */
  348. show(type) {
  349. if (type !== 1) {
  350. this.form = this.task
  351. if (this.task.taskStatus === 4 || this.task.taskStatus === 5) {
  352. this.isFinish = true
  353. }
  354. this.init(this.task)
  355. } else {
  356. this.form.taskStatus = 1
  357. this.form.level = 1
  358. this.canEdit = true
  359. }
  360. this.showDialog = true
  361. },
  362. /**
  363. * 获取成果文件info
  364. */
  365. resultFileInfo() {
  366. if (this.task.id !== undefined) {
  367. this.$api.task.taskFileInfo({ id: this.task.id }).then(res => {
  368. if (res.code === 200) {
  369. this.resultFiles = res.data
  370. }
  371. })
  372. }
  373. },
  374. changeStatus(res, type) {
  375. if (type === 1) {
  376. this.form.taskStatus = res.value
  377. } else {
  378. this.form.level = res.value
  379. }
  380. },
  381. submit() {
  382. if (this.form.taskStatus === 3 && this.resultFiles.length === 0) {
  383. this.$message.error('请上传成果文件')
  384. return
  385. } else {
  386. this.saveResultFile()
  387. }
  388. if (
  389. this.form.taskStatus === 4 &&
  390. this.isMove === false &&
  391. this.isFinish === false
  392. ) {
  393. // 已完成 需要转移文件
  394. this.$refs.move.showDialog()
  395. return
  396. }
  397. if (this.isFinish) {
  398. this.showDialog = false
  399. return
  400. }
  401. // 从项目详情,添加任务
  402. if (this.projectId !== undefined && this.projectId.length > 0) {
  403. this.form.projectId = this.projectId
  404. }
  405. this.form.relatedIds = this.fileList.map(ele => ele.id).join(',')
  406. this.$api.task.addTask(this.form).then(res => {
  407. this.showDialog = false
  408. if (res.code === 200) {
  409. this.$message.success(res.msg)
  410. } else {
  411. this.$message.error(res.msg)
  412. }
  413. this.$emit('success')
  414. })
  415. },
  416. saveResultFile() {
  417. if (this.editResult === false) {
  418. return
  419. }
  420. this.$api.task
  421. .taskFile({
  422. taskId: this.form.id,
  423. ids: this.resultFiles.map(ele => ele.id).join(',')
  424. })
  425. .then(res => {
  426. if (res.code === 200) {
  427. console.log(res)
  428. }
  429. })
  430. },
  431. selection(list, extra) {
  432. this.fileList = list.map(ele => {
  433. return ele
  434. })
  435. },
  436. selected(list) {
  437. this.form.executeUser = list.map(ele => ele.id).join(',')
  438. },
  439. handleTags(tags) {
  440. this.form.tags = tags.find(ele => ele.type === 'tags').id
  441. this.form.category = tags.find(ele => ele.type === 'category').id
  442. },
  443. /**
  444. * 成果文件上传成果
  445. * @param list
  446. */
  447. uploadResult(list) {
  448. this.editResult = true
  449. this.resultFiles = list.map(ele => {
  450. return {
  451. id: ele.id,
  452. fileVO: ele,
  453. createUserName: this.user.info.nickName
  454. }
  455. })
  456. },
  457. close() {
  458. this.form = {
  459. title: '',
  460. taskStatus: 1,
  461. level: 1,
  462. remark: '',
  463. startTime: '',
  464. endTime: '',
  465. executeUser: '',
  466. tags: '',
  467. taskProcess: ''
  468. }
  469. this.resultFiles.length = 0
  470. this.executeUser.length = 0
  471. this.fileList.length = 0
  472. this.showDialog = false
  473. this.editResult = false
  474. this.isMove = false
  475. this.isFinish = false
  476. this.$refs.tasker.reset()
  477. },
  478. /**
  479. * 文件移动成功
  480. */
  481. moveSucc() {
  482. this.isMove = true
  483. },
  484. removeFile(item) {
  485. console.log(item)
  486. this.$confirm('确定将选择数据删除?', {
  487. confirmButtonText: '确定',
  488. cancelButtonText: '取消',
  489. type: 'warning'
  490. }).then(res => {
  491. console.log(res)
  492. if (res === 'confirm') {
  493. this.$api.task.delTaskFile({ ids: item.id }).then(res => {
  494. if (res.code === 200) {
  495. this.resultFiles = this.resultFiles.filter(e => e.id !== item.id)
  496. }
  497. })
  498. }
  499. })
  500. },
  501. goProject() {
  502. const routeData = this.$router.resolve({
  503. path: '/project',
  504. query: { id: this.task.projectId }
  505. })
  506. window.open(routeData.href, '_blank')
  507. }
  508. }
  509. }
  510. </script>
  511. <style lang="scss" scoped>
  512. .title {
  513. min-width: 60px;
  514. }
  515. :deep(.el-input__wrapper) {
  516. box-shadow: none;
  517. font-weight: bold;
  518. font-size: 16px;
  519. background-color: #eeeeee;
  520. }
  521. :deep(.el-input__wrapper:hover) {
  522. box-shadow: none;
  523. background-color: #eeeeee;
  524. }
  525. .tag {
  526. min-width: 60px;
  527. height: 20px;
  528. border-radius: 20px;
  529. background-color: #a3773d;
  530. padding: 2px 10px;
  531. margin: 0 5px;
  532. color: white;
  533. }
  534. </style>