绘制 商品列表 页面基本结构并美化
<template>
<div class="goods-list">
<div class="goods-item">
<img src="https://img14.360buyimg.com/n0/jfs/t1/68656/34/7819/87669/5d5e0becE1808db2a/4f29ef986f103e36.jpg" alt="">
<h1 class="title">小米(Mi)小米Note 16G双网通版</h1>
<div class="info">
<p class="price">
<span class="now">¥899</span>
<span class="old">¥999</span>
</p>
<p class="sell">
<span>热卖中</span>
<span>剩60件</span>
</p>
</div>
</div>
<div class="goods-item">
<img src="https://img14.360buyimg.com/n0/jfs/t1/68656/34/7819/87669/5d5e0becE1808db2a/4f29ef986f103e36.jpg" alt="">
<h1 class="title">尼康(Nikon)D3300套机(18-55mm f/3.5-5.6G VRII)(黑色)</h1>
<div class="info">
<p class="price">
<span class="now">¥899</span>
<span class="old">¥999</span>
</p>
<p class="sell">
<span>热卖中</span>
<span>剩60件</span>
</p>
</div>
</div>
<div class="goods-item">
<img src="https://img14.360buyimg.com/n0/jfs/t1/68656/34/7819/87669/5d5e0becE1808db2a/4f29ef986f103e36.jpg" alt="">
<h1 class="title">小米(Mi)小米Note 16G双网通版</h1>
<div class="info">
<p class="price">
<span class="now">¥899</span>
<span class="old">¥999</span>
</p>
<p class="sell">
<span>热卖中</span>
<span>剩60件</span>
</p>
</div>
</div>
</div>
</template>
<script>
</script>
<style lang="scss" scoped>
.goods-list{
display: flex;
flex-wrap: wrap;//默认情况下,flex项目都会尝试适合一行。 wrap规定灵活的项目在必要的时候拆行或拆列。
padding: 7px;
justify-content: space-between; //让里面的两个盒子靠边对齐
.goods-item{
width: 49%; //中间有2%的空隙
border: 1px solid #ccc;
box-shadow: 0 0 8px #ccc;
margin: 4px 0;
padding: 2px;
display: flex;
flex-direction: column;//改变主轴row沿x轴的方向成沿y轴
justify-content: space-between;//沿y轴上两个盒子靠边对齐,不然底部可能有很大空隙
min-height: 293px;//找盒子的最小高度,在还没加载出来有一个最小高度
img{
width: 100%;
}
.title{
font-size: 14px;
}
.info{
background-color: #eee;
p{
margin: 0;//p标签有默认Margin
padding: 5px;
}
.price{
.now{
color: red;
font-weight: bold;
font-size: 16px;
}
.old{
text-decoration: line-through; //删除线
font-size: 12px;
margin-left: 10px;
}
}
.sell{
display: flex;
justify-content: space-between;
font-size: 13px;
}
}
}
}
</style>
尝试在手机上 去进行项目的预览和测试
vue项目如何通过电脑连接手机热点在手机上访问:(wifi同理 无线局域网适配器 本地连接的IPV4)
- 首先当然得让电脑连上手机热点,使他们处于同一个局域网中;
- 去命令行工具输入
ipconfig
获取电脑的ip,如下图所示IPv4地址就是本机电脑的IP
无线局域网适配器 WLAN: IPv4 地址 - 去vue项目中修改配置项, package.json文件
“dev”: “webpack-dev-server --open --port 3000 --hot --host IPv4 地址” - 在电脑上打开之后,把地址输入到手机上即可。
在手机运行的时候,有的页面显示不该显示的东西。把click换成tap。
mui提供了tap事件替换了html5的click事件
@tap=“getPhotoListByCateId(item.id)”
加载商品列表中的数据
vue-router中编程式导航
<!-- 在网页中,有两种跳转方式: -->
<!-- 方式1: 使用 a 标签 的形式叫做 标签跳转 -->
<!-- 方式2: 使用 window.location.href 的形式,叫做 编程式导航 -->
<div class="goods-item" v-for="item in goodslist" :key="item.id" @click="goDetail(item.id)"></div>
goDetail(id) {
// 使用JS的形式进行路由导航
// 注意: 一定要区分 this.$route 和 this.$router 这两个对象,
// 其中: this.$route 是路由【参数对象】,所有路由中的参数, params, query 都属于它
// 其中: this.$router 是一个路由【导航对象】,用它 可以方便的 使用 JS 代码,实现路由的 前进、后退、 跳转到新的 URL 地址
// 1. 最简单的传字符串
// this.$router.push("/home/goodsinfo/" + id);
// 2. 传递对象
// this.$router.push({ path: "/home/goodsinfo/" + id });
// 3. 传递命名的路由
this.$router.push({ name: "goodsinfo", params: { id } });
}
绘制商品详情页面的卡片视图
在hello-mui,examples,card.html中 找对应的代码拷贝。
父级元素内部有子元素,如果给子元素添加margin-top样式,那么父级元素也会跟着下来,造成外边距塌陷:
overflow: hidden;
解决外边距塌陷
id: this.$route.params.id, // 将路由参数对象中的 id 挂载到 data , 方便后期调用
抽离轮播图组件 轮播图宽度的设置
<template>
<div>
<mt-swipe :auto="4000">
<!-- 将来,谁使用此 轮播图组件,谁为我们传递 lunbotuList -->
<!-- 此时,lunbotuList 应该是 父组件向子组件传值来设置 -->
<mt-swipe-item v-for="item in lunbotuList" :key="item.url">
<img :src="item.img" alt="" :class="{'full': isfull}">
<!--如果isfull为true,则应用full类宽度为100%。,否则宽度自适应-->
</mt-swipe-item>
</mt-swipe>
</div>
</template>
<script>
export default {
props: ["lunbotuList", "isfull"]
};
</script>
<style>
.full {
width: 100%;
}
</style>
其他页面引入该组件
import swiper from "../subcomponents/swiper.vue";
components: {swiper}
<swiper :lunbotuList="lunbotuList" :isfull="false"></swiper>
方法自己写
getLunbotu() {
this.$http.get("api/getthumimages/" + this.id).then(result => {
if (result.body.status === 0) {
// 先循环轮播图数组的每一项,为 item 添加 img 属性,因为 轮播图 组件中,只认识 item.img, 不认识 item.src
result.body.message.forEach(item => {
item.img = item.src;
});
this.lunbotu = result.body.message;
}
});
},
一点样式:
<del>¥oldprice</del>
删除线
display: flex;
的时候 br 不换行,若不想一行排列,可以让display: block;
购物车小球动画效果
页面中id="badge"购物车的位置:
<router-link class="mui-tab-item-llb" to="/shopcar">
<span class="mui-icon mui-icon-extra mui-icon-extra-cart">
<span class="mui-badge" id="badge">0</span>
</span>
<span class="mui-tab-label">购物车</span>
</router-link>
一些关键代码:
<!-- transition的过渡-->
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<!-- 只要想要在Vue中直接操作DOM元素,就必须用ref属性进行注册
ref在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;this.$refs.ball获取当前元素-->
<div class="ball" v-show="ballFlag" ref="ball"></div>
</transition>
<mt-button type="danger" size="small" @click="addToShopCar">加入购物车</mt-button>
data() {
return {
ballFlag: false, // 控制小球的隐藏和显示的标识符
};
addToShopCar() {
// 添加到购物车
this.ballFlag = !this.ballFlag;
},
beforeEnter(el) {
el.style.transform = "translate(0, 0)";
//translate(0, 0)位置(0, 0)
},
enter(el, done) {
el.offsetWidth;
//offsetWidth = width + padding +border
// 小球动画优化思路:
// 1. 先分析导致 动画 不准确的 本质原因: 我们把 小球 最终 位移到的 位置,已经局限在了某一分辨率下的 滚动条未滚动的情况下;
// 2. 只要分辨率和 测试的时候不一样,或者 滚动条有一定的滚动距离之后, 问题就出现了;
// 3. 因此,我们经过分析,得到结论: 不能把 位置的 横纵坐标 直接写死了,而是应该 根据不同情况,动态计算这个坐标值;
// 4. 经过分析,得出解题思路: 先得到 徽标的 横纵 坐标,再得到 小球的 横纵坐标,然后 让 y 值 求差, x 值也求 差,得到 的结果,就是横纵坐标要位移的距离
// 5. 如何 获取 徽标和小球的 位置 domObject.getBoundingClientRect()
// 获取小球的 在页面中的位置
const ballPosition = this.$refs.ball.getBoundingClientRect();
// 获取 徽标 在页面中的位置,只要页面中有,就可以获取到
const badgePosition = document
.getElementById("badge")
.getBoundingClientRect();
const xDist = badgePosition.left - ballPosition.left;
const yDist = badgePosition.top - ballPosition.top;
// ${xDist}模版字符串,等价于' + xDist + '
el.style.transform = `translate(${xDist}px, ${yDist}px)`;
el.style.transition = "all 0.5s cubic-bezier(.4,-0.3,1,.68)";
done();
},
afterEnter(el) {
this.ballFlag = !this.ballFlag;
}
},
.ball {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: red;
position: absolute;
z-index: 99; //让其在最上面显示
top: 390px; //找位置调试的时候在f12里鼠标滑动值
left: 146px;
}
当子组件numbox里面的值发生变化立即传给父组件,同时有最大数量
子组件:
<template>
<!-- 问题: 因为该max值是从父组件调用的方法里取到的,可能在拿到值前面该组件就被渲染完毕。我们不知道什么时候能够拿到 max 值,但是,总归有一刻,会得到一个真正的 max 值 -->
<!-- 我们可以 使用 watch 属性监听,来 监听 父组件传递过来的 max 值,不管 watch 会被触发几次,但是,最后一次,肯定是一个 合法的 max 数值 -->
<div class="mui-numbox" data-numbox-min='1'>
<button class="mui-btn mui-btn-numbox-minus" type="button">-</button>
<!--@change="countChanged" 值改变了就会触发事件countChanged -->
<input id="test" class="mui-input-numbox" type="number" value="1" @change="countChanged" ref="numbox" />
<button class="mui-btn mui-btn-numbox-plus" type="button">+</button>
</div>
</template>
<script>
import mui from "../../lib/mui/js/mui.min.js";
export default {
mounted() {
// 初始化数字选择框组件
mui(".mui-numbox").numbox();
console.log(this.max);
},
methods: {
countChanged() {
// 每当 文本框的数据被修改的时候,立即把 最新的数据,通过事件调用,传递给父组件
//在子组件中需要向父组件传值处使用this.$emit("function",param);
//其中function为父组件定义函数,param为需要传递参数
//在父组件中子组件引用处添加函数 @:function="function1";
其中function为子组件中定义函数,function1为父组件定义函数--用于接收子组件传值并进行相应数据处理,可定义为同一名称
this.$emit("getcount", parseInt(this.$refs.numbox.value));
}
},
props: ["max"],//从父组件取到的值
watch: {
// 属性监听
max: function(newVal, oldVal) {
// 使用Mui里该组件的 JS API , 设置 numbox 的最大值
mui(".mui-numbox")
.numbox()
.setOption("max", newVal);
}
}
};
</script>
<style lang="scss" scoped>
</style>
父组件:
<!-- 分析: 如何实现加入购物车时候,拿到 选择的数量 -->
<!-- 1. 经过分析发现: 按钮属于 goodsinfo 页面, 数字 属于 numberbox 组件 -->
<!-- 2. 由于涉及到了父子组件的嵌套了,所以,无法直接在 goodsinfo 页面zhong 中获取到 选中的商品数量值-->
<!-- 3. 怎么解决这个问题:涉及到了 子组件向父组件传值了(事件调用机制) -->
<!-- 4. 事件调用的本质: 父向子传递方法,子调用这个方法, 同时把 数据当作参数 传递给这个方法 -->
<p>购买数量:<numbox @getcount="getSelectedCount" :max="goodsinfo.stock_quantity"></numbox></p>
data里 selectedCount: 1 // 保存用户选中的商品数量, 默认,认为用户买1个
getSelectedCount(count) {
// 当子组件把 选中的数量传递给父组件的时候,把选中的值保存到 data 上
this.selectedCount = count;
console.log("父组件拿到的数量值为: " + this.selectedCount);
}