主要是將cartStore持久化

main
Wayne 3 years ago
parent 04405ddb28
commit f6a19f95b7

1
.gitignore vendored

@ -1,2 +1,3 @@
pos pos
data data
fds

@ -21,17 +21,12 @@ declare module '@vue/runtime-core' {
VanCollapse: typeof import('vant/es')['Collapse'] VanCollapse: typeof import('vant/es')['Collapse']
VanCollapseItem: typeof import('vant/es')['CollapseItem'] VanCollapseItem: typeof import('vant/es')['CollapseItem']
VanDatePicker: typeof import('vant/es')['DatePicker'] VanDatePicker: typeof import('vant/es')['DatePicker']
VanDialog: typeof import('vant/es')['Dialog']
VanField: typeof import('vant/es')['Field'] VanField: typeof import('vant/es')['Field']
VanForm: typeof import('vant/es')['Form'] VanForm: typeof import('vant/es')['Form']
VanGrid: typeof import('vant/es')['Grid']
VanGridItem: typeof import('vant/es')['GridItem']
VanIcon: typeof import('vant/es')['Icon'] VanIcon: typeof import('vant/es')['Icon']
VanList: typeof import('vant/es')['List'] VanList: typeof import('vant/es')['List']
VanNavBar: typeof import('vant/es')['NavBar'] VanNavBar: typeof import('vant/es')['NavBar']
VanOverlay: typeof import('vant/es')['Overlay']
VanPicker: typeof import('vant/es')['Picker'] VanPicker: typeof import('vant/es')['Picker']
VanPopover: typeof import('vant/es')['Popover']
VanPopup: typeof import('vant/es')['Popup'] VanPopup: typeof import('vant/es')['Popup']
VanPullRefresh: typeof import('vant/es')['PullRefresh'] VanPullRefresh: typeof import('vant/es')['PullRefresh']
VanRadio: typeof import('vant/es')['Radio'] VanRadio: typeof import('vant/es')['Radio']
@ -42,6 +37,5 @@ declare module '@vue/runtime-core' {
VanSwitch: typeof import('vant/es')['Switch'] VanSwitch: typeof import('vant/es')['Switch']
VanTab: typeof import('vant/es')['Tab'] VanTab: typeof import('vant/es')['Tab']
VanTabs: typeof import('vant/es')['Tabs'] VanTabs: typeof import('vant/es')['Tabs']
VanUploader: typeof import('vant/es')['Uploader']
} }
} }

@ -21,9 +21,7 @@
<link rel="stylesheet" id="rtl-link" type="text/css" href="/assets/css/vendors/bootstrap.css" /> <link rel="stylesheet" id="rtl-link" type="text/css" href="/assets/css/vendors/bootstrap.css" />
<!-- Iconly Icon css --> <!-- Iconly Icon css -->
<!-- <link rel="stylesheet" type="text/css" href="assets/css/iconly.css" /> --> <link href="https://dev.iconly.io/public/YRbcuMFyfelp/iconly.css" rel="stylesheet"/>
<link href="https://dev.iconly.io/public/zmWANpvJMPK2/iconly.css" rel="stylesheet" />
<!-- Slick css --> <!-- Slick css -->
<!-- <link rel="stylesheet" type="text/css" href="assets/css/vendors/slick.css" /> <!-- <link rel="stylesheet" type="text/css" href="assets/css/vendors/slick.css" />
<link rel="stylesheet" type="text/css" href="assets/css/vendors/slick-theme.css" /> --> <link rel="stylesheet" type="text/css" href="assets/css/vendors/slick-theme.css" /> -->
@ -35,7 +33,7 @@
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.js"></script>
<script src="https://js.tappaysdk.com/sdk/tpdirect/v5.15.0"></script> <script src="https://js.tappaysdk.com/sdk/tpdirect/v5.15.0"></script>
<script src="/assets/js/bootstrap.bundle.min.js"></script> <script src="/assets/js/bootstrap.bundle.min.js"></script>
</body> </body>

@ -24,6 +24,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.28", "pinia": "^2.0.28",
"pinia-plugin-persistedstate": "^3.1.0",
"postcss": "^8.4.20", "postcss": "^8.4.20",
"qrcode.vue": "^3.4.0", "qrcode.vue": "^3.4.0",
"reconnecting-websocket": "^4.4.0", "reconnecting-websocket": "^4.4.0",

@ -10,7 +10,7 @@
</li> </li>
<li class="footer-item" :class="{active: $route.path=='/category'}"> <li class="footer-item" :class="{active: $route.path=='/category'}">
<router-link to="/category?type=1" class="footer-link"> <router-link to="/category?type=1" class="footer-link">
<i class="icon-cart-variant icli"></i> <i class="icon-food-fork-drink icli"></i>
<span>線上點餐</span> <span>線上點餐</span>
</router-link> </router-link>
</li> </li>

