文章目录
- 14-商品详情-数量选择组件
- 15-商品详情-按钮组件
- 16-商品详情-同类推荐组件
14-商品详情-数量选择组件
目的:封装一个通用的数量选中组件。
大致功能分析:
- 默认值为1
- 可限制最大最小值
- 点击-就是减1 点击+就是加1
- 需要完成v-model得实现
- 存在无label情况
基础布局代码:src/components/library/xtx-numbox.vue
<template>
<div class="xtx-numbox">
<div class="label">数量</div>
<div class="numbox">
<a href="javascript:;">-</a>
<input type="text" readonly value="1">
<a href="javascript:;">+</a>
</div>
</div>
</template>
<script>
export default {
name: 'XtxNumbox'
}
</script>
<style scoped lang="less">
.xtx-numbox {
display: flex;
align-items: center;
.label {
width: 60px;
color: #999;
padding-left: 10px;
}
.numbox {
width: 120px;
height: 30px;
border: 1px solid #e4e4e4;
display: flex;
> a {
width: 29px;
line-height: 28px;
text-align: center;
background: #f8f8f8;
font-size: 16px;
color: #666;
&:first-of-type {
border-right:1px solid #e4e4e4;
}
&:last-of-type {
border-left:1px solid #e4e4e4;
}
}
> input {
width: 60px;
padding: 0 5px;
text-align: center;
color: #666;
}
}
}
</style>
逻辑功能实现:
src/components/library/xtx-numbox.vue
<script>
import { useVModel } from '@vueuse/core'
export default {
name: 'XtxNumbox',
props: {
label: {
type: String
},
modelValue: {
type: Number,
default: 1
},
min: {
type: Number,
default: 1
},
max: {
type: Number,
default: 100
}
},
setup (props, { emit }) {
const num = useVModel(props, 'modelValue', emit)
const changeNum = (value) => {
const newValue = num.value + value
if (newValue < props.min) return
if (newValue > props.max) return
num.value = newValue
emit('change', newValue)
}
return { num, changeNum }
}
}
src/views/goods/index.vue
<XtxNumbox label="数量" v-model="num" :max="goods.inventory"/>
// 选择的数量
+ const num = ref(1)
+ return { goods, changeSku, num }
15-商品详情-按钮组件
目的:封装一个通用按钮组件,有大、中、小、超小四种尺寸,有默认、主要、次要、灰色四种类型。
大致步骤:
- 完成组件基本结构
- 介绍各个参数的使用
- 测试按钮组件
落地代码:
- 封装组件:
src/components/library/xtx-numbox.vue
<template>
<button class="xtx-button ellipsis" :class="[size,type]">
<slot />
</button>
</template>
<script>
export default {
name: 'XtxButton',
props: {
size: {
type: String,
default: 'middle'
},
type: {
type: String,
default: 'default'
}
}
}
</script>
<style scoped lang="less">
.xtx-button {
appearance: none;
border: none;
outline: none;
background: #fff;
text-align: center;
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;
}
.large {
width: 240px;
height: 50px;
font-size: 16px;
}
.middle {
width: 180px;
height: 50px;
font-size: 16px;
}
.small {
width: 100px;
height: 32px;
font-size: 14px;
}
.mini {
width: 60px;
height: 32px;
font-size: 14px;
}
.default {
border-color: #e4e4e4;
color: #666;
}
.primary {
border-color: @xtxColor;
background: @xtxColor;
color: #fff;
}
.plain {
border-color: @xtxColor;
color: @xtxColor;
background: lighten(@xtxColor,50%);
}
.gray {
border-color: #ccc;
background: #ccc;;
color: #fff;
}
</style>
- 使用组件:
src/views/goods/index.vue
<div class="spec">
<GoodsName :goods="goods"/>
<GoodsSku :goods="goods" @change="changeSku"/>
<XtxNumbox label="数量" v-model="num" :max="goods.inventory"/>
+ <XtxButton type="primary" style="margin-top:20px;">加入购物车</XtxButton>
</div>
16-商品详情-同类推荐组件
目的:实现商品的同类推荐与猜你喜欢展示功能。
大致功能需求:
- 完成基础布局(头部),后期改造xtx-carousel.vue组件来展示商品效果。
- 然后可以通过是否传入商品ID来区别同类推荐和猜你喜欢。
落的代码开始:
- 基础布局
src/views/goods/components/goods-relevant.vue
<template>
<div class="goods-relevant">
<div class="header">
<i class="icon" />
<span class="title">同类商品推荐</span>
</div>
<!-- 此处使用改造后的xtx-carousel.vue -->
</div>
</template>
<script>
export default {
// 同类推荐,猜你喜欢
name: 'GoodsRelevant'
}
</script>
<style scoped lang='less'>
.goods-relevant {
background: #fff;
min-height: 460px;
margin-top: 20px;
.header {
height: 80px;
line-height: 80px;
padding: 0 20px;
.title {
font-size: 20px;
padding-left: 10px;
}
.icon {
width: 16px;
height: 16px;
display: inline-block;
border-top: 4px solid @xtxColor;
border-right: 4px solid @xtxColor;
box-sizing: border-box;
position: relative;
transform: rotate(45deg);
&::before {
content: "";
width: 10px;
height: 10px;
position: absolute;
left: 0;
top: 2px;
background: lighten(@xtxColor, 40%);
}
}
}
}
</style>
- 获取数据传入xtx-carousel.vue组件
src/views/goods/index.vue
传ID
<!-- 商品推荐 -->
<GoodsRelevant :goodsId="goods.id"/>
src/api/goods.js
定义获取数据的API
/**
* 获取商品同类推荐-未传入ID为猜喜欢
* @param {String} id - 商品ID
* @param {Number} limit - 获取条数
*/
export const findRelGoods = (id, limit = 16) => {
return request('/goods/relevant', 'get', { id, limit })
}
src/views/goods/components/goods-relevant.vue
获取数据
<div class="header">
<i class="icon" />
+ <span class="title">{{goodsId?'同类商品推荐':'猜你喜欢'}}</span>
</div>
<script>
import { findRelGoods } from '@/api/goods'
import { ref } from 'vue'
// 得到需要的数据
const useRelGoodsData = (id) => {
const sliders = ref([])
findRelGoods(id).then(data => {
// 每页4条
const size = 4
const total = Math.ceil(data.result.length / size)
for (let i = 0; i < total; i++) {
sliders.value.push(data.result.slice(i * size, (i + 1) * size))
}
})
return sliders
}
export default {
// 同类推荐,猜你喜欢
name: 'GoodsRelevant',
props: {
goodsId: {
type: String,
default: undefined
}
},
setup (props) {
const sliders = useRelGoodsData(props.goodsId)
return { sliders }
}
}
</script>
<!-- 此处使用改造后的xtx-carousel.vue -->
<XtxCarousel :sliders="sliders" style="height:380px" auto-play />
- 改造xtx-carousel.vue组件
src/components/library/xtx-carousel.vue
+ <RouterLink v-if="item.hrefUrl" :to="item.hrefUrl">
<img :src="item.imgUrl" alt="">
</RouterLink>
+ <div v-else class="slider">
+ <RouterLink v-for="goods in item" :key="goods.id" :to="`/product/${goods.id}`">
+ <img :src="goods.picture" alt="">
+ <p class="name ellipsis">{{goods.name}}</p>
+ <p class="price">¥{{goods.price}}</p>
+ </RouterLink>
// 轮播商品
.slider {
display: flex;
justify-content: space-around;
padding: 0 40px;
> a {
width: 240px;
text-align: center;
img {
padding: 20px;
width: 230px!important;
height: 230px!important;
}
.name {
font-size: 16px;
color: #666;
padding: 0 40px;
}
.price {
font-size: 16px;
color: @priceColor;
margin-top: 15px;
}
}
}
- 覆盖xtx-carousel.vue的样式在
src/views/goods/components/goods-relevant.vue
:deep(.xtx-carousel) {
height: 380px;
.carousel {
&-indicator {
bottom: 30px;
span {
&.active {
background: @xtxColor;
}
}
}
&-btn {
top: 110px;
opacity: 1;
background: rgba(0,0,0,0);
color: #ddd;
i {
font-size: 30px;
}
}
}
}
注意:vue3.0使用深度作用选择器写法 :deep(选择器)