detail.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. <template>
  2. <div class="j-order-detail">
  3. <!-- 骨架屏 -->
  4. <div class="skeleton" v-if="loading">
  5. <div class="skeleton-main">
  6. <van-skeleton title title-width="100%" />
  7. <van-skeleton title title-width="100%" />
  8. <div class="card-desc">
  9. <div class="card-desc-l">
  10. <van-skeleton avatar avatar-size="55px" avatar-shape="square" />
  11. </div>
  12. <div class="card-desc-r">
  13. <van-skeleton :row="1" />
  14. <van-skeleton :row="1" />
  15. </div>
  16. </div>
  17. <div class="card-container">
  18. <van-skeleton title title-width="100%" />
  19. <van-skeleton title title-width="100%" />
  20. <van-skeleton title title-width="100%" />
  21. <van-skeleton title title-width="100%" />
  22. <van-skeleton title title-width="100%" />
  23. </div>
  24. <div class="card-container">
  25. <van-skeleton title title-width="100%" />
  26. <van-skeleton title title-width="100%" />
  27. <van-skeleton title title-width="100%" />
  28. <van-skeleton title title-width="100%" />
  29. </div>
  30. </div>
  31. <div class="skeleton-bottom">
  32. <van-skeleton title title-width="100%" />
  33. <van-skeleton title title-width="100%" />
  34. <van-skeleton title title-width="100%" />
  35. </div>
  36. </div>
  37. <div class="j-container content" v-else>
  38. <div class="j-main no-scrollbar">
  39. <div>
  40. <div class="header-pic arc-container" :class="orderStateMap[orderInfo.state].bgcClassName">
  41. <div class="order-state">{{ orderStateMap[orderInfo.state].text }}</div>
  42. <div class="surplus-time" v-if="orderStateMap[orderInfo.state].surplusTimeShow && orderInfo.surplusTime > 0">
  43. <span>剩余支付时间:</span>
  44. <van-count-down @finish="onCountdowFinish" :time="orderInfo.surplusTime * 1000" />
  45. </div>
  46. </div>
  47. <div class="card-list">
  48. <div class="j-card report-p">
  49. <span class="rp-left">
  50. <img class="card-l-pic" src="@/assets/image/order-shujubaogao.png">
  51. </span>
  52. <span class="rp-right">
  53. <span class="product-type">数据报告</span>
  54. <span class="pay-money">&yen; {{ orderInfo.payMoney | fen2Yuan | formatPrice(-1, true) }}</span>
  55. </span>
  56. </div>
  57. <div class="j-card product-info">
  58. <div class="j-card-title">购买信息</div>
  59. <div class="j-card-items">
  60. <div class="j-card-item">
  61. <span class="card-item-l">报告名称:</span>
  62. <span class="card-item-r">{{ productInfo.productName }}</span>
  63. </div>
  64. <div class="j-card-item">
  65. <span class="card-item-l">邮箱地址:</span>
  66. <span class="card-item-r">{{ productInfo.email }}</span>
  67. </div>
  68. <div class="j-card-item">
  69. <span class="card-item-l">手机号:</span>
  70. <span class="card-item-r">{{ productInfo.phone | addSpaceForTel }}</span>
  71. </div>
  72. <div class="j-card-item">
  73. <span class="card-item-l">公司名称:</span>
  74. <span class="card-item-r">{{ productInfo.entName }}</span>
  75. </div>
  76. <div class="j-card-item">
  77. <span class="card-item-l">发布日期:</span>
  78. <span class="card-item-r">{{ productInfo.releaseTime * 1000 | dateFormatter('yyyy/MM/dd') }}</span>
  79. </div>
  80. </div>
  81. </div>
  82. <div class="j-card report-info">
  83. <div class="j-card-title">订单信息</div>
  84. <div class="j-card-items">
  85. <div class="j-card-item">
  86. <span class="card-item-l">订单编号:</span>
  87. <span class="card-item-r">{{ orderInfo.ordercode }}</span>
  88. </div>
  89. <div class="j-card-item">
  90. <span class="card-item-l">下单时间:</span>
  91. <span class="card-item-r">{{ orderInfo.createTime * 1000 | dateFormatter('yyyy/MM/dd HH:mm:ss') }}</span>
  92. </div>
  93. <div class="j-card-item" v-if="orderInfo.payTime">
  94. <span class="card-item-l">支付时间:</span>
  95. <span class="card-item-r">{{ orderInfo.payTime * 1000 | dateFormatter('yyyy/MM/dd HH:mm:ss') }}</span>
  96. </div>
  97. <div class="j-card-item" v-if="orderInfo.payWay">
  98. <span class="card-item-l">支付方式:</span>
  99. <span class="card-item-r">{{ payWayMap[orderInfo.payWay] }}</span>
  100. </div>
  101. <div class="j-card-item" v-if="orderInfo.paymentId">
  102. <span class="card-item-l">支付单号:</span>
  103. <span class="card-item-r">{{ orderInfo.paymentId }}</span>
  104. </div>
  105. <div class="j-card-item">
  106. <span class="card-item-l">产品类型:</span>
  107. <span class="card-item-r">{{ orderInfo.productType }}</span>
  108. </div>
  109. <div class="j-card-item">
  110. <span class="card-item-l">实付金额:</span>
  111. <span class="card-item-r">&yen; {{ orderInfo.payMoney | fen2Yuan | formatPrice(-1, true) }} 元</span>
  112. </div>
  113. </div>
  114. </div>
  115. </div>
  116. </div>
  117. </div>
  118. <div class="j-button-group j-footer" v-if="orderStateMap[orderInfo.state].bottomButton.show">
  119. <button class="j-button-confirm" @click="onConfirm">{{ orderStateMap[orderInfo.state].bottomButton.text }}</button>
  120. </div>
  121. </div>
  122. </div>
  123. </template>
  124. <script lang="ts">
  125. import { Component, Vue } from 'vue-property-decorator'
  126. import { Skeleton, CountDown } from 'vant'
  127. import { mapMutations, mapActions } from 'vuex'
  128. import { dateFormatter } from '@/utils/globalFunctions.ts'
  129. @Component({
  130. name: 'order-detail',
  131. components: {
  132. [Skeleton.name]: Skeleton,
  133. [CountDown.name]: CountDown
  134. },
  135. methods: {
  136. dateFormatter,
  137. ...mapMutations({
  138. updateHeader: 'updateLayoutConfig'
  139. }),
  140. ...mapActions({
  141. getOrderDetail: 'pay/orderDetail',
  142. checkInvokeIsOver: 'pay/invokeIsOver'
  143. })
  144. }
  145. })
  146. export default class OrderDetail extends Vue {
  147. protected updateHeader!: any
  148. protected getOrderDetail!: any
  149. protected checkInvokeIsOver!: any
  150. loading = true
  151. ordercode = ''
  152. orderStateMap = {
  153. 0: {
  154. text: '待付款',
  155. bgcColor: '',
  156. bgcClassName: 'orange',
  157. surplusTimeShow: true,
  158. bottomButton: {
  159. show: true,
  160. url: '',
  161. text: '立即支付'
  162. }
  163. },
  164. 1: {
  165. text: '已完成',
  166. bgcColor: '',
  167. bgcClassName: 'blue',
  168. surplusTimeShow: false,
  169. bottomButton: {
  170. show: true,
  171. url: '',
  172. text: '开发票'
  173. }
  174. },
  175. '-2': {
  176. text: '已取消',
  177. bgcColor: '',
  178. bgcClassName: 'grey',
  179. surplusTimeShow: false,
  180. bottomButton: {
  181. show: false,
  182. url: '',
  183. text: ''
  184. }
  185. }
  186. }
  187. invokeUrlMap = {}
  188. payWayMap = {
  189. wx_js: '微信支付',
  190. wx_app: '微信支付',
  191. ali_app: '支付宝支付',
  192. wx_pc: '微信支付',
  193. ali_pc: '支付宝支付'
  194. }
  195. productInfo = {
  196. picImg: '',
  197. productName: '',
  198. email: '',
  199. phone: '',
  200. entName: '',
  201. releaseTime: ''
  202. }
  203. orderInfo = {
  204. // 0:待支付 1:已完成 -2:已取消 -3:已退款
  205. state: 1,
  206. applybillStatus: 1,
  207. ordercode: '',
  208. createTime: '',
  209. payTime: 0,
  210. payWay: '',
  211. paymentId: '',
  212. productType: '',
  213. payMoney: '',
  214. // 单位: 秒(s)
  215. surplusTime: null
  216. // surplusTime: 60 * 60 * 24 * 3
  217. }
  218. created () {
  219. this.updateHeader({
  220. transparentHeader: true,
  221. titleStyle: {
  222. color: '#fff',
  223. left: '22%'
  224. }
  225. })
  226. }
  227. mounted () {
  228. this.ordercode = this.$route.params.ordercode
  229. this.getInfo()
  230. }
  231. getInfo () {
  232. const toast = this.$toast.loading({
  233. message: '加载中...',
  234. forbidClick: true,
  235. duration: 0
  236. })
  237. this.getOrderDetail({ ordercode: this.ordercode, all: 1 }).then(res => {
  238. if (res.error_code === 0) {
  239. this.loading = false
  240. toast.clear()
  241. if (res.data && Object.keys(res.data).length !== 0) {
  242. // this.productInfo = res.data.productInfo
  243. // this.orderInfo = res.data.orderInfo
  244. if (res.data && res.data.orderInfo && res.data.orderInfo.state === -3) {
  245. res.data.orderInfo.state = -2
  246. }
  247. for (const key in res.data.productInfo) {
  248. this.productInfo[key] = res.data.productInfo[key]
  249. }
  250. for (const key in res.data.orderInfo) {
  251. this.orderInfo[key] = res.data.orderInfo[key]
  252. }
  253. this.correctPageState()
  254. } else {
  255. this.$toast(res.error_msg || '查询订单信息失败')
  256. }
  257. } else if (res.error_code === -1) {
  258. this.$router.replace('/order/deleted')
  259. }
  260. })
  261. }
  262. // 修正页面的展示数据
  263. correctPageState () {
  264. // 已完成支付,并且发票已开
  265. if (this.orderInfo.state === 1) {
  266. if (this.orderInfo.applybillStatus === 0) {
  267. this.orderStateMap[this.orderInfo.state].bottomButton.text = '开发票'
  268. } else {
  269. this.orderStateMap[this.orderInfo.state].bottomButton.text = '查看发票'
  270. }
  271. }
  272. // 设置开发票跳转url
  273. // orderInfo.applybillStatus 0:未开票 1:已开票
  274. this.invokeUrlMap = {
  275. // 0:未开票 -> 去开发票
  276. 0: {
  277. wx: {
  278. can: `/front/invoice/showpage?order_code=${this.ordercode}`,
  279. cannot: '/front/invoice/cantInvoice'
  280. },
  281. app: {
  282. can: `/jyapp/front/invoice/showpage?order_code=${this.ordercode}`,
  283. cannot: '/jyapp/front/cantInvoice'
  284. }
  285. },
  286. // 1:已开票 -> 查看发票
  287. 1: {
  288. wx: {
  289. can: `/front/invoice/check_invoice.html?order_code=${this.ordercode}`
  290. },
  291. app: {
  292. can: `/jyapp/front/invoice/check_invoice.html?order_code=${this.ordercode}`
  293. }
  294. }
  295. }
  296. }
  297. onCountdowFinish () {
  298. console.log('onCountdowFinish')
  299. this.orderInfo.state = -2
  300. }
  301. onConfirm () {
  302. // orderInfo 0:待支付 1:已完成 -2:已取消
  303. const payUrl = {
  304. app: `/pay?ordercode=${this.ordercode}`,
  305. wx: `/weixin/pay/datareport?ordercode=${this.ordercode}`
  306. }
  307. if (this.orderInfo.state === 0) {
  308. // 去支付, 微信需要跳转到指定路径
  309. if (this.$env.isWeiXinBrowser) {
  310. location.href = payUrl[this.$env.platform]
  311. } else {
  312. this.$router.push(payUrl[this.$env.platform])
  313. }
  314. } else if (this.orderInfo.state === 1) {
  315. // 已完成状态是否已开发票
  316. if (this.orderInfo.applybillStatus === 0) {
  317. // 0:未开票 -> 去开发票
  318. this.checkInvokeIsOverFn()
  319. } else {
  320. // 1:已开票 -> 查看发票
  321. location.href = this.invokeUrlMap[this.orderInfo.applybillStatus][this.$env.platform].can
  322. }
  323. }
  324. }
  325. checkInvokeIsOverFn () {
  326. const toast = this.$toast.loading({
  327. message: '加载中...',
  328. forbidClick: true,
  329. duration: 0
  330. })
  331. this.checkInvokeIsOver({ order_code: this.ordercode }).then(res => {
  332. toast.clear()
  333. if (res) {
  334. if (res.status === 1) {
  335. // 可以开发票
  336. location.href = this.invokeUrlMap[this.orderInfo.applybillStatus][this.$env.platform].can
  337. } else {
  338. // 开票时间过期
  339. location.href = this.invokeUrlMap[this.orderInfo.applybillStatus][this.$env.platform].cannot
  340. }
  341. } else {
  342. this.$toast(res.error_msg || '查询发票状态失败')
  343. }
  344. })
  345. }
  346. }
  347. </script>
  348. <style lang="scss">
  349. .j-order-detail {
  350. // wx微信端样式自定义
  351. &.wx {
  352. .content .header-pic {
  353. margin-top: -40px;
  354. &:after {
  355. height: 240px;
  356. top: -20px;
  357. }
  358. }
  359. }
  360. // 骨架屏
  361. .skeleton {
  362. display: flex;
  363. flex-direction: column;
  364. height: 100%;
  365. overflow: hidden;
  366. background-color: #fff;
  367. .skeleton-main {
  368. flex: 1;
  369. overflow-y: scroll;
  370. &>.van-skeleton:nth-of-type(1) {
  371. margin: 100px auto 8px;
  372. width: 18%;
  373. .van-skeleton__title {
  374. height: 30px;
  375. }
  376. }
  377. &>.van-skeleton:nth-of-type(2) {
  378. margin: 0 auto 11px;
  379. width: 45%;
  380. height: 24px;
  381. }
  382. .card-desc,
  383. .card-container {
  384. display: flex;
  385. margin: 0 16Px 16px;
  386. padding: 10px 0;
  387. background-color: #fafafa;
  388. box-shadow: 0px 2px 8px rgba(54, 147, 179, 0.051);
  389. border-radius: 8px;
  390. box-sizing: border-box;
  391. }
  392. .card-desc {
  393. // height: 80px;
  394. .card-desc-l {
  395. .van-skeleton {
  396. padding-right: 0;
  397. }
  398. }
  399. .card-desc-r {
  400. flex: 1;
  401. .van-skeleton {
  402. padding-left: 0;
  403. }
  404. .van-skeleton:nth-of-type(1) {
  405. margin-bottom: 12px;
  406. }
  407. .van-skeleton:nth-of-type(2) {
  408. .van-skeleton__row {
  409. height: 25px;
  410. }
  411. }
  412. }
  413. }
  414. .card-container {
  415. align-items: center;
  416. justify-content: space-around;
  417. flex-direction: column;
  418. .van-skeleton {
  419. width: 90%;
  420. margin: 3px 0;
  421. }
  422. }
  423. &>.card {
  424. margin-bottom: 16px;
  425. .van-skeleton__title {
  426. height: 80px;
  427. box-shadow: 0px 2px 8px rgba(54, 147, 179, 0.051);
  428. border-radius: 8px;
  429. }
  430. }
  431. }
  432. .skeleton-bottom {
  433. padding: 20px 0;
  434. }
  435. }
  436. // content 部分
  437. // 头部使用header-mask实现
  438. // 参考: https://www.sunzhongwei.com/css-div-arc-at-bottom
  439. .arc-container {
  440. position: relative;
  441. &:after {
  442. content: '';
  443. width: 150%;
  444. height: 260px;
  445. position: absolute;
  446. left: 50%;
  447. top: -40px;
  448. background: url(~@/assets/image/order-detail-header-mask3x.png) no-repeat top center;
  449. background-size: contain;
  450. border-radius: 0 0 50% 50%;
  451. transform: translateX(-50%);
  452. }
  453. &.blue:after {
  454. background-color: #2ABED1;
  455. }
  456. &.orange:after {
  457. background-color: #FF9F40;
  458. }
  459. &.grey:after {
  460. background-color: #C0C4CC;
  461. }
  462. &>div {
  463. position: relative;
  464. z-index: 2;
  465. }
  466. // &.blue {
  467. // background: transparent url(~@/assets/image/order-detail-bg-blue2x.png) no-repeat top left;
  468. // background-size: cover;
  469. // }
  470. // &.orange {
  471. // background: transparent url(~@/assets/image/order-detail-bg-orange2x.png) no-repeat top left;
  472. // background-size: cover;
  473. // }
  474. // &.grey {
  475. // background: transparent url(~@/assets/image/order-detail-bg-grey2x.png) no-repeat top left;
  476. // background-size: cover;
  477. // }
  478. }
  479. .content .header-pic {
  480. display: flex;
  481. flex-direction: column;
  482. align-items: center;
  483. justify-content: center;
  484. width: 100%;
  485. height: 220px;
  486. color: #F7F9FA;
  487. box-sizing: border-box;
  488. .order-state {
  489. margin-top: 30px;
  490. margin-bottom: 8px;
  491. font-size: 22px;
  492. line-height: 32px;
  493. }
  494. .surplus-time {
  495. display: flex;
  496. font-size: 14px;
  497. line-height: 20px;
  498. .van-count-down {
  499. color: #F7F9FA;
  500. }
  501. }
  502. }
  503. .card-list {
  504. margin-top: -50px;
  505. .report-p {
  506. position: relative;
  507. flex-direction: row;
  508. align-items: center;
  509. .rp-right {
  510. display: flex;
  511. flex-direction: column;
  512. justify-content: space-between;
  513. margin-left: 12px;
  514. flex: 1;
  515. .product-type {
  516. margin-bottom: 8px;
  517. font-size: 14px;
  518. line-height: 20px;
  519. color: #5F5E64;
  520. word-break: break-all;
  521. }
  522. .pay-money {
  523. font-weight: bold;
  524. font-size: 18px;
  525. line-height: 26px;
  526. color: #171826;
  527. }
  528. }
  529. .card-l-pic {
  530. width: 54px;
  531. height: 54px;
  532. }
  533. }
  534. }
  535. .j-card {
  536. display: flex;
  537. flex-direction: column;
  538. margin: 0 12px 16px;
  539. padding: 16px;
  540. box-shadow: 0px 2px 8px rgba(54, 147, 179, 0.051);
  541. border-radius: 8px;
  542. background-color: #fff;
  543. // &:not(:last-of-type) {
  544. // margin-bottom: 16px;
  545. // }
  546. .j-card-title {
  547. margin-bottom: 6px;
  548. font-weight: bold;
  549. font-size: 18px;
  550. line-height: 26px;
  551. color: #171826;
  552. }
  553. .j-card-item {
  554. position: relative;
  555. display: flex;
  556. padding: 6px 0;
  557. &:not(:last-of-type):after {
  558. content: '';
  559. position: absolute;
  560. left: 0;
  561. bottom: 0;
  562. width: 100%;
  563. height: 1Px;
  564. background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.1) 65%, transparent 35%);
  565. }
  566. .card-item-l {
  567. margin-right: 4px;
  568. font-size: 13px;
  569. line-height: 20px;
  570. color: #5F5E64;
  571. min-width: 66px;
  572. }
  573. .card-item-r {
  574. flex: 1;
  575. font-size: 13px;
  576. line-height: 20px;
  577. color: #171826;
  578. word-break: break-all;
  579. }
  580. }
  581. }
  582. }
  583. </style>