@ -1,474 +0,0 @@
<template>
<!-- Main Start -->
<van-tabs v-model:active="active">
<van-tab title="商品"></van-tab>
<van-tab title="詳情"></van-tab>
<!-- <van-tab title="評價"></van-tab> -->
</van-tabs>
<main class="main-wrap product-page mb-xxl" v-if="active === 0">
<!-- Banner Section Start -->
<div class="banner-box product-banner">
<van-swipe>
<van-swipe-item v-for="images in goods.goods_gallery" :index="images.img_id">
<img class="banner_img" :src="images.img_url" />
</van-swipe-item>
<template #indicator="{ active, total }">
<div class="custom-indicator">{{ active + 1 }}/{{ total }}</div>
</template>
</van-swipe>
</div>
<!-- Banner Section End -->
<!-- Product Section Section Start -->
<section class="product-section">
<div class="name">
<div class="font-md">
{{ goods.goods_name }}
</div>
<div class="share">
<van-popover placement="left-start" @open="handleOpenPoppver" v-model:show="showPopover">
<van-grid square clickable :border="false" column-num="4" style="width: 300px">
<van-grid-item @click="handleClickPoppver('qrcode')">
<div class="share-icon">
<div>
<img src="@/assets/icons/png/qrcode.png" />
</div>
<div>QRCoce</div>
</div>
</van-grid-item>
<van-grid-item @click="handleClickPoppver('link')">
<div class="share-icon">
<div>
<img src="@/assets/icons/png/link.png" />
</div>
<div>分享連結</div>
</div>
</van-grid-item>
<van-grid-item @click="handleClickPoppver('line')">
<div class="share-icon">
<div>
<img src="@/assets/icons/png/line.png" />
</div>
<div>Line</div>
</div>
</van-grid-item>
<van-grid-item text="QRCoce" @click="handleClickPoppver('fb')">
<div class="share-icon">
<div>
<img src="@/assets/icons/png/Facebook.png" />
</div>
<div>FB</div>
</div>
</van-grid-item>
</van-grid>
<template #reference>
<i class="icon-share"></i>
</template>
</van-popover>
</div>
</div>
<div class="rating">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-star"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
></polygon>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-star"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
></polygon>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-star"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
></polygon>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-star"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
></polygon>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-star"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
></polygon>
</svg>
<span class="font-xs content-color">({{goods.click_count}}次點擊)</span>
</div>
<div class="price">
<span>${{ goods.shop_price }}</span>
<del>${{ goods.market_price }}</del>
<span>{{ discount }}</span>
</div>
<div class="brief">
{{ goods.goods_brief }}
</div>
</section>
<!-- Card Data-->
<card-data v-model="cardData" />
<div style="margin: 16px">
<van-button round block type="primary" @click="addToCart(1, 1)"> 立即購買 </van-button>
</div>
<!-- Product Section Section End -->
<!-- Product Review Section Start -->
<!-- <section class="product-review pb-0">
<div class="top-content">
<h3 class="title-color">用户評論(15)</h3>
<a href="javascript:void(0)" data-bs-toggle="offcanvas" data-bs-target="#all-review" class="font-xs">See
all</a>
</div>
<div class="review-wrap">
<div class="review-box">
<div class="media">
<img src="@/assets/images/avatar/avatar.jpg" alt="avatar" />
<div class="media-body">
<h4 class="font-sm title-color">Andrea Joanne</h4>
<div class="rating">
<i data-feather="star"></i>
<i data-feather="star"></i>
<i data-feather="star"></i>
<i data-feather="star"></i>
<i data-feather="star"></i>
</div>
</div>
</div>
<p class="font-sm content-color">It's a really cute skirt! I didn't expect to feel so good in a
polyester material. The print is slightly</p>
</div>
<div class="review-box">
<div class="media">
<img src="@/assets/images/avatar/avatar.jpg" alt="avatar" />
<div class="media-body">
<h4 class="font-sm title-color">Andrea Joanne</h4>
<div class="rating">
<i data-feather="star"></i>
<i data-feather="star"></i>
<i data-feather="star"></i>
<i data-feather="star"></i>
<i data-feather="star"></i>
</div>
</div>
</div>
<p class="font-sm content-color">It's a really cute skirt! I didn't expect to feel so good in a
polyester material. The print is slightly</p>
</div>
</div>
</section> -->
<!-- Product Review Section End -->
<section class="check-delivery-section">
<div class="service-section">
<ul>
<li class="font-sm content-color">
<img src="@/assets/icons/svg/delivery.svg" class="img-fluid" alt="" />
NFC感應卡片將在三個工作天寄至您的收件地址
</li>
<!-- <li class="font-sm content-color"><img src="@/assets/icons/svg/payment.svg" class="img-fluid"
alt="" />
Cash On delivery Available
</li>
<li class="font-sm content-color"><img src="@/assets/icons/svg/refund.svg" class="img-fluid"
alt="" />
Easy 21 days returns and exchanges
</li> -->
</ul>
</div>
</section>
</main>
<!-- Main End -->
<!-- Main Start -->
<main class="main-wrap detail-page mb-xxl" v-if="active == 1">
<section class="detail-section" v-html="goods.goods_desc"></section>
</main>
<!-- Main End -->
<!-- 分享二維碼 Start -->
<van-dialog
v-model:show="showShareQrcode"
title="分享二維碼"
:show-cancel-button="true"
cancel-button-text="關閉"
:show-confirm-button="false"
>
<div class="qrcode-block">
<qrcode-vue :value="`https://shop.slash1000.com/m/card?refer=${refer_code}`" size="200" level="M" />
</div>
</van-dialog>
<!-- Main End -->
</template>
<script setup>
import Cookies from "js-cookie";
import QrcodeVue from "qrcode.vue";
import { toClipboard } from '@soerenmartius/vue3-clipboard'
import { ref, onMounted, watch, computed } from "vue";
import { getGoods } from "@/services/goods";
import { useRoute, useRouter } from "vue-router";
import { useCartStore } from "@/store/Cart.js";
import { useUserStore } from "@/store/User.js";
import { storeToRefs } from "pinia";
import { showToast } from "vant";
import "vant/es/toast/style";
import { addCart } from "@/services/cart";
import CardData from "./compoments/CardData.vue";
const route = useRoute();
const router = useRouter();
const cartStore = useCartStore();
const userStore = useUserStore();
const active = ref(0);
const goods = ref({});
const cardData = ref({
cname: "",
ename: "",
image: "",
user_id: Cookies.get("uid") || "",
});
onMounted(async () => {
let res = await getGoods(1);
if (res.code === 200) {
goods.value = res.data;
}
});
const discount = computed(() => {
return Math.ceil((goods.value.shop_price / goods.value.market_price) * 10);
});
const addToCart = async (id, type) => {
//
let goods = {
quick: 1,
spec: "",
goods_id: id,
number: 1,
parent: 0,
cardInfo: cardData.value,
};
let res = await cartStore.addCart(goods);
if (!res) {
return showToast("添加失敗!");
}
if (type === 0) {
showToast("添加成功!");
} else {
//
if (Cookies.get("uid")) {
return router.push("/checkout");
}
return router.push({
path: "/login",
query: {
redirect: "/checkout",
},
});
}
return;
};
const goCheckout = async () => {
//
let goods = {
quick: 1,
spec: "",
goods_id: 1,
number: 1,
parent: 0,
cardInfo: form.value,
};
let res = await addCart(goods);
if (res.code === 200) {
//
if (Cookies.get("uid")) {
return router.push("/checkout");
}
return router.push("/login");
}
alert("操作錯誤,請重新操作");
return;
};
//
const showPopover = ref(false);
const showShareQrcode = ref(false);
const refer_code = computed(() => {
if (!Cookies.get("uid")) {
return "";
}
return userStore.info.refer_code;
});
const handleOpenPoppver = () => {
//
if (!Cookies.get("uid")) {
return showToast("注意!正式會員分享才能獲得獎勵喔,如果您已是正式會員,請先登入!");
}
};
const handleClickPoppver = (type) => {
const share_url = "https://shop.slash1000.com/m/card?refer=" + refer_code.value;
switch (type) {
case "qrcode":
showShareQrcode.value = true;
break;
case "link":
toClipboard(share_url);
showToast("已放入剪貼簿");
break;
case "line":
window.location.href = "https://social-plugins.line.me/lineit/share?url=" + encodeURI(share_url);
break;
case "fb":
window.open("https://www.facebook.com/share.php?u=" + encodeURI(share_url), "_blank");
break;
default:
break;
}
showPopover.value = false;
};
const onSelect = (action) => Toast(action.text);
</script>
<style lang="less" scoped>
.van-swipe-item {
color: #fff;
font-size: 20px;
line-height: 200px;
text-align: center;
background-color: #39a9ed;
}
.custom-indicator {
position: absolute;
right: 5px;
bottom: 5px;
padding: 2px 5px;
font-size: 12px;
background: rgba(0, 0, 0, 0.1);
}
.banner_img {
width: 100%;
}
.product-section {
.name {
font-weight: 700;
color: #222;
line-height: 20px;
height: 80px;
// margin-top: -3px;
display: flex;
background-color: #f2f2f2;
padding: 15px;
margin-left: -15px;
margin-right: -15px;
.font-md {
flex: 1;
margin-right: 15px;
border-right: 1px #666 solid;
}
.share {
display: flex;
align-items: center;
width: 20px;
font-size: 18px;
}
}
.brief {
margin: 10px 0;
font-size: 18px;
}
}
.share-icon {
text-align: center;
div:nth-child(1) {
img {
width: 30px;
}
}
// div:child-nth(2){
// }
}
.qrcode-block {
text-align: center;
}
</style>

