detail.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. <template>
  2. <view v-if="!refreshPage" class="uni-app">
  3. <view class="status-bar" />
  4. <wk-nav-bar
  5. :command-list="commandList"
  6. :refresh-prev="refreshPrevPage"
  7. title="合同详情"
  8. theme="white"
  9. class="nav-bar"
  10. @command="handleCommand" />
  11. <scroll-view
  12. v-if="!$isEmpty(detailData)"
  13. :scroll-y="true"
  14. class="main-container scroll-view-hook"
  15. @scrolltolower="handleScrollTolower">
  16. <view class="header-top">
  17. <view class="bg linear-gradient" />
  18. <view class="card">
  19. <view class="title-cell">
  20. <image :src="$static('images/crm/gray_contract.png')" class="type-icon" />
  21. <view class="title-text">
  22. {{ detailData.name || '' }}
  23. </view>
  24. <view
  25. v-if="showCheckStatus"
  26. :style="{
  27. backgroundColor: statusObj.bg,
  28. color: statusObj.color
  29. }"
  30. class="title-status">
  31. <text
  32. :class="statusObj.icon"
  33. class="wk" />
  34. <text>{{ statusObj.label }}</text>
  35. </view>
  36. </view>
  37. <view class="info-cell">
  38. <view class="info-cell-item">
  39. <text class="name">
  40. 回款进度({{ detailData.receivedProgress }}%)
  41. </text>
  42. <view class="progress">
  43. <view :style="{width: detailData.receivedProgress + '%'}" class="now" />
  44. </view>
  45. </view>
  46. <view class="info-cell-item">
  47. <text class="name">
  48. 合同金额
  49. </text>
  50. <text class="money">
  51. ¥{{ splitNumber(detailData.money) || '--' }}
  52. </text>
  53. </view>
  54. <view class="info-cell-item">
  55. <text class="text first">
  56. 已回款:¥{{ splitNumber(detailData.receivablesMoney) || '0.0' }}
  57. </text>
  58. <text class="text">
  59. 待回款:¥{{ splitNumber(detailData.unreceivedMoney) || '0.0' }}
  60. </text>
  61. </view>
  62. <view class="info-cell-item">
  63. <text class="text first">
  64. 签约时间:{{ detailData.orderDate | formatTime }}
  65. </text>
  66. </view>
  67. </view>
  68. <view class="main-info">
  69. <flow-progress
  70. v-if="detailData"
  71. :type="comType"
  72. :detail-id="id"
  73. :detail-data="detailData" />
  74. </view>
  75. <view
  76. class="relevance-cell"
  77. @click="handleToCustomer">
  78. <image :src="$static('images/application/customer.png')" class="cell-icon" />
  79. <text class="cell-body">
  80. {{ detailData.customerName }}
  81. </text>
  82. <text class="cell-link wk wk-arrow-right" />
  83. </view>
  84. <view
  85. v-if="auditInfo && auditInfo.isCheck === 1"
  86. class="check-box">
  87. <view class="btn success" @click="handleAuditAction('pass')">
  88. <text class="wk wk-check-fill icon" />
  89. <text>通过</text>
  90. </view>
  91. <view class="btn error" @click="handleAuditAction('refuse')">
  92. <text class="wk wk-close-fill icon" />
  93. <text>拒绝</text>
  94. </view>
  95. </view>
  96. </view>
  97. </view>
  98. <view class="wk-tabs">
  99. <view
  100. v-for="(tab, index) in tabs"
  101. :key="index"
  102. :class="{active: activeTab === tab.value}"
  103. class="wk-tab-item"
  104. @click="handleToggleTabs(tab.value)">
  105. {{ tab.label }}
  106. </view>
  107. </view>
  108. <wk-keep-alive v-if="id" v-model="activeTab">
  109. <wk-keep-alive-item>
  110. <detail-activity-list
  111. ref="activity"
  112. :detail-id="id"
  113. :type="comType"
  114. activity-title="合同" />
  115. </wk-keep-alive-item>
  116. <wk-keep-alive-item>
  117. <detail-base-info :detail-id="id" :type="comType" class="detail-container" />
  118. </wk-keep-alive-item>
  119. <wk-keep-alive-item>
  120. <detail-about
  121. :detail-id="id"
  122. :detail-data="detailData"
  123. :batch-id="detailData.batchId"
  124. class="detail-about-container" />
  125. </wk-keep-alive-item>
  126. </wk-keep-alive>
  127. </scroll-view>
  128. <uni-popup ref="popup" type="dialog">
  129. <uni-popup-dialog
  130. :content="dialogMsg"
  131. type="warning"
  132. @confirm="handleDialogConfirm" />
  133. </uni-popup>
  134. </view>
  135. </template>
  136. <script>
  137. import {
  138. QueryById,
  139. ChangeOwnerUser,
  140. DeleteByIds,
  141. DiscardById
  142. } from 'API/crm/contract'
  143. import { QueryExamineRecordList, AuditExamine } from 'API/examine'
  144. import DetailBaseInfo from '../components/detailSection/baseInfo'
  145. import DetailActivityList from '../components/detailSection/activityList'
  146. import DetailAbout from './components/tabsAbout'
  147. import FlowProgress from '../components/customFlow/flowProgress'
  148. import detailMixins from '../mixins/detail.js'
  149. import { mapGetters } from 'vuex'
  150. import { splitNumber } from '@/utils/lib.js'
  151. import moment from 'moment'
  152. export default {
  153. name: 'ContractDetail',
  154. filters: {
  155. formatTime(val) {
  156. if (!val) return '--'
  157. return moment(val).format('YYYY-MM-DD')
  158. }
  159. },
  160. components: {
  161. DetailBaseInfo,
  162. DetailActivityList,
  163. DetailAbout,
  164. FlowProgress
  165. },
  166. mixins: [detailMixins],
  167. data() {
  168. return {
  169. comType: 'crm_contract',
  170. commandList: [],
  171. auditInfo: null
  172. }
  173. },
  174. computed: {
  175. ...mapGetters({
  176. calcStatus: 'base/calcStatus',
  177. userInfo: 'user/userInfo'
  178. }),
  179. showCheckStatus() {
  180. return this.detailData.hasOwnProperty('checkStatus') && this.detailData.examineRecordId
  181. },
  182. statusObj() {
  183. if (!this.showCheckStatus) return {}
  184. return this.calcStatus(this.detailData.checkStatus) || {}
  185. }
  186. },
  187. methods: {
  188. splitNumber(num) {
  189. return splitNumber(num)
  190. },
  191. /**
  192. * 获取详情
  193. */
  194. getDetail() {
  195. QueryById({contractId: this.id}).then(response => {
  196. this.detailData = response
  197. this.getAuditInfo()
  198. }).catch(() => {
  199. this.goBack()
  200. })
  201. },
  202. /**
  203. * 获取审批流
  204. */
  205. getAuditInfo() {
  206. if (!this.detailData.examineRecordId) {
  207. this.auditInfo = null
  208. this.renderCommands()
  209. return
  210. }
  211. QueryExamineRecordList({
  212. recordId: this.detailData.examineRecordId,
  213. ownerUserId: this.detailData.ownerUserId
  214. }).then(res => {
  215. this.auditInfo = res || null
  216. this.renderCommands()
  217. }).catch()
  218. },
  219. /**
  220. * 渲染更多操作选项
  221. */
  222. renderCommands() {
  223. this.commandList = [
  224. {label: '查看审批流', imgIcon: 'read', noCheck: true, value: 'read'},
  225. {label: '转移', imgIcon: 'transfer', auth: 'crm.contract.transfer', value: 'transfer'},
  226. {label: '编辑', imgIcon: 'update', auth: 'crm.contract.update', value: 'update'},
  227. {label: '合同作废', imgIcon: 'discard', auth: 'crm.contract.discard', value: 'discard'},
  228. {label: '删除', imgIcon: 'delete', auth: 'crm.contract.delete', value: 'delete'}
  229. ]
  230. // 判断是否能够撤回审批
  231. let index = this.commandList.findIndex(item => item.label === '撤回审批')
  232. if (this.auditInfo && this.auditInfo.isRecheck === 1 && index === -1) {
  233. this.commandList.splice(1, 0, {
  234. label: '撤回审批',
  235. imgIcon: 'reset',
  236. noCheck: true,
  237. value: 'reset'
  238. })
  239. } else if (this.auditInfo && this.auditInfo.isRecheck !== 1 && index !== -1) {
  240. this.commandList.splice(index, 1)
  241. }
  242. index = null
  243. // 如果没有审批流则删除查看审批流
  244. index = this.commandList.findIndex(item => item.value === 'read')
  245. if (index !== -1 && !this.detailData.examineRecordId) {
  246. this.commandList.splice(index, 1)
  247. }
  248. index = null
  249. const checkStatus = this.detailData.checkStatus
  250. // 判断是否能编辑
  251. index = this.commandList.findIndex(item => item.value === 'update')
  252. if (index !== -1) {
  253. // 审核拒绝 审核撤回 未提交才能编辑
  254. // if (
  255. // this.userInfo.userId !== this.detailData.createUserId ||
  256. // (![2, 4, 5].includes(checkStatus) && this.detailData.examineRecordId)) {
  257. // this.commandList.splice(index, 1)
  258. // }
  259. if (![2, 4, 5].includes(checkStatus) && this.detailData.examineRecordId) {
  260. this.commandList.splice(index, 1)
  261. }
  262. }
  263. index = null
  264. // 判断是否能删除
  265. index = this.commandList.findIndex(item => item.value === 'delete')
  266. if (index !== -1) {
  267. // 已撤回 未提交 作废 并且是超管才能删除
  268. if (!this.detailData.isAdmin === 1 ||
  269. (![4, 5, 8].includes(checkStatus) && this.detailData.examineRecordId)) {
  270. this.commandList.splice(index, 1)
  271. }
  272. }
  273. // 判断是否能作废
  274. index = this.commandList.findIndex(item => item.value === 'discard')
  275. if (index !== -1) {
  276. // 审核通过,有作废权限才能作废合同
  277. if (checkStatus !== 1) {
  278. this.commandList.splice(index, 1)
  279. }
  280. }
  281. // 如果合同已作废
  282. if (checkStatus === 8) {
  283. this.commandList = this.commandList.filter(item => ['read', 'delete'].includes(item.value))
  284. }
  285. },
  286. handleCommand(command) {
  287. this.commandValue = command.value
  288. switch (command.value) {
  289. case 'read':
  290. this.$Router.navigateTo({
  291. url: '/pages_crm/auditList',
  292. query: {
  293. id: this.detailData.examineRecordId,
  294. u_id: this.detailData.ownerUserId
  295. }
  296. })
  297. break
  298. case 'reset':
  299. this.handleAuditAction('cancel')
  300. break
  301. case 'transfer':
  302. this.handleToChangeOwnerUser()
  303. break
  304. case 'discard':
  305. this.dialogMsg = '您确定要作废该合同吗'
  306. this.$refs.popup.open()
  307. break
  308. case 'update':
  309. this.handleEdit()
  310. break
  311. case 'delete':
  312. this.dialogMsg = '您确定要删除该合同吗?'
  313. this.$refs.popup.open()
  314. break
  315. }
  316. },
  317. handleDialogConfirm(next) {
  318. switch (this.commandValue) {
  319. case 'discard':
  320. this.handleDiscard()
  321. break
  322. case 'delete':
  323. this.handleDelete()
  324. break
  325. }
  326. next()
  327. },
  328. /**
  329. * 审核操作
  330. * @param action 审核动作
  331. */
  332. handleAuditAction(action) {
  333. getApp().globalData.auditInfo = this.auditInfo
  334. uni.$once('save-audit', this.handleAudit)
  335. this.$Router.navigateTo({
  336. url: '/pages_common/audit/index',
  337. query: {
  338. type: action
  339. }
  340. })
  341. },
  342. /**
  343. * 审核
  344. * @param form 审核信息
  345. */
  346. handleAudit(form) {
  347. let params = {
  348. typeId: this.id,
  349. recordId: this.detailData.examineRecordId,
  350. ...form
  351. }
  352. // console.log('audit', params)
  353. AuditExamine(params).then(() => {
  354. this.$toast(params.status === 4 ? '撤回成功' : '审核成功')
  355. this.getDetail()
  356. this.refreshPrevPage = true
  357. }).catch(() => {})
  358. },
  359. /**
  360. * 跳转到客户
  361. */
  362. handleToCustomer() {
  363. this.$Router.navigateTo({
  364. url: '/pages_crm/customer/detail',
  365. query: {
  366. id: this.detailData.customerId
  367. }
  368. })
  369. },
  370. /**
  371. * 转移
  372. */
  373. handleTransfer(user) {
  374. let params = {
  375. ids: [Number(this.id)],
  376. ownerUserId: user.userId,
  377. transferType: 1
  378. }
  379. ChangeOwnerUser(params).then(() => {
  380. this.$toast('转移成功')
  381. this.getDetail()
  382. this.$refreshAndToPrev(this)
  383. }).catch(() => {})
  384. },
  385. /**
  386. * 合同作废
  387. */
  388. handleDiscard() {
  389. DiscardById({contractId: this.id}).then(() => {
  390. this.$toast('作废成功')
  391. this.$refreshAndToPrev(this)
  392. }).catch(() => {})
  393. },
  394. /**
  395. * 删除
  396. */
  397. handleDelete() {
  398. DeleteByIds([this.id]).then(() => {
  399. this.$toast('删除成功')
  400. this.$refreshAndToPrev(this)
  401. }).catch(() => {})
  402. }
  403. }
  404. }
  405. </script>
  406. <style scoped lang="scss">
  407. @import "../style/detail.scss";
  408. .info-cell {
  409. padding: 0 32rpx;
  410. margin-top: 20rpx;
  411. .info-cell-item {
  412. margin-bottom: 15rpx;
  413. @include left;
  414. .icon {
  415. width: 36rpx;
  416. height: 36rpx;
  417. vertical-align: middle;
  418. margin-right: 15rpx;
  419. }
  420. .name {
  421. font-size: $wk-font-sm;
  422. font-weight: 500;
  423. color: $dark;
  424. margin-right: 15rpx;
  425. }
  426. .progress {
  427. width: 160rpx;
  428. height: 12rpx;
  429. border-radius: 6rpx;
  430. background-color: #e1e1e1;
  431. margin-left: 10rpx;
  432. overflow: hidden;
  433. .now {
  434. height: 12rpx;
  435. background-color: $theme-color;
  436. }
  437. }
  438. .money {
  439. font-size: $wk-font-sm;
  440. font-weight: bold;
  441. color: $warning;
  442. }
  443. .text {
  444. font-size: $wk-font-mini;
  445. color: $gray;
  446. &.first {
  447. margin-right: 15rpx;
  448. }
  449. }
  450. }
  451. }
  452. </style>