Files
cashier_wx/userPackage/index/index.vue
2025-12-20 14:41:46 +08:00

857 lines
18 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="min-page bg-f7 u-font-28">
<up-sticky :offsetTop="0" :customNavHeight="0">
<view class="top" :style="topStyle">
<up-navbar bg-color="transparent" :fixed="false" :placeholder="false" title="套餐推广" @leftClick="back()"
left-icon-color="#fff" title-color="#fff"></up-navbar>
<view class="u-flex info u-col-center">
<image :src="imgs.map" class="map"></image>
<view class="u-line-1 u-m-l-20">{{shopInfo.shopName||''}}</view>
</view>
</view>
<view class="">
<view class="filters ">
<view class="u-flex tabs">
<view class="tab" v-for="(item,index) in tabs.list" :class="{'active':tabs.sel==index}"
@click="tabs.sel=index">{{item}}</view>
</view>
<view class="u-flex u-col-center" v-if="tabs.sel==0">
<up-search v-model="query.packageName" @search="refresh" @clear="refresh"
@custom="refresh"></up-search>
<!-- <perpoleNumber v-model="query.groupPeopleNum"></perpoleNumber> -->
</view>
<view class="u-flex orderStatus" v-if="tabs.sel==1">
<view class="orderState" :class="{'active':orders.sel==index}"
v-for="(item,index) in orders.list" :key="index" @click="orders.sel=index">
{{item.name}}
</view>
</view>
</view>
</view>
</up-sticky>
<view class="lists" v-if="tabs.sel==0">
<view class="item" v-for="(item,index) in list" :key="index" @click="toDetail(item)">
<view class="relative img-box">
<up-image width="218rpx" radius="16rpx" height="218rpx" :src="returnCoverImg(item)"></up-image>
<view class="tag" v-if="isHasDiscount(item)">阶梯优惠</view>
</view>
<view class="u-flex-1 u-p-l-16" style="max-width: 416rpx;">
<view class="u-line-1 name">{{item.packageName}}</view>
<view class="desc u-line-1 u-m-t-10">
{{item.description}}
</view>
<view class="sale-number">已售{{item.saleNum||0}}</view>
<view class="info ">
<view class="left u-flex u-col-baseline">
<text class="u-font-24" v-if="isHasDiscount(item)">最低到手</text>
<text class="u-font-40 font-bold"> ¥{{minPrice(item)}}</text>
<text class="color-999 old-price"> ¥{{item.originPrice}}</text>
</view>
<view class="right">
<image class="pin" :src="imgs.pin"></image>
</view>
</view>
</view>
</view>
</view>
<view class="orders" v-else-if="tabs.sel==1">
<view class="item" v-for="(item,index) in list" :key="index" @click="toOrderDetail(item)">
<view class="u-flex u-col-center u-row-between">
</view>
<view class="u-m-t-32 u-flex">
<view class="img-box">
<up-image width="174rpx" height="174rpx" radius="12rpx" :src="item.goodsImg"></up-image>
<view class="tag" v-if="isHasDiscount(item)">阶梯优惠</view>
</view>
<view class="u-flex-1 u-flex u-p-l-30 u-col-center u-row-between">
<view style="max-width: 336rpx;">
<view class="u-line-2 font-bold u-font-32">{{item.packageName}}</view>
<view class="u-line-1 u-m-t-10 color-666" >{{item.packageName}}
</view>
</view>
<view>
<statusVue :status="item.status"></statusVue>
<view class="u-m-t-16">
<template v-if="item.status=='ing'||item.status=='timeout'">
<view class=" main-color main-color">
当前到手
</view>
<view class=" font-bold price main-color u-m-t-4">
<text class="u-font-26"> ¥</text>
<text class="u-font-40">{{nowPrice(item)}}</text>
</view>
<view class=" main-color old-price" v-if="item">
¥{{item.originPrice}}
</view>
</template>
<template v-else>
<template v-if="item&&item.finalPrice">
<view class=" font-bold price main-color" >
<text class="u-font-26"> ¥</text>
<text class="u-font-40">{{item.finalPrice}}</text>
</view>
<view class=" main-color old-price" >
¥{{item.originPrice}}
</view>
</template>
<template v-else>
<view class=" font-bold price main-color" >
<text class="u-font-26"> ¥</text>
<text class="u-font-40">{{item.price}}</text>
</view>
<view class=" main-color old-price" >
¥{{item.originPrice}}
</view>
</template>
</template>
</view>
</view>
</view>
</view>
<view class="u-m-t-32 info">
<view class="u-flex u-col-center">
<text class="title">可核销门店</text>
<text class="stitle">{{item.shopName}}</text>
</view>
<view class="u-flex u-m-t-32 u-col-center">
<text class="title ">门店地址</text>
<text class="stitle">{{item.shopAddress}}</text>
</view>
<view class="u-flex u-m-t-32 u-col-center" v-if="showTime(item)">
<text class="title">剩余成团时间</text>
<text class="stitle price">{{returnTime(item)}}</text>
</view>
</view>
<view class="btns" v-if="item&&item.status">
<template v-if="item.status=='ing'">
<view class="btn " @click.stop="cancelOrder(item)">取消活动</view>
<view class="btn " @click.stop="nowBuy(item)">立即购买</view>
<button class="btn black" open-type="share" @click.stop="share(item)">分享</button>
</template>
<template v-if="item.status=='timeout'">
<view class="btn " @click.stop="cancelOrder(item)">取消活动</view>
<view class="btn " @click.stop="nowBuy(item)">立即购买</view>
</template>
<template v-if="item.status=='wait_verify'">
<view class="btn " @click.stop="refund(item)">申请退款</view>
<button class="btn black" @click.stop="lookCode(item)">查看券码</button>
</template>
<template v-if="item.status=='refunding'">
<view class="btn " @click.stop="lookCode(item)">查看券码</view>
<button class="btn black" @click.stop="cancelRefund(item)">取消退款</button>
</template>
</view>
</view>
</view>
<LookQrcode v-model="modalData.show" :qrcode="qrcode"></LookQrcode>
</view>
</template>
<script setup>
import {
back
} from '@/utils/uniapp.js'
import LookQrcode from "@/components/look-qrcode/look-qrcode.vue";
import {
wxShare
} from '@/utils/share.js'
import {
getRemainingHMS
} from '@/utils/countdown.js'
import {
onShow
} from '@dcloudio/uni-app'
import * as Api from '@/common/api/market/package.js'
import perpoleNumber from './components/perpole-number.vue'
import statusVue from '@/userPackage/components/status.vue'
import {
computed,
reactive,
watch
} from 'vue'
import {
APIusershopInfodetail
} from '@/common/api/member.js'
// 判断是否有阶梯优惠
function isHasDiscount(item) {
return item.tieredDiscount && item.tieredDiscount.length
}
const canRefundStatus = ['待成团', '待核销']
function canRefund(item) {
if (canRefundStatus.includes(item.status)) {
return true
}
return false
}
const modalData = reactive({
show: false,
});
const qrcode = ref("");
function lookCode(item) {
qrcode.value = item.verifyCode
modalData.show = true;
}
const imgs = {
bg: 'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/4/dd298d6a503d432f832f3c7c2bb7fb0e.png',
bg1: 'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/1/604c3f917daa41af9239145196c6d3f3.png',
map: 'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/1/0a293f6e1a6a4e7b956379a5b6701104.png',
pin: 'https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/5/8eaf3bdd44ce422889e5feeb30bcb2ea.png'
}
const shopInfo = ref(uni.cache.get('shopInfo'))
const steps = ['发起拼团', '邀请好友', '成团优惠']
const topStyle = {
backgroundImage: 'url(' + imgs.bg + ')'
}
const tabs = reactive({
list: ['活动列表', '我的活动'],
sel: 0
})
const orders = reactive({
list: [{
name: '全部',
value: ''
},
{
name: '进行中',
value: 'ing'
},
{
name: '待核销',
value: 'wait_verify'
},
{
name: '已核销',
value: 'finish'
},
{
name: '退款',
value: 'ref'
},
],
sel: 0
})
const query = reactive({
page: 1,
size: 10,
packageName: '',
groupPeopleNum: '',
shopId: uni.cache.get('shopId'),
})
const list = ref([])
const isEnd = ref(false)
const status = computed(() => {
return orders.list[orders.sel].value
})
watch(() => status.value, () => {
refresh()
})
async function nowBuy(item) {
uni.setStorageSync('group_buying_order', {
...item,
number: 1,
price: nowPrice(item),
paramId: item.id
})
uni.navigateTo({
url: '/userPackage/confirm-order/confirm-order'
})
}
function getData() {
if (tabs.sel == 0) {
Api.getPackage(query).then(res => {
const newArr = res.records || []
if (query.page == 1) {
list.value = newArr
} else {
list.value.push(...newArr)
}
isEnd.value = query.page >= res.totalPage * 1 ? true : false
})
return
}
if (tabs.sel == 1) {
const {
page,
size,
shopId
} = query
Api.order({
page,
size,
shopId,
status: status.value,
}).then(res => {
const newArr = (res.records || []).map(v => {
return {
...v,
goodsImg: v.images[0],
}
})
if (query.page == 1) {
list.value = newArr
} else {
list.value.push(...newArr)
}
console.log(list.value)
isEnd.value = query.page >= res.totalPage * 1 ? true : false
})
return
}
}
function toDetail(item) {
uni.navigateTo({
url: '/userPackage/goodsDetail/goodsDetail?id=' + item.id + '&shopId=' + item.shopId
})
}
function toOrderDetail(item) {
uni.navigateTo({
url: '/userPackage/order/detail?orderId=' + item.id + '&shopId=' + item.shopId
})
}
function returnCoverImg(item) {
if (!item.images) {
return ''
}
return item.images[0]
}
function nowPrice(item) {
if (!item.tieredDiscount.length) {
return 0
}
let nowStep = null
for (let i in item.tieredDiscount) {
const nowItem = item.tieredDiscount[i]
const nextItem = item.tieredDiscount[i + 1]
if (!nextItem) {
if (item.shareNum >= nowItem.peopleNum) {
nowStep = nowItem
}
} else {
if (item.shareNum >= nowItem.peopleNum && item.shareNum < nextItem.peopleNum) {
nowStep = nowItem
}
}
}
console.log('nowStep', nowStep);
return nowStep ? nowStep.price : item.price
}
function refresh() {
query.page = 1
isEnd.value = false
getData()
}
function cancelOrder(item) {
uni.showModal({
title: '提示',
content: '是否取消该活动?',
showCancel: true,
success(res) {
if (res.confirm) {
Api.cancel({
orderId: item.id,
}).then(res => {
if (res) {
uni.showToast({
title: '取消成功'
})
setTimeout(() => {
refresh()
}, 1000)
}
})
}
}
})
}
function refund(item) {
uni.showModal({
title: '提示',
content: '是否申请退款?',
showCancel: true,
success(res) {
if (res.confirm) {
Api.applyRefund({
recordId: item.id,
orderNo: item.orderNo,
}).then(res => {
if (res) {
uni.showToast({
title: '申请成功'
})
setTimeout(() => {
refresh()
}, 1000)
}
})
}
}
})
}
function cancelRefund(item) {
uni.showModal({
title: '提示',
content: '是否取消退款?',
showCancel: true,
success(res) {
if (res.confirm) {
Api.cancelRefund({
recordId: item.id,
orderNo: item.orderNo,
}).then(res => {
if (res) {
uni.showToast({
title: '取消成功'
})
setTimeout(() => {
refresh()
}, 1000)
}
})
}
}
})
}
watch(() => tabs.sel, (newval) => {
orders.sel = 0
refresh()
})
function showBtns(item) {
if (item.status == '已退款') {
return false
}
return true
}
function showShare(item) {
if (item.status == '待成团') {
return true
}
return false
}
function showTime(item) {
if (item.status == '待成团') {
return true
}
return false
}
let shareItem = null
function share(item) {
shareItem = item
}
onShareAppMessage(() => {
console.log('onShareAppMessage')
console.log(shareItem)
const query = `orderId=${shareItem.id}&shopId=${shareItem.shopId}`
return wxShare({
title: shareItem.packageName,
imageUrl: shareItem.goodsImg,
path: '/userPackage/order/detail' + '?' + query,
query,
})
})
onLoad(() => {
APIusershopInfodetail({
shopId: uni.cache.get('shopId')
}).then(res => {
console.log(res);
shopInfo.value = res.shopInfo
uni.cache.set('shopInfo', res.shopInfo)
})
})
onShow(getData)
let timer = null
let nowTime = ref(Date.now())
timer = setInterval(() => {
nowTime.value = Date.now()
}, 1000)
function returnTime(item) {
nowTime.value
return getRemainingHMS(item)
}
function minPrice(item) {
if (!item || item.tieredDiscount <= 0) {
return item.price
}
return item.tieredDiscount[item.tieredDiscount.length - 1].price
}
</script>
<style lang="scss" scoped>
.top {
height: 422rpx;
background-size: cover;
.info {
padding: 32rpx 30rpx 0 30rpx;
color: #fff;
font-size: 32rpx;
font-weight: 700;
}
.map {
width: 48rpx;
height: 48rpx;
}
}
.steps {
display: flex;
padding: 20rpx 20rpx;
gap: 20rpx;
border-radius: 14rpx 14rpx 0 0;
background: linear-gradient(90deg, #FFF5E6 0%, #FFD2CA 100%);
align-items: center;
.step {
display: flex;
align-items: center;
}
.index {
border-radius: 60rpx;
color: #fff;
width: 44rpx;
height: 44rpx;
line-height: 44rpx;
text-align: center;
background: #E55626;
}
.text {
color: #5A352F;
margin-left: 10rpx;
margin-right: 22rpx;
white-space: nowrap;
}
.icon {
color: rgba(90, 53, 47, 0.42);
}
.icon1 {
color: #5A352F;
}
}
.lists {
padding: 18rpx 10rpx;
.item {
display: flex;
padding: 32rpx 16rpx;
border-radius: 32rpx;
background: #FFF;
margin-bottom: 14rpx;
.img-box {
position: relative;
.tag {
position: absolute;
left: 0;
top: 0;
padding: 8rpx 18rpx;
border-radius: 16rpx 0;
background: #4C2828;
color: #faeac6;
font-size: 700;
}
}
.name {
font-size: 32rpx;
font-weight: 700;
color: #333333;
}
.desc {
color: #666;
}
.sale-number {
margin-top: 16rpx;
color: #999;
}
.numbers {
background: #4C2828;
padding: 8rpx 18rpx;
border-radius: 10rpx;
font-weight: 700;
color: #FAEAC6;
white-space: nowrap;
}
.name {
max-width: 334rpx;
}
.members {
margin-top: 26rpx;
background: #FDF1CB;
padding: 4rpx 18rpx;
border-radius: 10rpx 10rpx 10rpx 0;
width: fit-content;
font-size: 24rpx;
font-weight: 700;
color: #ED5A2E;
}
.info {
background: linear-gradient(270deg, #FFF7F4 44.8%, #ffffff00 89.37%);
width: 452rpx;
height: 92rpx;
border-radius: 12rpx;
background-size: cover;
transform: translateY(-10rpx);
display: flex;
justify-content: space-between;
.left {
width: 306rpx;
padding: 16rpx 22rpx;
color: #eb532a;
flex: 1;
}
.old-price {
text-decoration: line-through;
font-size: 24rpx;
}
.right {
background-color: #ED5A2E;
border-radius: 0 16rpx 16rpx 0;
padding-right: 10rpx;
padding-left: 16rpx;
display: flex;
justify-content: center;
align-items: center;
.pin {
width: 76rpx;
height: 54rpx;
}
}
}
}
}
.filters {
padding: 16rpx 28rpx;
border-radius: 6rpx 6rpx 16rpx 16rpx;
background: #FFF;
}
.tabs {
display: flex;
justify-content: space-between;
font-size: 24rpx;
color: #999;
padding: 32rpx 88rpx;
.tabs {
transition: all .3s linear;
}
.active {
color: #ED5A2E;
font-size: 32rpx;
font-weight: 700;
font-weight: 700;
}
}
.translateY20 {
transform: translateY(-20rpx);
}
.orderStatus {
padding: 26rpx 28rpx;
justify-content: space-between;
height: 100rpx;
.orderState {
color: #999;
transition: all .3s linear;
&.active {
color: #000;
font-size: 32rpx;
font-weight: 700;
}
}
}
.orders {
padding: 0 26rpx 36rpx;
margin-top: 32rpx;
.item {
padding: 32rpx 24rpx;
border-radius: 16rpx;
background: #FFF;
margin-bottom: 32rpx;
.main-color {
color: #eb532a;
}
.img-box {
position: relative;
.tag {
position: absolute;
left: 0;
top: 0;
padding: 8rpx 18rpx;
border-radius: 16rpx 0;
background: #4C2828;
color: #faeac6;
font-size: 700;
}
}
.old-price {
font-size: 24rpx;
color: #999;
text-decoration: line-through;
}
.numbers {
background-color: #ED5A2E;
padding: 16rpx 30rpx;
border-radius: 12rpx;
font-size: 32rpx;
color: #fff;
font-weight: 700;
line-height: 36rpx;
}
.price {
color: #ED5A2E;
font-weight: 700;
line-height: 36rpx;
font-size: 40rpx;
}
.info {
padding: 16rpx 34rpx;
flex-direction: column;
align-items: flex-start;
border-radius: 8rpx;
background: #F8F8F8;
.title {
min-width: 208rpx;
padding-right: 8rpx;
box-sizing: border-box;
}
.stitle {
color: #666;
&.price {
font-size: 32rpx;
color: #ed5a2e;
font-weight: 700;
}
}
}
.btns {
margin-top: 28rpx;
display: flex;
justify-content: flex-end;
gap: 34rpx;
.btn {
line-height: 1;
margin: 0;
padding: 12rpx 14rpx;
border-radius: 10rpx;
border: 2rpx solid #D9D9D9;
background: #FFF;
font-size: 28rpx;
&.black {
border-color: #343030;
background-color: #343030;
color: #fff;
}
}
}
}
}
.copy {
width: 28rpx;
height: 28rpx;
}
</style>