settleCenter.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. <template>
  2. <view class="common-page">
  3. <!-- 地址选择区域,点击可触发地址选择 -->
  4. <view class="common-card-box">
  5. <view class="address-box" @click="selectAddress">
  6. <!-- 未选择地址时显示的提示 -->
  7. <view v-if="!mAddress || !mAddress.code" class="address-view">
  8. <text class="t1" style="margin-top: 0;">请选择收货地址</text>
  9. </view>
  10. <!-- 已选择地址时显示具体地址信息 -->
  11. <view v-if="mAddress && mAddress.code" class="address-view">
  12. <text class="t1" space="ensp">
  13. {{ mAddress.province }} {{ mAddress.city }}{{ mAddress.area }}{{ mAddress.address }}
  14. </text>
  15. <text class="t2" space="emsp">{{ mAddress.name }} {{ mAddress.phone }}</text>
  16. </view>
  17. <!-- 右侧箭头图标,表示可以点击展开地址选择 -->
  18. <u-icon name="arrow-right" color="#AEAEAE" size="34rpx"></u-icon>
  19. </view>
  20. </view>
  21. <!-- 显示已选商品列表 -->
  22. <view class="common-card-box">
  23. <view class="goods-list display-flex-center" v-for="(item, index) in goodsList" :key="index">
  24. <!-- 商品图片 -->
  25. <image style="width: 155rpx; height: 155rpx; border-radius: 10rpx;margin-right: 10rpx;flex-shrink: 0;"
  26. :src="item.goods_cover" mode="aspectFill">
  27. </image>
  28. <!-- 商品名称、规格和数量 -->
  29. <view style="width: 100%;">
  30. <view class="display-flex-between goods-name">
  31. <text>{{ item.goods_name }}</text>
  32. <text>¥{{ item.price_selling }}</text>
  33. </view>
  34. <view class="display-flex-between goods-number">
  35. <text class="gray-tag">{{ item.goods_spec }}</text>
  36. <text>×{{ item.stock_sales }}</text>
  37. </view>
  38. </view>
  39. </view>
  40. <!-- 商品总价区域 -->
  41. <view class="goods-total display-flex-center">
  42. <text class="fs13 color-4a mr10">共{{ orderData.number_goods }}件商品</text>
  43. <view class="fs14 color-23 fw500">
  44. 商品总价:
  45. <text class="fs16 fw600 color-red">¥{{ orderData.amount_goods }}</text>
  46. </view>
  47. </view>
  48. </view>
  49. <!-- 运费显示区域 -->
  50. <view class="common-card good-card display-flex-between">
  51. <view>运费</view>
  52. <view>¥{{ orderData.amount_express || 0 }}</view>
  53. </view>
  54. <!-- 优惠券和积分使用区域 -->
  55. <template v-if="pageOptions.isGift != '1' && pageOptions.type != 'msGoods'">
  56. <!-- 优惠券选择 -->
  57. <view class="common-card good-card display-flex-between" @click="choseCoupon">
  58. <view>优惠券</view>
  59. <text v-if="checkedCoupon.coupon_id">{{ checkedCoupon.coupon_price }}</text>
  60. <u-icon v-else name="arrow-right" color="#AEAEAE" size="34rpx"></u-icon>
  61. </view>
  62. <!-- 使用积分抵扣 -->
  63. <view class="common-card good-card display-flex-between" v-if="usePointsFlag">
  64. <view>使用{{ pointsShowData.points }}积分抵扣{{ pointsShowData.price }}元</view>
  65. <u-checkbox-group v-model="usePoints" size="20" activeColor="#F39800">
  66. <u-checkbox :disabled="pointsDisabled" name="points" shape="circle" label=" "></u-checkbox>
  67. </u-checkbox-group>
  68. </view>
  69. </template>
  70. <!-- 留言区域 -->
  71. <view class="common-card good-card">
  72. <view class="fs14 mb10">留言</view>
  73. <u--textarea height="110" v-model="order_remark" placeholder="100字以内" maxlength="100" count></u--textarea>
  74. </view>
  75. <!-- 底部支付区域 -->
  76. <view class="bottom-box display-flex-between">
  77. <view>应付:¥{{ payTotal }}</view>
  78. <view class="common-btn" @click="toPay">去支付</view>
  79. </view>
  80. <!-- 优惠券选择弹窗 -->
  81. <u-popup :show="showCoupon" :round="10" mode="bottom" @close="showCoupon = false">
  82. <view class="coupon-list">
  83. <view class="popop-title text-center mb30">
  84. <text class="color-3 fw700 fs18">选择优惠券</text>
  85. <view class="float-right" @click="showCoupon = false">
  86. <u-icon name="close-circle" color="#8E8E8E" size="28"></u-icon>
  87. </view>
  88. </view>
  89. <!-- 优惠券列表 -->
  90. <couponItem v-for="item in couponList" :key="item.id" :data="item" :config="getItemConfig(item)"
  91. @on-click="handleItem(item)"/>
  92. </view>
  93. </u-popup>
  94. </view>
  95. </template>
  96. <script>
  97. import couponItem from '@/subPages/coupon/couponItem.vue'
  98. export default {
  99. components: {couponItem},
  100. data() {
  101. return {
  102. // 订单编号
  103. orderNo: '',
  104. // 页面选项
  105. pageOptions: {},
  106. // 优惠券弹窗显示标志
  107. showCoupon: false,
  108. // 优惠券列表
  109. couponList: [],
  110. // 收货地址
  111. mAddress: {},
  112. // 使用积分标志
  113. usePoints: [],
  114. // 订单备注
  115. order_remark: '',
  116. // 已选择的优惠券
  117. checkedCoupon: {},
  118. // 商品列表
  119. goodsList: [],
  120. // 订单数据,包括商品总价和运费等
  121. orderData: {amount_goods: 0, amount_express: 0},
  122. // 购物车商品ID列表
  123. cartIds: [],
  124. // 积分相关数据
  125. pointsData: {},
  126. // 积分显示数据
  127. pointsShowData: {price: 0, points: 0},
  128. // 使用积分标志
  129. usePointsFlag: true,
  130. }
  131. },
  132. computed: {
  133. // 计算支付总价
  134. payTotal() {
  135. const {amount_goods = 0, amount_express = 0} = this.orderData
  136. let result = Number(amount_goods) + Number(amount_express)
  137. if (this.checkedCoupon.coupon_id) {
  138. result = result - Number(this.checkedCoupon.coupon_price)
  139. }
  140. if (this.integral == 1) {
  141. result = result - Number(this.pointsShowData.price)
  142. }
  143. return result.toFixed(2)
  144. },
  145. // 计算使用的积分数量
  146. integral() {
  147. return this.usePoints.length
  148. },
  149. // 计算用户可用积分
  150. userPoints() {
  151. const {integral_total = 0, integral_used = 0} = JSON.parse(uni.getStorageSync('userInfo'))
  152. return integral_total - integral_used
  153. },
  154. // 积分使用限制判断
  155. pointsDisabled() {
  156. const {min_money, ratio, to_money} = this.pointsData
  157. return min_money > this.orderData.amount_goods
  158. },
  159. },
  160. watch: {
  161. // 监听积分使用变化
  162. integral(nv) {
  163. if (this.pageOptions.type != 'cart') {
  164. this.$api.orderUserPoints({order_no: this.orderNo, integral: nv})
  165. }
  166. },
  167. // 监听商品总价变化
  168. 'orderData.amount_goods'(nv) {
  169. if (nv) {
  170. if (this.pageOptions.type != 'cart') {
  171. this.$nextTick(() => {
  172. this.getPoints()
  173. })
  174. }
  175. }
  176. }
  177. },
  178. onLoad(options) {
  179. // 页面加载时获取订单编号和页面选项
  180. this.orderNo = options.orderNo
  181. this.pageOptions = options
  182. },
  183. onShow() {
  184. // 页面显示时根据类型获取数据
  185. const {type} = this.pageOptions
  186. if (type == 'cart') {
  187. this.getCartData()
  188. const selectAddress = uni.getStorageSync('selectAddress') || undefined
  189. if (selectAddress) {
  190. const tempAddress = JSON.parse(selectAddress)
  191. if (!this.mAddress.code || (this.mAddress && this.mAddress.code != tempAddress.code)) {
  192. this.mAddress = tempAddress
  193. this.getExpressFee()
  194. }
  195. uni.removeStorageSync('selectAddress')
  196. }
  197. } else {
  198. this.getCouponList()
  199. this.getOrderDetail()
  200. }
  201. },
  202. methods: {
  203. /**
  204. * 获取优惠券配置
  205. * @param {Object} item - 优惠券对象
  206. * @returns {Object} - 优惠券配置对象
  207. */
  208. getItemConfig(item) {
  209. let itemConfig = {
  210. btnText: '选择使用',
  211. disabled: false,
  212. btnPlain: true
  213. }
  214. if (item.coupon_id == this.checkedCoupon.coupon_id) {
  215. itemConfig = {
  216. btnText: '取消使用',
  217. disabled: false,
  218. btnPlain: true
  219. }
  220. }
  221. return itemConfig
  222. },
  223. /**
  224. * 获取积分使用数据
  225. */
  226. getPoints() {
  227. this.$api.getPointsUseData().then(res => {
  228. if (!res.data.ratio) {
  229. this.usePointsFlag = false
  230. return
  231. } else {
  232. this.usePointsFlag = true
  233. }
  234. this.pointsData = res.data
  235. const {min_money, ratio, to_money} = this.pointsData
  236. const amount = this.orderData.amount_goods
  237. if (this.orderData.integral) {
  238. this.usePoints = ['points']
  239. let price = this.orderData.integral_price
  240. this.pointsShowData = {
  241. price: price.toFixed(2),
  242. points: Math.floor(price / to_money)
  243. }
  244. } else {
  245. let userPoints = 1000
  246. let price = Math.floor(amount * ratio / 100)
  247. let points = price * to_money
  248. points = this.userPoints > points ? points : this.userPoints
  249. price = points / to_money
  250. this.pointsShowData = {
  251. points: Math.floor(points),
  252. price: price.toFixed(2)
  253. }
  254. }
  255. })
  256. },
  257. /**
  258. * 获取购物车数据
  259. */
  260. getCartData() {
  261. this.cartIds = this.pageOptions.ids.split('_')
  262. this.$api.createPrevOrderByCart({id: this.cartIds}).then(res => {
  263. const {items, coupons, count_total, amount_total, integral = {}} = res.data
  264. this.goodsList = items.map(item => {
  265. const {goods_spec, price_selling} = item.sku
  266. item.stock_sales = item.num
  267. item = {
  268. goods_spec,
  269. price_selling,
  270. ...item
  271. }
  272. return item
  273. })
  274. this.couponList = coupons
  275. this.orderData.number_goods = count_total
  276. this.orderData.amount_goods = amount_total
  277. if (res.data.integral_use && res.data.integral_price) {
  278. this.usePointsFlag = true
  279. this.pointsShowData = {
  280. points: res.data.integral_use,
  281. price: (res.data.integral_price).toFixed(2)
  282. }
  283. } else {
  284. this.usePointsFlag = false
  285. }
  286. })
  287. },
  288. /**
  289. * 获取运费
  290. */
  291. getExpressFee() {
  292. this.$api.getPrevCartExpress({id: this.cartIds, addr_code: this.mAddress.code}).then(res => {
  293. this.orderData.amount_express = res.data
  294. })
  295. },
  296. /**
  297. * 前往支付
  298. */
  299. toPay() {
  300. if (!this.mAddress.code) {
  301. uni.showToast({
  302. title: '请选择收货地址'
  303. })
  304. return
  305. }
  306. if (this.pageOptions.type == 'cart') {
  307. const sendData = {
  308. id: this.cartIds,
  309. addr_code: this.mAddress.code,
  310. coupon_id: this.checkedCoupon.id,
  311. order_remark: this.order_remark,
  312. integral: this.integral
  313. }
  314. this.$api.createOrderByCart(sendData).then(res => {
  315. const {appId, nonceStr, paySign, signType, timeStamp} = res.data.param
  316. uni.requestPayment({
  317. provider: 'wxpay',
  318. nonceStr,
  319. package: res.data.param.package,
  320. paySign,
  321. signType,
  322. timeStamp,
  323. success: function (success) {
  324. uni.navigateTo({url: `/subPages/paySuccess/paySuccess?orderNo=${res.data.order.order_no}`})
  325. },
  326. fail: function (err) {
  327. uni.navigateTo({url: `/subPages/orderDetail/orderDetail?orderNo=${res.data.order.order_no}`})
  328. }
  329. })
  330. })
  331. } else {
  332. // uni.navigateTo({url: `/subPages/paySuccess/paySuccess?orderNo=${this.orderNo}`})
  333. this.$toWechatPay(this.orderNo, this.order_remark)
  334. }
  335. },
  336. /**
  337. * 地址确认
  338. */
  339. addressConfirm() {
  340. this.$api.submitOrderByAdress({order_no: this.orderNo, code: this.mAddress.code}).then(res => {
  341. this.orderData.amount_express = res.data.amount
  342. })
  343. },
  344. /**
  345. * 选择优惠券
  346. */
  347. choseCoupon() {
  348. if (this.couponList.length) {
  349. this.showCoupon = true
  350. } else {
  351. uni.showToast({
  352. title: '暂无可使用优惠券',
  353. icon: 'none'
  354. })
  355. }
  356. },
  357. /**
  358. * 获取优惠券列表
  359. */
  360. getCouponList() {
  361. this.$api.getCouponByOrder({order_no: this.orderNo}).then(res => {
  362. this.couponList = res.data
  363. })
  364. },
  365. /**
  366. * 处理优惠券选择
  367. * @param {Object} item - 优惠券对象
  368. */
  369. handleItem(item) {
  370. this.showCoupon = false
  371. if (item.coupon_id == this.checkedCoupon.coupon_id) {
  372. this.checkedCoupon = {id: 0, coupon_id: 0}
  373. } else {
  374. this.checkedCoupon = item
  375. }
  376. if (this.pageOptions.type != 'cart') {
  377. this.$api.submitOrderByCoupon({order_no: this.orderNo, id: this.checkedCoupon.id})
  378. }
  379. },
  380. /**
  381. * 获取订单详情
  382. */
  383. getOrderDetail() {
  384. this.$api.orderDetail({order_no: this.orderNo}).then(res => {
  385. this.goodsList = res.data.items || []
  386. this.orderData = res.data
  387. this.checkedCoupon = res.data.coupon || {}
  388. if (res.data.addr_code) {
  389. const {
  390. address_area,
  391. address_city,
  392. address_province,
  393. address_content,
  394. address_name,
  395. address_phone
  396. } = res.data.truck || {}
  397. this.mAddress = {
  398. code: res.data.addr_code,
  399. area: address_area,
  400. city: address_city,
  401. province: address_province,
  402. address: address_content,
  403. name: address_name,
  404. phone: address_phone
  405. }
  406. }
  407. const selectAddress = uni.getStorageSync('selectAddress') || undefined
  408. if (selectAddress) {
  409. const tempAddress = JSON.parse(selectAddress)
  410. if (!this.mAddress.code || (this.mAddress && this.mAddress.code != tempAddress.code)) {
  411. this.mAddress = tempAddress
  412. this.addressConfirm()
  413. }
  414. uni.removeStorageSync('selectAddress')
  415. }
  416. })
  417. },
  418. /**
  419. * 选择地址
  420. */
  421. selectAddress() {
  422. uni.navigateTo({
  423. url: '/subPages/addressMg/addressMg?isBack=true'
  424. })
  425. }
  426. }
  427. }
  428. </script>
  429. <style lang="scss" scoped>
  430. .coupon-list {
  431. padding: 40rpx 20rpx;
  432. }
  433. .common-page {
  434. height: 100vh;
  435. overflow-y: auto;
  436. padding: 20rpx;
  437. color: #2C2C2C;
  438. padding-bottom: 188rpx;
  439. box-sizing: border-box;
  440. }
  441. .good-card {
  442. padding: 38rpx 22rpx;
  443. color: #7A7A7A;
  444. ::v-deep {
  445. .u-textarea {
  446. background: #F6F6F6;
  447. border-radius: 12rpx 12rpx 12rpx 12rpx;
  448. border: none;
  449. padding: 18rpx;
  450. .u-textarea__count {
  451. background-color: transparent !important;
  452. }
  453. }
  454. }
  455. }
  456. .goods-list {
  457. margin: 0 30rpx;
  458. padding: 30rpx 0;
  459. border-bottom: 1rpx solid #F5F5F5;
  460. }
  461. .goods-total {
  462. justify-content: flex-end;
  463. margin: 27rpx 20rpx;
  464. }
  465. .address-box {
  466. // width: 100%;
  467. display: flex;
  468. flex-direction: row;
  469. align-items: center;
  470. padding: 56rpx 22rpx;
  471. .address-view {
  472. flex: 1;
  473. display: flex;
  474. flex-direction: column;
  475. .t1 {
  476. font-size: 28rpx;
  477. margin-top: 10rpx;
  478. font-family: PingFang SC-Medium, PingFang SC;
  479. font-weight: 500;
  480. color: #141414;
  481. }
  482. .t2 {
  483. font-size: 28rpx;
  484. font-family: PingFang SC-Medium, PingFang SC;
  485. font-weight: 500;
  486. color: #606060;
  487. margin-top: 20rpx;
  488. }
  489. }
  490. }
  491. </style>