From 0b23a4f1cae5dd34b407614baa88dee3af162f29 Mon Sep 17 00:00:00 2001
From: peng <peng.com>
Date: 星期六, 27 九月 2025 16:02:57 +0800
Subject: [PATCH] 扫码门店领取优惠卷
---
api/store-coupon.js | 27 +++
pages.json | 23 +++
pages/storeClaim/store-claim.vue | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 411 insertions(+), 1 deletions(-)
diff --git a/api/store-coupon.js b/api/store-coupon.js
new file mode 100644
index 0000000..b1dddb5
--- /dev/null
+++ b/api/store-coupon.js
@@ -0,0 +1,27 @@
+import { http, Method } from "@/utils/request.js";
+
+/**
+ * 鏍规嵁搴楅摵浼樻儬鍒稿叧鑱擨D鑾峰彇浼樻儬鍒歌鎯�
+ * @param {string|number} storeCoupRef - 搴楅摵浼樻儬鍒稿叧鑱擨D
+ * @returns {Promise} 杩斿洖浼樻儬鍒歌鎯�
+ */
+export function getStoreCouponDetail(storeCoupRef) {
+ return http.request({
+ url: `/lmk/store/coupon/${storeCoupRef}`,
+ method: Method.GET,
+ needToken: true,
+ });
+}
+
+/**
+ * 棰嗗彇搴楅摵浼樻儬鍒�
+ * @param {string|number} storeCoupRef - 搴楅摵浼樻儬鍒稿叧鑱擨D
+ * @returns {Promise} 杩斿洖棰嗗彇缁撴灉
+ */
+export function claimStoreCoupon(storeCoupRef) {
+ return http.request({
+ url: `/lmk/store/coupon/${storeCoupRef}`,
+ method: Method.POST,
+ needToken: true,
+ });
+}
\ No newline at end of file
diff --git a/pages.json b/pages.json
index 080939b..ebbcf32 100644
--- a/pages.json
+++ b/pages.json
@@ -198,6 +198,7 @@
// "path": "pages/tabbar/user/my",
// "style": {
// "navigationBarTextStyle": "white",
+ // "navigationBarTextStyle": "white",
// "enablePullDownRefresh": true,
// "navigationStyle": "custom",
// "componentPlaceholder": {
@@ -2247,7 +2248,27 @@
}
}
]
- }
+ },
+ {
+ "root": "pages/storeClaim",
+ "pages": [{
+ "path" : "store-claim",
+ "style" :
+ {
+ "navigationBarTitleText" : "浼樻儬鍗烽鍙�",
+ "componentPlaceholder":{
+ "u-card": "view",
+ "u-navbar": "view",
+ "u-tag": "view",
+ "u-icon": "view",
+ "u-button": "view",
+ "u-empty": "view"
+
+ }
+ }
+ }]
+ }
+
],
"globalStyle": {
"navigationBarTextStyle": "black",
diff --git a/pages/storeClaim/store-claim.vue b/pages/storeClaim/store-claim.vue
new file mode 100644
index 0000000..59c0eea
--- /dev/null
+++ b/pages/storeClaim/store-claim.vue
@@ -0,0 +1,362 @@
+<template>
+ <view class="container">
+ <!-- 浼樻儬鍒稿崱鐗� -->
+ <view class="coupon-card" v-if="couponInfo.storeCoupRef">
+ <u-card :border="false" :head-style="{ padding: '30rpx' }" :body-style="{ padding: '0 30rpx 30rpx' }">
+ <!-- 澶撮儴锛氬簵閾轰俊鎭� -->
+ <view slot="head" class="card-head">
+ <view class="store-info">
+ <u-icon name="home" size="36" color="#999"></u-icon>
+ <text class="store-name">{{ couponInfo.storeName }}</text>
+ </view>
+ <u-tag v-if="couponInfo.claimStatus === 'NOT_CLAIM'" text="鏈鍙�" type="warning" mode="plain" size="mini" />
+ <u-tag v-else-if="couponInfo.claimStatus === 'CLAIM'" text="宸查鍙�" type="success" mode="plain" size="mini" />
+ <u-tag v-else text="宸茶繃鏈�" type="info" mode="plain" size="mini" />
+ </view>
+
+ <!-- 涓讳綋锛氫紭鎯犲埜淇℃伅 -->
+ <view slot="body" class="card-body">
+ <view class="coupon-title">
+ <text class="title">{{ couponInfo.couponName }}</text>
+ <text class="coupon-no">缂栧彿锛歿{ couponInfo.couponNo }}</text>
+ </view>
+
+ <view class="coupon-desc" v-if="couponInfo.couponDesc">
+ <text class="desc">{{ couponInfo.couponDesc }}</text>
+ </view>
+
+ <view class="coupon-rule" v-if="couponInfo.couponRule">
+ <text class="rule-title">浣跨敤瑙勫垯锛�</text>
+ <text class="rule-content">{{ couponInfo.couponRule }}</text>
+ </view>
+
+ <view class="coupon-time" v-if="couponInfo.startTime && couponInfo.endTime">
+ <u-icon name="clock" size="28" color="#999"></u-icon>
+ <text class="time-text">鏈夋晥鏈燂細{{ formatDate(couponInfo.startTime) }} 鑷� {{ formatDate(couponInfo.endTime) }}</text>
+ </view>
+
+ <view class="coupon-condition" v-if="couponInfo.consumeThreshold > 0">
+ <u-icon name="coupon" size="28" color="#999"></u-icon>
+ <text class="condition-text">婊{ formatAmount(couponInfo.consumeThreshold) }}鍏冨彲鐢�</text>
+ </view>
+ </view>
+
+ <!-- 搴曢儴锛氭搷浣滄寜閽� -->
+ <view slot="foot" class="card-foot">
+ <u-button
+ :type="couponInfo.claimStatus === 'NOT_CLAIM' ? 'primary' : 'default'"
+ :disabled="couponInfo.claimStatus !== 'NOT_CLAIM'"
+ :loading="loading"
+ @click="claimCoupon"
+ :ripple="true"
+ :hair-line="false"
+ >
+ {{ couponInfo.claimStatus === 'NOT_CLAIM' ? '绔嬪嵆棰嗗彇' : couponInfo.claimStatus === 'CLAIM' ? '宸查鍙�' : '宸茶繃鏈�' }}
+ </u-button>
+ </view>
+ </u-card>
+ </view>
+
+ <!-- 绌虹姸鎬� -->
+ <view class="empty-state" v-else>
+ <u-empty text="浼樻儬鍒镐俊鎭笉瀛樺湪" mode="coupon" :icon-size="200"></u-empty>
+ </view>
+
+ <!-- 浣跨敤璇存槑 -->
+ <view class="instructions">
+ <view class="instructions-title">
+ <u-icon name="info-circle" size="28" color="#999"></u-icon>
+ <text class="title-text">浣跨敤璇存槑</text>
+ </view>
+ <view class="instructions-content">
+ <text class="content-text">1. 浼樻儬鍒镐粎闄愬湪鎸囧畾搴楅摵浣跨敤\n2. 姣忎釜鐢ㄦ埛闄愰涓�寮燶n3. 浼樻儬鍒镐笉鍙厬鎹㈢幇閲慭n4. 璇峰湪鏈夋晥鏈熷唴浣跨敤</text>
+ </view>
+ </view>
+
+ <!-- 绌虹櫧鍗犱綅 -->
+ <view class="placeholder"></view>
+ </view>
+</template>
+
+<script>
+ import { getStoreCouponDetail, claimStoreCoupon } from '@/api/store-coupon.js';
+ import { formatPrice } from '@/utils/Foundation.js';
+
+ export default {
+ data() {
+ return {
+ loading: false,
+ storeCoupRef: '', // 搴楅摵浼樻儬鍒稿叧鑱擨D
+ couponInfo: {
+ id: "",
+ storeCoupRef: "",
+ storeId: "",
+ storeName: "",
+ couponId: "",
+ couponName: "",
+ couponNo: "",
+ couponAmount: 0, // 浼樻儬閲戦
+ couponDesc: "", // 浼樻儬鍒告弿杩�
+ couponRule: "", // 浣跨敤瑙勫垯
+ startTime: "", // 寮�濮嬫椂闂�
+ endTime: "", // 缁撴潫鏃堕棿
+ consumeThreshold: 0, // 娑堣垂闂ㄦ
+ claimStatus: "NOT_CLAIM", // 棰嗗彇鐘舵��
+ }
+ }
+ },
+ onLoad(options) {
+ // 鑾峰彇浼犻�掔殑搴楅摵浼樻儬鍒稿叧鑱擨D
+ if (options.storeCoupRef) {
+ this.storeCoupRef = options.storeCoupRef;
+ this.getCouponDetail(options.storeCoupRef);
+ } else if (options.id) {
+ // 鍏煎鏃у弬鏁�
+ this.storeCoupRef = options.id;
+ this.getCouponDetail(options.id);
+ }else if(options.q){
+ // 鍙岄噸瑙g爜锛氬井淇″URL杩涜浜嗕袱娆$紪鐮�
+ const decodedUrl = decodeURIComponent(decodeURIComponent(options.q));
+ console.log('鍘熷URL:', decodedUrl);
+
+ // 瑙f瀽URL涓殑鏌ヨ鍙傛暟
+ const params = this.parseUrlParams(decodedUrl);
+ this.storeCoupRef = params.id;
+ this.getCouponDetail(this.storeCoupRef);
+ }
+ else {
+ this.$u.toast('鍙傛暟閿欒');
+ }
+ },
+ methods: {
+ // 瑙f瀽URL鍙傛暟
+ parseUrlParams(url) {
+ const params = {};
+ // 澶勭悊鍙兘瀛樺湪鐨刪ash锛堝鏋滄湁鐨勮瘽锛�
+ const cleanUrl = url.split('#')[0];
+ const queryStr = cleanUrl.split('?')[1] || '';
+
+ queryStr.split('&').forEach(pair => {
+ const [key, value] = pair.split('=');
+ if (key) {
+ // 濡傛灉鍊煎瓨鍦紝鍒欒В鐮侊紝鍚﹀垯璁句负绌哄瓧绗︿覆
+ params[key] = value ? decodeURIComponent(value) : '';
+ }
+ });
+
+ return params;
+ },
+ // 鏍煎紡鍖栭噾棰�
+ formatAmount(amount) {
+ if (!amount) return '0';
+ return formatPrice(parseFloat(amount));
+ },
+
+ // 鏍煎紡鍖栨棩鏈�
+ formatDate(dateStr) {
+ if (!dateStr) return '';
+ return dateStr.split(' ')[0];
+ },
+
+ // 鑾峰彇浼樻儬鍒歌鎯�
+ async getCouponDetail(storeCoupRef) {
+ uni.showLoading({
+ title: '鍔犺浇涓�...'
+ });
+
+ try {
+ const res = await getStoreCouponDetail(storeCoupRef);
+ console.log(JSON.stringify(res))
+ if (res.data.code === 200) {
+ this.couponInfo = {
+ ...this.couponInfo,
+ ...res.data.data,
+ couponAmount: res.data.data.couponAmount || 0,
+ consumeThreshold: res.data.data.consumeThreshold || 0
+ };
+ } else {
+ this.$u.toast(res.data.message || '鑾峰彇浼樻儬鍒歌鎯呭け璐�');
+ }
+ } catch (err) {
+ this.$u.toast('鑾峰彇浼樻儬鍒歌鎯呭け璐ワ紝璇风◢鍚庨噸璇�');
+ console.error('鑾峰彇浼樻儬鍒歌鎯呭け璐�:', err);
+ } finally {
+ uni.hideLoading();
+ }
+ },
+
+ // 棰嗗彇浼樻儬鍒�
+ async claimCoupon() {
+ if (this.couponInfo.claimStatus !== 'NOT_CLAIM') {
+ return;
+ }
+
+ // 纭棰嗗彇
+ uni.showModal({
+ title: '鎻愮ず',
+ content: `纭畾瑕侀鍙�"${this.couponInfo.couponName}"浼樻儬鍒稿悧锛焋,
+ success: (res) => {
+ if (res.confirm) {
+ this.doClaimCoupon();
+ }
+ }
+ });
+ },
+
+ // 鎵ц棰嗗彇浼樻儬鍒告搷浣�
+ async doClaimCoupon() {
+ this.loading = true;
+ try {
+ // 璋冪敤棰嗗彇浼樻儬鍒告帴鍙�
+ const res = await claimStoreCoupon(this.storeCoupRef);
+ if (res.data.code === 200) {
+ this.$u.toast('棰嗗彇鎴愬姛');
+ this.couponInfo.claimStatus = 'CLAIM';
+
+ // 寤惰繜杩斿洖涓婁竴椤碉紝璁╃敤鎴风湅鍒伴鍙栨垚鍔熺殑鎻愮ず
+ setTimeout(() => {
+ uni.navigateBack();
+ }, 1500);
+ } else {
+ this.$u.toast(res.data.message || '棰嗗彇澶辫触');
+ }
+ } catch (err) {
+ this.$u.toast('棰嗗彇澶辫触锛岃绋嶅悗閲嶈瘯');
+ console.error('棰嗗彇浼樻儬鍒稿け璐�:', err);
+ } finally {
+ this.loading = false;
+ }
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+.container {
+ background-color: #f5f5f5;
+ min-height: 100vh;
+}
+
+.coupon-card {
+ margin: 20rpx;
+}
+
+.card-head {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.store-info {
+ display: flex;
+ align-items: center;
+}
+
+.store-name {
+ margin-left: 10rpx;
+ font-size: 32rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.card-body {
+ padding-top: 20rpx;
+}
+
+.coupon-title {
+ margin-bottom: 30rpx;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.title {
+ font-size: 36rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.coupon-no {
+ font-size: 24rpx;
+ color: #999;
+ margin-left: 20rpx;
+}
+
+.coupon-desc {
+ margin-bottom: 30rpx;
+}
+
+.desc {
+ font-size: 28rpx;
+ color: #666;
+}
+
+.coupon-rule {
+ margin-bottom: 30rpx;
+}
+
+.rule-title {
+ font-size: 28rpx;
+ color: #333;
+ font-weight: bold;
+}
+
+.rule-content {
+ font-size: 26rpx;
+ color: #666;
+}
+
+.coupon-time, .coupon-condition {
+ display: flex;
+ align-items: center;
+ margin-bottom: 20rpx;
+}
+
+.time-text, .condition-text {
+ font-size: 24rpx;
+ color: #999;
+ margin-left: 10rpx;
+}
+
+.card-foot {
+ padding: 20rpx 0;
+}
+
+.instructions {
+ background-color: #ffffff;
+ margin: 20rpx;
+ padding: 30rpx;
+ border-radius: 16rpx;
+}
+
+.instructions-title {
+ display: flex;
+ align-items: center;
+ margin-bottom: 20rpx;
+}
+
+.title-text {
+ margin-left: 10rpx;
+ font-size: 30rpx;
+ font-weight: bold;
+ color: #333;
+}
+
+.instructions-content {
+ padding-left: 40rpx;
+}
+
+.content-text {
+ font-size: 26rpx;
+ color: #666;
+ line-height: 1.6;
+}
+
+.placeholder {
+ height: 40rpx;
+}
+
+.empty-state {
+ margin-top: 200rpx;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.8.0