Explorar el Código

增加优惠券页面

zhaoyang hace 2 meses
padre
commit
feaa6286c2

+ 1 - 9
api/login.js

@@ -1,12 +1,4 @@
 import { api } from '@/utils/request.js';
 import { api } from '@/utils/request.js';
 
 
 // 小程序登录
 // 小程序登录
-export const LoginLoginForWechat = (params) => api.post({ url: '/Login/LoginForWechat', params });
-// 发送短信验证码
-export const LoginSendCode = (params) => api.post({ url: '/Login/SendCode', params });
-// 手机号登录
-export const LoginTelLogin = (params) => api.post({ url: '/Login/TelLogin', params });
-// 手机号码授权登录
-export const LoginLoginForTelFast = (params) => api.post({ url: '/Login/LoginForTelFast', params });
-// 获取公众号授权地址
-export const LoginGetMpAuthUrl = (params) => api.post({ url: '/Login/GetMpAuthUrl', params });
+export const LoginLoginForWechat = (params) => api.post({ url: '/Login/LoginForWechat', params });

+ 6 - 0
pages.json

@@ -76,6 +76,12 @@
 			"style": {
 			"style": {
 				"navigationBarTitleText": "订单详情"
 				"navigationBarTitleText": "订单详情"
 			}
 			}
