Vue2电商项目(七)、订单与支付

news2024/11/25 0:37:07

文章目录

  • 一、交易业务Trade
    • 1. 获取用户地址
    • 2. 获取订单信息
  • 二、提交订单
  • 三、支付
    • 1. 获取支付信息
    • 2. 支付页面--ElementUI
      • (1) 引入Element UI
      • (2) 弹框支付的业务逻辑(这个逻辑其实没那么全)
      • (3) 支付逻辑知识点小总结
  • 四、个人中心
    • 1. 搭建二级路由
    • 2. 展示动态数据
      • (1). 接口
      • (2). 组件获取数据
      • (3). 页面表格的合并与展示
      • (4). 页面分页器

一、交易业务Trade

首先要配置静态组件及路由,这里没什么重点,不多说了。然后是动态数据的展示

1. 获取用户地址

在这里插入图片描述

(1) 接口
用文档里的接口向服务器发送请求时,电商系统的账号和密码得用视频里老师的账号密码登录,才会有数据返回。如果用自己的账号密码,那就自己mock模拟一些数据。

// 文档的接口
export const reqAddressList = () => {
  return requests({ url: 'user/userAddress/auth/findUserAddressList', method: 'get' })
}
// 自己mock的请求接口
export const reqAddressList = () => {
  return mockRequests({ url: '/userAddress/auth/findUserAddressList', method: 'get' })
}

(2) Vuex三连环
新建trade小仓库存放交易业务的数据

  state: {
    addressList: [],
  },
  actions: {
    // 获取用户地址信息
    async getUserAddress (context) {
      let res = await reqAddressList()
      console.log('用户地址', res);
      if (res.code === 200) {
        context.commit('GETUSERADDRESS', res.data)
      }
    }
  },
  mutations: {
    GETUSERADDRESS (state, data) {
      state.addressList = data
    },
  },

在这里插入图片描述

(3)动态数据渲染组件页面

在mounted函数里dispatch请求,通过mapState获取Vuex的数据。
第一个业务逻辑是修改默认地址。点击哪个标签,哪个成为默认地址。
在这里插入图片描述

 // 修改默认地址,排他思想
 changeDefault (address) {
   this.addressList.forEach(el => {
     el.isDefault = 0 
   });
   address.isDefault = 1 // 值为1表示为选中的默认地址
 },

第二个业务逻辑是获取到默认地址的对象信息,动态展示在页面低端。
在这里插入图片描述
采用计算属性。需要注意,这里用到了Vuex的值,可能会有undefined的错误(因为addressList是异步请求获得的数据,页面刚加载完时,addressList的值还未获取到,则会报undefined的错误),所以要 || {}

 <div class="receiveInfo">
   寄送至:
   <!--这里会报undefined的错误,fullAddress属性undefined-->
   <span>{{ userDefAddress.fullAddress }}</span>
   收货人:<span>{{ userDefAddress.consignee }}</span>
   <span>{{ userDefAddress.phoneNum }}</span>
 </div>
<script>
 computed: {
   ...mapState('trade', ['addressList']),
   // 将来提交订单最终选中的地址 表达式:undefined || {} 的值是{}
   userDefAddress () {
     return this.addressList.find((item) => {
       return item.isDefault == 1
     }) || {}
   }
 },
</script>

2. 获取订单信息

就是trade页面的这部分信息
在这里插入图片描述

(1) 接口

export const reqOrderInfo = () => {
  return requests({
    url: '/order/auth/trade',
    method: 'get',
  })
}

(2) vuex存储数据

state: {
  addressList: [],
},
actions: {
  // 获取订单交易信息
  async getOrderInfo (context) {
    let res = await reqOrderInfo()
    if (res.code === 200) {
      context.commit('GETORDERINFO', res.data)
    }
  }
},
mutations: {
  GETORDERINFO (state, data) {
    state.orderInfo = data
  }
},

在这里插入图片描述

(3) 动态数据渲染界面。
这部分没什么重点,组件内取到orderInfo之后,渲染到页面即可。
在这里插入图片描述

二、提交订单

这个功能主要包括收集参数、提交订单,跳转页面。
(测试这个功能用的老师的账号 :账号:13700000000,密码:111111)

1. 提交订单的接口
//URL:/api/order/auth/submitOrder?tradeNo={tradeNo}  method:post
export const reqSubmitOrder = (tradeNo, data) => requests(
  {
    url: `/order/auth/submitOrder?tradeNo=${tradeNo}`,
    data,
    method: 'post'
  });

