一、购物车
本地购物车
创建cartStore.js文件
创建cartStore.js文件, 将购物车列表数据存在pinia中
import { ref, computed } from "vue";
import { defineStore } from "pinia";
import { useUserStore } from "./user";
import {
insertCartAPI,
findNewCartListAPI,
delCartAPI,
} from "@/api/cart/index";
export const useCartStore = defineStore(
"cart",
() => {
const userStore = useUserStore();
const isLogin = computed(() => userStore.userInfo.token);
console.log("已经执行登录验证");
//1.定义state - cartList
const cartList = ref([]);
// 2. 定义action - addCart
const addCart = async (goods) => {
console.log(goods);
const { id, count } = goods;
if (isLogin.value) {
// 登录之后的加入购车逻辑
await insertCartAPI({ id, count });
const res = await findNewCartListAPI();
cartList.value = res.data;
console.log("cartList已经赋值");
} else {
console.log("cartList没有赋值");
// 添加购物车操作
// 已添加过 - count + 1
// 没有添加过 - 直接push
// 思路:通过匹配传递过来的商品对象中的id能不能在cartList中找到,找到了就是添加过
const item = cartList.value.find((item) => goods.id === item.goodsId);
console.log("item=" + item);
if (item) {
// 找到了
item.buyCount++;
} else {
console.log("这里运行了");
// 没找到
cartList.value.push(goods);
}
}
};
// 删除购物车
const delCart = async (goodsId) => {
if (isLogin.value) {
// 调用接口实现接口购物车中的删除功能
await delCartAPI([goodsId]);
const res = await findNewCartListAPI();
cartList.value = res.data;
} else {
console.log("cartdel操作已执行");
// 思路:
// 1. 找到要删除项的下标值 - splice
// 2. 使用数组的过滤方法 - filter
const idx = cartList.value.findIndex(
(item) => goodsId === item.goodsId
);
cartList.value.splice(idx, 1);
}
};
//更新购物车
const updateNewList = async () => {
const res = await findNewCartListAPI();
cartList.value = res.data;
console.log("已更新购物车")
};
//清除购物车
const clearCart = () => {
cartList.value = [];
};
// 计算属性
// 1. 总的数量 所有项的count之和
const allCount = computed(() =>
cartList.value.reduce((a, c) => a + c.buyCount, 0)
);
// 2. 总价 所有项的count*price之和
const allPrice = computed(() =>
cartList.value.reduce((a, c) => a + c.buyCount * c.goodsPrice, 0)
);
// 单选功能
const singleCheck = (goodsId, selected) => {
// 通过id找到要修改的那一项 然后把它的selected修改为传过来的selected
const item = cartList.value.find((item) => item.goodsId === goodsId);
item.selected = selected;
};
// 全选功能action
const allCheck = (selected) => {
// 把cartList中的每一项的selected都设置为当前的全选框状态
cartList.value.forEach((item) => (item.selected = selected));
};
// 是否全选计算属性
const isAll = computed(() => cartList.value.every((item) => item.selected));
// 3. 已选择数量
const selectedCount = computed(() =>
cartList.value
.filter((item) => item.selected)
.reduce((a, c) => a + c.buyCount, 0)
);
// 4. 已选择商品价钱合计
const selectedPrice = computed(() =>
cartList.value
.filter((item) => item.selected)
.reduce((a, c) => a + c.buyCount * c.goodsPrice, 0)
);
return {
updateNewList,
clearCart,
selectedPrice,
selectedCount,
isAll,
allCheck,
singleCheck,
cartList,
allCount,
allPrice,
addCart,
delCart,
};
},
{
persist: true,
}
);
封装接口
创建文件
import http from '@/utils/http'
// 加入购物车
export function insertCartAPI ({ id,count}) {
return http({
url: '/cart',
method: 'POST',
data:{
"goodsId":id,
"buyCount":count
},
})
}
//获取最新的购物车列表
export function findNewCartListAPI () {
return http({
url: '/cart',
method: 'GET',
})
}
//获取最新的购物车列表
export function delCartAPI (ids) {
return http({
url: '/cart',
method: 'DELETE',
data:{
"ids":ids
}
})
}
前往商品详情页面 Detail\index.vue绑定事件和事件逻辑
<script setup>
import { getDetail } from "@/api/goods/index";
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { useCartStore } from '@/store/cartStore';
const cartStore = useCartStore()
const route = useRoute();
const goods = ref({});
const category = ref({});
const seller = ref({});
const getGoods = async () => {
const res = await getDetail(route.params.id);
goods.value = res.data.good;
category.value = res.data.category;
seller.value = res.data.seller;
console.log(res.data.pictureList)
imageList.value = res.data.pictureList
};
//count
const count = ref(1)
const countChange = (count) => {
console.log(count);
}
//添加购物车
const addCart = () => {
//console.log(goods)
cartStore.addCart({
id: goods.value.id,
name: goods.value.goodsName,
picture: goods.value.picture1,
price: goods.value.price,
count: count.value,
// attrsText: skuObj.specsText,
selected: true
})
}
onMounted(() => {
getGoods();
});
console.log(imageList);
</script>
<template>
<!-- 数据组件 -->
<el-input-number :min="1" v-model="count" @change="countChange" />
<!-- 按钮组件 -->
<div>
<el-button size="large" class="btn" @click="addCart">
加入购物车
</el-button>
</div>
</template>
头部购物车
1.创建文件
views/Layout/compopnent/HeaderCart.vue
<script setup>
import { useCartStore } from "@/store/cartStore";
const cartStore = useCartStore();
</script>
<template>
<div class="cart">
<a class="curr" href="javascript:;">
<i class="iconfont icon-cart"></i><em>{{ cartStore.allCount }}</em>
</a>
<div class="layer">
<div class="list">
<div class="item" v-for="i in cartStore.cartList" :key="i.id">
<RouterLink to="">
<img :src="i.picture1" alt="" />
<div class="center">
<p class="name ellipsis-2">
{{ i.goodsName }}
</p>
<!-- <p class="attr ellipsis">{{ i.attrsText }}</p> -->
</div>
<div class="right">
<p class="price">¥{{ i.goodsPrice }}</p>
<p class="count">x{{ i.buyCount }}</p>
</div>
</RouterLink>
<i class="iconfont icon-close-new" @click="cartStore.delCart(i.id)"></i>
</div>
</div>
<div class="foot">
<div class="total">
<p>共 {{ cartStore.allCount }} 件商品</p>
<p>¥ {{ cartStore.allPrice }}</p>
</div>
<el-button size="large" type="primary" @click="$router.push('/cartlist')">去购物车结算</el-button>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.cart {
width: 50px;
position: relative;
z-index: 600;
.curr {
height: 32px;
line-height: 32px;
text-align: center;
position: relative;
display: block;
.icon-cart {
font-size: 22px;
}
em {
font-style: normal;
position: absolute;
right: 0;
top: 0;
padding: 1px 6px;
line-height: 1;
background: $helpColor;
color: #fff;
font-size: 12px;
border-radius: 10px;
font-family: Arial;
}
}
&:hover {
.layer {
opacity: 1;
transform: none;
}
}
.layer {
opacity: 0;
transition: all 0.4s 0.2s;
transform: translateY(-200px) scale(1, 0);
width: 400px;
height: 400px;
position: absolute;
top: 50px;
right: 0;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
background: #fff;
border-radius: 4px;
padding-top: 10px;
&::before {
content: "";
position: absolute;
right: 14px;
top: -10px;
width: 20px;
height: 20px;
background: #fff;
transform: scale(0.6, 1) rotate(45deg);
box-shadow: -3px -3px 5px rgba(0, 0, 0, 0.1);
}
.foot {
position: absolute;
left: 0;
bottom: 0;
height: 70px;
width: 380px;
padding: 10px;
display: flex;
justify-content: space-between;
background: #f8f8f8;
align-items: center;
.total {
padding-left: 10px;
color: #999;
p {
&:last-child {
font-size: 18px;
color: $priceColor;
}
}
}
}
}
.list {
height: 310px;
overflow: auto;
padding: 0 10px;
&::-webkit-scrollbar {
width: 10px;
height: 10px;
}
&::-webkit-scrollbar-track {
background: #f8f8f8;
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: #eee;
border-radius: 10px;
}
&::-webkit-scrollbar-thumb:hover {
background: #ccc;
}
.item {
border-bottom: 1px solid #f5f5f5;
padding: 10px 0;
position: relative;
i {
position: absolute;
bottom: 38px;
right: 0;
opacity: 0;
color: #666;
transition: all 0.5s;
}
&:hover {
i {
opacity: 1;
cursor: pointer;
}
}
a {
display: flex;
align-items: center;
img {
height: 80px;
width: 80px;
}
.center {
padding: 0 10px;
width: 200px;
.name {
font-size: 16px;
}
.attr {
color: #999;
padding-top: 5px;
}
}
.right {
width: 100px;
padding-right: 20px;
text-align: center;
.price {
font-size: 16px;
color: $priceColor;
}
.count {
color: #999;
margin-top: 5px;
font-size: 16px;
}
}
}
}
}
}
</style>
2.修改LayoutHeader.vue
在views/Layout/compopnent/LayouHeader.vue中注册和使用HeaderCart组件
<script setup>
import HeaderCart from './HeaderCart.vue'
</script>
<template>
<!-- 头部购物车 -->
<HeaderCart></HeaderCart>
</template>
购物车列表页面
创建文件
<script setup>
import { useCartStore } from "@/store/cartStore";
const cartStore = useCartStore();
// 单选回调
const singleCheck = (i, selected) => {
console.log(i, selected);
// store cartList 数组 无法知道要修改谁的选中状态?
// 除了selected补充一个用来筛选的参数 - skuId
cartStore.singleCheck(i.goodsId, selected);
};
// 全选
const allCheck = (selected) => {
cartStore.allCheck(selected)
}
</script>
<template>
<div class="lyg-cart-page">
<div class="container m-top-20">
<div class="cart">
<table >
<thead>
<tr>
<th width="120">
<el-checkbox :model-value="cartStore.isAll" @change="allCheck" />
</th>
<th width="400">商品信息</th>
<th width="220">单价</th>
<th width="180">数量</th>
<th width="180">小计</th>
<th width="140">操作</th>
</tr>
</thead>
<!-- 商品列表 -->
<tbody >
<tr v-for="i in cartStore.cartList" :key="i.id" >
<td >
<!-- 单选框 -->
<el-checkbox
:model-value="i.selected"
@change="(selected) => singleCheck(i, selected)"
/>
</td>
<td>
<div class="goods">
<RouterLink to="/"
><img :src="i.goodsPicture" alt=""
/></RouterLink>
<div>
<p class="name ellipsis">
{{ i.goodsName }}
</p>
</div>
</div>
</td>
<td class="tc">
<p>¥{{ i.goodsPrice }}</p>
</td>
<td class="tc">
<el-input-number :min="0" v-model="i.buyCount" />
</td>
<td class="tc">
<p class="f16 red">¥{{ (i.goodsPrice * i.buyCount).toFixed(2) }}</p>
</td>
<td class="tc">
<p>
<el-popconfirm
title="确认删除吗?"
confirm-button-text="确认"
cancel-button-text="取消"
@confirm="cartStore.delCart(i.id)"
>
<template #reference>
<a href="javascript:;">删除</a>
</template>
</el-popconfirm>
</p>
</td>
</tr>
<tr v-if="cartStore.cartList.length === 0">
<td colspan="6">
<div class="cart-none">
<el-empty description="购物车列表为空">
<el-button type="primary" @click="$router.push('/category/hot')">随便逛逛</el-button>
</el-empty>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 操作栏 -->
<div class="action">
<div class="batch">
共 {{ cartStore.allCount }} 件商品,已选择 {{ cartStore.selectedCount }} 件,商品合计:
<span class="red">¥ {{ cartStore.selectedPrice.toFixed(2) }} </span>
</div>
<div class="total">
<el-button size="large" type="primary" @click="$router.push('/checkout')">下单结算</el-button>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.lyg-cart-page {
margin-top: 20px;
.cart {
background: #fff;
color: #666;
table {
border-spacing: 0;
border-collapse: collapse;
line-height: 24px;
th,
td {
padding: 10px;
border-bottom: 1px solid #f5f5f5;
&:first-child {
text-align: left;
padding-left: 30px;
color: #999;
}
}
th {
font-size: 16px;
font-weight: normal;
line-height: 50px;
}
}
}
.cart-none {
text-align: center;
padding: 120px 0;
background: #fff;
p {
color: #999;
padding: 20px 0;
}
}
.tc {
text-align: center;
a {
color: $lygColor;
}
.lyg-numbox {
margin: 0 auto;
width: 120px;
}
}
.red {
color: $priceColor;
}
.green {
color: $lygColor;
}
.f16 {
font-size: 16px;
}
.goods {
display: flex;
align-items: center;
img {
width: 100px;
height: 100px;
}
> div {
width: 280px;
font-size: 16px;
padding-left: 10px;
.attr {
font-size: 14px;
color: #999;
}
}
}
.action {
display: flex;
background: #fff;
margin-top: 20px;
height: 80px;
align-items: center;
font-size: 16px;
justify-content: space-between;
padding: 0 30px;
.lyg-checkbox {
color: #999;
}
.batch {
a {
margin-left: 20px;
}
}
.red {
font-size: 18px;
margin-right: 20px;
font-weight: bold;
}
}
.tit {
color: #666;
font-size: 16px;
font-weight: normal;
line-height: 50px;
}
}
</style>
二、订单信息确认页面
创建文件
创建 views/Checkout/index.vue 文件
编写代码
<script setup>
import { ref, onMounted } from "vue";
import { useCartStore } from "@/store/cartStore";
import { getCheckInfoAPI, createOrderAPI } from "@/api/order/index";
import { useRouter } from "vue-router";
const cartStore = useCartStore();
const router = useRouter();
console.log(cartStore);
const checkInfo = ref({}); // 订单对象
const curAddress = ref({}); // 地址对象
const time = ref({}); // 时间
const Time = new Date();
time.value = Time.getTime();
//控制弹窗打开
const showDialog = ref(false);
const getCheckInfo = async () => {
const res = await getCheckInfoAPI();
checkInfo.value = res.data;
console.log("data数据");
console.log(checkInfo);
//适配默认地址
//从地址列表中筛选出来 isDefault === 0 那一项
const item = checkInfo.value.address.find((item) => item.isDefault === 0);
console.log("cauraddress数据");
curAddress.value = item;
console.log(curAddress);
};
//切换地址
const activeAddress = ref({});
const switchAddres = (item) => {
console.log("switchAddres运行");
activeAddress.value = item;
};
//覆盖地址
const confirm = () => {
curAddress.value = activeAddress.value;
showDialog.value = false;
};
// 创建订单
const createOrder = async () => {
const res = await createOrderAPI({
// deliveryTimeType: 1,
// payType: 1,
// payChannel: 1,
// buyerMessage: "",
// goods: checkInfo.value.goods.map((item) => {
// return {
// goodId: item.goodsId,
// buyCount: item.buyCount,
// };
// }),
// addressId: curAddress.value.id,
// goodsIds: cartStore.cartList.filter((item) => item.selected).goodsId,
// orderid: time.getTime(),
goods: cartStore.cartList
.filter((item) => item.selected)
.map((item) => {
return {
id: item.id,
goodsId: item.goodsId,
buyCount: item.buyCount,
goodsPrice: (item.goodsPrice * item.buyCount).toFixed(2),
};
}),
addressId: curAddress.value.id,
amount: cartStore.selectedPrice,
orderId: time.value,
});
console.log("已经运行到这");
const orderId = time.value;
console.log(orderId);
console.log(orderId);
cartStore.updateNewList();
router.push({
path: "/pay",
query: {
id: orderId,
},
});
};
onMounted(() => {
getCheckInfo();
});
</script>
<template>
<div class="lyg-pay-checkout-page">
<div class="container">
<div class="wrapper">
<!-- 收货地址 -->
<h3 class="box-title">收货地址</h3>
<div class="box-body">
<div class="address">
<div class="text">
<div class="none" v-if="!curAddress">
您需要先添加收货地址才可提交订单。
</div>
<ul v-else>
<li>
<span>收<i />货<i />人:</span>{{ curAddress.userName }}
</li>
<li><span>联系方式:</span>{{ curAddress.phone }}</li>
<li>
<span>收货地址:</span>
{{ curAddress.address }}
</li>
</ul>
</div>
<div class="action">
<el-button size="large" @click="showDialog = true"
>切换地址</el-button
>
<el-button size="large">添加地址</el-button>
</div>
</div>
</div>
<!-- 商品信息 -->
<h3 class="box-title">商品信息</h3>
<div class="box-body">
<table class="goods">
<thead>
<tr>
<th width="520">商品信息</th>
<th width="170">单价</th>
<th width="170">数量</th>
<th width="170">小计</th>
<th width="170">实付</th>
</tr>
</thead>
<tbody>
<tr
v-for="i in cartStore.cartList.filter((item) => item.selected)"
:key="i.id"
>
<td>
<a href="javascript:;" class="info">
<img :src="i.picture" alt="" />
<div class="right">
<p>{{ i.goodsName }}</p>
<!-- <p>{{ i.attrsText }}</p> -->
</div>
</a>
</td>
<td>¥{{ i.goodsPrice }}</td>
<td>{{ i.buyCount }}</td>
<td>¥{{ (i.goodsPrice * i.buyCount).toFixed(2) }}</td>
<td>¥{{ (i.goodsPrice * i.buyCount).toFixed(2) }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 配送时间 -->
<!-- <h3 class="box-title">配送时间</h3>
<div class="box-body">
<a class="my-btn active" href="javascript:;"
>不限送货时间:周一至周日</a
>
<a class="my-btn" href="javascript:;">工作日送货:周一至周五</a>
<a class="my-btn" href="javascript:;">双休日、假日送货:周六至周日</a>
</div> -->
<!-- 支付方式 -->
<!-- <h3 class="box-title">支付方式</h3>
<div class="box-body">
<a class="my-btn active" href="javascript:;">在线支付</a>
<a class="my-btn" href="javascript:;">货到付款</a>
<span style="color: #999">货到付款需付5元手续费</span>
</div> -->
<!-- 金额明细 -->
<h3 class="box-title">金额明细</h3>
<div class="box-body">
<div class="total">
<dl>
<dt>商品件数:</dt>
<dd>{{ cartStore.selectedCount }} 件</dd>
</dl>
<dl>
<dt>商品总价:</dt>
<dd>¥{{ cartStore.selectedPrice.toFixed(2) }}</dd>
</dl>
<!-- <dl>
<dt>运<i></i>费:</dt>
<dd>¥{{ checkInfo.summary?.postFee.toFixed(2) }}</dd>
</dl> -->
<dl>
<dt>应付总额:</dt>
<dd class="price">
{{ cartStore.selectedPrice.toFixed(2) }}
</dd>
</dl>
</div>
</div>
<!-- 提交订单 -->
<div class="submit">
<el-button @click="createOrder" type="primary" size="large"
>提交订单</el-button
>
</div>
</div>
</div>
</div>
<!-- 切换地址 -->
<el-dialog v-model="showDialog" title="切换收货地址" width="30%" center>
<div class="addressWrapper">
<div
class="text item"
:class="{ active: activeAddress.id === item.id }"
@click="switchAddres(item)"
v-for="item in checkInfo.address"
:key="item.id"
>
<ul>
<li>
<span>收<i />货<i />人:</span>{{ item.userName }}
</li>
<li><span>联系方式:</span>{{ item.phone }}</li>
<li><span>收货地址:</span>{{ item.address }}</li>
</ul>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = flase">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 添加地址 -->
</template>
<style scoped lang="scss">
.lyg-pay-checkout-page {
margin-top: 20px;
.wrapper {
background: #fff;
padding: 0 20px;
.box-title {
font-size: 16px;
font-weight: normal;
padding-left: 10px;
line-height: 70px;
border-bottom: 1px solid #f5f5f5;
}
.box-body {
padding: 20px 0;
}
}
}
.address {
border: 1px solid #f5f5f5;
display: flex;
align-items: center;
.text {
flex: 1;
min-height: 90px;
display: flex;
align-items: center;
.none {
line-height: 90px;
color: #999;
text-align: center;
width: 100%;
}
> ul {
flex: 1;
padding: 20px;
li {
line-height: 30px;
span {
color: #999;
margin-right: 5px;
> i {
width: 0.5em;
display: inline-block;
}
}
}
}
> a {
color: $lygColor;
width: 160px;
text-align: center;
height: 90px;
line-height: 90px;
border-right: 1px solid #f5f5f5;
}
}
.action {
width: 420px;
text-align: center;
.btn {
width: 140px;
height: 46px;
line-height: 44px;
font-size: 14px;
&:first-child {
margin-right: 10px;
}
}
}
}
.goods {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
.info {
display: flex;
text-align: left;
img {
width: 70px;
height: 70px;
margin-right: 20px;
}
.right {
line-height: 24px;
p {
&:last-child {
color: #999;
}
}
}
}
tr {
th {
background: #f5f5f5;
font-weight: normal;
}
td,
th {
text-align: center;
padding: 20px;
border-bottom: 1px solid #f5f5f5;
&:first-child {
border-left: 1px solid #f5f5f5;
}
&:last-child {
border-right: 1px solid #f5f5f5;
}
}
}
}
.my-btn {
width: 228px;
height: 50px;
border: 1px solid #e4e4e4;
text-align: center;
line-height: 48px;
margin-right: 25px;
color: #666666;
display: inline-block;
&.active,
&:hover {
border-color: $lygColor;
}
}
.total {
dl {
display: flex;
justify-content: flex-end;
line-height: 50px;
dt {
i {
display: inline-block;
width: 2em;
}
}
dd {
width: 240px;
text-align: right;
padding-right: 70px;
&.price {
font-size: 20px;
color: $priceColor;
}
}
}
}
.submit {
text-align: right;
padding: 60px;
border-top: 1px solid #f5f5f5;
}
.addressWrapper {
max-height: 500px;
overflow-y: auto;
}
.text {
flex: 1;
min-height: 90px;
display: flex;
align-items: center;
&.item {
border: 1px solid #f5f5f5;
margin-bottom: 10px;
cursor: pointer;
&.active,
&:hover {
border-color: $lygColor;
background: lighten($lygColor, 50%);
}
> ul {
padding: 10px;
font-size: 14px;
line-height: 30px;
}
}
}
</style>
封装接口
代码模版
import http from '@/utils/http'
export function getCheckInfoAPI () {
return http({
url: '/order',
method: 'GET',
})
}
export function createOrderAPI (data) {
return http({
url: '/order',
method: 'POST',
data
})
}
export function getOrderAPI (id) {
return http({
url: `/order/${id}`,
method: 'GET',
})
}
配置路由
routes: [
{
path: '/',
component: Layout,
children: [
...
{
path: "checkout",
component: Checkout
}
]
}
]
生成订单
调用接口生成订单id, 并且携带id跳转到支付页
调用更新购物车列表接口,更新购物车状态
// 创建订单
const createOrder = async () => {
const res = await createOrderAPI({
goods: cartStore.cartList
.filter((item) => item.selected)
.map((item) => {
return {
id: item.id,
goodsId: item.goodsId,
buyCount: item.buyCount,
goodsPrice: (item.goodsPrice * item.buyCount).toFixed(2),
};
}),
addressId: curAddress.value.id,
amount: cartStore.selectedPrice,
orderId: time.value,
});
console.log("已经运行到这");
const orderId = time.value;
console.log(orderId);
console.log(orderId);
cartStore.updateNewList();
router.push({
path: "/pay",
query: {
id: orderId,
},
});
};
支付页面组件
创建文件 views/Pay/index.vue
编写代码
<script setup>
import { ref, onMounted } from 'vue'
import { getOrderAPI } from '@/api/order/index'
import { useRoute } from 'vue-router';
import { useCountDown } from '@/composables/useCountDown'
const { formatTime, start } = useCountDown()
const route = useRoute()
const payInfo = ref({})
console.log(route.query.id)
const getPayInfo = async () => {
console.log("已经运行")
console.log(route.query.id)
const res = await getOrderAPI(route.query.id)
payInfo.value = res.data
start(res.data.countdown)
}
onMounted(() => { getPayInfo() })
</script>
<template>
<div class="lyg-pay-page">
<div class="container">
<!-- 付款信息 -->
<div class="pay-info">
<span class="icon iconfont icon-queren2"></span>
<div class="tip">
<p>订单提交成功!请尽快完成支付。</p>
<p>支付还剩 <span>{{ formatTime }}</span>, 超时后将取消订单</p>
</div>
<div class="amount">
<span>应付总额:</span>
<span>¥{{ payInfo.amount?.toFixed(2) }}</span>
</div>
</div>
<!-- 付款方式 -->
<div class="pay-type">
<p class="head">选择以下支付方式付款</p>
<div class="item">
<p>支付平台</p>
<a class="btn wx" href="javascript:;"></a>
<a class="btn alipay" :href="payUrl"></a>
</div>
<div class="item">
<p>支付方式</p>
<a class="btn" href="javascript:;">招商银行</a>
<a class="btn" href="javascript:;">工商银行</a>
<a class="btn" href="javascript:;">建设银行</a>
<a class="btn" href="javascript:;">农业银行</a>
<a class="btn" href="javascript:;">交通银行</a>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.lyg-pay-page {
margin-top: 20px;
}
.pay-info {
background: #fff;
display: flex;
align-items: center;
height: 240px;
padding: 0 80px;
.icon {
font-size: 80px;
color: #1dc779;
}
.tip {
padding-left: 10px;
flex: 1;
p {
&:first-child {
font-size: 20px;
margin-bottom: 5px;
}
&:last-child {
color: #999;
font-size: 16px;
}
}
}
.amount {
span {
&:first-child {
font-size: 16px;
color: #999;
}
&:last-child {
color: $priceColor;
font-size: 20px;
}
}
}
}
.pay-type {
margin-top: 20px;
background-color: #fff;
padding-bottom: 70px;
p {
line-height: 70px;
height: 70px;
padding-left: 30px;
font-size: 16px;
&.head {
border-bottom: 1px solid #f5f5f5;
}
}
.btn {
width: 150px;
height: 50px;
border: 1px solid #e4e4e4;
text-align: center;
line-height: 48px;
margin-left: 30px;
color: #666666;
display: inline-block;
&.active,
&:hover {
border-color: $lygColor;
}
&.alipay {
background: url(https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/7b6b02396368c9314528c0bbd85a2e06.png) no-repeat center / contain;
}
&.wx {
background: url(https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/c66f98cff8649bd5ba722c2e8067c6ca.jpg) no-repeat center / contain;
}
}
}
</style>
倒计时组件
创建文件
// 封装倒计时函数
import { ref,computed, onUnmounted } from "vue"
import dayjs from "dayjs"
export const useCountDown =()=>{
// 响应式数据
const time = ref(0)
let timer = null
// 格式化事件为xx分xx秒
const formatTime = computed(()=>dayjs.unix(time.value).format('mm分ss秒'))
// 开启倒计时的函数
const start =(currentTime)=>{
// 开启倒计时的逻辑
time.value = currentTime
timer = setInterval(() => {
time.value--
}, 1000);
}
// 组件销毁清除定时器
onUnmounted(() => {
timer && clearInterval(timer)
})
return {
formatTime,start
}
}