+		},
+		{
+			"path": "pages/coupon/index",
+			"style": {
+				"navigationBarTitleText": "我的券包"
+			}
 		}
 		}
 	],
 	],
 	"globalStyle": {
 	"globalStyle": {

+ 319 - 0
pages/coupon/components/RulesModal.vue

@@ -0,0 +1,319 @@
+<template>
+  <!-- 使用规则弹窗 -->
+  <BasicModal type="bottom" v-model:open="getOpen" :is-mack="true">
+    <view class="rules-modal">
+      <!-- 标题栏 -->
+      <view class="rules-modal__header">
+        <text class="header-title">使用规则</text>
+        <view class="close-btn" @click="handleClose">
+          <view class="close-icon"></view>
+        </view>
+      </view>
+
+      <!-- 优惠券卡片 - 与列表页样式一致 -->
+      <view class="coupon-card-wrapper">
+        <view class="coupon-card">
+          <image :src="getFileUrl('img/personal/coupon.png')" mode="widthFix" class="coupon-card-bg"></image>
+          <image :src="getFileUrl('img/personal/couponLeft.png')" mode="heightFix" class="coupon-card-bgLeft"></image>
+          <view class="coupon-card-content">
+            <!-- 左侧金额区域 -->
+            <view class="coupon-card__left">
+              <view class="amount-wrapper">
+                <text class="amount-number">{{ couponData?.amount || 0 }}</text>
+                <text class="amount-unit">元</text>
+              </view>
+              <text class="condition-text">满{{ couponData?.minAmount || 0 }}可用</text>
+            </view>
+
+            <!-- 右侧信息区域 -->
+            <view class="coupon-card__right">
+              <view class="coupon-info">
+                <text class="coupon-name">{{ couponData?.name || '6元通用优惠券' }}</text>
+                <text class="coupon-expire">{{ couponData?.expireDate || '' }}到期</text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <!-- 规则详情 -->
+      <view class="rules-content">
+        <view class="rules-item">
+          <text class="rules-label">有效期</text>
+          <text class="rules-value">{{ couponData?.validDays || 90 }}天</text>
+        </view>
+        
+        <view class="rules-item">
+          <text class="rules-label">补充说明</text>
+          <text class="rules-value">{{ couponData?.description || '周一、周五不可用' }}</text>
+        </view>
+      </view>
+
+      <!-- 底部按钮 -->
+      <view class="rules-modal__footer">
+        <view class="use-btn" hover-class="hover-active" @click="handleUse">
+          去使用
+        </view>
+      </view>
+    </view>
+  </BasicModal>
+</template>
+
+<script setup>
+import { computed } from 'vue';
+import BasicModal from '@/components/Modal/BasicModal.vue';
+import { getFileUrl } from '@/utils/file.js';
+
+const props = defineProps({
+  open: {
+    type: Boolean,
+    default: false
+  },
+  couponData: {
+    type: Object,
+    default: () => ({
+      amount: 6,
+      minAmount: 66,
+      name: '6元通用优惠券',
+      expireDate: '2025/07/12',
+      validDays: 90,
+      description: '周一、周五不可用'
+    })
+  }
+});
+
+const emit = defineEmits(['update:open', 'use']);
+
+const getOpen = computed({
+  get: () => props.open,
+  set: (val) => emit('update:open', val)
+});
+
+// 关闭弹窗
+const handleClose = () => {
+  getOpen.value = false;
+};
+
+// 使用优惠券
+const handleUse = () => {
+  emit('use', props.couponData);
+  getOpen.value = false;
+};
+</script>
+
+<style lang="scss" scoped>
+.rules-modal {
+  width: 100%;
+  background: #FFFFFF;
+  border-radius: 24rpx 24rpx 0 0;
+  padding: 32rpx 30rpx;
+  padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
+  box-sizing: border-box;
+
+  // 标题栏
+  &__header {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 32rpx;
+    position: relative;
+
+    .header-title {
+      font-size: 36rpx;
+      font-weight: bold;
+      color: #151515;
+    }
+
+    .close-btn {
+      position: absolute;
+      right: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 48rpx;
+      height: 48rpx;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .close-icon {
+        width: 28rpx;
+        height: 28rpx;
+        position: relative;
+
+        &::before,
+        &::after {
+          content: '';
+          position: absolute;
+          width: 28rpx;
+          height: 2rpx;
+          background: #999999;
+          top: 50%;
+          left: 50%;
+          transform-origin: center;
+        }
+
+        &::before {
+          transform: translate(-50%, -50%) rotate(45deg);
+        }
+
+        &::after {
+          transform: translate(-50%, -50%) rotate(-45deg);
+        }
+      }
+    }
+  }
+
+  // 优惠券卡片容器
+  .coupon-card-wrapper {
+    margin-bottom: 32rpx;
+  }
+
+  // 优惠券卡片样式 - 与列表页完全一致
+  .coupon-card {
+    display: flex;
+    align-items: center;
+    overflow: hidden;
+    position: relative;
+    box-sizing: border-box;
+
+    .coupon-card-bg {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 1;
+    }
+
+    .coupon-card-bgLeft {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: auto;
+      height: 190rpx;
+      z-index: 1;
+    }
+
+    .coupon-card-content {
+      position: relative;
+      z-index: 3;
+      display: flex;
+      align-items: center;
+      width: 100%;
+      height: 200rpx;
+    }
+
+    &__left {
+      width: 200rpx;
+      padding: 32rpx 0;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+
+      .amount-wrapper {
+        display: flex;
+        align-items: baseline;
+        margin-bottom: 8rpx;
+
+        .amount-number {
+          font-size: 64rpx;
+          font-weight: bold;
+          color: #F47D1F;
+          line-height: 1;
+        }
+
+        .amount-unit {
+          font-size: 28rpx;
+          color: #F47D1F;
+          margin-left: 4rpx;
+        }
+      }
+
+      .condition-text {
+        font-size: 22rpx;
+        color: #F47D1F;
+      }
+    }
+
+    &__right {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 32rpx 24rpx;
+
+      .coupon-info {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+
+        .coupon-name {
+          font-size: 28rpx;
+          font-weight: bold;
+          color: #151515;
+          margin-bottom: 12rpx;
+        }
+
+        .coupon-expire {
+          font-size: 22rpx;
+          color: #999999;
+        }
+      }
+    }
+  }
+
+  // 规则详情
+  .rules-content {
+    margin-bottom: 40rpx;
+    padding: 0 8rpx;
+
+    .rules-item {
+      display: flex;
+      align-items: flex-start;
+      margin-bottom: 20rpx;
+      font-size: 26rpx;
+      line-height: 40rpx;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .rules-label {
+        color: #666666;
+        flex-shrink: 0;
+      }
+
+      .rules-value {
+        color: #151515;
+        margin-left: 16rpx;
+        flex: 1;
+      }
+    }
+  }
+
+  // 底部按钮
+  &__footer {
+    .use-btn {
+      width: 100%;
+      height: 88rpx;
+      background: linear-gradient(135deg, #FF8A57 0%, #F47D1F 100%);
+      border-radius: 44rpx;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 32rpx;
+      font-weight: bold;
+      color: #FFFFFF;
+      letter-spacing: 2rpx;
+    }
+  }
+}
+
+.hover-active {
+  opacity: 0.8;
+  transform: scale(0.98);
+  transition: all 0.2s;
+}
+</style>
+

+ 480 - 0
pages/coupon/index.vue

@@ -0,0 +1,480 @@
+<template>
+  <!-- 优惠券页面 -->
+  <view class="page">
+
+    <!-- 标签页 -->
+    <view class="tabs">
+      <view v-for="(tab, index) in tabs" :key="index"
+        :class="['tab-item', { 'tab-item--active': currentTab === index }]" @click="handleTabChange(index)">
+        {{ tab }}
+      </view>
+    </view>
+
+    <!-- 优惠券列表 -->
+    <scroll-view class="content" scroll-y>
+      <view class="coupon-list" v-if="filteredCoupons.length > 0">
+        <view v-for="(coupon, index) in filteredCoupons" :key="coupon.id || index"
+          :class="['coupon-card', getCouponCardClass(coupon)]">
+          <image :src="getFileUrl('img/personal/coupon.png')" mode="widthFix" class="coupon-card-bg"></image>
+          <image :src="getFileUrl('img/personal/couponLeft.png')" mode="heightFix" class="coupon-card-bgLeft"></image>
+          <view class="coupon-card-content">
+            <!-- 左侧金额区域 -->
+            <view class="coupon-card__left">
+              <view class="amount-wrapper">
+                <text class="amount-number">{{ coupon.amount }}</text>
+                <text class="amount-unit">元</text>
+              </view>
+              <text class="condition-text">满{{ coupon.minAmount }}可用</text>
+            </view>
+
+            <!-- 右侧信息区域 -->
+            <view class="coupon-card__right">
+              <view class="coupon-info">
+                <text class="coupon-name">{{ coupon.name }}</text>
+                <view class="coupon-rules" @click="handleShowRules(coupon)">
+                  使用规则
+                  <text class="arrow">›</text>
+                </view>
+                <text class="coupon-expire">{{ coupon.expireDate }}到期</text>
+              </view>
+
+              <!-- 操作按钮 -->
+              <view class="coupon-action">
+                <view v-if="coupon.status === 0" class="action-btn action-btn--use" hover-class="hover-active"
+                  @click="handleUseCoupon(coupon)">
+                  去使用
+                </view>
+                <view v-else-if="coupon.status === 1" class="action-btn action-btn--use" hover-class="hover-active"
+                  @click="handleUseCoupon(coupon)">
+                  去使用
+                </view>
+                <view v-else-if="coupon.status === 2" class="status-text status-text--used">
+                  已使用
+                </view>
+                <view v-else class="status-text status-text--expired">
+                  已过期
+                </view>
+              </view>
+            </view>
+          </view>
+
+        </view>
+      </view>
+
+      <!-- 空状态 -->
+      <view class="empty-state" v-else>
+        <image :src="getFileUrl('img/icon/noCoupon.png')" mode="widthFix" class="empty-icon"></image>
+        <text class="empty-text">暂无优惠券</text>
+      </view>
+    </scroll-view>
+
+    <!-- 使用规则弹窗 -->
+    <RulesModal v-model:open="showRulesModal" :couponData="selectedCoupon" @use="handleUseCoupon" />
+  </view>
+</template>
+
+<script setup>
+import { onLoad } from "@dcloudio/uni-app";
+import { ref, computed } from "vue";
+import { go } from "@/utils/utils.js";
+import { getFileUrl } from "@/utils/file.js";
+import RulesModal from "./components/RulesModal.vue";
+
+// 标签页数据
+const tabs = ['未使用', '即将过期', '已使用', '已过期'];
+const currentTab = ref(0);
+
+// 弹窗控制
+const showRulesModal = ref(false);
+const selectedCoupon = ref({
+  amount: 0,
+  minAmount: 0,
+  name: '',
+  expireDate: '',
+  validDays: 90,
+  description: '周一、周五不可用'
+});
+
+// 优惠券数据(模拟数据)
+const couponList = ref([
+  {
+    id: 1,
+    amount: 1000,
+    minAmount: 2000,
+    name: '美食必吃套餐专享券',
+    expireDate: '2024/07/28',
+    status: 0 // 0: 未使用, 1: 即将过期, 2: 已使用, 3: 已过期
+  },
+  {
+    id: 2,
+    amount: 38,
+    minAmount: 158,
+    name: '美食必吃套餐专享券',
+    expireDate: '2024/07/28',
+    status: 0
+  },
+  {
+    id: 3,
+    amount: 999,
+    minAmount: 2000,
+    name: '美食必吃套餐专享券',
+    expireDate: '2024/07/28',
+    status: 0
+  },
+  {
+    id: 4,
+    amount: 5,
+    minAmount: 158,
+    name: '美食必吃套餐专享券',
+    expireDate: '2024/07/28',
+    status: 0
+  }
+]);
+
+// 根据当前标签页过滤优惠券
+const filteredCoupons = computed(() => {
+  return couponList.value.filter(coupon => {
+    if (currentTab.value === 0) return coupon.status === 0;
+    if (currentTab.value === 1) return coupon.status === 1;
+    if (currentTab.value === 2) return coupon.status === 2;
+    if (currentTab.value === 3) return coupon.status === 3;
+    return true;
+  });
+});
+
+// 切换标签页
+const handleTabChange = (index) => {
+  currentTab.value = index;
+};
+
+// 获取优惠券卡片样式类
+const getCouponCardClass = (coupon) => {
+  if (coupon.status === 2) return 'coupon-card--used';
+  if (coupon.status === 3) return 'coupon-card--expired';
+  return '';
+};
+
+// 使用优惠券
+const handleUseCoupon = (coupon) => {
+  console.log('使用优惠券:', coupon);
+  // 跳转到点餐页面
+  go('/pages/orderFood/index');
+};
+
+// 查看使用规则
+const handleShowRules = (coupon) => {
+  selectedCoupon.value = {
+    ...coupon,
+    validDays: 90,
+    description: '周一、周五不可用'
+  };
+  showRulesModal.value = true;
+};
+
+onLoad(() => {
+  // 页面加载时获取优惠券列表
+  // TODO: 调用API获取真实数据
+});
+</script>
+
+<style lang="scss" scoped>
+.page {
+  min-height: 100vh;
+  background: #F5F5F5;
+  display: flex;
+  flex-direction: column;
+}
+
+.header {
+  background: #FFFFFF;
+  padding: 20rpx 30rpx;
+  padding-top: calc(20rpx + env(safe-area-inset-top));
+
+  .header-title {
+    font-size: 36rpx;
+    font-weight: bold;
+    color: #151515;
+    text-align: center;
+  }
+}
+
+.tabs {
+  background: #FFFFFF;
+  display: flex;
+  align-items: center;
+  padding: 0 30rpx;
+
+  .tab-item {
+    flex: 1;
+    text-align: center;
+    font-size: 28rpx;
+    color: #666666;
+    padding: 28rpx 0;
+    position: relative;
+    transition: all 0.3s;
+
+    &--active {
+      color: #151515;
+      font-weight: bold;
+
+      &::after {
+        content: '';
+        position: absolute;
+        bottom: 20rpx;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 40rpx;
+        height: 6rpx;
+        background: linear-gradient(90deg, #FF8A57 0%, #F47D1F 100%);
+        border-radius: 3rpx;
+      }
+    }
+  }
+}
+
+.content {
+  flex: 1;
+  padding: 24rpx 30rpx;
+  padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
+  box-sizing: border-box;
+}
+
+.coupon-list {
+  position: relative;
+
+  .coupon-card-bg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 1;
+  }
+
+  .coupon-card-bgLeft {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: auto;
+    height: 190rpx;
+    z-index: 1;
+  }
+
+  .coupon-card-content {
+    position: relative;
+    z-index: 3;
+    display: flex;
+    align-items: center;
+  }
+
+  .coupon-card {
+    display: flex;
+    align-items: center;
+    margin-bottom: 24rpx;
+    overflow: hidden;
+    position: relative;
+    box-sizing: border-box;
+    position: relative;
+    z-index: 3;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+
+    &__left {
+      width: 200rpx;
+      padding: 32rpx 0;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+
+      .amount-wrapper {
+        display: flex;
+        align-items: baseline;
+        margin-bottom: 8rpx;
+
+        .amount-number {
+          font-size: 64rpx;
+          font-weight: bold;
+          color: #F47D1F;
+          line-height: 1;
+        }
+
+        .amount-unit {
+          font-size: 28rpx;
+          color: #F47D1F;
+          margin-left: 4rpx;
+        }
+      }
+
+      .condition-text {
+        font-size: 22rpx;
+        color: #F47D1F;
+      }
+    }
+
+    &__divider {
+      width: 2rpx;
+      height: 100%;
+      position: relative;
+
+      .dash-line {
+        width: 2rpx;
+        height: 100%;
+        // background-image: linear-gradient(to bottom, #FFD9C2 0%, #FFD9C2 50%, transparent 50%, transparent 100%);
+        background-size: 2rpx 12rpx;
+        background-repeat: repeat-y;
+      }
+    }
+
+    &__right {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 32rpx 24rpx;
+
+      .coupon-info {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+
+        .coupon-name {
+          font-size: 28rpx;
+          font-weight: bold;
+          color: #151515;
+          margin-bottom: 12rpx;
+        }
+
+        .coupon-rules {
+          font-size: 22rpx;
+          color: #999999;
+          margin-bottom: 12rpx;
+          display: flex;
+          align-items: center;
+
+          .arrow {
+            font-size: 28rpx;
+            margin-left: 4rpx;
+          }
+        }
+
+        .coupon-expire {
+          font-size: 22rpx;
+          color: #999999;
+        }
+      }
+
+      .coupon-action {
+        margin-left: 20rpx;
+
+        .action-btn {
+          width: 120rpx;
+          height: 56rpx;
+          border-radius: 28rpx;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 26rpx;
+          font-weight: 500;
+
+          &--use {
+            // background: linear-gradient(135deg, #FF8A57 0%, #F47D1F 100%);
+            background: #F47D1F;
+            color: #FFFFFF;
+            // box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(255, 107, 53, 0.3);
+          }
+        }
+
+        .status-text {
+          font-size: 24rpx;
+          padding: 8rpx 16rpx;
+
+          &--used {
+            color: #999999;
+          }
+
+          &--expired {
+            color: #CCCCCC;
+          }
+        }
+      }
+    }
+
+    // 已使用状态
+    &--used {
+      background: #F8F8F8;
+
+      .coupon-card__left {
+
+        .amount-number,
+        .amount-unit,
+        .condition-text {
+          color: #CCCCCC;
+        }
+      }
+
+      .coupon-card__right {
+        .coupon-info {
+
+          .coupon-name,
+          .coupon-rules,
+          .coupon-expire {
+            color: #CCCCCC;
+          }
+        }
+      }
+    }
+
+    // 已过期状态
+    &--expired {
+      background: #F8F8F8;
+
+      .coupon-card__left {
+
+        .amount-number,
+        .amount-unit,
+        .condition-text {
+          color: #CCCCCC;
+        }
+      }
+
+      .coupon-card__right {
+        .coupon-info {
+
+          .coupon-name,
+          .coupon-rules,
+          .coupon-expire {
+            color: #CCCCCC;
+          }
+        }
+      }
+    }
+  }
+}
+
+.hover-active {
+  opacity: 0.8;
+  transform: scale(0.98);
+}
+
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding-top: 200rpx;
+
+  .empty-icon {
+    width: 300rpx;
+    height: 280rpx;
+    margin-bottom: 40rpx;
+  }
+
+  .empty-text {
+    font-size: 28rpx;
+    color: #AAAAAA;
+  }
+}
+</style>

+ 1 - 1
pages/personal/index.vue

@@ -40,7 +40,7 @@
 							</image>
 							</image>
 						</view>
 						</view>
 					</view>
 					</view>
-					<view class="card-list" hover-class="hover-active">
+					<view class="card-list" hover-class="hover-active" @click='go("/pages/coupon/index")'>
 						<view class='left'>
 						<view class='left'>
 							<image :src="getFileUrl('img/personal/wdqb.png')" mode="widthFix" class="card-icon"></image>
 							<image :src="getFileUrl('img/personal/wdqb.png')" mode="widthFix" class="card-icon"></image>
 							<view class="title">我的券包</view>
 							<view class="title">我的券包</view>

+ 0 - 5
pages/personal/setPhone.vue

@@ -38,7 +38,6 @@
 <script setup>
 <script setup>
 import { ref, computed, onUnmounted } from 'vue';
 import { ref, computed, onUnmounted } from 'vue';
 import { useUserStore } from '@/store/user.js';
 import { useUserStore } from '@/store/user.js';
-import { LoginSendCode } from '@/api/login.js';
 import { isPhone } from '@/utils/is.js';
 import { isPhone } from '@/utils/is.js';
 import { go } from '@/utils/utils.js';
 import { go } from '@/utils/utils.js';
 
 
@@ -67,10 +66,6 @@ const handleGetCode = async () => {
 	}
 	}
 
 
 	try {
 	try {
-		const res = await LoginSendCode({
-			phone: phone.value
-		});
-
 		uni.showToast({
 		uni.showToast({
 			title: '验证码已发送',
 			title: '验证码已发送',
 			icon: 'success'
 			icon: 'success'