feat:商户活动

This commit is contained in:
ctw committed 2025-10-31 21:08:17 +08:00
1 parent 1a7a6ec54d
commit 50b8652a64
6 files changed
+128 -63

No files matched your search

+11 -4
View File
@@ -23,21 +23,28 @@
<text class="file-size">{{ formatFileSize(file.size) }}</text>
</view>
</view>
<tm-button v-if="noticeId == '1969326082705055746'" class="px-2 my-4" block @click="goGetCoupon">去 领 券</tm-button>
<tm-button v-if="actDetail" class="px-2 my-4" block @click="goGetCoupon">去 领 券</tm-button>
</view>
</template>
<script setup lang="ts">
import { computed, ref } from "vue"
import { onLoad } from "@dcloudio/uni-app"
import { noticeApi } from "@/sheep/api"
import { actCancel, noticeApi } from "@/sheep/api"
const noticeId = ref("")
const detail = ref<any>({})
const actDetail = ref<any>({})
onLoad(async (option: any) => {
const { id } = option
if (!id) return uni.showToast({ title: "参数错误", icon: "error" })
const { data = {} }: any = await actCancel.getDetailByNoticeId({ id })
actDetail.value = data
})
// 临时商户活动的通知公告跳转链接
const goGetCoupon = () => {
uni.navigateTo({ url: "/pages/merchant/merchant-activity" })
uni.navigateTo({ url: `/pages/merchant/merchant-activity?actId=${actDetail.value.id}&mchId=${actDetail.value.mchId}` })
}
const nodes = computed(() => {
+39 -12
View File
@@ -1,13 +1,35 @@
<template>
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
<z-paging ref="pageRef" height="100%" :fixed="false" v-model="pageHook.list" :default-page-size="10"
safe-area-inset-bottom :loading-more-enabled="true" :show-scrollbar="false" @query="query"
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
<z-paging
ref="pageRef"
height="100%"
:fixed="false"
v-model="pageHook.list"
:default-page-size="10"
safe-area-inset-bottom
:loading-more-enabled="true"
:show-scrollbar="false"
@query="query"
:auto-clean-list-when-reload="false"
:auto-scroll-to-top-when-reload="false"
@scroll="pageHook.scroll"
>
<template #top>
<tm-input class="mt-2" v-model="params.receiverUserName" type="text" height="72" minHeight="72"
fontSize="28" :showClear="true" :focus="false" placeholder="请输入领取人姓名"
style="border: 4rpx solid #0779fd; border-radius: 14rpx" :focus-highlight="false" color="#ECF4F9"
@clear="inputClear">
<tm-input
class="mt-2"
v-model="params.receiverUserName"
type="text"
height="72"
minHeight="72"
fontSize="28"
:showClear="true"
:focus="false"
placeholder="请输入领取人姓名"
style="border: 4rpx solid #0779fd; border-radius: 14rpx"
:focus-highlight="false"
color="#ECF4F9"
@clear="inputClear"
>
<template v-slot:rightIcon>
<tm-button class="mr-1 px-3 py-26rpx text-30rpx!" size="xs" @click="search">搜 索</tm-button>
</template>
@@ -17,18 +39,23 @@
</view>
</template>
<view class="card w-full h-400rpx relative activity-card overflow-hidden mb-2 py-2"
:style="{ backgroundColor: getActStatus(item.actStatus)?.color }" v-for="(item, index) in pageHook.list"
:key="index">
<view
class="card w-full h-400rpx relative activity-card overflow-hidden mb-2 py-2"
:style="{ backgroundColor: getActStatus(item.actStatus)?.color }"
v-for="(item, index) in pageHook.list"
:key="index"
>
<view class="ribbon" :style="{ backgroundColor: getActStatus(item.actStatus)?.color }">
{{ getActStatus(item.actStatus)?.name }}
</view>
<view class="text-28 c-#fff text-right px-2">ID {{ item.id }}</view>
<view class="flex justify-between font-bold mt-1 px-8">
<text class="text-32 c-#FFD941">限时领取</text>
<text class="text-30 c-#FFD941 px-1 rounded-full bg-#eeeeee44">2025-09-21~2025-12-20</text>
<text class="text-30 c-#FFD941 px-1 rounded-full bg-#eeeeee44">
{{ (item.actStartTime?.split(" ")[0] || "") + "~" + (item.actExpireTime?.split(" ")[0] || "") }}
</text>
</view>
<view class="w-4/5 py-2 text-28 c-#fff px-8">店内核销将会自动扣费1元并领取价值89元的速干T恤1件</view>
<view class="w-4/5 py-2 text-28 c-#fff px-8">{{ item.actTitlel }}</view>
<view class="absolute bottom-100 left-60 text-28 bg-#eeeeee44 c-#fff px-2 rounded-full">限量领取 先领先得</view>
<view v-if="item.receiverTime" class="absolute bottom-40 right-20 text-22 c-#fff">
<text class="mr-2">领取人 {{ item.receiverUsername }}</text>
+61 -27
View File
@@ -1,25 +1,31 @@
<template>
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
<view class="px-2 py-4">
<view class="card w-full h-400rpx relative activity-card overflow-hidden">
<view v-if="state.actId" class="px-2 py-4">
<view class="card rounded-lb-0! rounded-rb-0! w-full h-400rpx relative activity-card overflow-hidden">
<view class="ribbon">兑换券</view>
<view class="px-8 pt-5 flex justify-between items-center font-bold">
<text class="text-32 c-#FF4F3F">限时领取</text>
<text class="text-30 c-#FF4F3F px-1 rounded-full bg-#eeeeee44">2025-09-21~2025-12-20</text>
<text class="text-30 c-#FF4F3F px-1 rounded-full bg-#eeeeee44">
{{ (state.couponDetail.startTime?.split(" ")[0] || "") + "~" + (state.couponDetail.expireTime?.split(" ")[0] || "") }}
</text>
</view>
<view class="w-4/5 px-8 pt-4 text-28 c-#fff">店内核销将会自动扣费1元并领取价值89元的速干T恤1件</view>
<view class="absolute bottom-60 left-60 text-28 bg-#eeeeee44 c-#fff px-2 rounded-full">限量领取 先领先得</view>
<view class="w-4/5 px-8 pt-4 text-28 c-#fff">{{ state.couponDetail.actTitle }}</view>
<view class="absolute bottom-100 left-60 text-40 italic">
<text class="color-red mr-2">¥{{ hooks.useMoney(state.couponDetail.activeCancelPrice) }}</text>
<text class="text-30 line-through color-red">¥{{ hooks.useMoney(state.couponDetail.activeOrgPrice) }}</text>
</view>
<view class="absolute bottom-40 left-60 text-28 bg-#eeeeee44 c-#fff px-2 rounded-full">限量领取 先领先得</view>
</view>
<view v-if="couponInfo?.actStatus == '2'" class="text-center card p-3 sub-text-30">已 核 销</view>
<view v-if="state.couponInfo.actStatus == '2'" class="text-center card rounded-lt-0! rounded-rt-0! p-3 sub-text-30">已 核 销</view>
<template v-else>
<view v-if="!isGet" class="text-center card p-3 sub-text-30" @tap="doGetCoupon">立 即 领 取</view>
<view v-if="isGet" class="text-center card p-3 sub-text-30">已 领 取</view>
<view v-if="!state.isGet" class="text-center card rounded-lt-0! rounded-rt-0! p-3 sub-text-30" @tap="getCoupon">立 即 领 取</view>
<view v-if="state.isGet" class="text-center card rounded-lt-0! rounded-rt-0! p-3 sub-text-30">已 领 取</view>
</template>
</view>
<template #main-bottom>
<view v-if="isGet" class="text-center">
<tm-button class="px-32 m-2" @click="CouponVerification">我的核销码</tm-button>
<view v-if="state.isGet" class="text-center">
<tm-button class="px-32 m-2" @click="genVerificationCode">我的核销码</tm-button>
</view>
</template>
</crud-layout>
@@ -33,31 +39,55 @@
</template>
<script setup lang="ts">
import { actCancel } from "@/sheep/api"
import { reactive, ref } from "vue"
import { onLoad } from "@dcloudio/uni-app"
import { ref } from "vue"
import { actCancel } from "@/sheep/api"
import uqrcode from "@@/utils/uqrcode"
import * as CryptoJS from "crypto-js"
import { hooks } from "@/sheep"
const isGet = ref(false) // 是否已领取
const couponInfo = ref<any>({})
const state = reactive<any>({
actId: "",
mchId: "",
isGet: false, // 是否已领取
couponDetail: {}, // 优惠券详情
couponInfo: {}, // 优惠券信息
})
const showQrCodeDialog = ref(false)
onLoad(() => {
onLoad((option: any) => {
const { actId, mchId } = option
if (!actId || !mchId) return uni.showToast({ title: "参数错误", icon: "error" })
state.actId = actId
state.mchId = mchId
getCouponDetail()
getCouponInfo()
})
// 获取优惠券信息
// 获取优惠券详情
const getCouponDetail = async () => {
try {
const { data } = await actCancel.getDetailById({
id: state.actId,
mchId: state.mchId,
})
state.couponDetail = data
} catch (error: any) {
uni.showToast({ title: error, icon: "error" })
}
}
// 获取用户的唯一优惠券信息
const getCouponInfo = async () => {
try {
uni.showLoading({ title: "加载中...", mask: true })
const { code, data }: any = await actCancel.appFindUnique()
if (code != 0) {
isGet.value = false
const { code, data }: any = await actCancel.appFindUnique({ actId: state.actId })
if (!data) {
state.isGet = false
return
}
couponInfo.value = data
isGet.value = true
state.couponInfo = data
state.isGet = true
} catch (error) {
uni.showToast({ title: "加载失败", icon: "none" })
} finally {
@@ -65,12 +95,16 @@ const getCouponInfo = async () => {
}
}
// 领取兑换券
const doGetCoupon = async () => {
// 领取券
const getCoupon = async () => {
try {
uni.showLoading({ title: "正在领取...", mask: true })
await new Promise((resolve) => setTimeout(resolve, 2_000))
const { code, msg }: any = await actCancel.appGet({ receiverUserId: uni.getStorageSync("user_info")?.sysUser?.userId })
const { code, msg }: any = await actCancel.appGet({
receiverUserId: uni.getStorageSync("user_info")?.sysUser?.userId,
actId: state.actId,
mchId: state.mchId,
})
if (code != 0) return uni.showToast({ title: msg || "领取失败", icon: "none", mask: true })
uni.showToast({ title: "领取成功", icon: "success" })
await getCouponInfo()
@@ -81,10 +115,10 @@ const doGetCoupon = async () => {
}
}
// 券核销
const CouponVerification = async () => {
// 生成核销二维码
const genVerificationCode = async () => {
showQrCodeDialog.value = true
const words = CryptoJS.enc.Utf8.parse(JSON.stringify({ actId: couponInfo.value.id, totalPrice: 1 }))
const words = CryptoJS.enc.Utf8.parse(JSON.stringify({ cancelId: state.couponInfo.id }))
uqrcode.make({
canvasId: "qrcodeId",
text: CryptoJS.enc.Base64.stringify(words),
+1 -1
View File
@@ -16,7 +16,7 @@
<view v-if="mchDetail.id" class="card p-20rpx mt-10rpx mx-20rpx">
<view class="w-full sub-text-32 truncate">{{ mchDetail.name }}</view>
<text class="sub-text-24 c-#0094ff! px-2 bg-#0094ff22 rounded-2 mr-4">营业</text>
<text class="second-text-24 truncate mt-1">营业人 {{ mchDetail.contactName }}·{{ mchDetail.contactPhone }}</text>
<text class="second-text-24 truncate mt-1">营业人{{ mchDetail.contactName }} · {{ mchDetail.contactPhone }}</text>
<view class="second-text-26 mt-1">
<image class="w-24rpx h-24rpx mr-1" src="@/static/page/address.svg" />
<text>{{ mchDetail.address }}</text>
+13 -14
View File
@@ -2,10 +2,13 @@
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
<template #main-top>
<view class="flex items-center py-3 px-5">
<image v-if="state.userInfo?.sysUser?.avatar" class="size-100rpx rounded-full"
:src="filePath(state.userInfo.sysUser.avatar)" mode="aspectFill" />
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png"
mode="aspectFill" />
<image
v-if="state.userInfo?.sysUser?.avatar"
class="size-100rpx rounded-full"
:src="filePath(state.userInfo.sysUser.avatar)"
mode="aspectFill"
/>
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png" mode="aspectFill" />
<view class="ml-30rpx" v-if="state.userInfo?.sysUser?.userId">
<view class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</view>
<view class="sub-text-26">ID {{ state.userInfo?.sysUser?.userId }}</view>
@@ -18,8 +21,7 @@
</template>
<view class="mt-10rpx rounded-20rpx shadow-main p-30rpx pb-40rpx bg-white">
<view class="service_item flex items-center justify-between" v-for="(v, i) in state.menuList" :key="i"
@tap="onTabMenu(v)">
<view class="service_item flex items-center justify-between" v-for="(v, i) in state.menuList" :key="i" @tap="onTabMenu(v)">
<view class="flex-row-center">
<image class="size-40rpx" :src="v.icon" mode="aspectFill" />
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
@@ -29,15 +31,13 @@
</view>
<view v-if="isDevEnv()" class="flex-col-center mt-40rpx mx-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
@tap="doLogout">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doLogout">
退出登录
</view>
</view>
<view class="h-30rpx" />
<view v-if="state.appInfo == APPENV.SH" class="flex-col-center mt-40rpx mx-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
@tap="doChangeRole">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doChangeRole">
切换角色
</view>
</view>
@@ -50,10 +50,9 @@
</view>
</crud-layout>
<tm-modal :show="state.actModalShow" :showCancel="false" title="核销活动兑换券" zIndex="999" @confirm="" @cancel=""
@close="">
<tm-modal :show="state.actModalShow" :showCancel="false" title="核销活动兑换券" zIndex="999" @confirm="" @cancel="" @close="">
<view class="text-center text-28">是否核销该活动兑换券?</view>
<view class="text-center text-28">确认核销将会对用户的 消费钱包 扣除 1元 钱</view>
<view class="text-center text-28">确认核销将自动对用户的 消费钱包 扣费</view>
<template #footer>
<view class="w-full flex justify-center gap-6">
<tm-button skin="thin" @tap="actModalCancel">取 消</tm-button>
@@ -276,7 +275,7 @@ const actModalConfirm = async () => {
try {
uni.showLoading({ title: "核销中...", mask: true })
const { code, msg }: any = await actCancel.appCreateActOrder(state.actVal)
if (code != 0) return uni.showToast({ title: msg || "核销失败", icon: "none" })
if (code != 0) return uni.showToast({ title: msg || "核销失败", icon: "none", duration: 4000 })
uni.showToast({ title: "核销成功", icon: "success" })
state.actModalShow = false
state.actVal = {}
+3 -5
View File
@@ -14,9 +14,7 @@ export function useScanCode(actModalShow) {
// const testQrCode = "eyJoYXJkRG9vcklkIjoiMTkxMzE1NzA4MDQxODI5NTgwOSIsImNhYmluZXRObyI6IjEifQ==" // 邓本地-测试单门智能柜柜门二维码
// const testQrCode = "http://192.168.101.45:8889/h5/#/pages/pay/scanPay?id=1905816751043452930&name=测试新县府超市" // 邓本地-测试环境-新县府超市收款二维码
// const testQrCode = "http://192.168.101.45:8889/h5/#/pages/pay/scanPay?hardDoorId=1913157080418295809&cabinetNo=1" // 测试智能柜柜门二维码
// const testQrCode = "eyJhY3RJZCI6IjE5NjkyNTAwOTg1MTM4MDUzMTQiLCJ0b3RhbFByaWNlIjoxfQ==" // 测试商户活动核销
// const testQrCode = "eyJhY3RJZCI6IjE5NjkzMjQwNjMwNTk4Nzc4OTAiLCJ0b3RhbFByaWNlIjoxfQ==" // 测试商户活动核销
const testQrCode = "eyJhY3RJZCI6IjE5NjkzMjU3NDQxNDUzMDk2OTciLCJ0b3RhbFByaWNlIjoxfQ==" // 测试商户活动核销
const testQrCode = "eyJjYW5jZWxJZCI6IjE5ODQyNDA4MzM5NDMyMDc5MzcifQ==" // 测试商户活动核销
uni.showModal({ content: "开发环境不是浙政钉和诗画环境,扫码仅供测试" })
return Promise.resolve(testQrCode)
}
@@ -70,8 +68,8 @@ export function useScanCode(actModalShow) {
try {
const decodedJson = JSON.parse(decodedString)
// 商户活动优惠券扫码核销
if (decodedJson.actId) {
actModalShow({ actId: decodedJson.actId, totalPrice: decodedJson.totalPrice })
if (decodedJson.cancelId) {
actModalShow({ cancelId: decodedJson.cancelId })
return
}
// 商户扫码支付