@ -1,274 +0,0 @@
<template>
<div>
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link" :class="{ active: !showCardType }" href="javascript:void(0)"
@click="showCardType = 0">正面</a>
</li>
<li class="nav-item">
<a class="nav-link" :class="{ active: showCardType }" href="javascript:void(0)"
@click="showCardType = 1">反面</a>
</li>
</ul>
<div class="card-preview">
<div class="front" v-show="showCardType === 0">
<!-- <img class="front-bg" src="@/assets/images/card/front.png"> -->
<div class="front-cname">{{ form.cname }}</div>
<div class="front-ename">{{ form.ename }}</div>
<img class="front-logo" :src="form.image || defaultLogo">
</div>
<div class="back" v-show="showCardType === 1">
<img src="@/assets/images/card/qrcode.png">
</div>
</div>
<van-cell-group inset>
<van-field v-model="form.cname" type="text" placeholder="任何您想寫的文字" label="中文姓名" />
<van-field v-model="form.ename" type="text" placeholder="任何您想寫的文字" label="英文姓名" />
</van-cell-group>
<div style="margin: 16px 32px;">
圖片<br />
<van-uploader :after-read="afterRead" @delete="handleDelete" accept="image/*" name="logo" class="img-uploader"
:max-count="1" v-model="fileList" />
<!-- <div class="upload-main">
<img class="upload-img" :src="form.avatar" alt="">
<p>上傳圖片</p>
</div> -->
<!-- </van-uploader> -->
</div>
</div>
<!-- crop : Start -->
<van-overlay :show="crop.show" @click="crop.show = false" />
<div class="cropper-section" v-if="crop.show">
<div class="crop-area">
<cropper class="cropper" ref="myCrop" :src="crop.img" :stencil-props="{
aspectRatio: 10 / 10,
}" :auto-zoom="true" />
</div>
<div class="crop-btn">
<van-button type="primary" size="small" plain @click="onClose"></van-button>
<van-button type="success" size="small" plain @click="onCrop"></van-button>
</div>
</div>
<!-- crop : End -->
</template>
<script setup>
import { ref, computed } from 'vue'
import { Cropper } from 'vue-advanced-cropper';
import 'vue-advanced-cropper/dist/style.css';
import { uploadFile } from '@/services/card'
import defaultLogo from '@/assets/images/logo/logo.png'
const showCardType = ref(0)
const fileList = ref([])
const props = defineProps({
modelValue: {
type: Object,
default: ()=>({})
}
})
const emit = defineEmits(['update:modelValue'])
const form = computed({
get() {
return props.modelValue
},
set(v) {
console.log('set', v)
emit('update:modelValue', v)
}
})
const afterRead = async (file) => {
crop.value.show = true
crop.value.img = file.content
return
let imgFile = new FormData()
imgFile.append("fileType", 'IMAGE');
imgFile.append("file", file.file);
file.status = 'uploading'
file.message = '上傳中...'
try {
let res = await uploadFile(imgFile)
if (res && res.code === 200) {
form.value.image = res.data
file.status = 'done';
file.message = '上傳完成';
} else {
file.status = 'failed';
file.message = '上傳失敗';
}
} catch (e) {
file.status = 'failed'
file.message = '上傳失敗'
}
}
// crop
const myCrop = ref(null);
const crop = ref({
show: false,
img: null,
outputType: "jpeg",
autoCrop: true,
autoCropWidth: 200,
autoCropHeight: 200,
});
const onCrop = () => {
const { canvas } = myCrop.value.getResult();
if (canvas) {
const imgFile = new FormData();
canvas.toBlob(async (blob) => {
let ufile = new File([blob], "image.jpg");
imgFile.append("fileType", "IMAGE");
imgFile.append("file", ufile);
crop.value.show = false;
// Toast.loading({
// duration: 0,
// message: "...",
// forbidClick: true,
// });
fileList.value[0] = {
status: 'uploading',
message: '上傳中...'
}
try {
let res = await uploadFile(imgFile)
if (res && res.code === 200) {
form.value.image = res.data
fileList.value[0] = {
url: res.data,
status: 'done',
message: '上傳成功'
}
} else {
fileList.value[0] = {
status: 'failed',
message: '上傳失敗'
}
}
} catch (e) {
fileList.value[0] = {
status: 'failed',
message: '上傳失敗'
}
}
}, "image/jpeg");
}
return;
};
const onClose = () => {
fileList.value = []
crop.value.show = false;
};
const handleDelete = (file) => {
console.log('delete')
console.log('file', file)
form.value.image = ''
}
</script>
<style lang="less" scoped>
.card-preview {
// height: 50%;
width: 345px;
margin-bottom: 10px;
.front {
width: 345px;
height: 220px;
position: relative;
background-image: url(@/assets/images/card/front.png);
background-size: 345px 220px;
.front-logo {
position: relative;
width: 100px;
top: 50px;
left: 20px;
}
.front-cname {
position: absolute;
top: 70px;
left: 180px;
font-size: 20px;
letter-spacing: 5px;
}
.front-ename {
position: absolute;
top: 110px;
left: 180px;
font-size: 12px;
letter-spacing: 5px;
}
}
.back {
width: 345px;
height: 220px;
position: relative;
background-image: url(@/assets/images/card/back.png);
background-size: 345px 220px;
display: flex;
align-items: center;
justify-content: center;
}
}
.cropper-section {
margin: 0 auto;
position: fixed;
text-align: center;
top: 50px;
// left: 0;
height: 350px;
width: 100%;
max-width: 500px;
background: #ddd;
z-index: 8888;
.crop-area {
margin: 5 auto;
width: 100%;
height: 330px;
}
.crop-btn {
background-color: #666;
text-align: center;
}
}
.cropper {
height: 300px;
// width: 300px;
background: #ddd;
}
</style>

