feat: 更新页面标题和优化多个页面功能
refactor(PointsDetail): 替换按钮为视图组件并更新图标 refactor(Rewards): 添加搜索功能并优化兑换券列表样式 refactor(Feedback): 重构反馈页面布局和交互逻辑 refactor(RewardDetail): 重新设计优惠券详情页面结构 style: 删除未使用的图片资源
Showing
12 changed files
with
338 additions
and
231 deletions
src/assets/images/券详情.png
deleted
100644 → 0
157 KB
| 1 | <template> | 1 | <template> |
| 2 | - <view class="min-h-screen flex flex-col bg-white"> | 2 | + <view class="min-h-screen bg-white flex flex-col"> |
| 3 | - <AppHeader title="意见反馈" /> | 3 | + <view class="flex-1 px-4 py-6 overflow-auto"> |
| 4 | - <view class="flex-1 px-4 py-6 pb-20"> | 4 | + <!-- Feedback Section --> |
| 5 | - <form @submit.prevent="handleSubmit" class="space-y-6"> | 5 | + <view class="mb-6"> |
| 6 | - <view> | 6 | + <view class="text-lg font-medium mb-2">您的反馈</view> |
| 7 | - <label for="feedback" class="block text-sm font-medium text-gray-700 mb-1"> | 7 | + <view class="bg-white rounded-lg border border-gray-200 p-4"> |
| 8 | - 您的反馈 | ||
| 9 | - </label> | ||
| 10 | <textarea | 8 | <textarea |
| 11 | - id="feedback" | 9 | + v-model="feedbackText" |
| 12 | - rows="5" | 10 | + class="w-full text-gray-600 focus:outline-none resize-none" |
| 13 | - class="w-full p-3 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" | ||
| 14 | placeholder="请描述您遇到的问题或建议..." | 11 | placeholder="请描述您遇到的问题或建议..." |
| 15 | - v-model="feedback" | 12 | + :rows="4" |
| 16 | - required | ||
| 17 | /> | 13 | /> |
| 18 | </view> | 14 | </view> |
| 19 | - <view> | 15 | + </view> |
| 20 | - <label class="block text-sm font-medium text-gray-700 mb-1"> | 16 | + |
| 21 | - 上传截图 (可选) | 17 | + <!-- Upload Screenshot Section --> |
| 22 | - </label> | 18 | + <view class="mb-6"> |
| 23 | - <view class="flex flex-wrap gap-2 mb-2"> | 19 | + <view class="text-lg font-medium mb-2">上传截图 (可选)</view> |
| 24 | - <view v-for="(image, index) in images" :key="index" class="relative w-20 h-20"> | 20 | + <view v-if="screenshot" class="mb-4"> |
| 25 | - <image :src="image" :alt="`上传图片 ${index + 1}`" class="w-full h-full object-cover rounded-lg" /> | 21 | + <view class="relative inline-block"> |
| 26 | - <button type="button" class="absolute -top-2 -right-2 bg-red-500 text-white rounded-full p-1" @click="removeImage(index)"> | 22 | + <image |
| 27 | - <Failure size="16" /> | 23 | + :src="screenshot" |
| 28 | - </button> | 24 | + class="w-24 h-24 rounded-lg object-cover" |
| 25 | + mode="aspectFill" | ||
| 26 | + @tap="previewImage" | ||
| 27 | + /> | ||
| 28 | + <view | ||
| 29 | + @click="deleteImage" | ||
| 30 | + class="absolute -top-2 -right-2 w-5 h-5 bg-red-500 rounded-full flex items-center justify-center" | ||
| 31 | + > | ||
| 32 | + <view class="text-white text-xs">×</view> | ||
| 33 | + </view> | ||
| 34 | + </view> | ||
| 29 | </view> | 35 | </view> |
| 30 | <view | 36 | <view |
| 31 | - class="w-20 h-20 flex flex-col items-center justify-center border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50" | 37 | + v-if="!screenshot" |
| 38 | + class="border border-dashed border-gray-300 rounded-lg p-6 flex flex-col items-center justify-center" | ||
| 32 | @click="chooseImage" | 39 | @click="chooseImage" |
| 33 | > | 40 | > |
| 34 | - <Upload size="24" class="text-gray-400" /> | 41 | + <view class="text-gray-400 mb-2"> |
| 35 | - <span class="text-xs text-gray-500 mt-1">添加图片</span> | 42 | + <Photograph size="24" /> |
| 36 | </view> | 43 | </view> |
| 44 | + <view class="text-center text-gray-400">添加图片</view> | ||
| 37 | </view> | 45 | </view> |
| 38 | </view> | 46 | </view> |
| 39 | - <view> | 47 | + |
| 40 | - <label for="name" class="block text-sm font-medium text-gray-700 mb-1"> | 48 | + <!-- Name Section --> |
| 41 | - 您的姓名 | 49 | + <view class="mb-6"> |
| 42 | - </label> | 50 | + <view class="text-lg font-medium mb-2">您的姓名</view> |
| 51 | + <view class="bg-white rounded-lg border border-gray-200 p-4"> | ||
| 43 | <input | 52 | <input |
| 44 | type="text" | 53 | type="text" |
| 45 | - id="name" | ||
| 46 | - class="w-full p-3 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" | ||
| 47 | - placeholder="请输入您的姓名" | ||
| 48 | v-model="name" | 54 | v-model="name" |
| 55 | + class="w-full text-gray-600 focus:outline-none" | ||
| 56 | + placeholder="请输入您的姓名" | ||
| 49 | /> | 57 | /> |
| 50 | </view> | 58 | </view> |
| 51 | - <view> | 59 | + </view> |
| 52 | - <label for="contact" class="block text-sm font-medium text-gray-700 mb-1"> | 60 | + |
| 53 | - 联系方式 | 61 | + <!-- Contact Section --> |
| 54 | - </label> | 62 | + <view class="mb-10"> |
| 63 | + <view class="text-lg font-medium mb-2">联系方式</view> | ||
| 64 | + <view class="bg-white rounded-lg border border-gray-200 p-4"> | ||
| 55 | <input | 65 | <input |
| 56 | type="text" | 66 | type="text" |
| 57 | - id="contact" | ||
| 58 | - class="w-full p-3 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" | ||
| 59 | - placeholder="请输入您的手机号或微信号" | ||
| 60 | v-model="contact" | 67 | v-model="contact" |
| 68 | + class="w-full text-gray-600 focus:outline-none" | ||
| 69 | + placeholder="请输入您的手机号或微信号" | ||
| 61 | /> | 70 | /> |
| 62 | </view> | 71 | </view> |
| 63 | - <button type="submit" class="w-full py-3 bg-blue-500 text-white font-medium rounded-lg hover:bg-blue-600 transition-colors"> | 72 | + </view> |
| 73 | + | ||
| 74 | + <!-- Submit Button --> | ||
| 75 | + <view | ||
| 76 | + @click="submitFeedback" | ||
| 77 | + class="w-full py-3 bg-blue-500 text-white text-lg font-medium rounded-lg flex items-center justify-center" | ||
| 78 | + > | ||
| 64 | 提交反馈 | 79 | 提交反馈 |
| 65 | - </button> | ||
| 66 | - </form> | ||
| 67 | </view> | 80 | </view> |
| 68 | - <BottomNav /> | 81 | + </view> |
| 82 | + | ||
| 83 | + <!-- Image Preview --> | ||
| 84 | + <nut-image-preview | ||
| 85 | + v-model:show="previewVisible" | ||
| 86 | + :images="previewImages" | ||
| 87 | + :init-no="0" | ||
| 88 | + @close="closePreview" | ||
| 89 | + /> | ||
| 69 | </view> | 90 | </view> |
| 70 | </template> | 91 | </template> |
| 71 | 92 | ||
| 72 | <script setup> | 93 | <script setup> |
| 73 | import { ref } from 'vue'; | 94 | import { ref } from 'vue'; |
| 74 | import Taro from '@tarojs/taro'; | 95 | import Taro from '@tarojs/taro'; |
| 75 | -import AppHeader from '../../components/AppHeader.vue'; | 96 | +import { Photograph } from '@nutui/icons-vue-taro'; |
| 76 | -import BottomNav from '../../components/BottomNav.vue'; | ||
| 77 | -import { Upload, Failure } from '@nutui/icons-vue-taro'; | ||
| 78 | 97 | ||
| 79 | -const feedback = ref(''); | 98 | +const feedbackText = ref(''); |
| 99 | +const screenshot = ref(''); | ||
| 80 | const name = ref(''); | 100 | const name = ref(''); |
| 81 | const contact = ref(''); | 101 | const contact = ref(''); |
| 82 | -const images = ref([]); | 102 | +const previewVisible = ref(false); |
| 103 | +const previewImages = ref([]); | ||
| 104 | + | ||
| 105 | +/** | ||
| 106 | + * 显示提示信息 | ||
| 107 | + */ | ||
| 108 | +const showToast = (message, type = 'success') => { | ||
| 109 | + const icon = type === 'error' ? 'error' : 'success'; | ||
| 110 | + Taro.showToast({ | ||
| 111 | + title: message, | ||
| 112 | + icon: icon, | ||
| 113 | + duration: 2000 | ||
| 114 | + }); | ||
| 115 | +}; | ||
| 83 | 116 | ||
| 117 | +/** | ||
| 118 | + * 选择图片 | ||
| 119 | + */ | ||
| 84 | const chooseImage = () => { | 120 | const chooseImage = () => { |
| 85 | Taro.chooseImage({ | 121 | Taro.chooseImage({ |
| 86 | - count: 9 - images.value.length, | 122 | + count: 1, |
| 87 | - sizeType: ['original', 'compressed'], | 123 | + sizeType: ['compressed'], |
| 88 | sourceType: ['album', 'camera'], | 124 | sourceType: ['album', 'camera'], |
| 89 | - success: (res) => { | 125 | + success: function (res) { |
| 90 | - images.value = [...images.value, ...res.tempFilePaths]; | 126 | + const tempFilePath = res.tempFilePaths[0]; |
| 127 | + screenshot.value = tempFilePath; | ||
| 128 | + }, | ||
| 129 | + fail: function () { | ||
| 130 | + showToast('选择图片失败', 'error'); | ||
| 91 | } | 131 | } |
| 92 | }); | 132 | }); |
| 93 | }; | 133 | }; |
| 94 | 134 | ||
| 95 | -const removeImage = (index) => { | 135 | +/** |
| 96 | - images.value = images.value.filter((_, i) => i !== index); | 136 | + * 预览图片 |
| 137 | + */ | ||
| 138 | +const previewImage = () => { | ||
| 139 | + previewImages.value = [{ src: screenshot.value }]; | ||
| 140 | + previewVisible.value = true; | ||
| 97 | }; | 141 | }; |
| 98 | 142 | ||
| 99 | -const handleSubmit = () => { | 143 | +/** |
| 100 | - // In a real app, we would send this data to a server | 144 | + * 关闭预览 |
| 101 | - Taro.showToast({ | 145 | + */ |
| 102 | - title: '感谢您的反馈!我们会尽快处理。', | 146 | +const closePreview = () => { |
| 103 | - icon: 'success', | 147 | + previewVisible.value = false; |
| 104 | - duration: 2000 | 148 | +}; |
| 105 | - }); | ||
| 106 | 149 | ||
| 107 | - setTimeout(() => { | 150 | +/** |
| 108 | - Taro.navigateBack(); | 151 | + * 删除图片 |
| 109 | - }, 2000); | 152 | + */ |
| 153 | +const deleteImage = () => { | ||
| 154 | + screenshot.value = ''; | ||
| 155 | +}; | ||
| 156 | + | ||
| 157 | +/** | ||
| 158 | + * 提交反馈 | ||
| 159 | + */ | ||
| 160 | +const submitFeedback = () => { | ||
| 161 | + if (!feedbackText.value) { | ||
| 162 | + showToast('请描述您遇到的问题或建议', 'error'); | ||
| 163 | + return; | ||
| 164 | + } | ||
| 165 | + if (!name.value) { | ||
| 166 | + showToast('请输入您的姓名', 'error'); | ||
| 167 | + return; | ||
| 168 | + } | ||
| 169 | + if (!contact.value) { | ||
| 170 | + showToast('请输入您的手机号或微信号', 'error'); | ||
| 171 | + return; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + // 在实际应用中,这里会处理提交逻辑,例如上传图片和发送数据到服务器 | ||
| 175 | + showToast('提交成功'); | ||
| 176 | + | ||
| 177 | + // 提交成功后清空表单 | ||
| 178 | + feedbackText.value = ''; | ||
| 179 | + screenshot.value = ''; | ||
| 180 | + name.value = ''; | ||
| 181 | + contact.value = ''; | ||
| 110 | }; | 182 | }; |
| 111 | </script> | 183 | </script> |
| 184 | + | ||
| 185 | +<style lang="less"> | ||
| 186 | +// 你可以在这里添加自定义样式 | ||
| 187 | +</style> | ... | ... |
| 1 | +/* | ||
| 2 | + * @Date: 2025-08-27 18:25:28 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-08-28 00:26:01 | ||
| 5 | + * @FilePath: /lls_program/src/pages/PointsDetail/index.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 1 | export default { | 8 | export default { |
| 2 | - navigationBarTitleText: '首页' | 9 | + navigationBarTitleText: '积分明细' |
| 3 | } | 10 | } | ... | ... |
| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | <view class="min-h-screen flex flex-col bg-white"> | 2 | <view class="min-h-screen flex flex-col bg-white"> |
| 3 | <!-- Blue header background --> | 3 | <!-- Blue header background --> |
| 4 | <view class="bg-blue-500 h-52 absolute w-full top-0 left-0 z-0"></view> | 4 | <view class="bg-blue-500 h-52 absolute w-full top-0 left-0 z-0"></view> |
| 5 | - <AppHeader title="积分明细" /> | 5 | + <!-- <AppHeader title="积分明细" /> --> |
| 6 | <!-- Content --> | 6 | <!-- Content --> |
| 7 | <view class="relative z-10 flex-1 pb-20"> | 7 | <view class="relative z-10 flex-1 pb-20"> |
| 8 | <!-- Points display --> | 8 | <!-- Points display --> |
| ... | @@ -14,10 +14,10 @@ | ... | @@ -14,10 +14,10 @@ |
| 14 | <view class="bg-white rounded-t-3xl px-4 pt-5"> | 14 | <view class="bg-white rounded-t-3xl px-4 pt-5"> |
| 15 | <view class="flex justify-between items-center mb-4"> | 15 | <view class="flex justify-between items-center mb-4"> |
| 16 | <h3 class="text-lg font-medium">积分攻略</h3> | 16 | <h3 class="text-lg font-medium">积分攻略</h3> |
| 17 | - <button class="text-blue-500 text-sm flex items-center"> | 17 | + <view class="text-blue-500 text-sm flex items-center"> |
| 18 | 查看全部 | 18 | 查看全部 |
| 19 | <Right size="16" /> | 19 | <Right size="16" /> |
| 20 | - </button> | 20 | + </view> |
| 21 | </view> | 21 | </view> |
| 22 | <!-- Strategy cards --> | 22 | <!-- Strategy cards --> |
| 23 | <view class="space-y-3 mb-6"> | 23 | <view class="space-y-3 mb-6"> |
| ... | @@ -34,7 +34,7 @@ | ... | @@ -34,7 +34,7 @@ |
| 34 | </view> | 34 | </view> |
| 35 | <view class="bg-blue-50 p-4 rounded-lg flex items-start"> | 35 | <view class="bg-blue-50 p-4 rounded-lg flex items-start"> |
| 36 | <view class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mr-3"> | 36 | <view class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mr-3"> |
| 37 | - <Trophy size="20" class="text-blue-500" /> | 37 | + <My size="20" class="text-blue-500" /> |
| 38 | </view> | 38 | </view> |
| 39 | <view> | 39 | <view> |
| 40 | <h4 class="font-medium">参与周末步数挑战</h4> | 40 | <h4 class="font-medium">参与周末步数挑战</h4> |
| ... | @@ -45,7 +45,7 @@ | ... | @@ -45,7 +45,7 @@ |
| 45 | </view> | 45 | </view> |
| 46 | <view class="bg-blue-50 p-4 rounded-lg flex items-start"> | 46 | <view class="bg-blue-50 p-4 rounded-lg flex items-start"> |
| 47 | <view class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mr-3"> | 47 | <view class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mr-3"> |
| 48 | - <People size="20" class="text-blue-500" /> | 48 | + <My size="20" class="text-blue-500" /> |
| 49 | </view> | 49 | </view> |
| 50 | <view> | 50 | <view> |
| 51 | <h4 class="font-medium">邀请家人加入家庭</h4> | 51 | <h4 class="font-medium">邀请家人加入家庭</h4> |
| ... | @@ -58,15 +58,15 @@ | ... | @@ -58,15 +58,15 @@ |
| 58 | <!-- Tabs --> | 58 | <!-- Tabs --> |
| 59 | <view class="border-b border-gray-200"> | 59 | <view class="border-b border-gray-200"> |
| 60 | <view class="flex space-x-8"> | 60 | <view class="flex space-x-8"> |
| 61 | - <button :class="['py-3 font-medium', activeTab === 'all' ? 'text-blue-500 border-b-2 border-blue-500' : 'text-gray-500']" @click="activeTab = 'all'"> | 61 | + <view :class="['py-3 font-medium', activeTab === 'all' ? 'text-blue-500 border-b-2 border-blue-500' : 'text-gray-500']" @click="activeTab = 'all'"> |
| 62 | 全部 | 62 | 全部 |
| 63 | - </button> | 63 | + </view> |
| 64 | - <button :class="['py-3 font-medium', activeTab === 'earned' ? 'text-blue-500 border-b-2 border-blue-500' : 'text-gray-500']" @click="activeTab = 'earned'"> | 64 | + <view :class="['py-3 font-medium', activeTab === 'earned' ? 'text-blue-500 border-b-2 border-blue-500' : 'text-gray-500']" @click="activeTab = 'earned'"> |
| 65 | 已发放 | 65 | 已发放 |
| 66 | - </button> | 66 | + </view> |
| 67 | - <button :class="['py-3 font-medium', activeTab === 'spent' ? 'text-blue-500 border-b-2 border-blue-500' : 'text-gray-500']" @click="activeTab = 'spent'"> | 67 | + <view :class="['py-3 font-medium', activeTab === 'spent' ? 'text-blue-500 border-b-2 border-blue-500' : 'text-gray-500']" @click="activeTab = 'spent'"> |
| 68 | 已消耗 | 68 | 已消耗 |
| 69 | - </button> | 69 | + </view> |
| 70 | </view> | 70 | </view> |
| 71 | </view> | 71 | </view> |
| 72 | <!-- Points history list --> | 72 | <!-- Points history list --> |
| ... | @@ -84,7 +84,7 @@ | ... | @@ -84,7 +84,7 @@ |
| 84 | </view> | 84 | </view> |
| 85 | </view> | 85 | </view> |
| 86 | </view> | 86 | </view> |
| 87 | - <BottomNav /> | 87 | + <!-- <BottomNav /> --> |
| 88 | </view> | 88 | </view> |
| 89 | </template> | 89 | </template> |
| 90 | 90 | ... | ... |
| 1 | +<!-- | ||
| 2 | + * @Date: 2025-08-27 17:49:58 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-08-28 00:30:58 | ||
| 5 | + * @FilePath: /lls_program/src/pages/PrivacyPolicy/index.vue | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | +--> | ||
| 1 | <template> | 8 | <template> |
| 2 | <view class="min-h-screen flex flex-col bg-white"> | 9 | <view class="min-h-screen flex flex-col bg-white"> |
| 3 | - <AppHeader title="隐私政策" /> | 10 | + <!-- <AppHeader title="隐私政策" /> --> |
| 4 | <view class="flex-1 px-4 py-6 pb-20"> | 11 | <view class="flex-1 px-4 py-6 pb-20"> |
| 5 | <view class="prose prose-sm max-w-none"> | 12 | <view class="prose prose-sm max-w-none"> |
| 6 | <h2 class="text-xl font-bold mb-4">老来赛隐私政策</h2> | 13 | <h2 class="text-xl font-bold mb-4">老来赛隐私政策</h2> |
| ... | @@ -108,7 +115,7 @@ | ... | @@ -108,7 +115,7 @@ |
| 108 | <p class="text-gray-700 mt-8">最后更新日期:2023年5月1日</p> | 115 | <p class="text-gray-700 mt-8">最后更新日期:2023年5月1日</p> |
| 109 | </view> | 116 | </view> |
| 110 | </view> | 117 | </view> |
| 111 | - <BottomNav /> | 118 | + <!-- <BottomNav /> --> |
| 112 | </view> | 119 | </view> |
| 113 | </template> | 120 | </template> |
| 114 | 121 | ... | ... |
| 1 | +/* | ||
| 2 | + * @Date: 2025-08-27 18:25:42 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-08-28 09:39:10 | ||
| 5 | + * @FilePath: /lls_program/src/pages/RewardDetail/index.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 1 | export default { | 8 | export default { |
| 2 | - navigationBarTitleText: '首页' | 9 | + navigationBarTitleText: '兑换券详情' |
| 3 | } | 10 | } | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | - <view class="min-h-screen flex flex-col bg-gradient-to-br from-blue-50 to-purple-50"> | 2 | + <view class="min-h-screen bg-white pb-24"> |
| 3 | - <AppHeader title="优惠券详情" /> | 3 | + <!-- <AppHeader title="优惠券详情" :showBack="true" /> --> |
| 4 | - <view class="flex-1 p-4"> | 4 | + <!-- Top Image --> |
| 5 | - <template v-if="!redeemed"> | 5 | + <view class="w-full h-48"> |
| 6 | - <view class="bg-white rounded-2xl shadow-sm overflow-hidden mb-4"> | 6 | + <image :src="reward.image" class="w-full h-full object-cover" /> |
| 7 | - <view class="h-40 bg-blue-600 flex items-center justify-center p-6"> | ||
| 8 | - <image :src="reward.image" :alt="reward.merchant" class="max-h-full max-w-full object-contain" /> | ||
| 9 | - </view> | ||
| 10 | - <view class="p-4"> | ||
| 11 | - <h1 class="text-xl font-medium mb-1">{{ reward.title }}</h1> | ||
| 12 | - <p class="text-gray-600">{{ reward.description }}</p> | ||
| 13 | - <view class="flex items-center mt-4 text-blue-600"> | ||
| 14 | - <Ticket size="20" class="mr-2" /> | ||
| 15 | - <span class="font-medium">{{ reward.points }} 积分</span> | ||
| 16 | </view> | 7 | </view> |
| 8 | + | ||
| 9 | + <!-- Main Content --> | ||
| 10 | + <view class="p-6"> | ||
| 11 | + <!-- Points and Title --> | ||
| 12 | + <view class="text-center mb-8"> | ||
| 13 | + <view class="text-4xl font-bold text-blue-500 mb-2"> | ||
| 14 | + <text class="text-2xl">{{ reward.points }} 积分</text> | ||
| 17 | </view> | 15 | </view> |
| 16 | + <h1 class="text-xl font-bold">{{ reward.title }}</h1> | ||
| 18 | </view> | 17 | </view> |
| 19 | 18 | ||
| 20 | - <GlassCard class="mb-4"> | 19 | + <!-- Details Sections --> |
| 21 | - <h2 class="text-lg font-medium mb-4">使用须知</h2> | 20 | + <view class="space-y-6"> |
| 22 | - <view class="space-y-3"> | 21 | + <!-- Applicable Stores --> |
| 23 | - <view class="flex"> | ||
| 24 | - <Calendar size="20" class="text-gray-500 mr-3 flex-shrink-0" /> | ||
| 25 | <view> | 22 | <view> |
| 26 | - <p class="font-medium">有效期</p> | 23 | + <h2 class="text-lg font-medium mb-3">可用门店</h2> |
| 27 | - <p class="text-gray-600">{{ reward.validUntil }}</p> | 24 | + <view class="space-y-2 text-gray-600"> |
| 25 | + <view v-for="store in reward.stores" :key="store" class="flex text-sm"> | ||
| 26 | + <span class="mr-2">·</span> | ||
| 27 | + <p>{{ store }}</p> | ||
| 28 | </view> | 28 | </view> |
| 29 | </view> | 29 | </view> |
| 30 | - <view class="flex"> | 30 | + </view> |
| 31 | - <Location2 size="20" class="text-gray-500 mr-3 flex-shrink-0" /> | 31 | + |
| 32 | + <!-- Redemption Rules --> | ||
| 32 | <view> | 33 | <view> |
| 33 | - <p class="font-medium">适用门店</p> | 34 | + <h2 class="text-lg font-medium mb-3">兑换规则</h2> |
| 34 | - <p class="text-gray-600">{{ reward.locations }}</p> | 35 | + <view class="space-y-2 text-gray-600"> |
| 36 | + <view v-for="rule in reward.redemption_rules" :key="rule" class="flex text-sm"> | ||
| 37 | + <span class="mr-2">·</span> | ||
| 38 | + <p>{{ rule }}</p> | ||
| 35 | </view> | 39 | </view> |
| 36 | </view> | 40 | </view> |
| 37 | </view> | 41 | </view> |
| 38 | - </GlassCard> | ||
| 39 | 42 | ||
| 40 | - <GlassCard class="mb-6"> | 43 | + <!-- Usage Rules --> |
| 41 | - <h2 class="text-lg font-medium mb-4">使用条款</h2> | 44 | + <view> |
| 42 | - <view class="space-y-2"> | 45 | + <h2 class="text-lg font-medium mb-3">使用规则</h2> |
| 43 | - <view v-for="(term, index) in reward.terms" :key="index" class="flex"> | 46 | + <view class="space-y-2 text-gray-600"> |
| 44 | - <Info size="16" class="text-gray-500 mr-2 flex-shrink-0 mt-0.5" /> | 47 | + <view v-for="rule in reward.usage_rules" :key="rule" class="flex text-sm"> |
| 45 | - <p class="text-gray-600">{{ term }}</p> | 48 | + <span class="mr-2">·</span> |
| 49 | + <p>{{ rule }}</p> | ||
| 50 | + </view> | ||
| 51 | + </view> | ||
| 52 | + </view> | ||
| 46 | </view> | 53 | </view> |
| 47 | </view> | 54 | </view> |
| 48 | - </GlassCard> | ||
| 49 | 55 | ||
| 50 | - <PrimaryButton @click="handleRedeem" :disabled="redeeming"> | 56 | + <!-- Bottom Button --> |
| 51 | - <template v-if="redeeming"> | 57 | + <view class="fixed bottom-0 left-0 right-0 p-4 bg-white border-t border-gray-100"> |
| 52 | - <view class="animate-spin mr-2"> | 58 | + <nut-button type="primary" size="large" block color="#3B82F6" @click="handleRedeem"> |
| 53 | - <Loading class="w-5 h-5" /> | ||
| 54 | - </view> | ||
| 55 | - 兑换中... | ||
| 56 | - </template> | ||
| 57 | - <template v-else> | ||
| 58 | 立即兑换 | 59 | 立即兑换 |
| 59 | - </template> | 60 | + </nut-button> |
| 60 | - </PrimaryButton> | ||
| 61 | - </template> | ||
| 62 | - | ||
| 63 | - <template v-else> | ||
| 64 | - <view class="flex flex-col items-center justify-center h-full"> | ||
| 65 | - <GlassCard class="w-full max-w-md"> | ||
| 66 | - <view class="flex flex-col items-center"> | ||
| 67 | - <view class="w-20 h-20 bg-green-100 rounded-full flex items-center justify-center mb-4"> | ||
| 68 | - <Check size="40" class="text-green-600" /> | ||
| 69 | - </view> | ||
| 70 | - <h2 class="text-xl font-medium mb-2">兑换成功</h2> | ||
| 71 | - <p class="text-gray-600 text-center mb-6"> | ||
| 72 | - 您已成功兑换{{ reward.title }} | ||
| 73 | - </p> | ||
| 74 | - <view class="bg-gray-50 w-full rounded-lg p-4 mb-6 text-center"> | ||
| 75 | - <p class="text-gray-600 mb-2">兑换码</p> | ||
| 76 | - <p class="text-2xl font-mono tracking-wider font-medium"> | ||
| 77 | - SB12345678 | ||
| 78 | - </p> | ||
| 79 | - </view> | ||
| 80 | - <image :src="reward.image" :alt="reward.merchant" class="w-24 h-24 object-contain mb-6" /> | ||
| 81 | - <view class="text-center mb-6"> | ||
| 82 | - <p class="font-medium">{{ reward.title }}</p> | ||
| 83 | - <p class="text-gray-600">{{ reward.description }}</p> | ||
| 84 | - <p class="text-gray-500 text-sm mt-2"> | ||
| 85 | - 有效期至: {{ reward.validUntil }} | ||
| 86 | - </p> | ||
| 87 | - </view> | ||
| 88 | - <PrimaryButton @click="goBack"> | ||
| 89 | - 返回兑换页面 | ||
| 90 | - </PrimaryButton> | ||
| 91 | - </view> | ||
| 92 | - </GlassCard> | ||
| 93 | - </view> | ||
| 94 | - </template> | ||
| 95 | </view> | 61 | </view> |
| 96 | </view> | 62 | </view> |
| 97 | </template> | 63 | </template> |
| ... | @@ -100,37 +66,58 @@ | ... | @@ -100,37 +66,58 @@ |
| 100 | import { ref } from 'vue'; | 66 | import { ref } from 'vue'; |
| 101 | import Taro from '@tarojs/taro'; | 67 | import Taro from '@tarojs/taro'; |
| 102 | import AppHeader from '../../components/AppHeader.vue'; | 68 | import AppHeader from '../../components/AppHeader.vue'; |
| 103 | -import GlassCard from '../../components/GlassCard.vue'; | ||
| 104 | -import PrimaryButton from '../../components/PrimaryButton.vue'; | ||
| 105 | -import { Ticket, Calendar, Location2, Info, Check, Loading } from '@nutui/icons-vue-taro'; | ||
| 106 | - | ||
| 107 | -const redeeming = ref(false); | ||
| 108 | -const redeemed = ref(false); | ||
| 109 | 69 | ||
| 110 | -// Mock reward data | 70 | +// Mock reward data based on the image |
| 111 | -const reward = { | 71 | +const reward = ref({ |
| 112 | id: 1, | 72 | id: 1, |
| 113 | - title: '星巴克咖啡券', | 73 | + title: '吴良材眼镜店85折券', |
| 114 | - description: '任意中杯咖啡一杯', | 74 | + points: 10, |
| 115 | - points: 500, | 75 | + image: 'https://placehold.co/800x400/e2f3ff/0369a1?text=LFX&font=roboto', // Placeholder image |
| 116 | - merchant: '星巴克', | 76 | + stores: [ |
| 117 | - image: 'https://upload.wikimedia.org/wikipedia/en/thumb/d/d3/Starbucks_Corporation_Logo_2011.svg/1200px-Starbucks_Corporation_Logo_2011.svg.png', | 77 | + '吴良材眼镜店 (南京东路店)', |
| 118 | - validUntil: '2023年12月31日', | 78 | + '吴良材眼镜店 (淮海中路店)', |
| 119 | - locations: '全国各星巴克门店', | 79 | + '吴良材眼镜店 (徐家汇店)' |
| 120 | - terms: ['每人限兑换1次', '不与其他优惠同享', '使用时请出示兑换码', '最终解释权归商家所有'] | 80 | + ], |
| 121 | -}; | 81 | + redemption_rules: [ |
| 82 | + '每人每月限兑换1次', | ||
| 83 | + '兑换后30天内有效', | ||
| 84 | + '兑换成功后不可退回积分' | ||
| 85 | + ], | ||
| 86 | + usage_rules: [ | ||
| 87 | + '仅限店内正价商品使用', | ||
| 88 | + '不可与其他优惠同时使用', | ||
| 89 | + '特价商品不可使用', | ||
| 90 | + '最终解释权归商家所有' | ||
| 91 | + ] | ||
| 92 | +}); | ||
| 122 | 93 | ||
| 94 | +/** | ||
| 95 | + * @description Handles the redemption of the coupon. | ||
| 96 | + */ | ||
| 123 | const handleRedeem = () => { | 97 | const handleRedeem = () => { |
| 124 | - redeeming.value = true; | 98 | + // Show a confirmation modal |
| 125 | - // Simulate API call | 99 | + Taro.showModal({ |
| 100 | + title: '确认兑换', | ||
| 101 | + content: `将消耗 ${reward.value.points} 积分兑换此优惠券,是否确认?`, | ||
| 102 | + success: (res) => { | ||
| 103 | + if (res.confirm) { | ||
| 104 | + // Simulate API call for redemption | ||
| 105 | + Taro.showLoading({ title: '兑换中...' }); | ||
| 106 | + setTimeout(() => { | ||
| 107 | + Taro.hideLoading(); | ||
| 108 | + Taro.showToast({ | ||
| 109 | + title: '兑换成功', | ||
| 110 | + icon: 'success', | ||
| 111 | + duration: 2000 | ||
| 112 | + }); | ||
| 113 | + // After successful redemption, you might want to navigate the user to their coupons page | ||
| 114 | + // For now, we'll just navigate back. | ||
| 126 | setTimeout(() => { | 115 | setTimeout(() => { |
| 127 | - redeeming.value = false; | 116 | + Taro.navigateBack(); |
| 128 | - redeemed.value = true; | 117 | + }, 2000); |
| 129 | }, 1500); | 118 | }, 1500); |
| 119 | + } | ||
| 120 | + } | ||
| 121 | + }); | ||
| 130 | }; | 122 | }; |
| 131 | - | ||
| 132 | -const goBack = () => { | ||
| 133 | - Taro.navigateTo({ url: '/pages/Rewards/index' }); | ||
| 134 | -}; | ||
| 135 | - | ||
| 136 | </script> | 123 | </script> | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-08-27 17:47:26 | 2 | * @Date: 2025-08-27 17:47:26 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-08-28 00:10:13 | 4 | + * @LastEditTime: 2025-08-28 09:37:06 |
| 5 | - * @FilePath: /lls_program/src/pages/Rewards/index.vue | ||
| 6 | - * @Description: 文件描述 | ||
| 7 | ---> | ||
| 8 | -<!-- | ||
| 9 | - * @Date: 2025-08-27 17:47:26 | ||
| 10 | - * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 11 | - * @LastEditTime: 2025-08-27 23:57:38 | ||
| 12 | * @FilePath: /lls_program/src/pages/Rewards/index.vue | 5 | * @FilePath: /lls_program/src/pages/Rewards/index.vue |
| 13 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 14 | --> | 7 | --> |
| ... | @@ -20,25 +13,32 @@ | ... | @@ -20,25 +13,32 @@ |
| 20 | <!-- Content --> | 13 | <!-- Content --> |
| 21 | <view class="relative z-10 flex-1 pb-20"> | 14 | <view class="relative z-10 flex-1 pb-20"> |
| 22 | <!-- Points display --> | 15 | <!-- Points display --> |
| 23 | - <view class="pt-4 pb-8 flex flex-col items-center"> | 16 | + <view class="pt-8 pb-8 flex flex-col items-center"> |
| 24 | <h2 class="text-4xl font-bold text-white mb-1">2580分</h2> | 17 | <h2 class="text-4xl font-bold text-white mb-1">2580分</h2> |
| 25 | <p class="text-white text-opacity-80">我的积分</p> | 18 | <p class="text-white text-opacity-80">我的积分</p> |
| 26 | </view> | 19 | </view> |
| 27 | <!-- Main content --> | 20 | <!-- Main content --> |
| 28 | <view class="bg-white rounded-t-3xl px-4 pt-5"> | 21 | <view class="bg-white rounded-t-3xl px-4 pt-5"> |
| 29 | <!-- Quick exchange options --> | 22 | <!-- Quick exchange options --> |
| 23 | + <!-- Search bar --> | ||
| 24 | + <view class="mb-6"> | ||
| 25 | + <view class="relative"> | ||
| 26 | + <input type="text" v-model="searchQuery" placeholder="搜索商户名称" | ||
| 27 | + class=" bg-gray-100 rounded-lg py-3 pl-10 pr-4 border border-transparent focus:bg-white focus:border-blue-500 focus:outline-none" /> | ||
| 28 | + <view class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none"> | ||
| 29 | + <Search2 size="20" class="text-gray-400" /> | ||
| 30 | + </view> | ||
| 31 | + </view> | ||
| 32 | + </view> | ||
| 33 | + | ||
| 30 | <view class="flex gap-3 mb-6"> | 34 | <view class="flex gap-3 mb-6"> |
| 31 | - <view | 35 | + <view v-for="option in quickExchangeOptions" :key="option.points" @click="selectedPoints = option.points" |
| 32 | - v-for="option in quickExchangeOptions" | ||
| 33 | - :key="option.points" | ||
| 34 | - @click="selectedPoints = option.points" | ||
| 35 | :class="[ | 36 | :class="[ |
| 36 | 'flex-1 py-3 rounded-lg border text-center', | 37 | 'flex-1 py-3 rounded-lg border text-center', |
| 37 | selectedPoints === option.points | 38 | selectedPoints === option.points |
| 38 | ? 'border-blue-500 bg-blue-50 text-blue-500' | 39 | ? 'border-blue-500 bg-blue-50 text-blue-500' |
| 39 | : 'border-gray-200 text-gray-700' | 40 | : 'border-gray-200 text-gray-700' |
| 40 | - ]" | 41 | + ]"> |
| 41 | - > | ||
| 42 | {{ option.label }} | 42 | {{ option.label }} |
| 43 | </view> | 43 | </view> |
| 44 | </view> | 44 | </view> |
| ... | @@ -51,37 +51,48 @@ | ... | @@ -51,37 +51,48 @@ |
| 51 | </view> | 51 | </view> |
| 52 | <!-- Rewards list --> | 52 | <!-- Rewards list --> |
| 53 | <view class="space-y-4"> | 53 | <view class="space-y-4"> |
| 54 | - <view v-for="reward in sortedRewardItems" :key="reward.id" class="flex items-center border-b border-gray-100 pb-4"> | 54 | + <view v-for="reward in sortedRewardItems" :key="reward.id" |
| 55 | - <view class="w-12 h-12 mr-4 flex-shrink-0"> | 55 | + class="bg-white rounded-xl p-4 flex items-center shadow-[0_2px_8px_rgba(0,0,0,0.08)]"> |
| 56 | - <image :src="reward.logo" :alt="reward.title" class="w-full h-full object-contain" /> | 56 | + <image :src="reward.logo" class="w-16 h-16 rounded-lg mr-4 flex-shrink-0" mode="aspectFill" /> |
| 57 | - </view> | 57 | + <view class="flex-1 min-w-0"> |
| 58 | - <view class="flex-1"> | 58 | + <view class="font-medium text-base">{{ reward.title }}</view> |
| 59 | - <h4 class="font-medium">{{ reward.title }}</h4> | 59 | + <view class="text-gray-500 text-sm mt-1">{{ reward.merchant }}</view> |
| 60 | - <p class="text-gray-500 text-sm">{{ reward.points }}积分</p> | ||
| 61 | </view> | 60 | </view> |
| 62 | - <view class="px-4 py-1 bg-blue-500 text-white rounded-full text-sm" @click="goToRewardDetail(reward)"> | 61 | + <view class="ml-4 px-4 py-2 bg-blue-500 text-white rounded-lg text-sm flex-shrink-0" |
| 63 | - 兑换 | 62 | + @click="goToRewardDetail(reward)"> |
| 63 | + {{ reward.points }}分兑换 | ||
| 64 | </view> | 64 | </view> |
| 65 | </view> | 65 | </view> |
| 66 | </view> | 66 | </view> |
| 67 | </view> | 67 | </view> |
| 68 | </view> | 68 | </view> |
| 69 | - <BottomNav /> | 69 | + <!-- <BottomNav /> --> |
| 70 | </view> | 70 | </view> |
| 71 | </template> | 71 | </template> |
| 72 | 72 | ||
| 73 | <script setup> | 73 | <script setup> |
| 74 | import { ref, computed } from 'vue'; | 74 | import { ref, computed } from 'vue'; |
| 75 | import Taro from '@tarojs/taro'; | 75 | import Taro from '@tarojs/taro'; |
| 76 | -import { ScreenLittle } from '@nutui/icons-vue-taro'; | 76 | +import { ScreenLittle, Search2 } from '@nutui/icons-vue-taro'; |
| 77 | import AppHeader from '../../components/AppHeader.vue'; | 77 | import AppHeader from '../../components/AppHeader.vue'; |
| 78 | import BottomNav from '../../components/BottomNav.vue'; | 78 | import BottomNav from '../../components/BottomNav.vue'; |
| 79 | 79 | ||
| 80 | +const searchQuery = ref(''); | ||
| 80 | const selectedPoints = ref(null); | 81 | const selectedPoints = ref(null); |
| 81 | const sortOrder = ref('desc'); // 'asc' or 'desc' | 82 | const sortOrder = ref('desc'); // 'asc' or 'desc' |
| 82 | 83 | ||
| 83 | const sortedRewardItems = computed(() => { | 84 | const sortedRewardItems = computed(() => { |
| 84 | - return [...rewardItems.value].sort((a, b) => { | 85 | + let items = [...rewardItems.value]; |
| 86 | + | ||
| 87 | + // Filter by search query | ||
| 88 | + if (searchQuery.value) { | ||
| 89 | + items = items.filter(item => | ||
| 90 | + item.merchant.toLowerCase().includes(searchQuery.value.toLowerCase()) | ||
| 91 | + ); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + // Sort items | ||
| 95 | + return items.sort((a, b) => { | ||
| 85 | if (sortOrder.value === 'asc') { | 96 | if (sortOrder.value === 'asc') { |
| 86 | return a.points - b.points; | 97 | return a.points - b.points; |
| 87 | } else { | 98 | } else { |
| ... | @@ -97,33 +108,38 @@ const toggleSortOrder = () => { | ... | @@ -97,33 +108,38 @@ const toggleSortOrder = () => { |
| 97 | const rewardItems = ref([ | 108 | const rewardItems = ref([ |
| 98 | { | 109 | { |
| 99 | id: 1, | 110 | id: 1, |
| 100 | - title: '星巴克中杯咖啡兑换券', | 111 | + logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=LFX&font=roboto', |
| 101 | - points: 1000, | 112 | + title: '杏花楼集团85折券', |
| 102 | - logo: 'https://upload.wikimedia.org/wikipedia/en/thumb/d/d3/Starbucks_Corporation_Logo_2011.svg/1200px-Starbucks_Corporation_Logo_2011.svg.png' | 113 | + merchant: '杏花楼集团', |
| 114 | + points: 10 | ||
| 103 | }, | 115 | }, |
| 104 | { | 116 | { |
| 105 | id: 2, | 117 | id: 2, |
| 106 | - title: '老凤祥20元抵用券', | 118 | + logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=LFX&font=roboto', |
| 107 | - points: 600, | 119 | + title: '吴良材眼镜店85折券', |
| 108 | - logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=LFX&font=roboto' | 120 | + merchant: '吴良材眼镜店', |
| 121 | + points: 10 | ||
| 109 | }, | 122 | }, |
| 110 | { | 123 | { |
| 111 | id: 3, | 124 | id: 3, |
| 112 | - title: '杏花楼集团8折券', | 125 | + logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=LFX&font=roboto', |
| 113 | - points: 500, | 126 | + title: '老凤祥银楼20元抵用券', |
| 114 | - logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=XHL&font=roboto' | 127 | + merchant: '老凤祥银楼', |
| 128 | + points: 1000 | ||
| 115 | }, | 129 | }, |
| 116 | { | 130 | { |
| 117 | id: 4, | 131 | id: 4, |
| 118 | - title: '肯德基汉堡套餐券', | 132 | + logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=LFX&font=roboto', |
| 119 | - points: 1000, | 133 | + title: '沈大成双酿团2元抵用券', |
| 120 | - logo: 'https://upload.wikimedia.org/wikipedia/en/thumb/b/bf/KFC_logo.svg/1200px-KFC_logo.svg.png' | 134 | + merchant: '沈大成双酿团', |
| 135 | + points: 100 | ||
| 121 | }, | 136 | }, |
| 122 | { | 137 | { |
| 123 | id: 5, | 138 | id: 5, |
| 124 | - title: '沈大成双黄团3元抵用券', | 139 | + logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=LFX&font=roboto', |
| 125 | - points: 300, | 140 | + title: '这是一个非常非常非常非常非常长的标题用于测试换行效果', |
| 126 | - logo: 'https://placehold.co/400x400/e2f3ff/0369a1?text=SDC&font=roboto' | 141 | + merchant: '一个名字很长的商家', |
| 142 | + points: 500 | ||
| 127 | } | 143 | } |
| 128 | ]); | 144 | ]); |
| 129 | 145 | ... | ... |
| 1 | +<!-- | ||
| 2 | + * @Date: 2025-08-27 17:50:27 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-08-28 00:31:28 | ||
| 5 | + * @FilePath: /lls_program/src/pages/UserAgreement/index.vue | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | +--> | ||
| 1 | <template> | 8 | <template> |
| 2 | <view class="min-h-screen flex flex-col bg-white"> | 9 | <view class="min-h-screen flex flex-col bg-white"> |
| 3 | - <AppHeader title="用户协议" /> | 10 | + <!-- <AppHeader title="用户协议" /> --> |
| 4 | <view class="flex-1 px-4 py-6 pb-20"> | 11 | <view class="flex-1 px-4 py-6 pb-20"> |
| 5 | <view class="prose prose-sm max-w-none"> | 12 | <view class="prose prose-sm max-w-none"> |
| 6 | <h2 class="text-xl font-bold mb-4">老来赛用户协议</h2> | 13 | <h2 class="text-xl font-bold mb-4">老来赛用户协议</h2> |
| ... | @@ -87,7 +94,7 @@ | ... | @@ -87,7 +94,7 @@ |
| 87 | <p class="text-gray-700 mt-8">本协议最终解释权归老来赛所有。</p> | 94 | <p class="text-gray-700 mt-8">本协议最终解释权归老来赛所有。</p> |
| 88 | </view> | 95 | </view> |
| 89 | </view> | 96 | </view> |
| 90 | - <BottomNav /> | 97 | + <!-- <BottomNav /> --> |
| 91 | </view> | 98 | </view> |
| 92 | </template> | 99 | </template> |
| 93 | 100 | ... | ... |
-
Please register or login to post a comment