接下来我们不再用vuex了。发请求就直接在组件内发了。为了方便使用接口,我们不再按需引入,直接配置到Vue原型上(类似于全局事件总线)。

// main.js
// 引入所有接口
import * as API from '@/api'
console.log(API); // 打印测试一下
new Vue({
  // KV一致时省略V[router小写的r]
  router,
  store,
  render: h => h(App),
  beforeCreate () {
    // 全局事件总线
    Vue.prototype.$bus = this
    // 接口挂在原型上
    Vue.prototype.$API = API 
  }
}).$mount('#app')

打印API得:
在这里插入图片描述

2. 组件内点击提交按钮,发送请求 trade组件
methods:{
 // 提交订单
 async submitOrder () {
    //交易编码,对象解构
    let { tradeNo } = this.orderInfo;
    //其余的六个参数
    let data = {
      consignee: this.userDefAddress.consignee, //最终收件人的名字
      consigneeTel: this.userDefAddress.phoneNum, //最终收件人的手机号
      deliveryAddress: this.userDefAddress.fullAddress, //收件人的地址
      paymentWay: 'ONLINE', //支付方式
      orderComment: this.notes, //订单备注。data里定义了notes属性,与文本框v-model双向绑定。
      orderDetailList: this.orderInfo.detailArrayList, //商品清单
    };
    
    // 参数: 订单编号tradeNo, 订单信息 data
    let res = await this.$API.reqSubmitOrder(tradeNo, data)
    console.log('提交订单', res);
    if (res.code === 200) {
      // 请求成功,跳转到支付页面。路由跳转+路由传参;res.data是服务器返回的订单编号
      this.$router.push('/pay?orderId=' + res.data)
    } else {
      alert(res.message)
    }
  },

但是尝试了好多次,都是201失败,不知道为啥。
找出原因了,参数orderDetailList写错了,写成orderDetaiList,少了个字母l。
在这里插入图片描述
这里服务器返回的订单编号需要带到支付页面。在支付页面根据订单编号发送请求,获取这个订单的支付信息。

三、支付

在这里插入图片描述
这两处是需要支付信息payInfo动态渲染的地方。

1. 获取支付信息

(1). 接口

// 获取订单支付信息 /api/payment/weixin/createNative/{orderId} get
export const reqOrderPay = (orderId) => {
  return requests({ url: `/payment/weixin/createNative/${orderId}`, method: 'get' })
}

(2). 组件中发送请求
在这里插入图片描述
请求成功返回的结果:
在这里插入图片描述

2. 支付页面–ElementUI

在这里插入图片描述
ElementUI主要用来实现这个点击按钮,弹窗的效果。
ElementUI网址:ElementUI官网

(1) 引入Element UI

  • 安装: npm i element-ui -S

引入方式分为按需引入和全部引入,按需引入可减少项目体积。本系统采用按需引入:

  • 安装babel-plugin-component:npm install babel-plugin-component -D
  • 修改配置文件,看官网,直接粘贴复制

在这里插入图片描述

  • 按需引入, main.js文件中

    // 按需引入Element-ui组件
    import { MessageBox } from 'element-ui'
    // Vue.use(MessageBox)  这样引入刚进入界面就会有弹窗
    Vue.component(MessageBox.name, MessageBox)
    Vue.prototype.$msgbox = MessageBox
    Vue.prototype.$alert = MessageBox.alert;
    

修改配置文件,需要重新启动项目

(2) 弹框支付的业务逻辑(这个逻辑其实没那么全)

这里弹框页面好写,重要的是支付的逻辑。盘一下逻辑,弹出支付的小窗口,窗口上有两个按钮:

  • 如果用户不点击这两个按钮,直接进行支付:
    ① 发请求获取支付信息,判断是否支付成功。问题是什么时候判断呢?当小窗口弹出来之后,不知道用户什么时候会判断,所以就需要一直发请求进行判断,采用定时器最合适了。
    ② 如果判断支付成功,清除定时器,关闭窗口,保存支付成功这个信息。进行路由跳转。
    ❌③如果支付失败,则,这时候用户只能去点窗口上的按钮了。

  • 如果用户点击窗口上的已支付成功按钮
    ① 判断用户是否真的支付(用到了上边②保存支付成功的信息)。真的支付成功的话,那就清除定时器,关闭窗口,跳转路由。

  • 如果点击窗口上 支付遇到问题按钮
    ① 给出提示信息,关闭弹框,清除定时器。

定时器部分
 <script>
 // 支付
async open () {
 // 如果没有定时器,就开启一个定时器
  if (!this.timer) {
    this.timer = setInterval(async () => {
      // 发请求判断是否已支付,需要一直判断;这个接口一会儿再说
      let res = await this.$API.reqPaymentState(this.payInfo.orderId)
      if (res.code === 200) {
        // 支付成功,清除定时器
        clearInterval(this.timer);
        this.timer = null
        this.code = res.code // 保存支付成功的信息
        this.$msgbox.close() // 关闭弹出框
        // 路由跳转
        this.$router.push("/paysuccess")
      }
    }, 1000)
  }
}
</script>

在这里插入图片描述

弹框部分
async open () {
  // 支付弹框
  this.$alert(`<img src=${url} />`, '请微信支付', {
  // 将dangerouslyUseHTMLString属性设置为 true,message 就会被当作 HTML 片段处理。
    dangerouslyUseHTMLString: true,   
    center: true, // 布局居中
    showClose: false,// 取消显示右上角的叉
    showCancelButton: true,  // 显示取消按钮
    confirmButtonText: '已支付成功', // 确认按钮
    cancelButtonText: '取消支付', // 取消按钮

    /* beforeClose MessageBox弹出框关闭前的回调
       action:区分取消|确定按钮 
       instance:当前组件实例
       done:关闭弹出框的方法
    */
    beforeClose: (action, instance, done) => {
      // 如果点击支付遇到问题
      if (action == 'cancel') {
        alert('支付有问题,请联系xxxx')
        clearInterval(this.timer) // 清除定时器
        this.timer = null
        done() // 关闭弹出框
      } else {
        // 点击已支付成功,判断是否真的支付成功
        if (this.code === 200) {
          // 清除定时器
          clearInterval(this.timer)
          this.timer = null;
          done();              // 关闭弹出框
          this.$router.push("/paysuccess"); // 路由跳转
         }
        }
      }
    }
  })
    .then(() => { })  // 添加错误捕获,具体解释在下边
    .catch(() => { });
}

