Vue3 + Vuex4 构建点餐页面
2020-05-04 本文已影响0人
小生不才_
前言
image前进!前进!不择手段地前进!!
距离Vue3.0 beta 发布已经过了半个多月了。本来这个东西上个月就应该写了,由于公司上个月赶项目一直没时间。趁着劳动节把这个东西写了一下,也顺便把一些坑过了一下。
介绍
页面比较简单,算是把 Composition API
过了一下了
- 基于
Vue3.0 beta
这种页面也比较老套
了 - 涉及了
Vue3.0 beta
andVuex4 beta
andaxios
安装vue3环境
题外话: 听说
vite
不错,这几天得了解下🤪
- 安装(升级)最近的
vue-cli
-
vue create projectName
ps: 如果自己不会手动装vuex4
或者vue-router4
这些库最好是直接安装步骤全部安装(减少一些踩坑的时间成本) -
vue add vue-next
这个命令会把项目中的一些依赖自动升级成支持vue3的版本 npm run serve
Composition-API
这里建议直接看文档 https://vue-composition-api-rfc.netlify.app/#summary
🤐直接贴代码了
毕竟思路都差不多,可以的话建议直接看 https://github.com/notbucai/vue3-demo
useScroll
import { ref } from 'vue';
export const useScrollTop = () => {
const top = ref(0);
window.addEventListener('scroll', () => {
const scrollTop = document.documentElement.scrollTop;
top.value = scrollTop;
});
return top;
}
store
import { createStore } from "vuex";
export default createStore({
state: {
shopcart: {},
foodList: [],
},
getters: {
// TODO: 这里的金额计算 是"有问题"的
// 计算金额与数量
settlement(state) {
let allPrice = 0;
let allCount = 0;
const shopcart = state.shopcart;
const foodList = state.foodList;
// 得到所有食物列表
const foods = foodList.reduce((previousValue, currentValue) => {
return previousValue.concat(currentValue.foods);
}, []);
// 通过购物车计算金额数量
Object.keys(shopcart).forEach(key => {
const count = shopcart[key];
if (!count) return;
const food = foods.find(item => item.item_id == key);
const price = food.specfoods[0].price;
// 这里只是一种简单的处理浮点金额的方案(不保险),实际上后端直接返回以分为单位的金额会更好,【在后端计算金额一定情况下可能更靠谱】
allPrice += (price * 10000 * count);
allCount += count;
});
return {
price: allPrice / 10000,
count: allCount,
}
}
},
mutations: {
ADD_ITEM(state, id) {
state.shopcart[id] = state.shopcart[id] || 0;
state.shopcart[id]++;
},
SUB_ITEM(state, id) {
state.shopcart[id] = state.shopcart[id] || 0;
state.shopcart[id]--;
},
SET_FOOD_LIST(state, payload) {
state.foodList = payload;
}
},
actions: {
setFoodList({ commit }, payload) {
commit('SET_FOOD_LIST', payload)
},
addItem({ commit }, id) {
commit('ADD_ITEM', id)
},
subItem({ commit }, id) {
commit('SUB_ITEM', id)
},
}
});
页面
<template>
<div class="Catering">
<header class="header">
<div class="shop">
<div class="shop-pic">
<img
src="https://upload.jianshu.io/users/upload_avatars/7759683/995f635d-357b-4539-b008-55aa6b0ac140.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp"
alt
/>
</div>
<div class="shop-name">一袋米</div>
</div>
<div class="navbar">
<div class="nav-item">点餐</div>
<div class="nav-item">评价</div>
<div class="nav-item">商家</div>
</div>
<div class="outher-box"></div>
</header>
<main class="main">
<div class="left">
<div
class="left-item"
v-for="(item, index) in list"
:class="{active:elIndex == index}"
:key="index"
@click="srcollTo(index)"
>{{item.name}}</div>
</div>
<div class="right">
<template v-for="(_item, index) in list">
<!-- 这里的ref就又点难受了 -->
<div :key="index" :ref="el=>itemEls[index] = el">
<food-item :item="_item" />
</div>
</template>
</div>
<settlement-bottom />
</main>
</div>
</template>
<script>
import { watch, ref, onBeforeUpdate, onMounted, computed } from 'vue';
import { useStore } from 'vuex';
import { useScrollTop } from '../hooks';
import { getList } from '../ajax/index';
import FoodItem from './components/FoodItem';
import SettlementBottom from './components/SettlementBottom';
export default {
components: { FoodItem, SettlementBottom },
setup() {
// store
const store = useStore();
const list = computed(() => store.state.foodList);
onMounted(async () => {
// NOTE: 获取数据
let resList = await getList();
// 存入store
store.dispatch('setFoodList', resList);
});
// 组节点列表
const itemEls = ref([]);
// 当前选中节点
const elIndex = ref(0);
// 监听数据
const scrollTop = useScrollTop([]);
onBeforeUpdate(() => {
// 更新时清空
itemEls.value = [];
});
// 监听滚动条变化
watch(scrollTop, () => {
let index = 0;
// 判断距离头部最近的元素 实际上这里是可以缓存高度的
itemEls.value.forEach((el, i) => {
// console.log('el=>',el,'val=>', scrollTop.value);
if (el.offsetTop > scrollTop.value) return;
index = i;
});
// 绑定节点
elIndex.value = index;
});
const srcollTo = index => {
elIndex.value = index;
const el = itemEls.value[index];
const offsetTop = el.offsetTop;
window.scrollTo(0, offsetTop);
};
return {
list,
itemEls,
elIndex,
srcollTo
};
}
};
</script>
<style lang="scss">
......
</style>
FoodItem.vue
<template>
<div class="FoodItem">
<div class="food-title">{{item.name}}</div>
<div class="food-item" v-for="(food) in item.foods" :key="food.item_id">
<div class="food-pic">
<img
src="https://upload.jianshu.io/users/upload_avatars/16175630/e2ee85e5-7cb0-429d-a517-bb1c6f1833e4?imageMogr2/auto-orient/strip|imageView2/1/w/80/h/80/format/webp"
alt
/>
</div>
<div class="food-main">
<div class="food-info">
<div class="title">{{food.name}}</div>
<div class="info">{{food.applicable_quantity_text}}</div>
</div>
<div class="money">¥{{food.specfoods[0].price}}</div>
</div>
<div class="food-action">
<div class="action-item minus" v-if="shopcart[food.item_id]" @click="handleMinus(food)">-</div>
<div class="action-num" v-if="shopcart[food.item_id]">{{shopcart[food.item_id]}}</div>
<div class="action-item puls" @click="handlePuls(food)">+</div>
</div>
</div>
</div>
</template>
<script>
// import { reactive, unref } from 'vue';
import { useStore } from 'vuex';
import { computed } from 'vue';
export default {
components: {},
props: {
item: Object
},
setup() {
// store
const store = useStore();
// 购物车
const shopcart = computed(() => store.state.shopcart);
const handleChangeCount = (food, type) => {
const id = food.item_id;
const actionName = type > 0 ? 'addItem' : 'subItem';
store.dispatch(actionName, id);
};
const handleMinus = food => {
handleChangeCount(food, -1);
};
const handlePuls = food => {
handleChangeCount(food, 1);
};
return {
shopcart,
handleMinus,
handlePuls
};
}
};
</script>
<style lang="scss" scoped>
......
</style>
SettlementBottom.vue
<template>
<div class="SettlementBottom">
<div class="num_box">x{{settlement.count}}</div>
<div class="price_box">¥{{settlement.price}}</div>
<div class="submit_btn" @click="handleSubmit">结算</div>
</div>
</template>
<script>
import { useStore } from 'vuex';
import { computed } from 'vue';
export default {
props: {},
setup() {
const store = useStore();
const settlement = computed(() => store.getters.settlement);
const handleSubmit = () => {
const sc = store.state.shopcart;
const shopcart = Object.keys(sc).map(key => ({
id: key,
count: sc[key]
}));
console.log('shopcart=>', shopcart);
console.log('如果需要原食物的参数可以直接去state中获取');
};
return {
settlement,
handleSubmit
};
}
};
</script>
<style lang="scss" scoped>
......
</style>