detail.vue 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252
  1. <template>
  2. <view class="uni-app">
  3. <view class="status-bar" />
  4. <view class="main-container">
  5. <wk-nav-bar
  6. title="任务详情"
  7. :command-list="commandList"
  8. @command="handleCommand" />
  9. <view v-if="!$isEmpty(detailData)" class="container">
  10. <view class="header-top">
  11. <view class="bg linear-gradient" />
  12. <view class="card">
  13. <view
  14. :class="{active: detailData.status === 5}"
  15. class="task-name"
  16. @click="handleCommand('name')">
  17. <view
  18. class="check-box"
  19. @click.stop="handleCommand('status')">
  20. <text class="icon wk wk-check" />
  21. </view>
  22. <view class="text">
  23. {{ detailData.name }}
  24. </view>
  25. </view>
  26. <view class="info-cell">
  27. <view
  28. class="info-cell-item main-user"
  29. @click="handleCommand('mainUserId')">
  30. <template v-if="!$isEmpty(detailData.mainUser)">
  31. <wk-avatar
  32. :name="detailData.mainUser.realname"
  33. :avatar="detailData.mainUser.img"
  34. :size="12"
  35. :preview="false"
  36. class="avatar" />
  37. <text class="text">
  38. {{ detailData.mainUser.realname || '' }}
  39. </text>
  40. </template>
  41. <template v-else>
  42. <view class="avatar empty-user">
  43. <i class="wk wk-plus" />
  44. </view>
  45. <text class="text">
  46. 负责人
  47. </text>
  48. </template>
  49. </view>
  50. <view
  51. class="info-cell-item"
  52. @click="handleCommand('stopTime')">
  53. <view class="icon-box">
  54. <text class="wk wk-calendar" />
  55. </view>
  56. <view class="time-box">
  57. {{ endTimeText }}
  58. </view>
  59. </view>
  60. </view>
  61. </view>
  62. </view>
  63. <view class="section task-section">
  64. <view class="task-header">
  65. <text class="wk wk-edit" />
  66. <text>任务描述</text>
  67. </view>
  68. <view class="cell" @click="handleCommand('description')">
  69. <view class="cell-body">
  70. {{ detailData.description || '' }}
  71. </view>
  72. </view>
  73. <view class="cell" @click="handleCommand('labelId')">
  74. <view class="cell-title">
  75. 任务标签
  76. </view>
  77. <view class="cell-body">
  78. <template v-if="detailData && detailData.labelList">
  79. <view
  80. v-for="(item, index) in detailData.labelList"
  81. :key="index"
  82. :style="{backgroundColor: item.color}"
  83. class="tag-item">
  84. {{ item.labelName }}
  85. </view>
  86. </template>
  87. </view>
  88. </view>
  89. <view class="cell" @click="handleCommand('ownerUserId')">
  90. <view class="cell-title">
  91. 参与人
  92. </view>
  93. <view class="cell-body">
  94. <template v-if="detailData && detailData.ownerUserList">
  95. <wk-avatar
  96. v-for="(item, index) in detailData.ownerUserList"
  97. :key="index"
  98. :name="item.realname"
  99. :avatar="item.img"
  100. :size="12"
  101. :preview="false"
  102. class="avatar avatar-list-item" />
  103. </template>
  104. </view>
  105. </view>
  106. <view class="cell" @click="handleCommand('startTime')">
  107. <view class="cell-title">
  108. 开始时间
  109. </view>
  110. <view class="cell-body">
  111. {{ detailData.startTime | formatTime }}
  112. </view>
  113. </view>
  114. <view class="cell" @click="handleCommand('priority')">
  115. <view class="cell-title">
  116. 优先级
  117. </view>
  118. <view class="cell-body">
  119. <template v-if="priorityObj">
  120. <view
  121. :style="{backgroundColor: priorityObj.color}"
  122. class="priority-item">
  123. {{ priorityObj.label }}
  124. </view>
  125. </template>
  126. </view>
  127. </view>
  128. </view>
  129. <view class="section relevance-section">
  130. <view class="cell-box" @click="handleCommand('relevance')">
  131. <view class="cell-box-left">
  132. <image :src="$static('images/icon/relate.png')" class="type-icon" />
  133. <text>关联业务</text>
  134. </view>
  135. <view class="cell-box-right">
  136. <view class="add-icon">
  137. <text class="wk wk-plus" />
  138. </view>
  139. </view>
  140. </view>
  141. <view
  142. v-if="showRelevance"
  143. class="section-item-content relevance">
  144. <relevance-section
  145. :relevance-data="relevanceData"
  146. is-add
  147. @delete="handleToDeleteRelevance" />
  148. </view>
  149. </view>
  150. <view class="section chlid-task-section">
  151. <view class="cell-box" @click="handleAddChildTask">
  152. <view class="cell-box-left">
  153. <image :src="$static('images/icon/sub.png')" class="type-icon" />
  154. <text>子任务</text>
  155. </view>
  156. <view class="cell-box-right">
  157. <view class="add-icon">
  158. <text class="wk wk-plus" />
  159. </view>
  160. </view>
  161. </view>
  162. <view
  163. v-if="detailData.childTask"
  164. class="section-item-content child-task">
  165. <child-task-item
  166. v-for="(child, index) in detailData.childTask"
  167. :key="index"
  168. :item-data="child"
  169. @status="childTaskStatus"
  170. @delete="childTaskDelete" />
  171. </view>
  172. </view>
  173. <view class="section file-section">
  174. <view class="cell-box" @click="handleAddFile">
  175. <view class="cell-box-left">
  176. <image :src="$static('images/icon/clip.png')" class="type-icon" />
  177. <text>附件</text>
  178. </view>
  179. <view class="cell-box-right">
  180. <view class="add-icon">
  181. <text class="wk wk-plus" />
  182. </view>
  183. </view>
  184. </view>
  185. <view
  186. v-if="detailData.file && detailData.file.length > 0"
  187. class="section-item-content file">
  188. <wk-file-content
  189. :list="detailData.file"
  190. show-delete
  191. @delete="handleToDeleteFile" />
  192. </view>
  193. </view>
  194. <view
  195. class="section comment-section">
  196. <view class="cell-box">
  197. <view class="cell-box-left">
  198. <image :src="$static('images/oa/log_comment.png')" class="type-icon" />
  199. <text>评论</text>
  200. </view>
  201. </view>
  202. <view class="section-item-content">
  203. <comment-list
  204. v-if="!$isEmpty(replyList)"
  205. :reply-list="replyList"
  206. :show-icon="false"
  207. @choose="handleCommentChoose" />
  208. <view v-else class="empty-reply">
  209. 暂无评论,发表第一条评论吧
  210. </view>
  211. </view>
  212. </view>
  213. <view class="section activity-section">
  214. <view class="cell-box">
  215. <view class="cell-box-left">
  216. <image :src="$static('images/oa/time_arrow.png')" class="type-icon" />
  217. <text>活动</text>
  218. </view>
  219. </view>
  220. <view class="section-item-content">
  221. <record-item
  222. v-for="(v, index) in recordList"
  223. :key="index"
  224. :item="v" />
  225. </view>
  226. </view>
  227. </view>
  228. <add-comment
  229. ref="comment"
  230. v-model="content"
  231. :replay="replayInfo"
  232. @change-user="changeCommentUser"
  233. @submit="submitComment" />
  234. </view>
  235. <uni-popup ref="popup" type="dialog">
  236. <uni-popup-dialog
  237. v-if="showEditPop"
  238. v-model="dialogMsg"
  239. :title="editPopTitle"
  240. :placeholder="editPlaceholder || '请输入内容'"
  241. :required="editPopRequired"
  242. :required-text="editPopRequiredText"
  243. mode="input"
  244. @confirm="handleDialogConfirm" />
  245. <uni-popup-dialog
  246. v-else
  247. :content="dialogMsg"
  248. type="warning"
  249. @confirm="handleDialogConfirm" />
  250. </uni-popup>
  251. <uni-popup ref="popupPicker" type="picker">
  252. <wk-date-picker
  253. v-model="timeValue"
  254. type="date"
  255. @select="handleSelectTime" />
  256. </uni-popup>
  257. <uni-popup ref="popupOptions">
  258. <view class="pop-wrapper">
  259. <view
  260. v-for="(item, index) in popOptions"
  261. :key="index"
  262. class="pop-item"
  263. @click.stop="handlePopChoose(item)">
  264. {{ item.label }}
  265. </view>
  266. </view>
  267. </uni-popup>
  268. </view>
  269. </template>
  270. <script>
  271. import * as TaskAPI from 'API/oa/task'
  272. import {QueryCommentList, SetComment} from 'API/oa/comment'
  273. import { FileDeleteById } from 'API/file'
  274. import CommentList from '@/components/base/comment-list.vue'
  275. import RelevanceSection from '@/components/base/relevance-section.vue'
  276. import AddComment from '@/components/base/add-comment.vue'
  277. import ChildTaskItem from './components/childTaskItem.vue'
  278. import RecordItem from './components/recordItem.vue'
  279. import moment from 'moment'
  280. import { deepCopy } from '@/utils/lib.js'
  281. import WkFile from '@/utils/file.js'
  282. export default {
  283. name: 'TaskDetail',
  284. filters: {
  285. formatTime(date) {
  286. if (!date) return ''
  287. return moment(date).format('YYYY-MM-DD')
  288. }
  289. },
  290. components: {
  291. CommentList,
  292. RecordItem,
  293. RelevanceSection,
  294. ChildTaskItem,
  295. AddComment
  296. },
  297. data() {
  298. return {
  299. guid: null,
  300. id: null,
  301. routerQuery: {},
  302. refreshPage: false,
  303. detailData: null,
  304. fileBatchId: null,
  305. childTaskList: [], // 子任务
  306. recordList: [], // 操作记录
  307. replyList: [], // 评论
  308. replayInfo: null,
  309. content: '',
  310. commandList: [
  311. {label: '删除', value: 'delete', icon: 'wk-bin', noCheck: true}
  312. ],
  313. priorityLib: [
  314. {value: 3, label: '高', color: '#E60000'},
  315. {value: 2, label: '中', color: '#FFA200'},
  316. {value: 1, label: '低', color: '#5DBC47'},
  317. {value: 0, label: '无', color: '#FFFFFF'},
  318. ],
  319. popOptions: [],
  320. dialogMsg: '', // popup内容信息
  321. dialogConfig: {}, // / popup额外参数
  322. showEditPop: false, // popup是否显示编辑
  323. editPopTitle: '', // 编辑标题
  324. editPlaceholder: '', // 输入框提示信息
  325. editPopRequired: false, // 输入框是否必填
  326. editPopRequiredText: '', // 输入框必填验证出错提示信息
  327. timeValue: null, // 时间选择
  328. allTime: {}
  329. }
  330. },
  331. computed: {
  332. endTimeText() {
  333. if (!this.detailData || !this.detailData.stopTime) return '截止时间'
  334. return moment(this.detailData.stopTime).format('MM-DD截止')
  335. },
  336. priorityObj() {
  337. if (!this.detailData) return null
  338. return this.priorityLib.find(o => o.value === this.detailData.priority) || null
  339. },
  340. relevanceData() {
  341. if (!this.detailData) return {}
  342. return {
  343. customerList: this.detailData.customerList || [],
  344. contactsList: this.detailData.contactsList || [],
  345. businessList: this.detailData.businessList || [],
  346. contractList: this.detailData.contractList || []
  347. }
  348. },
  349. showRelevance() {
  350. if (!this.detailData) return false
  351. return (this.detailData.customerList || []).length > 0 ||
  352. (this.detailData.contactsList || []).length > 0 ||
  353. (this.detailData.businessList || []).length > 0 ||
  354. (this.detailData.contractList || []).length > 0
  355. }
  356. },
  357. created() {
  358. this.guid = this.$guid()
  359. },
  360. onLoad(options) {
  361. this.routerQuery = options
  362. this.id = options.id
  363. if (this.id) {
  364. this.getDetail()
  365. this.getCommentList()
  366. this.getPopOptions()
  367. }
  368. },
  369. onShow() {
  370. if (this.refreshPage) {
  371. this.$nextTick(function() {
  372. this.refreshPage = false
  373. this.getDetail()
  374. })
  375. }
  376. },
  377. methods: {
  378. /**
  379. * 获取任务详情
  380. */
  381. getDetail() {
  382. TaskAPI.QueryTaskInfo({
  383. taskId: this.id,
  384. }).then(res => {
  385. this.allTime = {
  386. startTime: res.startTime || '',
  387. stopTime: res.stopTime || ''
  388. }
  389. this.detailData = res
  390. this.fileBatchId = res.batchId
  391. this.childTaskList = this.detailData.childTask || []
  392. this.getRecordList()
  393. }).catch()
  394. },
  395. /**
  396. * 获取评论列表
  397. */
  398. getCommentList() {
  399. QueryCommentList({
  400. typeId: this.id,
  401. type: 1
  402. }).then(res => {
  403. this.replyList = res
  404. }).catch()
  405. },
  406. /**
  407. * 修改记录列表
  408. */
  409. getRecordList() {
  410. TaskAPI.TaskLog({
  411. taskId: this.id,
  412. }).then(response => {
  413. this.recordList = response
  414. }).catch()
  415. },
  416. /**
  417. * 根据权限筛选出可选关联项
  418. */
  419. getPopOptions() {
  420. const map = {
  421. customer: this.$auth('crm.customer.index'),
  422. contacts: this.$auth('crm.contacts.index'),
  423. business: this.$auth('crm.business.index'),
  424. contract: this.$auth('crm.contract.index')
  425. }
  426. const options = [
  427. { label: '客户', value: 'customer' },
  428. { label: '联系人', value: 'contacts' },
  429. { label: '商机', value: 'business' },
  430. { label: '合同', value: 'contract' }
  431. ]
  432. this.popOptions = options.filter(o => {
  433. return map[o.value]
  434. })
  435. },
  436. /**
  437. * 操作
  438. * @param {String} command
  439. */
  440. handleCommand(command) {
  441. this.showEditPop = false
  442. const value = command.value || command
  443. const that = this
  444. const bridge = getApp().globalData.selectedValBridge
  445. this.dialogConfig = { command: value }
  446. switch (value) {
  447. case 'delete':
  448. this.dialogMsg = '您确定要删除该任务吗?'
  449. this.dialogConfig.params = {
  450. taskId: this.id
  451. }
  452. break
  453. case 'status':
  454. this.dialogMsg = '你确定要更改任务的状态吗?'
  455. break
  456. case 'name':
  457. this.showEditPop = true
  458. this.editPopTitle = '任务名称'
  459. this.editPlaceholder = '请输入任务名称'
  460. this.editPopRequired = true
  461. this.editPopRequiredText = '任务名称不能为空'
  462. this.dialogMsg = this.detailData.name
  463. break
  464. case 'description':
  465. this.showEditPop = true
  466. this.editPopTitle = '任务描述'
  467. this.editPlaceholder = '请输入任务描述'
  468. this.editPopRequired = false
  469. this.dialogMsg = this.detailData.description
  470. break
  471. case 'mainUserId':
  472. bridge.user = {
  473. guid: this.guid,
  474. title: '选择负责人',
  475. defaultVal: this.detailData.mainUser ? [this.detailData.mainUser] : [],
  476. maxlength: 1,
  477. config: {
  478. checkFn: ({ data }) => {
  479. if (data.length === 0) {
  480. this.$toast('请选择负责人')
  481. return false
  482. }
  483. if (
  484. that.detailData.mainUser &&
  485. data[0].userId == that.detailData.mainUser.userId
  486. ) {
  487. this.$toast('已经是负责人啦')
  488. return false
  489. }
  490. return true
  491. }
  492. }
  493. }
  494. uni.$on('selected-user', this.selectedUser)
  495. this.$Router.navigateTo('/pages_common/selectList/user')
  496. return
  497. case 'ownerUserId':
  498. // eslint-disable-next-line no-case-declarations
  499. const list = this.detailData.ownerUserList || []
  500. bridge.user = {
  501. guid: this.guid,
  502. title: '选择参与人',
  503. defaultVal: list
  504. }
  505. uni.$on('selected-user', this.selectedUser)
  506. this.$Router.navigateTo('/pages_common/selectList/user')
  507. return
  508. case 'startTime':
  509. this.timeValue = this.detailData.startTime
  510. this.$refs.popupPicker.open()
  511. return
  512. case 'stopTime':
  513. this.timeValue = this.detailData.stopTime
  514. this.$refs.popupPicker.open()
  515. return
  516. case 'priority':
  517. bridge.options = {
  518. guid: this.guid,
  519. title: '选择优先级',
  520. list: this.priorityLib,
  521. defaultVal: this.priorityLib.filter(o => o.value === this.detailData.priority),
  522. maxlength: 1,
  523. config: {
  524. checkFn: ({ data }) => {
  525. if (data.length === 0) {
  526. this.$toast('请选择优先级')
  527. return false
  528. }
  529. return true
  530. }
  531. }
  532. }
  533. uni.$on('selected-options', this.selectedOptions)
  534. this.$Router.navigateTo('/pages_common/selectList/options')
  535. return
  536. case 'labelId':
  537. bridge.options = {
  538. guid: this.guid,
  539. title: '选择标签',
  540. request: TaskAPI.GetLabelList,
  541. defaultVal: this.detailData.labelList,
  542. config: {
  543. labelField: 'name',
  544. valueField: 'labelId'
  545. }
  546. }
  547. uni.$on('selected-options', this.selectedOptions)
  548. this.$Router.navigateTo('/pages_common/selectList/options')
  549. return
  550. case 'relevance':
  551. this.$refs.popupOptions.open()
  552. return
  553. }
  554. this.$refs.popup.open()
  555. },
  556. /**
  557. * popup点击确定
  558. */
  559. handleDialogConfirm(next, inputValue = null) {
  560. const value = this.dialogConfig.command
  561. const params = this.dialogConfig.params
  562. switch (value) {
  563. case 'delete':
  564. this.deleteTask(params)
  565. break
  566. case 'status':
  567. this.updateTask('status', this.detailData.status === 5 ? 1 : 5)
  568. break
  569. case 'name':
  570. this.updateTask('name', inputValue)
  571. break
  572. case 'description':
  573. this.updateTask('description', inputValue)
  574. break
  575. case 'deleteFile':
  576. this.deleteFile(params)
  577. break
  578. case 'deleteRelevance':
  579. // eslint-disable-next-line no-case-declarations
  580. const data = deepCopy(this.relevanceData)
  581. data[`${params.type}List`].splice(params.index, 1)
  582. this.updateTaskRelevance(data)
  583. break
  584. case 'deleteChild':
  585. this.deleteTask(params)
  586. break
  587. case 'statusChild':
  588. this.updateChildStatus(params)
  589. break
  590. }
  591. next()
  592. },
  593. /**
  594. * 选择负责人/参与人
  595. * @param {Object} data
  596. */
  597. selectedUser(data) {
  598. if (this.guid === data.guid) {
  599. this.$nextTick(function() {
  600. const command = this.dialogConfig.command
  601. if (command === 'mainUserId') {
  602. this.updateTask(command, data.data[0].userId)
  603. } else if (command === 'ownerUserId') {
  604. this.updateTask(command, data.data.map(o => o.userId).join(','))
  605. }
  606. })
  607. }
  608. uni.$off('selected-user')
  609. },
  610. /**
  611. * 选择开始时间/结束时间
  612. * @param {Object} date
  613. * @param {Object} next
  614. */
  615. handleSelectTime(date, next) {
  616. next()
  617. const field = this.dialogConfig.command
  618. if (field === 'startTime' &&
  619. date &&
  620. this.detailData.stopTime) {
  621. if (moment(this.detailData.stopTime).isBefore(date)) {
  622. this.$toast('开始时间不能大于结束时间')
  623. return
  624. }
  625. }
  626. if (field === 'stopTime' &&
  627. date &&
  628. this.detailData.startTime) {
  629. if (moment(date).isBefore(this.detailData.startTime)) {
  630. this.$toast('开始时间不能大于结束时间')
  631. return
  632. }
  633. }
  634. if (field === 'startTime') {
  635. this.allTime.startTime = date
  636. } else if (field === 'stopTime') {
  637. this.allTime.stopTime = date
  638. }
  639. this.updateTask(field, date)
  640. },
  641. /**
  642. * 选择标签/优先级
  643. * @param {Object} data
  644. */
  645. selectedOptions(data) {
  646. if (this.guid === data.guid) {
  647. const command = this.dialogConfig.command
  648. if (command === 'labelId') {
  649. this.updateTask(command, data.data.map(o => o.labelId).join(','))
  650. } else if (command === 'priority') {
  651. this.updateTask(command, data.data[0].value)
  652. }
  653. }
  654. uni.$off('selected-options')
  655. },
  656. /**
  657. * 去选择关联项
  658. * @param {Object} opt
  659. */
  660. handlePopChoose(opt) {
  661. this.$refs.popupOptions.close()
  662. const data = deepCopy(this.relevanceData[`${opt.value}List`])
  663. const map = {
  664. customer: {labelField: 'customerName', valueField: 'customerId', title: '客户'},
  665. contacts: {labelField: 'name', valueField: 'contactsId', title: '联系人'},
  666. business: {labelField: 'businessName', valueField: 'businessId', title: '商机'},
  667. contract: {labelField: 'name', valueField: 'contractId', title: '合同'}
  668. }
  669. const bridge = getApp().globalData.selectedValBridge
  670. bridge[opt.value] = {
  671. guid: this.guid,
  672. defaultVal: data.map(o => {
  673. return {
  674. [`${opt.value}Id`]: o.id || o[`${opt.value}Id`],
  675. [map[opt.value].labelField]: o.name || o[map[opt.value].labelField]
  676. }
  677. })
  678. }
  679. uni.$on('selected-relevance', this.selectedRelevance)
  680. this.$Router.navigateTo({
  681. url: '/pages_common/selectList/relevance',
  682. query: {
  683. type: opt.value
  684. }
  685. })
  686. },
  687. /**
  688. * 选择关联项回调
  689. * @param {Object} data
  690. */
  691. selectedRelevance(data) {
  692. if (this.guid === data.guid) {
  693. let relevance = deepCopy(this.relevanceData)
  694. relevance = {
  695. ...relevance,
  696. [`${data.type}List`]: data.data
  697. }
  698. this.updateTaskRelevance(relevance)
  699. }
  700. uni.$off('selected-relevance')
  701. },
  702. /**
  703. * 删除关联项
  704. * @param {String} type
  705. * @param {Number} index
  706. */
  707. handleToDeleteRelevance(type, index) {
  708. this.showEditPop = false
  709. this.dialogMsg = '您确定要取消关联吗?'
  710. this.dialogConfig = {
  711. command: 'deleteRelevance',
  712. params: {
  713. type,
  714. index
  715. }
  716. }
  717. this.$refs.popup.open()
  718. },
  719. /**
  720. * 更新任务的关联业务
  721. */
  722. updateTaskRelevance(data) {
  723. const keys = [
  724. 'customer',
  725. 'contacts',
  726. 'business',
  727. 'contract'
  728. ]
  729. const params = {
  730. taskId: this.id
  731. }
  732. keys.forEach(key => {
  733. params[`${key}Ids`] = data[`${key}List`].map(o => {
  734. return o.hasOwnProperty('id') ? o.id : o[`${key}Id`]
  735. }).join(',')
  736. })
  737. TaskAPI.TaskRelation(params).then(() => {
  738. this.$toast('修改成功')
  739. this.refreshPrev()
  740. this.getDetail()
  741. }).catch()
  742. },
  743. /**
  744. * 添加附件
  745. */
  746. handleAddFile() {
  747. const params = {}
  748. if (this.fileBatchId) {
  749. params.batchId = this.fileBatchId
  750. }
  751. const fileUpload = new WkFile(params)
  752. fileUpload.choose().then(data => {
  753. console.log('upload res: ', data)
  754. const fileList = this.detailData.file.concat(data)
  755. this.refreshPrev()
  756. this.$set(this.detailData, 'file', fileList)
  757. })
  758. },
  759. /**
  760. * 点击删除附件
  761. * @param {Number} index
  762. */
  763. handleToDeleteFile(index) {
  764. this.showEditPop = false
  765. this.dialogMsg = '您确定要删除该文件吗?'
  766. this.dialogConfig = {
  767. command: 'deleteFile',
  768. params: index
  769. }
  770. this.$refs.popup.open()
  771. },
  772. /**
  773. * 删除附件
  774. * @param {Object} index
  775. */
  776. deleteFile(index) {
  777. const fileId = this.detailData.file[index].fileId
  778. FileDeleteById({id: fileId}).then(() => {
  779. this.detailData.file.splice(index, 1)
  780. this.getRecordList()
  781. }).catch()
  782. },
  783. /**
  784. * 添加子任务
  785. */
  786. handleAddChildTask() {
  787. this.$Router.navigateTo({
  788. url: '/pages_task/add',
  789. query: {
  790. pid: this.id
  791. }
  792. })
  793. },
  794. /**
  795. * 去修改子任务状态
  796. * @param {Object} data
  797. */
  798. childTaskStatus(data) {
  799. this.dialogMsg = '您确定要更改该子任务的状态吗?'
  800. this.dialogConfig = {
  801. command: 'statusChild',
  802. params: data
  803. }
  804. this.$refs.popup.open()
  805. },
  806. /**
  807. * 修改子任务的状态
  808. * @param {Object} params
  809. */
  810. updateChildStatus(params) {
  811. TaskAPI.SetWorkChildTaskStatus({
  812. taskId: params.taskId,
  813. status: params.status
  814. }).then(() => {
  815. this.$toast('修改成功')
  816. const findIndex = this.detailData.childTask.findIndex(o => o.taskId === params.taskId)
  817. if (findIndex !== -1) {
  818. this.detailData.childTask[findIndex].status = params.status
  819. this.$set(this.detailData, 'childTask', this.detailData.childTask)
  820. } else {
  821. this.getDetail()
  822. }
  823. this.getRecordList()
  824. }).catch()
  825. },
  826. /**
  827. * 删除子任务
  828. * @param {String|Number} taskId
  829. */
  830. childTaskDelete(taskId) {
  831. this.dialogMsg = '您确定要删除该子任务吗?'
  832. this.dialogConfig = {
  833. command: 'deleteChild',
  834. params: {
  835. taskId: taskId
  836. }
  837. }
  838. this.$refs.popup.open()
  839. },
  840. /**
  841. * 删除任务
  842. */
  843. deleteTask(params) {
  844. TaskAPI.DeleteTask(params).then(() => {
  845. this.$toast('删除成功')
  846. if (this.dialogConfig.command === 'deleteChild') {
  847. this.refreshPrev()
  848. this.getDetail()
  849. this.getRecordList()
  850. } else {
  851. this.$refreshAndToPrev(this)
  852. }
  853. })
  854. },
  855. /**
  856. * 选择回复人
  857. * @param {Object} user
  858. */
  859. handleCommentChoose(user) {
  860. console.log('user--', user)
  861. if (this.replayInfo) {
  862. let str = `回复@${this.replayInfo.user.realname} `
  863. if (this.content.startsWith(str)) {
  864. this.content = this.content.replace(str, `回复@${user.user.realname} `)
  865. } else {
  866. this.content = `回复@${user.user.realname} ` + this.content
  867. }
  868. } else {
  869. this.content = `回复@${user.user.realname} ` + this.content
  870. }
  871. this.replayInfo = user
  872. this.$refs.comment.focus()
  873. },
  874. changeCommentUser(data) {
  875. this.replayInfo = data
  876. },
  877. /**
  878. * 提交回复
  879. */
  880. submitComment() {
  881. let params = {
  882. type: 1,
  883. typeId: this.id,
  884. content: this.content
  885. }
  886. if (this.replayInfo) {
  887. params.pid = this.replayInfo.userId; // 被回复人的id
  888. params.mainId = this.replayInfo.mainId != 0 ? this.replayInfo.mainId : this.replayInfo.commentId; // 回复主id
  889. let str = `回复@${this.replayInfo.user.realname} `
  890. if (params.content.startsWith(str)) {
  891. params.content = params.content.replace(str, '')
  892. }
  893. }
  894. if (this.$isEmpty(params.content)) {
  895. this.$toast('回复内容不能为空')
  896. return
  897. }
  898. console.log('回复:', params)
  899. SetComment(params).then(() => {
  900. this.$toast('回复成功')
  901. this.content = ''
  902. this.replayInfo = null
  903. this.refreshPrev()
  904. this.getCommentList()
  905. this.getRecordList()
  906. })
  907. },
  908. /**
  909. * 编辑保存任务基本信息
  910. * @param {Object} key
  911. * @param {Object} value
  912. */
  913. updateTask(key, value) {
  914. let params = {}
  915. if (key === 'startTime' || key === 'stopTime') {
  916. params = {
  917. taskId: this.id,
  918. startTime: this.allTime.startTime.split(' ')[0] || '',
  919. stopTime: this.allTime.stopTime.split(' ')[0] || ''
  920. }
  921. } else {
  922. params = {
  923. taskId: this.id,
  924. [key]: value
  925. }
  926. }
  927. if (key === 'mainUserId') {
  928. params.userId = value
  929. delete params.mainUserId
  930. }
  931. if (!this.detailData.startTime) {
  932. this.detailData.startTime = ''
  933. }
  934. if (!this.detailData.stopTime) {
  935. this.detailData.stopTime = ''
  936. }
  937. if ((key === 'startTime' || key === 'stopTime') &&
  938. (this.detailData.startTime.split(' ')[0] ===
  939. this.allTime.startTime.split(' ')[0]) &&
  940. this.detailData.stopTime === this.allTime.stopTime) return
  941. const requestMap = {
  942. name: TaskAPI.SetWorkTaskTitle,
  943. description: TaskAPI.SetWorkTaskDescription,
  944. mainUserId: TaskAPI.SetWorkTaskMainUser,
  945. ownerUserId: TaskAPI.SetWorkTaskOwnerUser,
  946. labelId: TaskAPI.SetWorkTaskLabel,
  947. priority: TaskAPI.SetWorkTaskPriority,
  948. status: TaskAPI.SetWorkTaskStatus,
  949. startTime: TaskAPI.SetWorkTaskTime,
  950. stopTime: TaskAPI.SetWorkTaskTime
  951. }
  952. const request = requestMap[key]
  953. if (!request) return
  954. request(params).then(() => {
  955. this.$toast('修改成功')
  956. this.refreshPrev()
  957. this.getDetail()
  958. }).catch(() => {})
  959. },
  960. refreshPrev() {
  961. this.$refreshAndToPrev(this, false)
  962. }
  963. }
  964. }
  965. </script>
  966. <style scoped lang="scss">
  967. .header-top {
  968. position: relative;
  969. width: 100%;
  970. padding-bottom: 30rpx;
  971. .bg {
  972. position: absolute;
  973. top: 0;
  974. left: 0;
  975. width: 100%;
  976. height: 160rpx;
  977. }
  978. .card {
  979. position: relative;
  980. width: calc(100% - 64rpx);
  981. border-radius: 10rpx;
  982. background-color: white;
  983. box-shadow: 0 14rpx 16rpx 0 rgba(28,108,255,0.10);
  984. padding: 10rpx;
  985. margin: 0 auto;
  986. }
  987. }
  988. .main-container {
  989. flex: 1;
  990. overflow: hidden;
  991. .container {
  992. width: 100%;
  993. height: 100%;
  994. overflow: auto;
  995. padding-bottom: 110rpx;
  996. .header-top {
  997. .task-name {
  998. width: 100%;
  999. font-size: 32rpx;
  1000. padding: 20rpx 30rpx;
  1001. overflow: hidden;
  1002. display: flex;
  1003. align-items: flex-start;
  1004. justify-content: center;;
  1005. .check-box {
  1006. width: 40rpx;
  1007. height: 40rpx;
  1008. border: 1rpx solid #ccc;
  1009. border-radius: 6rpx;
  1010. overflow: hidden;
  1011. margin-right: 25rpx;
  1012. margin-top: 5rpx;
  1013. @include center;
  1014. .icon {
  1015. font-size: 28rpx;
  1016. line-height: 1;
  1017. color: white;
  1018. }
  1019. }
  1020. .text {
  1021. flex: 1;
  1022. }
  1023. &.active {
  1024. .check-box {
  1025. background-color: $theme-color;
  1026. border-color: $theme-color;
  1027. }
  1028. .text {
  1029. text-decoration: line-through;
  1030. }
  1031. }
  1032. }
  1033. .info-cell {
  1034. margin: 20rpx 0;
  1035. @include center;
  1036. .info-cell-item {
  1037. width: 48%;
  1038. @include center;
  1039. .text {
  1040. font-size: $wk-font-medium;
  1041. }
  1042. }
  1043. .avatar, .icon-box {
  1044. width: 60rpx;
  1045. height: 60rpx;
  1046. border-radius: 50%;
  1047. margin-right: 15rpx;
  1048. }
  1049. .empty-user {
  1050. border: 1rpx dashed #C0C0C0;
  1051. @include center;
  1052. .wk-plus {
  1053. font-size: $wk-font-base;
  1054. color: $light;
  1055. }
  1056. }
  1057. .icon-box {
  1058. background-color: $theme-color;
  1059. color: white;
  1060. font-size: $wk-font-large;
  1061. @include center;
  1062. }
  1063. .time-box {
  1064. font-size: $wk-font-mini;
  1065. color: $light;
  1066. background-color: #F1F1F1;
  1067. border-radius: 8rpx;
  1068. padding: 5rpx 10rpx;
  1069. }
  1070. }
  1071. }
  1072. .section {
  1073. width: 100%;
  1074. background-color: white;
  1075. padding: 15rpx 36rpx;
  1076. margin-top: 10rpx;
  1077. }
  1078. .task-section {
  1079. border-radius: 20rpx 20rpx 0 0;
  1080. margin-top: 20rpx;
  1081. padding-bottom: 0;
  1082. .task-header {
  1083. font-size: $wk-font-large;
  1084. color: $dark;
  1085. font-weight: 500;
  1086. padding: 0 15rpx 15rpx 15rpx;
  1087. .wk {
  1088. font-weight: bold;
  1089. margin-right: 15rpx;
  1090. }
  1091. }
  1092. .cell {
  1093. border-bottom: 1rpx solid $border-color;
  1094. padding: 15rpx;
  1095. &:last-child {
  1096. border: 0 none;
  1097. }
  1098. .cell-title {
  1099. font-size: $wk-font-base;
  1100. color: $light;
  1101. margin-bottom: 10rpx;
  1102. }
  1103. .cell-body {
  1104. min-height: 45rpx;
  1105. flex-wrap: wrap;
  1106. @include left;
  1107. .tag-item {
  1108. font-size: 24rpx;
  1109. color: white;
  1110. border-radius: 6rpx;
  1111. padding: 5rpx 20rpx;
  1112. margin: 8rpx;
  1113. &:first-child {
  1114. margin-left: 0;
  1115. }
  1116. }
  1117. .avatar, .priority-item {
  1118. width: 60rpx;
  1119. height: 60rpx;
  1120. border-radius: 50%;
  1121. margin: 8rpx;
  1122. }
  1123. .priority-item {
  1124. color: white;
  1125. font-size: $wk-font-base;
  1126. @include center;
  1127. }
  1128. }
  1129. }
  1130. }
  1131. .comment-section, .activity-section {
  1132. .cell-box {
  1133. border-bottom: 1rpx solid $border-color;
  1134. padding-bottom: 20rpx;
  1135. margin-bottom: 15rpx;
  1136. }
  1137. }
  1138. .comment-section {
  1139. .cell-box {
  1140. border-bottom: 1rpx solid $border-color;
  1141. padding-bottom: 20rpx;
  1142. margin-bottom: 20rpx;
  1143. }
  1144. .section-item-content {
  1145. .empty-reply {
  1146. width: 100%;
  1147. font-size: $wk-font-sm;
  1148. color: $light;
  1149. text-align: center;
  1150. padding: 50rpx 0 70rpx;
  1151. }
  1152. }
  1153. }
  1154. .cell-box {
  1155. padding: 8rpx 0;
  1156. @include left;
  1157. .cell-box-left {
  1158. flex: 1;
  1159. font-size: $wk-font-medium;
  1160. @include left;
  1161. .type-icon {
  1162. width: 38rpx;
  1163. height: 38rpx;
  1164. margin-right: 15rpx;
  1165. }
  1166. }
  1167. .cell-box-right {
  1168. .add-icon {
  1169. width: 38rpx;
  1170. height: 38rpx;
  1171. background-color: #E1E1E1;
  1172. border-radius: 50%;
  1173. @include center;
  1174. .wk {
  1175. font-size: $wk-font-mini;
  1176. color: white;
  1177. }
  1178. }
  1179. }
  1180. }
  1181. }
  1182. }
  1183. </style>