@ -28,7 +28,7 @@
v-swiperight="(event) => handleSwipeRight(event, item)"> v-swiperight="(event) => handleSwipeRight(event, item)">
<div class="product-list media"> <div class="product-list media">
<a href="javascript:void(0);"> <a href="javascript:void(0);">
<img :src="img_url + item.goods_thumb" alt="offer" /> <img :src="item.goods_thumb" alt="offer" />
</a> </a>
<div class="media-body"> <div class="media-body">
<a href="javascript:void(0)" class="font-md"> {{ item.goods_name }} </a> <a href="javascript:void(0)" class="font-md"> {{ item.goods_name }} </a>
@ -44,7 +44,7 @@
</div> </div>
</div> </div>
<div class="delete-button" data-bs-toggle="offcanvas" data-bs-target="#confirmation" <div class="delete-button" data-bs-toggle="offcanvas" data-bs-target="#confirmation"
aria-controls="confirmation" @click="handleDeleteItem(item.rec_id)"> aria-controls="confirmation" @click="handleDeleteItem(item.goods_id)">
<i class="icon-trash icli"></i> <i class="icon-trash icli"></i>
</div> </div>
</div> </div>
@ -389,7 +389,7 @@ const handleDeleteItem = async (id) => {
const changeItemNum = (item, num) => { const changeItemNum = (item, num) => {
if (item.goods_number + num <= 0) { if (item.goods_number + num <= 0) {
cartStore.delCart(item.rec_id); cartStore.delCart(item.goods_id);
} else { } else {
cartStore.updateCart(item, item.goods_number + num); cartStore.updateCart(item, item.goods_number + num);
} }

@ -41,7 +41,7 @@
</div> </div>
</div> </div>
<div class="delete-button" data-bs-toggle="offcanvas" data-bs-target="#confirmation" <div class="delete-button" data-bs-toggle="offcanvas" data-bs-target="#confirmation"
aria-controls="confirmation" @click="handleDeleteItem(item.rec_id)"> aria-controls="confirmation" @click="handleDeleteItem(item.goods_id)">
<i class="icon-trash icli"></i> <i class="icon-trash icli"></i>
</div> </div>
</div> </div>
@ -106,7 +106,7 @@ const handleDeleteItem = async (id) => {
const changeItemNum = (item, num) => { const changeItemNum = (item, num) => {
if (item.goods_number + num <= 0) { if (item.goods_number + num <= 0) {
cartStore.delCart(item.rec_id); cartStore.delCart(item.goods_id);
} else { } else {
cartStore.updateCart(item, item.goods_number + num); cartStore.updateCart(item, item.goods_number + num);
} }
@ -114,7 +114,7 @@ const changeItemNum = (item, num) => {
const changeInputNum = (item, num) => { const changeInputNum = (item, num) => {
if (num <= 0) { if (num <= 0) {
cartStore.delCart(item.rec_id); cartStore.delCart(item.goods_id);
} else { } else {
cartStore.updateCart(item, num); cartStore.updateCart(item, num);
} }

@ -9,28 +9,18 @@
<!-- Search Box End --> <!-- Search Box End -->
<!-- Search Box Start --> <!-- Search Box Start -->
<div class="info-box"> <div class="category-header">
<div> <van-field v-model="type" readonly size="large" label="點餐類型" />
點餐類型: <van-field v-model="cartStore.table_no" is-link readonly size="large" label="桌號" placeholder="請選擇桌號"
</div> @click="showPicker = true" v-if="cartStore.type === 1" />
<div> <van-popup v-model:show="showPicker" round position="bottom">
{{ type }} <van-picker
</div> :columns="columns"
</div> @cancel="showPicker = false"
<div class="info-box" v-if="cartStore.type === 1"> confirm-button-text="確定"
<div> cancel-button-text="取消"
桌號: @confirm="onConfirm" />
</div> </van-popup>
<div>
{{ cartStore.table_no }}
</div>
</div>
<div class="info-box" v-else-if="state.type === 3">
<div>
地址:
</div>
<div>
</div>
</div> </div>
<!-- Search Box End --> <!-- Search Box End -->
@ -70,8 +60,9 @@
<p class="price">${{ v.shop_price }}</p> <p class="price">${{ v.shop_price }}</p>
</div> </div>
<div> <div>
<van-stepper theme="round" :min="0" :default-value="cartNum(v.goods_id)" button-size="22" <van-stepper theme="round" v-model="goodsNumber[v.goods_id]" :min="0"
isable-input @plus="changeItemNum(v.goods_id, 1)" @minus="changeItemNum(v.goods_id, -1)" /> :default-value="cartNum(v.goods_id)" button-size="22" isable-input @plus="changeItemNum(v, 1)"
@minus="changeItemNum(v, -1)" />
</div> </div>
</div> </div>
@ -100,7 +91,7 @@
<!-- 彈窗 --> <!-- 彈窗 -->
<van-popup v-model:show="showBottom" round position="bottom" closeable close-icon="close" :style="{ height: '650px' }"> <van-popup v-model:show="showBottom" round position="bottom" closeable close-icon="close" :style="{ height: '650px' }">
<div class="popup-content"> <div class="popup-content">
<goods :id="goods_id" @change="changeItemNum" /> <goods :id="goods_id" @close="handlePopupClose" />
</div> </div>
</van-popup> </van-popup>
</template> </template>
@ -130,6 +121,8 @@ const cate_id = ref(1)
const goods = ref({}) const goods = ref({})
const goods_id = ref({}) const goods_id = ref({})
const goodsNumber = ref({})
const showBottom = ref(false) const showBottom = ref(false)
const state = reactive( const state = reactive(
@ -191,15 +184,15 @@ const handleClickItem = async (cat_id) => {
} }
} }
const changeItemNum = (goods_id, num) => { const changeItemNum = (goods, num) => {
//goods_iditem //goods_iditem
let item = cartItems.value[goods_id]; let item = cartItems.value[goods.goods_id];
if (!item) { if (!item) {
return addToCart(goods_id, num); return addToCart(goods, num);
} }
if (item.goods_number + num <= 0) { if (item.goods_number + num <= 0) {
cartStore.delCart(item.rec_id); cartStore.delCart(item.goods_id);
} else { } else {
cartStore.updateCart(item, item.goods_number + num); cartStore.updateCart(item, item.goods_number + num);
} }
@ -213,18 +206,23 @@ const cartTotal = computed(() => {
}, 0); }, 0);
}) })
const addToCart = async (id, num, type = 0) => { const addToCart = async (goods, num, type = 0) => {
// //
let goods = {
quick: 1, let gs = {
spec: "", add_info: '',
goods_id: id, buymax: goods.buymax,
number: num, cat_id: goods.cat_id,
parent: 0, goods_id: goods.goods_id,
cardInfo: "", goods_name: goods.goods_name,
goods_number: 1,
goods_price: goods.shop_price,
goods_sn: goods.goods_sn,
goods_thumb: goods.goods_thumb
}; };
let res = await cartStore.addCart(goods); let res = await cartStore.addCart(gs);
if (!res) { if (!res) {
return showToast("添加失敗!"); return showToast("添加失敗!");
@ -244,24 +242,49 @@ const handleShowGoods = (id) => {
showBottom.value = true showBottom.value = true
} }
const handlePopupClose = (value) => {
goodsNumber.value[value.id] = value.num
showBottom.value = false
}
const columns = [
{ text: '1', value: '1' },
{ text: '2', value: '2' },
{ text: '3', value: '3' },
{ text: '4', value: '4' },
{ text: '5', value: '5' },
];
const fieldValue = ref('');
const showPicker = ref(false);
const onConfirm = ({ selectedOptions }) => {
console.log(selectedOptions)
showPicker.value = false;
cartStore.table_no = selectedOptions[0].value;
};
const onSubmit = () => { const onSubmit = () => {
router.push("/checkout"); router.push("/checkout");
}; };
</script> </script>
<style lang="less"> <style lang="less">
.category-header {
font-size: 16px;
.info-box { .info-box {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
// justify-content: space-between; // justify-content: space-between;
margin: 0 0px; margin: 0 0px;
font-size: 18px;
color: #666; color: #666;
div { div {
padding: 0 5px; padding: 0 5px;
} }
} }
}
.price { .price {
color: red; color: red;
@ -322,7 +345,7 @@ const onSubmit = () => {
} }
.popup-content { .popup-content {
margin-top: 50px; // margin-top: 50px;
// padding: 0 20px; // padding: 0 20px;
} }

@ -138,6 +138,19 @@
<section class="comment-section">評論</section> <section class="comment-section">評論</section>
</main> </main>
<!-- Main End --> <!-- Main End -->
<footer class="footer-nav">
<ul class="footer-left">
<li class="footer-item">
<div>
<van-stepper theme="round" v-model="goodsNumber" :min="0" :default-value="0" button-size="24" />
</div>
</li>
</ul>
<ul class="footer-right">
<li class="add-cart" @click="handleAddCart"></li>
</ul>
</footer>
</template> </template>
<script setup> <script setup>
@ -158,6 +171,8 @@ const cartStore = useCartStore();
const active = ref(0); const active = ref(0);
const goods = ref({}); const goods = ref({});
const goodsNumber = ref(0);
//goods_id //goods_id
const props = defineProps({ const props = defineProps({
id: { id: {
@ -166,13 +181,14 @@ const props = defineProps({
}, },
}); });
const emit = defineEmits(["changeNum"]); const emit = defineEmits(["close"]);
watch(() => props.id, async (nVal) => { watch(() => props.id, async (nVal) => {
if (nVal > 0) { if (nVal > 0) {
let res = await getGoods(nVal); let res = await getGoods(nVal);
if (res.code === 200) { if (res.code === 200) {
goods.value = res.data; goods.value = res.data;
goodsNumber.value = 0;
} }
} }
}, { immediate: true }) }, { immediate: true })
@ -181,19 +197,28 @@ const discount = computed(() => {
return Math.ceil((goods.value.shop_price / goods.value.market_price) * 10); return Math.ceil((goods.value.shop_price / goods.value.market_price) * 10);
}); });
const addToCart = async (id, type) => { const handleAddCart = () => {
// addToCart(goods.value, goodsNumber.value)
emit("close", {id:goods.value.goods_id,num:goodsNumber.value})
};
const addToCart = async (goods, num, type = 0) => {
let goods = { //
quick: 1, let gs = {
spec: "", add_info: '',
goods_id: id, buymax: goods.buymax,
number: 1, cat_id: goods.cat_id,
parent: 0, goods_id: goods.goods_id,
cardInfo: "", goods_name: goods.goods_name,
goods_number: num,
goods_price: goods.shop_price,
goods_sn: goods.goods_sn,
goods_thumb: goods.goods_thumb
}; };
let res = await cartStore.addCart(goods);
let res = cartStore.addCart(gs);
if (!res) { if (!res) {
return showToast("添加失敗!"); return showToast("添加失敗!");
@ -201,12 +226,11 @@ const addToCart = async (id, type) => {
if (type === 0) { if (type === 0) {
showToast("添加成功!"); showToast("添加成功!");
} else {
router.push("/cart");
} }
return; return;
}; };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -282,38 +306,25 @@ const addToCart = async (id, type) => {
display: flex; display: flex;
.footer-left { .footer-left {
width: 40%; width: 70%;
display: flex; display: flex;
li { li {
// line-height: 50px; line-height: 50px;
text-align: center; text-align: center;
width: 33%; width: 100%;
border-right: 1px solid #eeeeee; border-right: 1px solid #eeeeee;
.icon {
height: 30px;
line-height: 30px;
color: #666;
font-size: 24px !important;
}
.text {
height: 20px;
line-height: 20px;
color: #666;
font-size: 14px;
}
} }
} }
.footer-right { .footer-right {
width: 60%; width: 30%;
font: 15px; font: 15px;
display: flex; display: flex;
.add-cart { .add-cart {
width: 50%; width: 100%;
line-height: 50px; line-height: 50px;
color: #fff; color: #fff;
background-color: #ff7f24; background-color: #ff7f24;
@ -321,7 +332,7 @@ const addToCart = async (id, type) => {
} }
.buy-now { .buy-now {
width: 50%; width: 100%;
line-height: 50px; line-height: 50px;
color: #fff; color: #fff;
background-color: #067062; background-color: #067062;

@ -13,35 +13,28 @@
<div class="user-info-block"> <div class="user-info-block">
<div class="user-info" @click="$router.push('/category?type=2')"> <div class="user-info" @click="$router.push('/category?type=2')">
<div> <div>
<van-button type="primary" size="large">外帶</van-button> <van-button type="primary" size="large">
</div> <i class="icon-takeout-dining"></i>&nbsp;&nbsp;外帶</van-button>
<div>
</div> </div>
</div> </div>
<div class="user-info" @click="$router.push('/category?type=3')"> <div class="user-info" @click="$router.push('/category?type=3')">
<div> <div>
<van-button type="primary" size="large">外送</van-button> <van-button type="primary" size="large">
</div> <i class="icon-motorcycle"></i>&nbsp;&nbsp;外送</van-button>
<div>
</div> </div>
</div> </div>
<div class="user-info"> <div class="user-info">
<div> <div>
<van-button type="primary" size="large">預約</van-button> <van-button type="primary" size="large">
</div> <i class="icon-calendar-check"></i>&nbsp;&nbsp;預約</van-button>
<div>
</div> </div>
</div> </div>
<div class="user-info"> <div class="user-info">
<div> <div>
<van-button type="primary" size="large">取號</van-button> <van-button type="primary" size="large">
</div> <i class="icon-numeric-9-plus-box-multiple"></i>&nbsp;&nbsp;取號</van-button>
<div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -111,7 +104,7 @@ onMounted(async ()=>{
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-around; justify-content: space-around;
font-size: 22px;
.user-info { .user-info {
// flex: 1; // flex: 1;
border: 1px gray solid; border: 1px gray solid;

@ -1,6 +1,8 @@
<script>
</script>
<template> <template>
<div>
取號 取號
</div>
</template> </template>
<script setup>
</script>

@ -30,11 +30,6 @@ let routes = [
name: 'Article', name: 'Article',
component: ()=> import("../pages/Article/Index.vue") component: ()=> import("../pages/Article/Index.vue")
}, },
{
path: '/card',
name: 'Card',
component: ()=> import("../pages/Card/Index.vue")
},
{ {
path: '/category', path: '/category',
name: 'Category', name: 'Category',

@ -5,6 +5,7 @@ import _ from 'lodash'
import { getItems, addCart, deleteCart, updateCart } from '@/services/cart' import { getItems, addCart, deleteCart, updateCart } from '@/services/cart'
export const useCartStore = defineStore('cart', { export const useCartStore = defineStore('cart', {
persist: true,
state: () => { state: () => {
return { return {
type: 0, type: 0,
@ -30,42 +31,45 @@ export const useCartStore = defineStore('cart', {
}, },
actions: { actions: {
async initCart() { async initCart() {
let res = await getItems() // let res = await getItems()
if (res.code === 200) { // if (res.code === 200) {
this.cartItems = res.data // this.cartItems = res.data
} // }
}, },
async addCart(goods) { addCart(goods) {
let res = await addCart(goods) this.cartItems.push(goods)
if (res.code === 200) {
this.cartItems = res.data
return true return true
} else { // let res = await addCart(goods)
return false
} // if (res.code === 200) {
// this.cartItems = res.data
// return true
// } else {
// return false
// }
}, },
async delCart(id) { delCart(id) {
let res = await deleteCart(id) // let res = await deleteCart(id)
if (res.code === 200) { // if (res.code === 200) {
_.remove(this.cartItems, function (i) { _.remove(this.cartItems, function (i) {
return i.rec_id === id return i.goods_id === id
}) })
} // }
}, },
async updateCart(goods, num) { updateCart(goods, num) {
let res = await updateCart({ ...goods, num }) // let res = await updateCart({ ...goods, num })
if (res.code === 200) { // if (res.code === 200) {
this.cartItems.map((element, index) => { this.cartItems.map((element, index) => {
if (element.rec_id === goods.rec_id) { if (element.goods_id === goods.goods_id) {
element.goods_number = num element.goods_number = num
} }
}) })
} // }
}, },
async clearCart() { clearCart() {
this.type = 0 this.type = 0
this.table_no = 0 this.table_no = 0
this.cartItems = [] this.cartItems = []

@ -1,4 +1,5 @@
import { createPinia, defineStore } from 'pinia' import { createPinia, defineStore } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export const useMainStore = defineStore('main', { export const useMainStore = defineStore('main', {
state: () => { state: () => {
@ -20,5 +21,7 @@ export const useMainStore = defineStore('main', {
}, },
}) })
export default createPinia() const pinia = createPinia().use(piniaPluginPersistedstate)
export default pinia

@ -19,6 +19,6 @@
}, },
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true
}, },
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/main.js"],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
} }

@ -2419,6 +2419,11 @@ pify@^4.0.1:
resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
pinia-plugin-persistedstate@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.1.0.tgz#eada2b61ecd478fce88e490a685210415cd7a1b4"
integrity sha512-8UN+vYMEPBdgNLwceY08mi5olI0wkYaEb8b6hD6xW7SnBRuPydWHlEhZvUWgNb/ibuf4PvufpvtS+dmhYjJQOw==
pinia@^2.0.28: pinia@^2.0.28:
version "2.0.28" version "2.0.28"
resolved "https://registry.npmjs.org/pinia/-/pinia-2.0.28.tgz" resolved "https://registry.npmjs.org/pinia/-/pinia-2.0.28.tgz"

Loading…
Cancel
Save