new-cashier/jeepay-ui-uapp-merchant/pagesCreateOrder/index/index.vue

996 lines
23 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="u-wrap">
<view class="top bg-fff w-full">
<view class="u-flex u-row-between choose-user" @tap="chooseUser">
<view>
<view v-if="!data.vipUser.id">选择用户</view>
<view class="u-flex" v-else>
<view class="headeimg">
<image class="img" :src="data.vipUser.headImg" mode=""></image>
</view>
<view class="u-m-l-20">{{data.vipUser.nickName}}</view>
<view class="color-main u-m-l-10 u-font-24">{{data.vipUser.isVip?'会员':'' }}</view>
<view class="u-font-24 u-m-l-30"><text>余额:</text><text
class="color-main">{{data.vipUser.amount}}</text></view>
<view class="u-font-24 u-m-l-30"><text>积分:</text><text
class="color-main">{{data.vipUser.totalScore}}</text></view>
</view>
</view>
<view class="u-flex">
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
<view class="search u-flex u-col-center ">
<view class="u-flex-1">
<uni-search-bar bgColor="#F9F9F9" cancelButton="none" placeholder="搜索店内商品" @confirm="search"
:focus="true" v-model="searchValue">
</uni-search-bar>
</view>
<view class="u-flex">
<image src="/pagesCreateOrder/static/images/icon-saoma.svg" class="icon-saoma" mode=""></image>
</view>
</view>
</view>
<view class="u-menu-wrap">
<scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="data.scrollTop"
:scroll-into-view="data.itemId">
<view v-for="(item,index) in data.tabbar" :key="index" class="u-tab-item"
:class="[data.current == index ? 'u-tab-item-active' : '']" @tap.stop="swichMenu(index)">
<text class="u-line-1">{{item.name}}</text>
</view>
</scroll-view>
<scroll-view :scroll-top="data.scrollRightTop" scroll-y scroll-with-animation class="right-box"
@scroll="rightScroll">
<view class="page-view u-p-l-24">
<view class="lingshi" @tap="toLinshi">
<uni-icons type="plus-filled" size="24" :color="color.ColorMain"></uni-icons>
<view class="u-m-t-24 color-main">临时菜</view>
</view>
<view class="class-item" :id="'item' + index" v-for="(item , index) in data.tabbar" :key="index">
<view class="item-title">
<text>{{item.name}}</text>
</view>
<view class="item-container">
<view class="thumb-box" v-for="(goodsItem, goodsIndex) in item.foods" :key="goodsIndex">
<goods-item @chooseGuige="chooseGuige($event,index)"
@add="goodsUpdate($event,index,true)" @reduce="goodsUpdate($event,index,false)"
:index="goodsIndex" :data="goodsItem"></goods-item>
</view>
<template v-if="item.name==='附加费'">
<view class="addCai" @tap="surchargeShow">
<uni-icons type="plus-filled" size="24" :color="color.ColorMain"></uni-icons>
<view class="u-m-t-24 color-main">自定义添加</view>
</view>
</template>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="bottom w-full">
<my-car @updateNumber="carsNumberChange" :user="data.vipUser" :masterId="data.masterId"
:table="data.table"
:data="cars" @clear="onClearCart"></my-car>
</view>
<!-- 选择规格 -->
<guige-model @update-sku="updateSkuSel" @confirm="guigeConfirm" ref="chooseGuigeModel" :title="guigeModelData.title"
:sku-map="guigeModelData.chooseGoods.skuMap" :skus="guigeModelData.chooseGoods.skus"></guige-model>
<!-- 添加附加费 -->
<my-surcharge @confirm="surchargeConfirm" ref="surcharge" title="添加附加费"></my-surcharge>
</view>
</template>
<script setup>
import _ from 'lodash';
import * as Api from '@/http/yskApi/Instead.js'
import {
$tbShopCategory
} from '@/http/yskApi/goods.js'
import util from './util.js';
import classifyData from './classify.data.js';
import color from '@/commons/color.js';
import guigeModel from './components/guige'
import goodsItem from './components/goods-item'
import mySurcharge from './components/surcharge'
import {
onLoad,
onReady,
onShow,
onPageScroll,
onPullDownRefresh
} from '@dcloudio/uni-app';
import {
onBeforeUnmount,
computed,
reactive,
ref,
nextTick
} from 'vue';
import myCar from './components/car'
import go from '@/commons/utils/go.js';
import infoBox from '@/commons/utils/infoBox.js';
const cars = reactive([])
const data = reactive({
scrollTop: 0, //tab标题的滚动条位置
oldScrollTop: 0,
current: 0, // 预设当前项的值
menuHeight: 0, // 左边菜单的高度
menuItemHeight: 0, // 左边菜单item的高度
itemId: '', // 栏目右边scroll-view用于滚动的id
tabbar: [],
menuItemPos: [],
arr: [],
scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
timer: null, // 定时器
topZhanwei: 136 + 24,
// 选择用户
vipUser: {
id: "",
},
//餐桌号
masterId: "",
table: {
tableId: ""
},
})
function setTabBar(category, goods, cars) {
const goodsCategoryMap = goods.reduce((prve, cur) => {
if (!prve.hasOwnProperty(cur.categoryId)) {
prve[cur.categoryId] = []
}
prve[cur.categoryId].push(cur)
return prve
}, {})
const chooseGoodsNumberMap = cars.reduce((prve, cur) => {
if (!prve.hasOwnProperty(cur.productId)) {
prve[cur.productId] = 0
}
prve[cur.productId] += cur.number
return prve
}, {})
let tabbar = category.map(v => {
const foods = goodsCategoryMap[v.id] || []
return {
...v,
foods: foods.map((fgoods, index) => {
return {
...fgoods,
chooseNumber: chooseGoodsNumberMap[fgoods.id],
price: fgoods.lowPrice,
isDan: fgoods.typeEnum !== 'sku'
}
})
}
})
tabbar.push({
name: '附加费',
foods: [{
name: "小费",
price: Math.ceil(Math.random() * 10),
chooseNumber: 0,
isDan: true
},
{
name: "打包费",
price: 1,
chooseNumber: 0,
isDan: true
}
]
})
tabbar = tabbar.filter(v => {
return v.foods.length
})
data.tabbar = tabbar
}
//request类
//获取分类
function getCategory(par = {
page: 0,
size: 300
}) {
return $tbShopCategory(par)
}
//获取商品列表
function getGoods(par = {
page: 0,
size: 300
}) {
return Api.getGoodsLists(par)
}
//获取购物车数据
function getCart(par = {
page: 0,
size: 300,
masterId: data.masterId,
tableId: data.table.tableId
}) {
return Api.getCart(par)
}
//获取取餐码
function getMasterId() {
return Api.$getMasterId({
tableId: data.table.tableId
})
}
//清空购物车
function clearCart() {
return Api.$clearCart({
masterId: data.masterId,
tableId: data.table.tableId
})
}
//加入购物车
function addCart(par) {
const submitPar = {
masterId: data.masterId,
tableId: data.table.tableId,
isPack: false,
num: 1,
productId: '',
skuId: '',
vipUserId: data.vipUser.id
}
Object.assign(submitPar, par)
return Api.addCart(submitPar)
}
//更新购物车
function updateCartGoods(par) {
const submitPar = {
cartId: '',
masterId: data.masterId,
tableId: data.table.tableId,
isPack: false,
num: 1,
productId: '',
skuId: '',
vipUserId: data.vipUser.id
}
Object.assign(submitPar, par)
return Api.$updateCart(submitPar)
}
//删除购物车某商品
function removeCartGoods(par) {
const submitPar = {
cartId: '',
masterId: data.masterId,
tableId: data.table.tableId,
}
Object.assign(submitPar, par)
return Api.$removeCart(submitPar)
}
//更新选择用户
function setUser(par) {
const submitPar = {
masterId: data.masterId,
tableId: data.table.tableId,
vipUserId: data.vipUser.id ? data.vipUser.id : '',
type: data.vipUser.id ? 0 : 1 //0 设置 1 取消
}
Object.assign(submitPar, par)
return Api.$setUser(submitPar)
}
//点击清空购物车
async function onClearCart() {
await clearCart()
cars.length = 0
for (let i in data.tabbar) {
for (let k in data.tabbar[i].fgoods) {
data.tabbar[i].fgoods[k].chooseNumber = 0
}
}
}
async function init() {
const {
masterId
} = await getMasterId()
data.masterId = masterId
const cartRes = await getCart()
cars.length = 0
for (let i in cartRes.records) {
cars.push(cartRes.records[i])
}
const categoryRes = await getCategory()
const category = categoryRes.content.reduce((prve, cur) => {
prve.push({
...cur,
childrenList: null
});
return [...prve, ...cur.childrenList];
}, []);
console.log(category);
const goodsRes = await getGoods()
const goods = goodsRes.records.filter((v) => {
let isShow = true;
if (v.typeEnum !== "sku") {
isShow = v.specList.length >= 1;
}
return isShow;
});
setTabBar(category, goods, cars)
}
// 监听选择用户事件
const surcharge = ref(null)
function surchargeConfirm(e) {
data.tabbar[data.tabbar.length - 1].foods.unshift({
...e,
chooseNumber: 0
})
}
function surchargeShow() {
surcharge.value.open()
}
let searchValue = ref('')
function search() {
}
function chooseUser() {
go.to('PAGES_CHOOSE_USER')
}
function toLinshi() {
go.to('PAGES_ADD_TEMP_CUISINE')
}
const chooseGuigeModel = ref(null)
const guigeModelData = reactive({
title: '',
chooseGoods: {
skuMap: {},
item: '',
skus: []
}
})
// 返回当前选中商品skuList
function returnSelGoodsSkuList(skuList, specSnap) {
const specSnapArr = specSnap ? specSnap.split(",") : [];
const result = skuList.map((v, index) => {
const values = v.value.split(",");
return {
...v,
valueArr: values,
sel: specSnap ? specSnapArr[index] : "",
values: values.map((name) => {
return {
name,
disabled: false
};
}),
};
});
return result
}
//返回当前选中商品skuMap
function returnSelGoodsSkuMap(specList) {
const skuMap = {}
for (let i in specList) {
skuMap[specList[i].specSnap] = specList[i];
}
return skuMap
}
//多规格商品弹窗时,找到默认可以下单的规格商品
function findGoods(skuList = [], goodsListMap = {}, specList) {
const skuMapNumber = skuList.reduce((prve, cur) => {
for (let i in cur.valueArr) {
prve[cur.valueArr[i]] = i;
}
return prve;
}, {});
const canBudyGoods = specList
.filter((v) => util.isCanBuy(v))
.sort((a, b) => {
const aNumber = a.specSnap.split(",").reduce((prve, cur) => {
return prve + skuMapNumber[cur];
}, 0);
const bNumber = b.specSnap.split(",").reduce((prve, cur) => {
return prve + skuMapNumber[cur];
}, 0);
return aNumber - bNumber;
});
return canBudyGoods[0];
}
//设置商品默认选中,规格禁止以及选中
function setSkugoodsDefaultInit(goods, skuList, skuMap, specList) {
guigeModelData.chooseGoods.item = goods
guigeModelData.chooseGoods.skus = skuList
guigeModelData.chooseGoods.skuMap = skuMap
const skuGoods = findGoods(skuList, skuMap, specList);
console.log(skuList, skuMap);
console.log(skuGoods);
if (skuGoods) {
// this.skuGoods.data = skuGoods;
// this.skuGoods.number = skuGoods.suit || 1;
skuGoods.specSnap.split(",").map((v, index) => {
guigeModelData.chooseGoods.skus[index].sel = v;
});
}
console.log(guigeModelData.chooseGoods.skus);
setTagDisabled();
}
//更新选中规格
function updateSkuSel(skusIndex, skdName) {
const skuList = guigeModelData.chooseGoods.skus
const skuMap = guigeModelData.chooseGoods.skuMap
guigeModelData.chooseGoods.skus[skusIndex].sel = skdName
const specSnap = guigeModelData.chooseGoods.skus.reduce((prve, cur) => {
prve.push(cur.sel)
return prve
}, []).join()
setTagDisabled();
// const canChooseGoods = skuList.every((v) => v.sel);
// if (canChooseGoods) {
// const skuGoods =skuMap[specSnap];
// guigeModelData.chooseGoods.item = skuGoods
// }
}
//设置规格按钮的禁止状态
function setTagDisabled() {
const skuList = guigeModelData.chooseGoods.skus
const skuMap = guigeModelData.chooseGoods.skuMap
const selArr = skuList.reduce((prve, cur) => {
if (cur.sel) {
prve.push(cur.sel);
} else {}
return prve;
}, []);
console.log(selArr);
let selArrAllGroup = util.generateCombinations(selArr, selArr.length - 1);
console.log(selArrAllGroup);
const matchArr = [];
for (let key in skuMap) {
const goods = skuMap[key];
const keyArr = key.split(",");
for (let spe of selArrAllGroup) {
if (util.arrayContainsAll(keyArr, spe)) {
matchArr.push(goods);
break;
}
}
}
//全部规格都已下架
if (!matchArr.length) {
for (let k in skuList) {
for (let i in skuList[k].values) {
skuList[k].values[i].disabled = true
}
}
return;
}
const includeSkuMap = matchArr.reduce((prve, cur) => {
const speArr = cur.specSnap.split(",");
for (let i of speArr) {
if (!prve.hasOwnProperty("i")) {
prve[i] = matchArr
.filter((v) => v.specSnap.match(i))
.every((v) => {
return util.isCanBuy(v)
});
}
}
return prve;
}, {});
for (let i in includeSkuMap) {
for (let k in skuList) {
const index = skuList[k].valueArr.findIndex((val) => val === i);
if (index !== -1) {
skuList[k].values[index].disabled = includeSkuMap[i]
}
}
}
}
function chooseGuige(foodsindex, index) {
const $goods = data.tabbar[index].foods[foodsindex]
guigeModelData.title = $goods.name
const specList = $goods.specList;
const tagSnap = JSON.parse($goods.skuResult.tagSnap)
const skuMap = returnSelGoodsSkuMap(specList)
const skuList = returnSelGoodsSkuList(tagSnap)
setSkugoodsDefaultInit($goods, skuList, skuMap, specList)
chooseGuigeModel.value.open()
}
async function guigeConfirm(sku, num) {
const goods = guigeModelData.chooseGoods.item
const skuId = sku.id
const productId = goods.id
const res = findGoodsInCar(goods, skuId)
if (res) {
//更新
const {
index
} = res
const carGoods = cars[index]
const cartId = carGoods.id
const newNumber = carGoods.number * 1 + num
const {
number
} = await updateCartGoods({
num: newNumber,
cartId,
productId,
skuId
})
carGoods.number = number
} else {
//添加
const cartGoods = await addCart({
num,
productId,
skuId
})
cars.push({
...cartGoods,
specSnap: sku.specSnap
})
}
}
//购物车商品数量改变
async function carsNumberChange(e) {
const {
num,
index,
goods
} = e
const {
productId,
categoryId,
skuId
} = goods
const cartId = goods.id
const tabbarIndex = data.tabbar.findIndex(v => v.id == categoryId)
const $goods = data.tabbar[tabbarIndex].foods.find(v => v.id == productId)
if (num === 0) {
//移除
await removeCartGoods({
cartId
})
cars.splice(index, 1)
$goods.chooseNumber = 0
return
}
await updateCartGoods({
num,
cartId,
productId,
skuId
})
cars[index].number = num
if ($goods) {
$goods.chooseNumber = num
}
}
// 找到该规格商品在购物车中是否存在并返回index值以及对应的数据
function findGoodsInCar($goods, skuId) {
const productId = $goods.id
const goodsInCarIndex = cars.findIndex((carsGoods) => {
return carsGoods.skuId == skuId && carsGoods.productId == productId;
});
const carGoods = cars[goodsInCarIndex]
return carGoods ? {
index: goodsInCarIndex,
carGoods
} : false
}
async function goodsUpdate(foodsindex, index, isAdd) {
const $goods = data.tabbar[index].foods[foodsindex]
if ($goods.isDan) {
//单规格
const goodsInCarIndex = cars.findIndex((carsGoods) => {
return carsGoods.skuId == $goods.specList[0].id && carsGoods.productId == $goods.id;
});
const productId = $goods.id
const skuId = $goods.specList[0].id
if (goodsInCarIndex !== -1) {
//更新
const carGoods = cars[goodsInCarIndex]
const cartId = carGoods.id
const step = isAdd ? 1 : -1
const num = carGoods.number * 1 + step
if (num === 0) {
//移除
cars.splice(goodsInCarIndex, 1)
$goods.chooseNumber = 0
return await removeCartGoods({
cartId
})
}
const {
number
} = await updateCartGoods({
num,
cartId,
productId,
skuId
})
carGoods.number = number
$goods.chooseNumber = number
} else {
//增加
const num = 1
const cartGoods = await addCart({
num,
productId,
skuId
})
$goods.chooseNumber = num
cars.push(cartGoods)
}
return
}
}
onReady(() => {
getMenuItemTop()
})
let isTabClickOver=true
// 点击左边的栏目切换
async function swichMenu(index) {
if (data.arr.length == 0) {
await getMenuItemTop();
}
if (index == data.current) return;
isTabClickOver=false;
data.scrollRightTop = data.oldScrollTop;
nextTick(function() {
data.scrollRightTop = data.arr[index] + data.topZhanwei;
data.current = index;
leftMenuStatus(index);
})
}
// 获取一个目标元素的高度
function getElRect(elClass, dataVal) {
new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query.select('.' + elClass).fields({
size: true
}, res => {
// 如果节点尚未生成res值为null循环调用执行
if (!res) {
setTimeout(() => {
getElRect(elClass);
}, 10);
return;
}
data[dataVal] = res.height;
resolve();
}).exec();
})
}
// 观测元素相交状态
async function observer() {
tabbar.map((val, index) => {
let observer = uni.createIntersectionObserver(this);
// 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
// 如果跟.right-box底部相交就动态设置左边栏目的活动状态
observer.relativeTo('.right-box', {
top: 0
}).observe('#item' + index, res => {
if (res.intersectionRatio > 0) {
let id = res.id.substring(4);
leftMenuStatus(id);
}
})
})
}
// 设置左边菜单的滚动状态
async function leftMenuStatus(index) {
if(!isTabClickOver){
return
}
data.current = index;
// 如果为0意味着尚未初始化
if (data.menuHeight == 0 || data.menuItemHeight == 0) {
await getElRect('menu-scroll-view', 'menuHeight');
await getElRect('u-tab-item', 'menuItemHeight');
}
// 将菜单活动item垂直居中
data.scrollTop = index * data.menuItemHeight + data.menuItemHeight / 2 - data.menuHeight / 2;
}
// 获取右边菜单每个item到顶部的距离
function getMenuItemTop() {
new Promise(resolve => {
let selectorQuery = uni.createSelectorQuery();
selectorQuery.selectAll('.class-item').boundingClientRect((rects) => {
// 如果节点尚未生成rects值为[](因为用selectAll所以返回的是数组),循环调用执行
if (!rects.length) {
setTimeout(() => {
getMenuItemTop();
}, 10);
return;
}
rects.forEach((rect) => {
// 这里减去rects[0].top是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
data.arr.push(rect.top - rects[0].top);
resolve();
})
}).exec()
})
}
// 右边菜单滚动
async function rightScroll(e) {
data.oldScrollTop = e.detail.scrollTop;
if (data.arr.length == 0) {
await getMenuItemTop();
}
if (data.timer) return;
if (!data.menuHeight) {
await getElRect('menu-scroll-view', 'menuHeight');
}
setTimeout(() => { // 节流
data.timer = null;
// scrollHeight为右边菜单垂直中点位置
let scrollHeight = e.detail.scrollTop + data.menuHeight / 2 + data.topZhanwei / 2;
for (let i = 0; i < data.arr.length; i++) {
let height1 = data.arr[i];
let height2 = data.arr[i + 1];
// 如果不存在height2意味着数据循环已经到了最后一个设置左边菜单为最后一项即可
if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
if(isTabClickOver){
leftMenuStatus(i);
}else{
isTabClickOver=true
}
return;
}
}
}, 10)
}
function watchChooseuser() {
uni.$off('choose-user')
uni.$on('choose-user', (user) => {
console.log(user);
data.vipUser = user ? user : {
id: ''
}
setUser()
})
}
onBeforeUnmount(() => {
})
onShow(()=>{
watchChooseuser()
})
onLoad((opt) => {
console.log(opt)
Object.assign(data.table,opt)
if (!opt.tableId) {
infoBox.showErrorToast('暂不支持不选择台桌下载,请从桌台点餐')
return setTimeout(() => {
go.back()
}, 1500)
}
init()
})
</script>
<style lang="scss" scoped>
.choose-user {
background: #F9F9F9;
padding: 22rpx 28rpx;
}
.search {
padding-right: 28rpx;
}
.icon-saoma {
margin-left: 20rpx;
width: 34rpx;
height: 32rpx;
}
$u-tips-color: $my-main-color;
$u-primary: $my-main-color;
$u-main-color: $my-main-color;
.u-wrap {
height: calc(100vh);
/* #ifdef H5 */
height: calc(100vh - var(--window-top));
/* #endif */
display: flex;
flex-direction: column;
}
.headeimg {
width: 60rpx;
height: 60rpx;
display: flex;
background-color: #eee;
border-radius: 12rpx;
overflow: hidden;
.img {
width: 60rpx;
height: 60rpx;
}
}
.u-menu-wrap {
flex: 1;
display: flex;
overflow: hidden;
}
.u-search-inner {
// background-color: rgb(234, 234, 234);
background-color: #fff;
border-radius: 100rpx;
display: flex;
align-items: center;
padding: 10rpx 16rpx;
}
.u-search-text {
font-size: 26rpx;
color: $u-tips-color;
margin-left: 10rpx;
}
.u-tab-view {
width: 178rpx;
height: 100%;
}
.u-tab-item {
height: 110rpx;
background: #f6f6f6;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #444;
font-weight: 400;
line-height: 1;
}
.u-tab-item-active {
position: relative;
color: #000;
font-size: 30rpx;
font-weight: 600;
background: #fff;
}
.u-tab-item-active::before {
content: "";
position: absolute;
border-left: 4px solid $u-primary;
left: 0;
top: 0;
bottom: 0;
}
.u-tab-view {
height: 100%;
background-color: #f6f6f6;
}
.addCai {
width: 250rpx;
height: 272rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
box-sizing: border-box;
border: 4rpx solid #90BDF6;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.lingshi {
width: 250rpx;
height: 136px;
background: #FFFFFF;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 4rpx solid #90BDF6;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 24px;
}
.right-box {
width: 572rpx;
// background-color: rgb(250, 250, 250);
background-color: rgb(255, 255, 255);
}
.page-view {
// padding: 24rpx 28rpx 24rpx 24rpx;
}
.class-item {
margin-bottom: 30rpx;
background-color: #fff;
border-radius: 8rpx;
}
.class-item:last-child {
min-height: 100vh;
}
.item-title {
font-size: 26rpx;
color: $u-main-color;
font-weight: bold;
}
.item-menu-name {
font-weight: normal;
font-size: 24rpx;
color: $u-main-color;
}
.item-container {
display: flex;
flex-wrap: wrap;
margin-top: 24rpx;
}
.thumb-box {
margin-right: 24rpx;
margin-bottom: 24rpx;
}
.item-menu-image {
width: 120rpx;
height: 120rpx;
}
</style>