当点击支付遇到问题按钮时,会报这个错误。和Promise有关,等看完Promise再来看这个地方。

参考博客:Uncaught (in promise) cancel 报错 及 解决方法
在这里插入图片描述
解决方法就是加上错误捕获。

(3) 支付逻辑知识点小总结

① 展示二维码。QRcode
可以在npm官网搜索QRcode;

  • 安装:npm i qrcode
  • 引入及使用
import QRcode from 'qrcode'
// 生成二维码,
 let url = await QRcode.toDataURL(this.payInfo.codeUrl) //这个url就是图片地址

② 获取订单支付状态reqPaymentState接口

// 查询支付订单状态 /api/payment/weixin/queryPayStatus/{orderId}
export const reqPaymentState = (orderId) => {
  return requests({ url: `/payment/weixin/queryPayStatus/${orderId}`, method: 'get' })
}

③ 路由跳转的支付成功页面paySuccess
这是一个新组件,配置好路由即可。没什么重点。
在这里插入图片描述

④ 完整代码
⑤⑥⑦⑧⑨⑩

四、个人中心

个人中心里,点击不同的订单,右侧显示对应的内容。
在这里插入图片描述
结构类似于之前学Vue2搭的Home、About;

1. 搭建二级路由

(1) 路由
在这里插入图片描述
注意
(1)、子路由的路径不要写/
(2)、如果不写重定向,进入个人中心时,右侧内容就一片空白。重定向是进入个人中心,默认跳转到我的订单这个路由上。
在这里插入图片描述

(2) 组件内的声明式导航
在这里插入图片描述

2. 展示动态数据

(1). 接口

//获取个人中心的数据
//api/order/auth/{page}/{limit}  get 
export const reqMyOrderList = (page, limit) => requests({ url: `/order/auth/${page}/${limit}`, method: 'get' });

(2). 组件获取数据

在这里插入图片描述

(3). 页面表格的合并与展示

数据结构与页面的对应关系:
在这里插入图片描述
每一个订单都是一个表格,所以v-for循环订单数组records,records有几条数据,就有几个表格。
在这里插入图片描述
表头thead,把对应的数据渲染上去即可

