list.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. <template>
  2. <view class="content">
  3. <view class="navbar">
  4. <view v-for="(item, index) in navList" :key="index" class="nav-item" :class="{current: tabCurrentIndex === index}"
  5. @click="tabClick(index)">
  6. {{item.text}}
  7. </view>
  8. </view>
  9. <swiper :current="tabCurrentIndex" class="swiper-box" duration="300" @change="changeTab">
  10. <swiper-item class="tab-content" v-for="(tabItem,tabIndex) in navList" :key="tabIndex">
  11. <scroll-view
  12. class="list-scroll-content"
  13. scroll-y
  14. refresher-enabled
  15. :refresher-triggered="tabItem.refreshing"
  16. @refresherrefresh="onRefresherRefresh(tabIndex)"
  17. @scrolltolower="loadData"
  18. >
  19. <!-- 空白页 -->
  20. <!-- <empty v-if="tabItem.loaded === true && tabItem.orderList.length === 0"></empty> -->
  21. <!-- 订单列表 -->
  22. <view v-for="(item,index) in tabItem.orderList" :key="index" class="order-item">
  23. <navigator :url="'/pages/order/detail?orderid=' + item.id">
  24. <view class="i-top b-b">
  25. <text class="time">{{item.createTime}}</text>
  26. <text class="state">
  27. {{item.shippingType === 2 && item.status === 20 ? '待取货' : item.stateText}}
  28. </text>
  29. </view>
  30. <scroll-view v-if="item.productList.length > 1" class="goods-box" scroll-x>
  31. <view v-for="(skuItem, skuIndex) in item.productList" :key="skuIndex" class="goods-item">
  32. <image class="goods-img" :src="JSON.parse(skuItem.img)[0].url" mode="aspectFill"></image>
  33. </view>
  34. </scroll-view>
  35. <view v-if="item.productList.length === 1" class="goods-box-single"
  36. v-for="(skuItem, skuIndex) in item.productList" :key="skuIndex">
  37. <image class="goods-img" :src="JSON.parse(skuItem.img)[0].url" mode="aspectFill"></image>
  38. <view class="right">
  39. <text class="title clamp">{{skuItem.productTitle}}</text>
  40. <text class="attr-box">{{skuItem.productTitle}} x {{skuItem.num}}</text>
  41. <text class="price">{{skuItem.price}}<text
  42. class="text-gray text-sm">/{{skuItem.unitName}}</text></text>
  43. </view>
  44. </view>
  45. <view class="price-box">
  46. <text class="num">{{item.skuCount}}</text>
  47. 件商品 实付款
  48. <text class="price" v-if="!item.payIntegral">{{item.payPrice}}</text>
  49. <text class="integral" v-if="item.payIntegral">积分{{item.payIntegral}}</text>
  50. </view>
  51. </navigator>
  52. <view class="action-box b-t" v-if="item.status == 10">
  53. <button :disabled="submiting" class="action-btn" @click="cancelOrder(item)">取消订单</button>
  54. <button class="action-btn recom" @click="payOrder(item)">立即支付</button>
  55. </view>
  56. <view class="action-box b-t" v-if="item.state == 1 && !item.payIntegral">
  57. <button :disabled="submiting" class="action-btn" @click="refundOrder(item)">申请退款</button>
  58. </view>
  59. <!-- <view class="action-box b-t" v-if="item.status == 30">
  60. <button :disabled="submiting" class="action-btn" @click="refundOrder(item)">申请退款</button>
  61. <button :disabled="submiting" class="action-btn" @click="showShipTrace(item)">查看物流</button>
  62. <button :disabled="submiting" class="action-btn recom" @click="confirmOrder(item)">确认收货</button>
  63. </view> -->
  64. <view class="action-box b-t" v-if="item.status == 40">
  65. <view>
  66. <button :disabled="submiting" class="action-btn recom" @click="appraiseOrder(item)">立即评价</button>
  67. </view>
  68. </view>
  69. </view>
  70. <view style="padding: 150rpx 0rpx;" v-if="tabItem.loaded === true && tabItem.orderList.length === 0">
  71. <missing v-if="tabItem.loaded === true && tabItem.orderList.length === 0"
  72. :imgUrl="'http://qiniuoss.nauzone.cn/%E7%BB%84%203%20%E6%8B%B7%E8%B4%9D@3x.png'" :desc="'暂时没有订单哦!'">
  73. </missing>
  74. </view>
  75. <uni-load-more v-else :status="tabItem.loadingType"></uni-load-more>
  76. </scroll-view>
  77. </swiper-item>
  78. </swiper>
  79. </view>
  80. </template>
  81. <script>
  82. // import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
  83. import missing from '@/components/missing.vue'
  84. // import empty from "@/components/empty";
  85. export default {
  86. components: {
  87. // uniLoadMore,
  88. missing
  89. // empty
  90. },
  91. data() {
  92. return {
  93. statusMap: {
  94. 10: '未付款',
  95. 12: '正在拼团',
  96. 14: '派单中',
  97. 16: '配货中',
  98. 20: '待出库',
  99. 30: '待收货',
  100. 40: '待评价',
  101. 50: '已完成',
  102. 60: '退款中',
  103. 70: '已退款',
  104. 80: '已取消',
  105. 90: '已取消(系统)'
  106. },
  107. submiting: false,
  108. tabCurrentIndex: 0,
  109. navList: [{
  110. state: "",
  111. text: '全部',
  112. loadingType: 'more',
  113. pageNo: 1,
  114. orderList: [],
  115. refreshing: false // 新增状态变量
  116. },
  117. {
  118. state: "10",
  119. text: '待付款',
  120. loadingType: 'more',
  121. pageNo: 1,
  122. orderList: [],
  123. refreshing: false // 新增状态变量
  124. },
  125. {
  126. state: "14,16,20,30",
  127. text: '已付款',
  128. loadingType: 'more',
  129. pageNo: 1,
  130. orderList: [],
  131. refreshing: false // 新增状态变量
  132. },
  133. {
  134. state: "40,50",
  135. text: '已完成',
  136. loadingType: 'more',
  137. pageNo: 1,
  138. orderList: [],
  139. refreshing: false // 新增状态变量
  140. }
  141. ],
  142. };
  143. },
  144. onLoad(options) {
  145. /**
  146. * 修复app端点击除全部订单外的按钮进入时不加载数据的问题
  147. * 替换onLoad下代码即可
  148. */
  149. this.tabCurrentIndex = 0
  150. for (let i = 0; i < this.navList.length; i++) {
  151. if (i === parseInt(options.state)) {
  152. this.tabCurrentIndex = i
  153. }
  154. }
  155. // #ifndef MP
  156. this.loadData()
  157. // #endif
  158. // #ifdef MP
  159. if (options.state == 0) {
  160. this.loadData()
  161. }
  162. // #endif
  163. },
  164. methods: {
  165. // 下拉刷新事件
  166. onRefresherRefresh(tabIndex) {
  167. const navItem = this.navList[tabIndex];
  168. navItem.refreshing = true; // 触发下拉刷新动画
  169. this.tabCurrentIndex = tabIndex;
  170. this.loadData('refresh');
  171. },
  172. loadData(source) {
  173. const that = this;
  174. let index = that.tabCurrentIndex;
  175. let navItem = that.navList[index];
  176. let state = navItem.state;
  177. if (source === 'tabChange' && navItem.loaded === true) {
  178. return;
  179. }
  180. if (navItem.loadingType === 'loading') {
  181. return;
  182. }
  183. if (source === 'refresh') {
  184. navItem.pageNo = 1;
  185. }
  186. navItem.loadingType = 'loading';
  187. that.$api.request('get', 'order/app/getOrderPage', {
  188. pageNo: navItem.pageNo,
  189. status: navItem.state
  190. }).then(res => {
  191. navItem.loadingType = navItem.pageNo < res.total / navItem.pageNo + (res.total % navItem.pageNo == 0 ? 0 : 1) ? 'more' : 'noMore';
  192. navItem.pageNo = navItem.pageNo + 1;
  193. if (source === 'refresh') {
  194. navItem.orderList = [];
  195. uni.stopPullDownRefresh(); // 确保在这里调用
  196. }
  197. res.rows.forEach(item => {
  198. item.stateText = that.statusMap[item.status] || '';
  199. navItem.orderList.push(item);
  200. item.skuCount = 0;
  201. item.productList.forEach(skuItem => {
  202. item.skuCount += skuItem.num;
  203. });
  204. });
  205. that.$set(navItem, 'loaded', true);
  206. }).catch(err => {
  207. console.error(err);
  208. }).finally(() => {
  209. // 无论成功失败,停止刷新
  210. const navItem = that.navList[index];
  211. navItem.refreshing = false; // 停止下拉刷新动画
  212. navItem.loadingType = 'more';
  213. });
  214. },
  215. //swiper 切换
  216. changeTab(e) {
  217. this.tabCurrentIndex = e.target.current;
  218. this.loadData('tabChange');
  219. },
  220. //顶部tab点击
  221. tabClick(index) {
  222. this.tabCurrentIndex = index;
  223. },
  224. payOrder(item) {
  225. // #ifdef APP-PLUS
  226. this.$api.msg('演示环境不支持支付')
  227. return
  228. // #endif
  229. uni.showLoading({})
  230. const that = this
  231. that.$api.request('get', 'order/app/wxPrepay', {
  232. orderId: item.orderId
  233. }, failres => {
  234. that.submiting = false
  235. that.$api.msg(failres.msg)
  236. }).then(prepayRes => {
  237. uni.hideLoading()
  238. that.submiting = false
  239. //#ifdef MP-WEIXIN
  240. const payParam = {
  241. appId: prepayRes.data.appId,
  242. nonceStr: prepayRes.data.nonceStr,
  243. package: prepayRes.data.packageValue,
  244. timeStamp: prepayRes.data.timeStamp,
  245. signType: prepayRes.data.signType,
  246. paySign: prepayRes.data.paySign,
  247. }
  248. //#endif
  249. //#ifdef APP-PLUS
  250. const payParam = {
  251. appid: prepayRes.data.appId,
  252. noncestr: prepayRes.data.nonceStr,
  253. package: prepayRes.data.packageValue,
  254. partnerid: prepayRes.data.partnerId,
  255. prepayid: prepayRes.data.prepayId,
  256. timestamp: parseInt(prepayRes.data.timeStamp),
  257. sign: prepayRes.data.sign,
  258. signType: 'MD5'
  259. }
  260. //#endif
  261. //#ifdef MP-WEIXIN || APP-PLUS
  262. uni.requestPayment({
  263. provider: 'wxpay',
  264. //#ifdef MP-WEIXIN
  265. ...payParam,
  266. //#endif
  267. //#ifdef APP-PLUS
  268. orderInfo: payParam,
  269. //#endif
  270. success: function(res) {
  271. uni.redirectTo({
  272. url: '/pages/pay/success'
  273. })
  274. },
  275. fail: function(res) {
  276. // console.log("支付过程失败");
  277. // that.$api.msg(JSON.stringify(res))
  278. },
  279. complete: function(res) {
  280. console.log("支付过程结束")
  281. }
  282. });
  283. //#endif
  284. //#ifdef H5
  285. that.$jweixin.chooseWXPay({
  286. nonceStr: prepayRes.data.nonceStr,
  287. timestamp: prepayRes.data.timeStamp,
  288. package: prepayRes.data.packageValue,
  289. signType: prepayRes.data.signType,
  290. paySign: prepayRes.data.paySign,
  291. success: (e) => {
  292. //支付成功
  293. uni.redirectTo({
  294. url: '/pages/pay/success'
  295. })
  296. },
  297. fail: function(res) {
  298. console.log("支付过程失败");
  299. that.$api.msg(JSON.stringify(res))
  300. },
  301. complete: function(res) {
  302. console.log("支付过程结束")
  303. }
  304. })
  305. //#endif
  306. })
  307. },
  308. //取消订单
  309. cancelOrder(item) {
  310. const that = this
  311. uni.showModal({
  312. title: '取消?',
  313. content: '您确定要取消此订单吗?',
  314. success: (e) => {
  315. if (e.confirm) {
  316. that.submiting = true
  317. that.$api.request('get', 'order/app/cancel', {
  318. orderId: item.orderId
  319. }, failres => {
  320. that.submiting = false
  321. that.$api.msg(failres.msg)
  322. }).then(res => {
  323. that.submiting = false
  324. item.status = 80
  325. // this.refresh()
  326. })
  327. }
  328. }
  329. })
  330. },
  331. //订单退款
  332. refundOrder(item) {
  333. const that = this
  334. uni.showModal({
  335. title: '退款?',
  336. content: '您确定要退款吗?',
  337. success: (e) => {
  338. if (e.confirm) {
  339. that.submiting = true
  340. that.$api.request('get', 'order/app/refund', {
  341. orderId: item.orderId
  342. }, failres => {
  343. that.submiting = false
  344. that.$api.msg(failres.msg)
  345. }).then(res => {
  346. that.submiting = false
  347. item.status = 60
  348. this.refresh()
  349. })
  350. }
  351. }
  352. })
  353. },
  354. //确认订单
  355. confirmOrder(item) {
  356. const that = this
  357. uni.showModal({
  358. title: '收货?',
  359. content: '您确定要确认收货吗?',
  360. success: (e) => {
  361. if (e.confirm) {
  362. that.submiting = true
  363. that.$api.request('get', 'order/app/confirm', {
  364. orderId: item.orderId
  365. }, failres => {
  366. that.submiting = false
  367. that.$api.msg(failres.msg)
  368. }).then(res => {
  369. that.submiting = false
  370. item.status = 40
  371. })
  372. }
  373. }
  374. })
  375. },
  376. showShipTrace(item) {
  377. uni.navigateTo({
  378. url: "/pages/order/trace?orderno=" + item.orderId
  379. })
  380. },
  381. //评价订单
  382. appraiseOrder(item) {
  383. uni.navigateTo({
  384. url: '/pages/order/appraise?orderid=' + item.id
  385. })
  386. }
  387. }
  388. }
  389. </script>
  390. <style lang="scss">
  391. page,
  392. .content {
  393. background: $page-color-base;
  394. height: 100%;
  395. }
  396. .swiper-box {
  397. height: calc(100% - 40px);
  398. }
  399. .list-scroll-content {
  400. height: 100%;
  401. }
  402. .navbar {
  403. display: flex;
  404. height: 40px;
  405. padding: 0 5px;
  406. background: #fff;
  407. box-shadow: 0 1px 5px rgba(0, 0, 0, .06);
  408. position: relative;
  409. z-index: 10;
  410. .nav-item {
  411. flex: 1;
  412. display: flex;
  413. justify-content: center;
  414. align-items: center;
  415. height: 100%;
  416. font-size: 15px;
  417. color: $font-color-dark;
  418. position: relative;
  419. &.current {
  420. color: $base-color;
  421. &:after {
  422. content: '';
  423. position: absolute;
  424. left: 50%;
  425. bottom: 0;
  426. transform: translateX(-50%);
  427. width: 44px;
  428. height: 0;
  429. border-bottom: 2px solid $base-color;
  430. }
  431. }
  432. }
  433. }
  434. .uni-swiper-item {
  435. height: auto;
  436. }
  437. .order-item {
  438. display: flex;
  439. flex-direction: column;
  440. padding-left: 30upx;
  441. background: #fff;
  442. margin-top: 16upx;
  443. .i-top {
  444. display: flex;
  445. align-items: center;
  446. height: 80upx;
  447. padding-right: 30upx;
  448. font-size: $font-base;
  449. color: $font-color-dark;
  450. position: relative;
  451. .time {
  452. flex: 1;
  453. }
  454. .state {
  455. color: $base-color;
  456. }
  457. .del-btn {
  458. padding: 10upx 0 10upx 36upx;
  459. font-size: $font-lg;
  460. color: $font-color-light;
  461. position: relative;
  462. &:after {
  463. content: '';
  464. width: 0;
  465. height: 30upx;
  466. border-left: 1px solid $border-color-dark;
  467. position: absolute;
  468. left: 20upx;
  469. top: 50%;
  470. transform: translateY(-50%);
  471. }
  472. }
  473. }
  474. /* 多条商品 */
  475. .goods-box {
  476. height: 160upx;
  477. padding: 20upx 0;
  478. white-space: nowrap;
  479. .goods-item {
  480. width: 120upx;
  481. height: 120upx;
  482. display: inline-block;
  483. margin-right: 24upx;
  484. }
  485. .goods-img {
  486. display: block;
  487. width: 100%;
  488. height: 100%;
  489. }
  490. }
  491. /* 单条商品 */
  492. .goods-box-single {
  493. display: flex;
  494. padding: 20upx 0;
  495. .goods-img {
  496. display: block;
  497. width: 120upx;
  498. height: 120upx;
  499. }
  500. .right {
  501. flex: 1;
  502. display: flex;
  503. flex-direction: column;
  504. padding: 0 30upx 0 24upx;
  505. overflow: hidden;
  506. .title {
  507. font-size: $font-base + 2upx;
  508. color: $font-color-dark;
  509. line-height: 1;
  510. }
  511. .attr-box {
  512. font-size: $font-sm + 2upx;
  513. color: $font-color-light;
  514. padding: 10upx 12upx;
  515. }
  516. .price {
  517. font-size: $font-base + 2upx;
  518. color: $font-color-dark;
  519. &:before {
  520. content: '¥';
  521. font-size: $font-sm;
  522. margin: 0 2upx 0 8upx;
  523. }
  524. }
  525. }
  526. }
  527. .price-box {
  528. display: flex;
  529. justify-content: flex-end;
  530. align-items: baseline;
  531. padding: 20upx 30upx;
  532. font-size: $font-sm + 2upx;
  533. color: $font-color-light;
  534. .num {
  535. margin: 0 8upx;
  536. color: $font-color-dark;
  537. }
  538. .price {
  539. font-size: $font-lg;
  540. color: $font-color-dark;
  541. &:before {
  542. content: '¥';
  543. font-size: $font-sm;
  544. margin: 0 2upx 0 8upx;
  545. }
  546. }
  547. .integral {
  548. font-size: $font-lg;
  549. color: red;
  550. &:before {
  551. font-size: $font-sm;
  552. margin: 0 2upx 0 8upx;
  553. }
  554. }
  555. }
  556. .action-box {
  557. display: flex;
  558. justify-content: flex-end;
  559. align-items: center;
  560. height: 100upx;
  561. position: relative;
  562. padding-right: 30upx;
  563. }
  564. .action-btn {
  565. width: 160upx;
  566. height: 60upx;
  567. margin: 0;
  568. margin-left: 24upx;
  569. padding: 0;
  570. text-align: center;
  571. line-height: 60upx;
  572. font-size: $font-sm + 2upx;
  573. color: $font-color-dark;
  574. background: #fff;
  575. border-radius: 100px;
  576. &:after {
  577. border-radius: 100px;
  578. }
  579. &.recom {
  580. // background: #fff9f9;
  581. color: $base-color;
  582. // &:after {
  583. // border-color: #f7bcc8;
  584. // }
  585. }
  586. }
  587. }
  588. /* load-more */
  589. .uni-load-more {
  590. display: flex;
  591. flex-direction: row;
  592. height: 80upx;
  593. align-items: center;
  594. justify-content: center
  595. }
  596. .uni-load-more__text {
  597. font-size: 28upx;
  598. color: #999
  599. }
  600. .uni-load-more__img {
  601. height: 24px;
  602. width: 24px;
  603. margin-right: 10px
  604. }
  605. .uni-load-more__img>view {
  606. position: absolute
  607. }
  608. .uni-load-more__img>view view {
  609. width: 6px;
  610. height: 2px;
  611. border-top-left-radius: 1px;
  612. border-bottom-left-radius: 1px;
  613. background: #999;
  614. position: absolute;
  615. opacity: .2;
  616. transform-origin: 50%;
  617. animation: load 1.56s ease infinite
  618. }
  619. .uni-load-more__img>view view:nth-child(1) {
  620. transform: rotate(90deg);
  621. top: 2px;
  622. left: 9px
  623. }
  624. .uni-load-more__img>view view:nth-child(2) {
  625. transform: rotate(180deg);
  626. top: 11px;
  627. right: 0
  628. }
  629. .uni-load-more__img>view view:nth-child(3) {
  630. transform: rotate(270deg);
  631. bottom: 2px;
  632. left: 9px
  633. }
  634. .uni-load-more__img>view view:nth-child(4) {
  635. top: 11px;
  636. left: 0
  637. }
  638. .load1,
  639. .load2,
  640. .load3 {
  641. height: 24px;
  642. width: 24px
  643. }
  644. .load2 {
  645. transform: rotate(30deg)
  646. }
  647. .load3 {
  648. transform: rotate(60deg)
  649. }
  650. .load1 view:nth-child(1) {
  651. animation-delay: 0s
  652. }
  653. .load2 view:nth-child(1) {
  654. animation-delay: .13s
  655. }
  656. .load3 view:nth-child(1) {
  657. animation-delay: .26s
  658. }
  659. .load1 view:nth-child(2) {
  660. animation-delay: .39s
  661. }
  662. .load2 view:nth-child(2) {
  663. animation-delay: .52s
  664. }
  665. .load3 view:nth-child(2) {
  666. animation-delay: .65s
  667. }
  668. .load1 view:nth-child(3) {
  669. animation-delay: .78s
  670. }
  671. .load2 view:nth-child(3) {
  672. animation-delay: .91s
  673. }
  674. .load3 view:nth-child(3) {
  675. animation-delay: 1.04s
  676. }
  677. .load1 view:nth-child(4) {
  678. animation-delay: 1.17s
  679. }
  680. .load2 view:nth-child(4) {
  681. animation-delay: 1.3s
  682. }
  683. .load3 view:nth-child(4) {
  684. animation-delay: 1.43s
  685. }
  686. @-webkit-keyframes load {
  687. 0% {
  688. opacity: 1
  689. }
  690. 100% {
  691. opacity: .2
  692. }
  693. }
  694. </style>