效果概述
列表页面显示数据,实现按尺码进行过滤,实现价格高低排序。点击添加至购物车,实现购物车所需内容。
具体效果
点击跳转至效果页面(Typescript React Shopping cart)
1.列表页Tab切换效果实现 2.列表页面点击排序效果 3.请求接口实现商品列表 4.添加购物车的数据保存到本地存储, 数据存放以及操作使用vuex。 5.商品点击加减变化效果 6.计算总数量和总价钱 7.移动端适配
实现后效果图:
列表添加至购物车(Typescript React Sho)
大致代码思路:
- 根页面:
router-view 标签接收数据
列表页
在 main.js 页面引入 axios 插件 在 main.js 页面引入 rem 移动端适配代码 通过 rem 进行移动端适配 通过 axios 接口地址请求数据 把请求到的数据赋值给data里的选项 在 html 代码中通过 v-for 实现循环,把数据渲染至视图中 点击添加至购物车 按价格实现从高到低,从低到高 按尺码进行过滤 实现分期购买价格
购物车页:
通过vuex获取添加过来得数据 在 html 代码中通过 v-for 实现循环,把数据渲染至视图中 数量加减 总价和分期价格
main.js代码:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import axios from 'axios' Vue.prototype.$axios = axios import '@/rem' Vue.config.productionTip = false new Vue({ router, store, render: h => h(App) }).$mount('#app')
rem 适配页面代码:
const WIDTH = 375//如果是尺寸的设计稿在这里修改 const setView = () => { //设置html标签的fontSize document.documentElement.style.fontSize = (100 * document.documentElement.clientWidth / WIDTH) + 'px' } window.onresize = setView setView()
实现路由跳转页代码:
import Vue from 'vue' import VueRouter from 'vue-router' import HomeView from '../views/HomeView.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'home', component: HomeView }, { path: '/shopping', name: 'Shopping', component:()=>import('@/views/Shopping.vue') } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
根页面代码:
<template> <div id="app"> <router-view></router-view> </div> </template> <style lang="scss"> * { margin: 0; padding: 0; box-sizing: border-box; font-size: 16px; } </style>
vuex页面代码:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { list: JSON.parse(localStorage.getItem('list')) || [], }, getters: { // 请求到多少个 totals(state) { let num = 0; state.list.map((item) => { num += item.num; }); return num; }, // 购物车有多少个 total(state) { let num = 0; state.list.map((item) => { num += item.num * item.price; }); return num; }, // 购物车总价 installments(state) { let num = 0; state.list.map((item) => { num += (item.num * item.price) / item.installments; }); return Math.floor(num * 100) / 100; }, }, mutations: { // 添加至购物车 addShopping(state, data) { // 需要添加的内容 let arr = {id:state.list+1,title:data.title,price:data.price,style:data.style,availableSizes:data.availableSizes,num:1,installments:data.installments} // 判断需要添加的内容在购物车中是否存在 let a = state.list.some((item) => { return item.title == data.title; }); // 获取添加的值在购物车中的下标 let num = 0 for(let i=0; i<state.list.length; i++){ if(state.list[i].title == data.title){ num = i } } // 判断添加的内容购物车中是否存在 if (a) { // 存在则数量加一 state.list[num].num = state.list[num].num+1 } else { // 不存在则添加至购物车 state.list.push(arr) } // 存储到本地 localStorage.setItem('list',JSON.stringify(state.list)) }, // 购物车单个数量减 jian(state, data) { // 判断数量是否大于一,大于一则点击一次减一,否则不变 data.num > 1 ? data.num = data.num - 1 : data.num // 本地存储 localStorage.setItem('list',JSON.stringify(state.list)) }, // 购物车单个数量加 jia(state, data) { // 点击加一 data.num = data.num + 1 // 本地存储 localStorage.setItem('list',JSON.stringify(state.list)) }, // 购物车中删除 deletes(state, data) { // 点击删除 state.list.splice(data.id - 1, 1) // 本地存储 localStorage.setItem('list',JSON.stringify(state.list)) }, }, actions: { }, modules: { } })
列表页代码:
<template> <div class="box"> <div class="top"> <div class="topleft"></div> <router-link to="/shopping" tag="div" class="topright"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfiCwwQEgYn7+gWAAABQklEQVRIx9WTsUoDQRRF72xMYSFokcRCjBFESGWRwtZe8AtEFFNrI1bB0tLGQuzs/AJB8AO2kZRCxBiRQNKIoIYoyLFwCZuss9nNgpBXzc7be3j3zow09mU48321zVF8gNtbZ5Q1UwmGYQPIxFU5vnVdUuHfARO+OD5oa9GzM6NcbICkem+CLZ0kA1zKHQ2w6tlpqRUN4AwA5knFiTAISGsuHqDfwoOkgp4kUkoP1WI+Azt02ZYk9hle3cAEBh69c7iKEOJ30IJU/71KpqbaKCH6b0LEGgTcaYXpeIj+GJf54pyI70CSTACxq1M5ehmqvDCHfwIkFrQW4S1WzXUCq+E5lNhkMqSfo0ze3t7hlWeqIfJ3GnQo2n644ZhZYMnSL9OQcKnYAHs0ueUeY+nn6eDyRskGcFjngGxIBkUqVvk41g+oBJ136GBf8AAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0xMS0xMlQxNjoxODowNiswMTowMEVm3zEAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMTEtMTJUMTY6MTg6MDYrMDE6MDA0O2eNAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAABJRU5ErkJggg==" alt="" /> <span>{{ totals }}</span> </router-link> </div> <div class="sizes"> <div class="sizes-li"> <h4>Sizes:</h4> <div class="size"> <button @click="sizes('XS')">XS</button> <button @click="sizes('S')">S</button> <button @click="sizes('M')">M</button> <button @click="sizes('ML')">ML</button> <button @click="sizes('L')">L</button> <button @click="sizes('XL')">XL</button> <button @click="sizes('XXL')">XXL</button> </div> <p>Leave a star on Github if this repository was useful :)</p> <a href="#">Star</a> </div> <p>{{ listd.length }} Product(s) found</p> <select name="" id="" v-model="val" @click="vals"> <option value="默认">默认</option> <option value="升序">高到低</option> <option value="降序">低到高</option> </select> </div> <ul class="content"> <li v-for="item in lists" :key="item.id"> <div class="img"> <p v-show="item.isFreeShipping">免运费</p> </div> <div class="title">{{ item.title }}</div> <p class="price">{{ item.currencyFormat }} {{ item.price }}</p> <p class="installments" v-show="item.installments == 0 ? false : true"> or {{ item.installments }} x {{ item.currencyFormat }} {{ Math.floor((item.price / item.installments) * 100) / 100 }} </p> <router-link to="/shopping" class="join" ><button @click="addShopping(item)">加入购物车</button></router-link > </li> </ul> </div> </template> <script> import { mapState, mapGetters } from "vuex"; export default { data() { return { listd: [], lists: [], val: "默认", }; }, // 获取 vuex 里的内容 computed: { ...mapState(["list"]), ...mapGetters(["totals"]), }, created() { // 进入时调用 this.request(); }, methods: { request() { // 请求接口 this.$store.commit("request"); this.$axios .get(`https://react-shopping-cart-67954.firebaseio.com/products.json`) .then(({ data }) => { console.log(data); // 把请求到的值分别赋值给 listd 和 lists this.listd = data.products; this.lists = data.products; // 存储到本地 localStorage.setItem("listsd", JSON.stringify(this.listd)); }); }, //添加到购物车 addShopping(item) { this.$store.commit("addShopping", item); }, // 点击显示尺码 sizes(val) { // lists 赋值为空 this.lists = []; // 给 listd 进行过滤 this.listd.filter((item, index) => { // listd值得availableSizes进行循环 item.availableSizes.some((items) => { // 判断availableSizes的值是否等于传递过来的参数 if (items == val) { // 等于则追加到 lists 中 this.lists.push(item); } }); }); }, // 升序降序 vals() { // 给 lists 用 sort 进行排序 this.lists.sort((p1, p2) => { // 判断val 等于升序 则 2 - 1 if (this.val == "升序") { return p2.price - p1.price; // 判断val 等于降序 则 1 - 2 } else if (this.val == "降序") { return p1.price - p2.price; // 判断val 等于默认 则把本地存储的值赋值给lists } else if (this.val == "默认") { this.lists = JSON.parse(localStorage.getItem("listsd")); } }); }, }, }; </script> <style lang="scss" scoped> .box { width: 100%; .top { width: 100%; height: 0.5rem; // background-color: red; position: relative; .topleft { width: 1.2rem; height: 1.2rem; background-color: #000; position: absolute; transform: rotate(45deg); top: -0.6rem; left: -0.6rem; } .topright { width: 0.5rem; height: 0.5rem; background-color: #000; position: absolute; right: 0; display: flex; justify-content: space-between; align-items: center; span { display: inline-block; position: absolute; bottom: 0; left: 0; width: 0.2rem; height: 0.2rem; border-radius: 50%; text-align: center; line-height: 0.2rem; background-color: yellow; } } } .sizes { width: 100%; padding: 0.15rem; .sizes-li { margin: 0 0.15rem; .size { margin: 0.2rem 0; display: flex; justify-content: space-between; button { width: 0.35rem; height: 0.35rem; border-radius: 50%; background-color: aliceblue; border: none; } } p { margin: 0.15rem 0; font-size: 0.1rem; } a { font-size: 0.15rem; } } p { margin: 0.3rem 0 0.2rem; } } .content { width: 100%; padding: 0 0.15rem; background-color: red; display: flex; justify-content: space-between; flex-wrap: wrap; li { width: 1.675rem; height: 4.5rem; list-style: none; margin: 0.1rem 0; text-align: center; .img { width: 100%; height: 2.7rem; background-color: pink; p { } } .title { height: 0.4rem; padding: 0 0.2rem; margin: 0.15rem 0; } .price { } .installments { color: gainsboro; font-size: 0.12rem; } .join { width: 100%; height: 0.6rem; padding: 0.15rem 0; display: inline-block; background-color: #000; border: none; margin-top: 0.1rem; button { border: none; background-color: #000; color: #fff; } } } } } </style>
购物车页代码:
<template> <div class="box"> <div class="top"> <router-link to="/" tag="div" class="topleft">X</router-link> </div> <div class="num"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfiCwwQEgYn7+gWAAABQklEQVRIx9WTsUoDQRRF72xMYSFokcRCjBFESGWRwtZe8AtEFFNrI1bB0tLGQuzs/AJB8AO2kZRCxBiRQNKIoIYoyLFwCZuss9nNgpBXzc7be3j3zow09mU48321zVF8gNtbZ5Q1UwmGYQPIxFU5vnVdUuHfARO+OD5oa9GzM6NcbICkem+CLZ0kA1zKHQ2w6tlpqRUN4AwA5knFiTAISGsuHqDfwoOkgp4kUkoP1WI+Azt02ZYk9hle3cAEBh69c7iKEOJ30IJU/71KpqbaKCH6b0LEGgTcaYXpeIj+GJf54pyI70CSTACxq1M5ehmqvDCHfwIkFrQW4S1WzXUCq+E5lNhkMqSfo0ze3t7hlWeqIfJ3GnQo2n644ZhZYMnSL9OQcKnYAHs0ueUeY+nn6eDyRskGcFjngGxIBkUqVvk41g+oBJ136GBf8AAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0xMS0xMlQxNjoxODowNiswMTowMEVm3zEAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMTEtMTJUMTY6MTg6MDYrMDE6MDA0O2eNAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAABJRU5ErkJggg==" alt="" /> <h4>Cart</h4> </div> <ul class="content"> <li v-for="item in list" :key="item.id"> <div class="img"></div> <div class="center"> <p>{{ item.title }}</p> <p>{{ item.availableSizes[0] }} | {{ item.style }}</p> <p>数量:{{ item.num }}</p> </div> <div class="right"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAOCAYAAADT0Rc6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzgwMTE3NDA3MjA2ODExODA4MzlFRjgwMkJGMENDMSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0NzRFMzQ0QjI3MzgxMUU4QjRFMUVBNEJCODU5RDAzMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0NzRFMzQ0QTI3MzgxMUU4QjRFMUVBNEJCODU5RDAzMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RUE3RjExNzQwNzIwNjgxMUIxQTQ5QTgyNkJBMzJBOEUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDM4MDExNzQwNzIwNjgxMTgwODM5RUY4MDJCRjBDQzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5cNiR0AAAA50lEQVR42qSUaw6EMAiEYY8rB7JcVrYoNaQCNbsk88N2ypc+HICrtq69CyEvNM8mIuCk33sXTuNeaJ5zrRZ1HV361RIw2pyYd4Cp65CrWgJGmxPz6gbvZpKAMfJYw9FMEjBGntGUE3AEVC+6ppyAI6B60e8mAldAWIBD4LjTCvwAqjG5txkcAmdoBWZ/z8UL5RVQ9YF3JfB7lWtXx9v+ON4WHW8E5GTszUPiZOx+SBkQq7kFEKs5yH6LxbFn4cBBOHAUDj4GuYhBdjFIQQxyEYPsYpBGUypyd45DmppSkbtzHJ5rvwIMAKXLCXxfiHXkAAAAAElFTkSuQmCC" @click="deletes(item)" /> <p>$ {{ item.price * item.num }}</p> <div> <button @click="jian(item)">-</button> <button @click="jia(item)">+</button> </div> </div> </li> </ul> <div class="bottom"> <div> <span>小计</span> <span> $ {{ total }} <p v-if="list.length < 1 ? false : true"> or up to {{ list[list.length - 1].installments }} X $ {{ installments }} </p> </span> </div> <button @click="collection">收款处</button> </div> </div> </template> <script> import { mapGetters, mapState } from "vuex"; export default { computed: { // 获取 vuex 里的内容 ...mapState(["list", "num"]), ...mapGetters(["total", "installments"]), }, methods: { // 减 jian(item) { this.$store.commit("jian", item); }, // 加 jia(item) { this.$store.commit("jia", item); }, // 收款 collection() { alert("Checkout - Subtotal: $ " + this.total); }, // 删除 deletes(item) { this.$store.commit("deletes", item); }, }, }; </script> <style lang="scss" scoped> .box { width: 100%; height: 100vh; background-color: #000; opacity: 0.9; color: #fff; .top { .topleft { width: 0.5rem; height: 0.5rem; text-align: center; line-height: 0.5rem; } } .num { width: 100%; // background-color: red; text-align: center; display: flex; justify-content: center; align-items: center; h4 { margin-left: 0.2rem; } } .content { li { width: 100%; padding: 0 0.15rem; border-top: 1px solid #000; opacity: 1; display: flex; justify-content: space-between; .img { width: 0.5rem; height: 0.75rem; background-color: #fff; } } } .bottom { width: 100%; position: fixed; bottom: 0; div { width: 100%; display: flex; justify-content: space-between; } button { width: 100%; height: 0.5rem; border: none; } } } </style>
以上就是列表添加至购物车(Typescript React Shopping cart)的代码,不懂得也可以在评论区里问我,以后会持续添加一些新的功能,敬请关注。