设计2个视图,分别展示产品列表和产品详情;具体设计可自由发挥。以下过程和具体内容仅仅作为参考
.多产品渲染 - 条件渲染
.多产品渲染 - 列表渲染
.价格折扣 - 计算属性
.轮播图组件
.权益保障组件 - 条件渲染或动态样式
.价格说明 - 条件渲染或动态样式
.甜品 dessert、推荐 recommend 可以先不组件化设计,后续内容学习完再优化为组件
.主路由 /home 展示产品列表组件 Goods.vue
.详情页路由 /details 展示详情页组件 DetailView.vue
.定制滚动条 scrollbar
.阿里字体图标 Iconfont
.主题切换
.返回顶部
.搭建数据服务器;可以使用自己熟悉的服务器环境
.准备 json 数据;可以使用 大树小站 提供的在线资源数据,也可以自己设计数据
.启动服务,响应用户的数据请求
.前端:从数据服务器拉取数据并渲染
{
"id": 0,
"title": "桂院螺蛳粉",
"desc": "Lorem ipsum dolor sit, amet consectetur adipisicing elit",
"img": "/coffee/coffee0.jpg",
"price_original": 15,
"price": 12,
"discount": 0.2,
"flavour": "辛辣",
"is_follow": false,
"stock": 10,
"specification": [
{
"id": 101,
"label": "杯型",
"name": "cup",
"options": [
{ "id": 1, "label": "中杯", "value": "中杯", "sel": true },
{ "id": 2, "label": "大杯", "value": "大杯", "sel": false },
{ "id": 3, "label": "超大杯", "value": "超大杯", "sel": false }
]
},
{
"id": 102,
"label": "温度",
"name": "ther",
"options": [
{ "id": 0, "label": "热", "value": "热", "sel": false },
{ "id": 1, "label": "冰", "value": "冰", "sel": true }
]
},
{
"id": 103,
"label": "糖度",
"name": "sugar",
"options": [
{ "id": 0, "label": "半糖", "value": "半糖", "sel": false },
{ "id": 1, "label": "标准糖", "value": "标准糖", "sel": false },
{ "id": 2, "label": "不加糖", "value": "不加糖", "sel": true }
]
}
],
"dessert": [
{
"id": 1,
"img": "/dessert/dessert1.png",
"title": "巧克力碎·甜甜圈",
"price_original": 5,
"discount": 0.3
},
{
"id": 2,
"img": "/dessert/dessert2.png",
"title": "原味·蛋挞",
"price_original": 8,
"discount": 0.4
},
{
"id": 3,
"img": "/dessert/dessert3.png",
"title": "蔓越莓·饼干",
"price_original": 10,
"discount": 0.5
}
],
"recommend": [
{
"id": 1,
"img": "/coffee/coffee1.jpg",
"title": "lorem ipsum dolor",
"desc": "lorem ipsum dolor sit amet consectetur elit",
"price_original": 12
},
{
"id": 2,
"img": "/coffee/coffee2.jpg",
"title": "lorem ipsum dolor",
"desc": "lorem ipsum dolor sit amet consectetur adipisicing",
"price_original": 15
},
{
"id": 3,
"img": "/coffee/coffee3.jpg",
"title": "lorem ipsum dolor",
"desc": "lorem ipsum dolor sit amet adipisicing elit",
"price_original": 20
}
]
}
<div class="goods">
<template v-if="goods.length">
<div class="item" v-for="(item, ind) in goods" :key="item.id">
<img class="img" :src="'https://glpla.github.io/utils' + item.img" alt="">
<div class="info">
<h4 class="title">{{ item.name }} <span class="flavour">{{ item.flavour }}</span> </h4>
<div class="desc">{{ item.desc.repeat(2) }}</div>
<div class="price">¥{{ item.price }} <span class="price-inner">(市场价<span>¥{{ item.price_original
}}</span>)</span> </div>
<div class="price-discount">预估到手 <span>¥{{ getDiscount(item) }}</span></div>
</div>
<div class="btn" @click="addCart(item)">
<span class="iconfont icon-gouwuche_o"></span>
</div>
</div>
</template>
<div v-else>lists empty</div>
</div>
import { computed, onMounted, ref } from 'vue';
const goods = ref([])
const getDiscount = computed(() => {
return item => {
return (item.price * (1 - item.discount)).toFixed(2)
}
})
// 获取在线数据*
onMounted(() => {
let json = fetch('https://glpla.github.io/utils/data/coffee.json')
.then(response => response.json())
.then(data => {
console.log(data.cont);
goods.value = data.cont
})
})
{
path: "/home",
name: "home",
component: () => import("@/views/HomeView.vue")
},
{
path: "/details",
name: "details",
component: () => import("@/views/DetailsView.vue")
}
.产品组件
.轮播图
.数据分页
.触底加载
.数据加载指示器以及动画