一、文件结构目录
二、效果
一开始显示红色的圈圈,点击之后显示步进器(stepper);
点击加入购物车,可以购物车对应的图标上会有显示;
点击购物车和立即购买,都会跳转到首页下购物车的页面。
三、各个文件代码
1.MyStore.vue
<template>
<div class="storeDetails">
<Header title="店铺" />
<div class="content">
<div class="img"></div>
<div class="foodClassify">
<div class="name">
{{ title }}
<img :src="img" class="store_img" />
</div>
<div class="classify">
<van-tabs color="#ffc400">
<van-tab
v-for="(i, index) in storeData"
:key="index"
:title="i.name"
>
<Food-list :index="index" :foodData="i.data" />
</van-tab>
</van-tabs>
</div>
</div>
</div>
<!-- 尾部 -->
<van-action-bar>
<van-action-bar-icon icon="chat-o" text="客服" @click="service" />
<van-action-bar-icon
icon="cart-o"
text="购物车"
:badge="store.state.cartList.length"
@click="goCart"
/>
<van-action-bar-button
type="warning"
text="加入购物车"
@click="handleAddCart"
/>
<van-action-bar-button type="danger" text="立即购买" @click="clickBuy" />
</van-action-bar>
</div>
</template>
<script>
import { defineComponent, reactive, toRefs } from "vue";
import Header from "../../components/Header.vue";
import FoodList from "./components/foodList.vue";
import { Toast } from "vant";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
// import { showToast } from 'vant';
export default defineComponent({
name: "App",
components: { Header, FoodList },
setup() {
const store = useStore();
const router = useRouter();
let data = reactive({
title: "鱼拿酸菜鱼",
img: "https://img1.baidu.com/it/u=1599947592,1695977044&fm=253&fmt=auto&app=138&f=JPEG?w=640&h=440",
storeData: [
{
name: "点菜",
data: {
content: "点菜",
items: [
{
text: "热销套餐",
children: [
{
pic: "https://img1.baidu.com/it/u=1599947592,1695977044&fm=253&fmt=auto&app=138&f=JPEG?w=640&h=440",
title: "招牌酸菜鱼",
num: 0,
price: 25.0,
id: 0,
add: true,
},
{
pic: "https://img1.baidu.com/it/u=1599947592,1695977044&fm=253&fmt=auto&app=138&f=JPEG?w=640&h=440",
title: "藤椒酸菜鱼",
num: 0,
price: 25.0,
id: 1,
add: true,
},
],
},
{
text: "澳洲肥牛",
children: [
{
pic: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbkimg.cdn.bcebos.com%2Fpic%2F8694a4c27d1ed21b0ef4f3137f24cac451da80cb91b8&refer=http%3A%2F%2Fbkimg.cdn.bcebos.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1645407747&t=ea2c9f772ba0df3a2d1b00b962875460",
title: "酸汤肥牛",
num: 0,
price: 25.0,
id: 3,
add: true,
},
{
pic: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbkimg.cdn.bcebos.com%2Fpic%2F8694a4c27d1ed21b0ef4f3137f24cac451da80cb91b8&refer=http%3A%2F%2Fbkimg.cdn.bcebos.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1645407747&t=ea2c9f772ba0df3a2d1b00b962875460",
title: "香辣肥牛",
num: 0,
price: 25.0,
id: 4,
add: true,
},
],
},
{
text: "超级折扣",
children: [
{
pic: "https://img1.baidu.com/it/u=1599947592,1695977044&fm=253&fmt=auto&app=138&f=JPEG?w=640&h=440",
title: "无骨酸菜鱼+肥牛双拼",
num: 0,
price: 25.0,
id: 5,
add: true,
},
{
pic: "https://img1.baidu.com/it/u=1599947592,1695977044&fm=253&fmt=auto&app=138&f=JPEG?w=640&h=440",
title: "香辣水煮鱼+肥牛双拼",
num: 0,
price: 25.0,
id: 6,
add: true,
},
],
},
],
},
},
{ name: "评价", data: { content: "评价" } },
{ name: "商家", data: { content: "商家" } },
],
});
// 客服
const service = () => {
// showToast('提示内容');
Toast.fail("敬请期待...");
// Toast("敬请期待...")
};
// 点击购物车按钮
const goCart = () => {
router.push("/cart");
};
// 添加到购物车
const handleAddCart = (type) => {
let newList = [];
data.storeData.forEach((item) => {
item.data.items?.forEach((items) => {
items.children.forEach((itemss) => {
if (itemss.num > 0) {
newList.push(itemss);
}
});
});
});
if (newList.length == 0) {
Toast.fail("请选择商品");
return;
}
store.commit("ADDCART", newList);
type == "buy" ? goCart() : "";
};
//立即购买
const clickBuy = () => {
handleAddCart(buy);
};
return {
...toRefs(data),
service,
handleAddCart,
clickBuy,
goCart,
store
};
},
});
</script>
<style lang="less" scoped>
.storeDetails {
height: 100%;
display: flex;
flex-flow: column;
.content {
flex: 1;
overflow-y: auto;
.img {
width: 100%;
height: 150px;
background: url("../../assets/yuna.jpg") no-repeat center/cover;
}
.foodClassify {
height: 500px;
background-color: #fff;
border-radius: 20px 20px 0 0;
margin-top: -30px;
.name {
display: flex;
padding: 20px;
justify-content: space-between;
font-size: 39px;
.store_img {
width: 80px;
height: 80px;
border-radius: 10px;
margin-top: -30px;
}
}
}
}
}
</style>
2.foodList.vue
<template>
<div class="food_list" v-if="index == 0">
<van-tree-select
height="88vw"
:items="items"
:main-active-index.sync="active"
@click-nav="navClick"
>
<template #content>
<div v-for="(i, index) in subItem" :key="index" class="item_bg">
<FoodAdd
:item="i"
:showAdd="true"
:addClick="addClick"
:onChange="onChange"
showAdd="true"
/>
</div>
</template>
</van-tree-select>
</div>
</template>
<script>
import { defineComponent, reactive, toRefs } from "vue";
import FoodAdd from "../../../components/FoodAdd.vue";
export default defineComponent({
name: "App",
props: ["index", "foodData"],
components: { FoodAdd },
setup(props) {
let data = reactive({
items: [],
active: 0,
subItem: [],
});
// console.log("foodData", props.foodData);
// 初始化数据
const init = () => {
const newList = [];
props.foodData?.items?.map((item, index) => {
newList.push({ text: item.text });
if (data.active == index) {
data.subItem = item.children;
}
});
// console.log("1.subItem", subItem);
data.items = newList;
// console.log("newlist",newList);
};
init();
// console.log("2.subItem", data.subItem);
const navClick = (i) => {
data.active = i;
init();
};
// 切换步进器
const addClick = (i) => {
data.subItem.forEach((item) => {
if (item.id == i) {
item.add = false;
item.num = 1;
}
});
};
// 步进器数量发生变化
const onChange = (value, datail) => {
data.subItem.forEach((item) => {
if (item.id == datail.name) {
item.num = value;
}
});
console.log("data.subItem", data.subItem);
};
return {
...toRefs(data),
navClick,
addClick,
onChange,
};
},
});
</script>
<style lang="less" scoped>
.food_list {
margin-top: 20px;
.item_bg {
padding: 10px;
}
}
/deep/ .van-sidebar-item--select::before {
background-color: #ffc400;
}
</style>
3.FoodAdd.vue
<template>
<div class="content_item">
<div class="left">
<img :src="item.pic" />
<div class="text">
<div class="title">{{ item.title }}</div>
<van-icon
name="add-o"
v-if="item.add && showAdd"
@click="addClick(item.id)"
/>
<van-stepper
v-model="item.num"
min="1"
v-else
:name="item.id"
@change="onChange"
/>
</div>
</div>
<div class="right">¥{{ item.price }}</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "App",
props: ["item", "onChange", "addClick", "showAdd"],
setup() {
// console.log("2.item", props.item);
},
});
</script>
<style lang="less" scoped>
.content_item {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.right {
font-size: 16px;
font-weight: 600;
}
.left {
display: flex;
align-items: center;
flex: 1;
img {
margin-left: 10px;
width: 60px;
height: 60px;
margin-right: 10px;
border-radius: 10px;
}
.text {
display: flex;
flex-flow: column;
justify-content: space-between;
height: 100%;
position: relative;
flex: 1;
.title {
font-size: 16px;
}
.van-icon {
color: red;
font-size: 20px;
position: absolute;
right: 4px;
bottom: 4px;
}
}
}
</style>
4.main.js
import { createApp } from 'vue'
import App from './App.vue'
import 'amfe-flexible'
import { Button, Icon, Tab, Tabs, TreeSelect, Stepper, ActionBar, ActionBarIcon, ActionBarButton, Toast } from 'vant'
import router from './router'
import "./common/css/base.less"
import store from './store'
const app = createApp(App)
app.use(router)
app.use(Button).use(Icon).use(Tab).use(Tabs).use(TreeSelect).use(Stepper).use(ActionBar).use(ActionBarIcon).use(ActionBarButton).use(Toast)
app.use(store)
app.mount('#app')
5.store(vuex)-->index.js
安装vuex, npm i vuex@next -S
import { createStore } from "vuex";
export default createStore({
state: {
cartList: []
},
mutations: {
ADDCART(state, value) {
state.cartList = value
}
},
actions: {
}
})