效果概述:
列表页面显示数据,点击跳转到对应的详情页,详情页可以添加并跳转到购物车,购物车具有常见功能。
具体效果:
1、所有页面根据效果图100%还原。
2、列表页面通过axios请求拿到数据并显示。
3、跳转到详情页时,显示对应列表的数据。
4、详情页内容可以添加到购物车中并显示。
5、购物车商品的所有小计可根据数量的变化而动态显示。
6、购物车商品的总计可根据数量的变化动态显示。
7、购物车的全选按钮可以选中所有商品,所有商品均被选中时,全选按钮也会自动显示被选中。
实现后效果图:
vue实现列表跳转至详情,且能加入购物车
大致代码思路
- 列表页:
在 main.js 页面引入 axios 插件在 main.js 页面引入 rem 移动端适配代码通过 rem 进行移动端适配通过 axios 接口地址请求数据把请求到的数据赋值给data里的选项在 html 代码中通过 v-for 实现循环,把数据渲染至视图中使用 router-link 标签实现跳转功能,并把列表的 id 通过 query 传递至跳转后的页面
- 详情页:
使用 this.$route.query.id 获取上个页面通过 query 传递的 id 值使用 axios 接口地址和接收到的 id 值请求详细数据把请求到的数据赋值给data里的选项在 html 代码中通过 v-for 实现循环,把数据渲染至视图中通过编程式导航,实现页面跳转效果,并在组件中整理需要添加至购物车里的内容把需要添加至购物车的内容,通过 vuex 的同步请求,把内容添加至 vuex 里面添加后把内容保存至本地
- 购物车页:
通过 vuex 的辅助函数获取到上个页面中添加的内容通过 v-for 循环,渲染到视图中通过全选按钮实现全选/全不选功能点击全选则全选的值进行反转通过 some 循环,把全选的值赋值给所有单选框单选全部选中则全选也选中使用 filter 过滤出所有单选框为 true 的列表把他的长度和所有列表的长度进行比较如果等于则全选框为 true,否则全选框的值为 false点击加则数量加一,点击减则数量减一单价等于 单个的价格乘以数量总价等于 单价乘以数量后所有的加在一起
main.js 代码
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
// 引入rem
import "./rem"
// 引入axios
import axios from 'axios'
// 将axios添加到vue的原型上
Vue.prototype.$axios = axios
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()
解决跨域问题,在vue.config.js里添加代码
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 解决跨域问题
devServer:{
proxy:{
'/api':{
target: 'http://192.168.1.13:8765/',
pathRewrite:{
'^/api': ''
}
}
}
}
})
实现路由跳转页代码
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'
Vue.use(VueRouter)
// 配置路由跳转项
const routes = [
{
path:'/',
name:'Home',
component: Home
},
{
path:'/detail',
name:'Detail',
component: ()=> import('@/views/Detail.vue')
},
{
path:'/shopping',
name:'Shopping',
component: ()=> import('@/views/Shopping.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
})
export default router
app根组件代码
<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>
效果列表页代码
<template>
<div class="home-box">
<ul>
<!-- 声明式导航,通过query把id值传递至详情页中 -->
<!-- 给 list 属性执行for循环 -->
<router-link
:to="{ name: 'Detail', query: { id: item.id } }"
class="home-row"
v-for="item in list"
:key="item.id"
>
<span class="left">
<!-- 图片 -->
<img :src="item.imgsrc" />
</span>
<span class="right">
<!-- 名称 -->
<h3>{{ item.name }}</h3>
<p>
<!-- 价格 -->
<b>¥{{ item.price }}</b>
</p>
</span>
</router-link>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
list: [],
};
},
created() {
// 进入页面时执行该方法
this.request();
},
methods: {
// 请求
request() {
// 请求到所以数据
this.$axios.get(`/api/meishi?star=1&num=15`).then((res) => {
// 把请求到的数据赋值给list
this.list = res.data;
});
},
},
};
</script>
<style lang="scss" scoped>
.home-box {
width: 100%;
background-color: #f6f6f6;
display: flex;
justify-content: center;
ul {
margin: 0 0.2rem;
.home-row {
width: 3.55rem;
height: 1rem;
color: #000;
margin-top: 0.1rem;
text-decoration: none;
padding: 0.1rem;
display: inline-block;
background-color: #fff;
display: flex;
justify-content: center;
.left {
img {
width: 0.8rem;
}
}
.right {
padding-left: 0.2rem;
h3 {
width: 2.35rem;
font-size: 0.18rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
p {
padding-top: 0.2rem;
font-size: 0.14rem;
text-align: center;
display: -webkit-box; //对象作为弹性伸缩盒子模型显示
overflow: hidden; //溢出隐藏
-webkit-box-orient: vertical; //设置伸缩盒子对象的子元素的排列方式
-webkit-line-clamp: 2; //设置 块元素包含的文本行数
}
}
}
}
}
</style>
详情页代码
<template>
<div class="detail-box">
<span>
<!-- 图片 -->
<img :src="list.imgsrc" />
</span>
<span>
<!-- 名称 -->
<h3>{{ list.name }}</h3>
</span>
<span>
<!-- 介绍 -->
<p>{{ list.contents.content.toString() }}</p>
</span>
<span>
<p>
<!-- 价格 -->
<b>¥{{ list.price }}</b>
</p>
</span>
<span>
<!-- 点击跳转至购物车页面,并添加当前页面内容至购物车 -->
<button @click="ShoppingClick">加入购物车</button>
</span>
</div>
</template>
<script>
// 引入vuex里的辅助函数
import { mapState } from "vuex";
export default {
data() {
return {
list: [],
};
},
computed: {
// 获取vuex里面的值
...mapState(["lists"]),
},
created() {
// 进入时执行请求
this.request();
},
methods: {
// 请求
request() {
// 使用axios请求所需下标的参数
this.$axios.get(`/api/meishi?id=${this.$route.query.id}`).then((res) => {
// 把请求到的值赋值给data里的list参数
this.list = res.data;
});
},
// 加入购物车
ShoppingClick() {
// 自定义添加的内容
let arr = {
// 单选框
checked: false,
// id值
id: this.lists.length + 1,
// 图片链接
img: this.list.imgsrc,
// name名称
name: this.list.name,
// 单价
Price: this.list.price,
// 数量
num: 1,
};
// 触发vuex里的功能,把自定义的内容传递值vuex
this.$store.commit("ShoppingClick", arr);
// 编程式导航,跳转至购物车页面
this.$router.push({
name: "Shopping",
});
},
},
};
</script>
<style lang="scss" scoped>
.detail-box {
width: 100%;
height: 100vh;
background-color: #f6f6f6;
span {
display: inline-block;
margin: 0 0.05rem;
display: flex;
justify-content: center;
img {
width: 3.65rem;
height: 3.65rem;
}
h3 {
font-size: 0.2rem;
margin-top: 0.15rem;
}
p {
margin-top: 0.15rem;
}
}
}
</style>
购物车页面代码
<template>
<div class="Shopping-box">
<div class="top">购物车</div>
<ul class="content">
<!-- 循环 -->
<li class="Shopping-row" v-for="item in lists" :key="item.id">
<!-- 单选框 -->
<input type="checkbox" v-model="item.checked" @change="checkClick" />
<!-- 图片 -->
<img :src="item.img" alt="" />
<div class="row-right">
<span class="name">{{ item.name }}</span>
<!-- 单个价格 -->
<p class="price">¥{{ item.Price * item.num }}</p>
<span class="number">
<!-- 点击实现单个加一 -->
<button @click="addClick(item)">+</button>
<!-- 单个数量 -->
<input type="text" v-model="item.num" />
<!-- 点击实现单个减一 -->
<button @click="subtract(item)">-</button>
</span>
</div>
</li>
</ul>
<div class="bottom">
<span>
<input type="checkbox" v-model="alls" @change="AllClick" />
全选
</span>
<!-- 总价 -->
<span>总计:¥{{ total }}</span>
</div>
</div>
</template>
<script>
// 引入 vuex 的辅助函数
import { mapState } from "vuex";
export default {
data() {
return {};
},
computed: {
// 获取vuex里面的值
...mapState(["lists", "alls", "number"]),
// 总计
total() {
// 声明一个变量 num ,默认为0
let num = 0;
// 给 lists 数组进行 map 循环
this.lists.map((item) => {
// 判断是否选中
if (item.checked) {
// num += 数量 乘以 单价
// +=代表:a+=2 a=a+2
num += item.num * item.Price;
}
});
// 把 num 值 return 给 total
return num;
},
},
methods: {
// 全选
AllClick() {
// 触发vuex里的同步提交
this.$store.commit("AllClick");
},
// 单选
checkClick() {
// 触发vuex里的同步提交
this.$store.commit("checkClick");
},
// 单个数量加
addClick(item) {
// 触发vuex里的同步提交
this.$store.commit("addClick", item);
},
// 单个数量减
subtract(item) {
// 触发vuex里的同步提交,把 item 代表的值传递过去
this.$store.commit("subtract", item);
},
},
};
</script>
<style lang="scss" scoped>
.Shopping-box {
width: 100%;
// height: 100vh;
min-height: 100vh;
background-color: #f6f6f6;
.top {
width: 100%;
height: 0.5rem;
text-align: center;
line-height: 0.5rem;
font-size: 0.2rem;
background-color: #fff;
position: fixed;
top: 0;
left: 0;
}
.content {
width: 3.55rem;
margin: 0 0.1rem;
overflow: hidden;
.Shopping-row:first-child {
margin-top: 0.6rem;
}
.Shopping-row:last-child {
margin-bottom: 0.6rem;
}
.Shopping-row {
height: 1rem;
margin-top: 0.12rem;
padding: 0.1rem;
background-color: #fff;
display: flex;
justify-content: center;
img {
width: 0.8rem;
height: 0.8rem;
margin: 0 0.1rem;
}
.row-right {
.name {
display: inline-block;
width: 2.3rem;
font-weight: 800;
font-size: 0.18rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.price {
color: red;
font-size: 0.16rem;
}
.number {
height: 0.2rem;
display: inline-block;
border: 1px solid #f6f6f6;
border-radius: 50%;
line-height: 0.3rem;
button {
width: 0.2rem;
border-radius: 50%;
border: none;
text-align: center;
}
input {
text-align: center;
width: 0.3rem;
border: none;
}
}
}
}
}
.bottom {
width: 3.75rem;
height: 0.5rem;
padding: 0 0.1rem;
display: flex;
line-height: 0.5rem;
font-size: 0.16rem;
background-color: #fff;
justify-content: space-between;
position: fixed;
bottom: 0;
left: 0;
}
}
</style>
vuex页面代码
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 如果有本地存储则显示否则显示空数组
lists:JSON.parse(localStorage.getItem('list')) || [],
// 全选框
alls:false,
},
getters: {
},
mutations: {
// 加入购物车
ShoppingClick(state,data){
// 给 lists 进行 some 循环
let a = state.lists.some((item)=>{
// lists 里存在的和添加的name名相同则返回true,否则返回false
return item.name == data.name
})
// 判断 添加的内容购物车内是否已经存在
if(a){
alert('购物车内部已存在')
}else{
// 如果不存在则添加
state.lists.push(data)
// 保存至本地
localStorage.setItem('list',JSON.stringify(state.lists))
}
},
// 全选
AllClick(state){
// 把全选的值改为相反
state.alls = !state.alls
// 循环lists
state.lists.some((item)=>{
// lists里input框的值,等于全选的值
item.checked = state.alls
})
// 保存至本地
localStorage.setItem('list',JSON.stringify(state.lists))
},
// 单选
checkClick(state){
// 给lists进行过滤
let arr = state.lists.filter((item) => {
// 返回checked为true的值
return item.checked;
});
// 判断lists里checked为true值得长度是否等于所以的长度,如果等于则全选框选中,否则不选中
arr.length == state.lists.length ? (state.alls = true) : (state.alls = false);
// 保存至本地
localStorage.setItem('list',JSON.stringify(state.lists))
},
// 单个价格加
addClick(state,item){
// 传递过来的当前列表里的num加1
item.num = item.num+1
// console.log(state.number);
// 保存至本地
localStorage.setItem('list',JSON.stringify(state.lists))
},
// 单个价格减
subtract(state,item){
// 判断数量是否大于1
if(item.num>1){
// 大于1则执行,传递过来的当前列表里的num减1
item.num = item.num-1
// 保存至本地
localStorage.setItem('list',JSON.stringify(state.lists))
}
}
},
actions: {
},
modules: {
}
})
以上就是列表跳转至详情并加入购物车的代码,以后会持续发布一些新文章,敬请关注。
我的其他文章:https://blog.csdn.net/weixin_62897746?type=blog
版权归原作者 清风 与我 所有, 如有侵权,请联系我们删除。