<!-- 显示订单时间,编号和删除操作 -->
<thead>
<tr>
 <th colspan="5">
   <span class="ordertitle">{{ order.createTime }} 订单编号:{{ order.outTradeNo }}
     <span class="pull-right delete" ><img src="../images/delete.png" /></span></span>
 </th>
</tr>
</thead>

tbody,每一行都是一类商品信息,所以v-for循环订单详细信息orderDetailList,orderDetailList有几条数据,表格就有几个行标签tr
在这里插入图片描述
效果:
在这里插入图片描述
  对于一个订单,收货人等信息都是一样的,所以我们更希望红框里的收货人等信息,展示一行就行了。用到合并单元格:rowspan 跨行合并单元格tdrospan的值取决于表格有几行,也就是orderDetailList数组的长度。
在这里插入图片描述

效果:
在这里插入图片描述
原因分析:如果orderDetailList只有一条数据,那orderDetailList[0]的收货人等信息就会跨一行合并。如果有两条数据,orderDetailList[0]的收货人等信息就会跨两行合并,而orderDetailList[1]的收货人就会被挤到后边。
解决方法:这些信息都一样,那就只展示orderDetailList[0]的收货人等信息。其余的都不显示。
在这里插入图片描述
template包裹这些标签,只展示第一行的所有信息。

(4). 页面分页器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2195451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Kubernetes】常见面试题汇总(六十)

目录 131. pod 一直处于 pending 状态&#xff1f; 132. helm 安装组件失败&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-113 属于…

企业经营异常怎么解除

经营异常是怎么回事&#xff1f;是什么意思&#xff1f;了解异常原因&#xff1a;我们到所属工商营业执照异常的具体原因。原因可能包括未按时提交年报、未履行即时信息公示义务、公示信息隐瞒真实情况或弄xu作jia、失联等。纠正违规行为&#xff1a;查到了异常原因&#xff0c…

洛谷P5723、P5728、P1428、P1319 Python解析

P5723 完整代码 def is_prime(y):if y < 2:return Falsefor i in range(2, int(y**0.5) 1):if y % i 0:return Falsereturn Truen int(input()) sum_primes 0 x 0if n < 2:print("0") elif n 2:print("2\n1") else:for i in range(2, n 1):i…

计数原理与组合 - 离散数学系列(三)

目录 1. 计数原理的基本概念 加法原理&#xff08;Rule of Sum&#xff09; 乘法原理&#xff08;Rule of Product&#xff09; 2. 排列与组合 排列&#xff08;Permutation&#xff09; 组合&#xff08;Combination&#xff09; 日常生活中的例子 3. 二项式定理 4. 实…

Mysql锁机制解读(敲详细)

目录 锁的概念 全局锁 表级锁 表锁 元数据锁 意向锁 锁的概念 全局锁 表级锁 表锁 元数据锁 主要是对未提交事务&#xff0c;修改表结构造成表结构混乱&#xff0c;进行控制。 在不涉及表结构变化的情况下,元素锁可以忽略。 意向锁 避免有行级锁影响加表级锁&#xff0…

Mysql(六) --- 聚合函数,分组和联合查询

文章目录 前言1.聚合函数1.1.常用的函数1.2.COUNT()1.3.SUM()1.4.AVG()1.5.MIN()、MAX() 2.GROUP BY 分组查询2.1.语法2.2.示例2.3.HAVING 子句 3.联合查询3.1.为什么要进行联合查询3.2.那么是如何进行联合查询的3.3.示例&#xff1a;一个完整的联合查询的过程3.4.内连接3.5.外…

Error:WPF项目中使用oxyplot,错误提示命名空间中不存在“Plot”名称

在OxyPlot中&#xff0c;<oxy:PlotView>和<oxy:Plot>都是用来显示图表的控件&#xff0c;在WPF项目中使用oxyplot之前&#xff0c;先通过NuGet安装依赖包&#xff1a;OxyPlot.Wpf。 <oxy:PlotView>和<oxy:Plot>使用示例&#xff1a; <oxy:PlotVie…

【算法】双指针(续)

一、盛最多水的容器 11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09; 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多…

OJ在线评测系统 微服务 OpenFeign调整后端下 nacos注册中心配置 不给前端调用的代码 全局引入负载均衡器

OpenFeign内部调用二 4.修改各业务服务的调用代码为feignClient 开启nacos注册 把Client变成bean 该服务仅内部调用&#xff0c;不是给前端的 将某个服务标记为“内部调用”的目的主要有以下几个方面&#xff1a; 安全性: 内部API通常不对外部用户公开&#xff0c;这样可以防止…

Nginx05-基础配置案例

零、文章目录 Nginx05-基础配置案例 1、案例需求 &#xff08;1&#xff09;有如下访问 http://192.168.119.161:8081/server1/location1 访问的是&#xff1a;index_sr1_location1.htmlhttp://192.168.119.161:8081/server1/location2 访问的是&#xff1a;index_sr1_loca…

慢接口分析与优化总结

文章目录 1. 慢接口优化的意义2. 接口耗时构成3. 优化技巧3.1. 内部代码逻辑异步执行[异步思想]并行优化拒绝阻塞等待预分配与循环使用[池化思想]线程池合理设计锁粒度避免过粗优化程序结构 3.2. 缓存恰当引入缓存[空间换时间思想]缓存延迟优化提前初始化缓存[预取思想] 3.3. 数…

工具函数(截取文本第一个字为图片)

const subStringToImage (params) > {const { str ,color #FFF,background #4F54FF,size 60,fontSize 20 } paramsif(str.length < 0) return console.error(字符不能为空!)const text str.slice(0, 1)const canvas document.createElement(canvas)const ctx …

github 国内文件加速下载

参看;https://www.cnblogs.com/ting1/p/18356265 在源网址前加上 https://hub.gitmirror.com/ 或https://mirror.ghproxy.com/&#xff0c;例如&#xff1a; https://hub.gitmirror.com/https://github.com/t1m0thyj/WinDynamicDesktop/releases/download/v5.4.1/WinDynamicD…

算法题总结(十)——二叉树上

#二叉树的递归遍历 // 前序遍历递归LC144_二叉树的前序遍历 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result new ArrayList<Integer>(); //也可以把result 作为全局变量&#xff0c;只需要一个函数即可。…

公开数据集网站分享

参考链接&#xff1a;常用的医学组织切片细胞图像数据集_细胞分割数据集-CSDN博客文章浏览阅读1.3w次&#xff0c;点赞32次&#xff0c;收藏133次。乳腺癌细胞图像数据集、血细胞图像数据集、HE染色切片、疟疾细胞图像图像识别、分类、分割_细胞分割数据集https://blog.csdn.ne…

Redis list 类型

list类型 类型介绍 列表类型 list 相当于 数组或者顺序表 list内部的编码方式更接近于 双端队列 &#xff0c;支持头插 头删 尾插 尾删。 需要注意的是&#xff0c;Redis的下标支持负数下标。 比如数组大小为5&#xff0c;那么要访问下标为 -2 的值可以理解为访问 5 - 2 3 …

Linux dlsym和直接调用函数地址解析分析

dlsym 函数是 Linux 下动态链接库&#xff08;shared library&#xff09;编程中的一个重要函数。它用于在运行时获取动态链接库中符号的地址&#xff0c;通常用于获取函数指针或变量的地址。 以下是 dlsym 函数的基本用法和示例。 1. 函数原型 void *dlsym(void *handle, c…

【超详细】基于YOLOv11的PCB缺陷检测

主要内容如下&#xff1a; 1、数据集介绍 2、下载PCB数据集 3、不同格式数据集预处理&#xff08;Json/xml&#xff09;&#xff0c;制作YOLO格式训练集 4、模型训练及可视化 5、Onnxruntime推理 运行环境&#xff1a;Python3.8&#xff08;要求>3.8&#xff09;&#xff…

ubuntu ssh远程执行k8s命令报错the connection to the server localhost:8080 was refused

修改前&#xff1a; ssh root192.168.31.167 kubectl apply -f /root/jenkinsexcute/saas.demo.api.k8s.yml --recordecho "export KUBECONFIG/etc/kubernetes/admin.conf" >> /root/.bashrc 修改后 添加一段&#xff1a;export KUBECONFIG/etc/kubernetes/a…

【专题】操作系统概述

1. 操作系统的目标和作用 操作系统的目标与应用环境有关。 在查询系统中所用的OS&#xff0c;希望能提供良好的人—机交互性&#xff1b; 对于应用于工 业控制、武器控制以及多媒体环境下的OS&#xff0c;要求其具有实时性&#xff1b; 对于微机上配置的OS&#xff0c;则更看…