Browse Source

gg

master
fzzj 10 months ago
parent
commit
e108df3e67

+ 31
- 3
RuoYi-App/App.vue View File

@@ -7,13 +7,32 @@
7 7
   import config from './config'
8 8
   import { getToken } from '@/utils/auth'
9 9
 import { useThemeStore } from '@/stores/theme'
10
-import { onLaunch } from '@dcloudio/uni-app'
10
+import { onLaunch, onShow } from '@dcloudio/uni-app'
11
+//import { useThemeStore } from '@/stores/theme'
12
+import { useUserStore } from '@/stores/user'
13
+import { useVipStore } from '@/stores/vip'
14
+
11 15
 // 初始化主题
12 16
 onLaunch(() => {
13 17
   const themeStore = useThemeStore()
14 18
   themeStore.initTheme()
15 19
 })
16
-
20
+// 每次启动时加载用户和VIP状态
21
+onShow(async () => {
22
+  const userStore = useUserStore()
23
+  const vipStore = useVipStore()
24
+  
25
+  // 检查登录状态
26
+  if (uni.getStorageSync('token')) {
27
+    // 加载用户信息
28
+    await userStore.initUser()
29
+    
30
+    // 加载VIP状态
31
+    if (userStore.userId) {
32
+      await vipStore.loadVipStatus(userStore.userId)
33
+    }
34
+  }
35
+})
17 36
   export default {
18 37
     onLaunch: function() {
19 38
       this.initApp()
@@ -58,7 +77,7 @@ onLaunch(() => {
58 77
   --bg-color: #f8f9fa;
59 78
   --text-color: #333;
60 79
   --card-bg: #ffffff;
61
-  
80
+  --header-bg: #ffffff;
62 81
   /* 确保所有页面继承主题 */
63 82
   background-color: var(--bg-color);
64 83
   color: var(--text-color);
@@ -70,6 +89,7 @@ onLaunch(() => {
70 89
   --bg-color: #e6f7ff;
71 90
   --text-color: #1a3353;
72 91
   --card-bg: #d0e8ff;
92
+  --header-bg: #2a5caa;
73 93
 }
74 94
 
75 95
 /* 深色模式主题变量覆盖 */
@@ -78,6 +98,7 @@ onLaunch(() => {
78 98
   --bg-color: #1a1a1a;
79 99
   --text-color: #e6e6e6;
80 100
   --card-bg: #2a2a2a;
101
+  --header-bg: #1a1a1a;
81 102
 }
82 103
 
83 104
 /* 公共组件样式 */
@@ -141,4 +162,11 @@ onLaunch(() => {
141 162
   padding: 10px 20px;
142 163
   font-weight: bold;
143 164
 }
165
+/* 全局样式 */
166
+page {
167
+  background-color: var(--bg-color);
168
+  color: var(--text-color);
169
+  font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
170
+}
171
+
144 172
 </style>

+ 33
- 1
RuoYi-App/components/NovelReader.vue View File

@@ -96,6 +96,10 @@ import { useUserStore } from '@/stores/user'
96 96
 import { useThemeStore } from '@/stores/theme'
97 97
 import { useReadingProgress } from '@/composables/useReadingProgress'
98 98
 import { cleanNovelContent, paginateContent } from '@/utils/contentUtils'
99
+// 导入清洗工具
100
+import { contentCleaner } from '@/utils/contentCleaner'
101
+//import { useAdManager } from '@/utils/adManager'
102
+import { useVipStore } from '@/stores/vip'
99 103
 
100 104
 const props = defineProps({
101 105
   chapterId: {
@@ -114,9 +118,12 @@ const props = defineProps({
114 118
 
115 119
 const emit = defineEmits(['back', 'prev', 'next', 'show-chapters'])
116 120
 
121
+const vipStore = useVipStore()
122
+const { showRewardAd, showFeedAd } = useAdManager()
123
+
117 124
 const userStore = useUserStore()
118 125
 const themeStore = useThemeStore()
119
-const { showRewardAd } = useAdManager()
126
+//const { showRewardAd } = useAdManager()
120 127
 const { saveProgress, loadProgress } = useReadingProgress()
121 128
 
122 129
 // 阅读状态
@@ -183,12 +190,16 @@ const onPageChange = (e) => {
183 190
   currentPage.value = e.detail.current
184 191
   saveReadingPosition()
185 192
   
193
+  // VIP用户跳过广告
194
+  if (vipStore.hasPrivilege('免广告')) return
195
+  
186 196
   // 每5页触发广告
187 197
   if (currentPage.value % 5 === 0) {
188 198
     showRewardAd(props.chapterId)
189 199
   }
190 200
 }
191 201
 
202
+
192 203
 // 滚动事件处理
193 204
 const onScroll = (e) => {
194 205
   lastScrollPosition.value = e.detail.scrollTop
@@ -270,6 +281,11 @@ const showChapterList = () => {
270 281
 
271 282
 // 初始化
272 283
 onMounted(() => {
284
+  // VIP用户不显示广告
285
+  if (vipStore.hasPrivilege('免广告')) return 
286
+    // 显示底部广告
287
+    feedAd.value = showFeedAd()
288
+	
273 289
   fetchChapterContent()
274 290
   
275 291
   // 设置广告单元ID
@@ -288,6 +304,22 @@ onMounted(() => {
288 304
     showBottomAd.value = !isVip
289 305
   })
290 306
 })
307
+
308
+
309
+
310
+// 在获取章节内容后清洗
311
+const fetchChapterContent = async () => {
312
+  const res = await uni.request({
313
+    url: `https://php-backend.aiyadianzi.ltd/chapter/${props.chapterId}`,
314
+    method: 'GET'
315
+  })
316
+  
317
+  // 清洗内容
318
+  chapterContent.value = contentCleaner.clean(res.data.content)
319
+  
320
+  // 分页处理
321
+  paginatedContent.value = paginateContent(chapterContent.value)
322
+}
291 323
 </script>
292 324
 
293 325
 <style scoped>

+ 224
- 106
RuoYi-App/pages/welfare/index.vue View File

@@ -1,36 +1,71 @@
1 1
 <template>
2
-  <view class="welfare-page">
2
+  <scroll-view scroll-y class="welfare-page">
3
+    <!-- 用户信息栏 -->
4
+    <view class="user-header">
5
+      <image :src="userAvatar" class="avatar" />
6
+      <view class="info">
7
+        <text class="name">{{ userName }}</text>
8
+        <text class="coins">金币: {{ coins }}</text>
9
+      </view>
10
+      <view class="vip-tag" v-if="isVIP">
11
+        <uni-icons type="crown" size="16" color="#ffc53d"></uni-icons>
12
+        <text>VIP会员</text>
13
+      </view>
14
+    </view>
3 15
     <!-- 签到区域 -->
4 16
     <view class="signin-section">
5
-      <text class="title">每日签到</text>
6
-      <view class="signin-grid">
17
+      <view class="sign-header">
18
+        <text class="title">每日签到</text>
19
+        <text class="subtitle">已连续签到 {{ signedDays }} 天</text>
20
+      </view>
21
+      
22
+      <view class="sign-calendar">
7 23
         <view 
8
-          v-for="(day, index) in 7" 
9
-          :key="day" 
10
-          :class="['day', { signed: index < signedDays, today: index === currentDay }]"
11
-          @click="signIn(index)"
24
+          v-for="day in 7" 
25
+          :key="day"
26
+          :class="[
27
+            'sign-day',
28
+            { 
29
+              'signed': day <= signedDays,
30
+              'today': day === currentDay && !todaySigned,
31
+              'active': day === currentDay && todaySigned
32
+            }
33
+          ]"
34
+          @click="handleSign(day)"
12 35
         >
13
-          <text>第{{ day }}天</text>
14
-          <text class="reward">+{{ calculateReward(index) }}金币</text>
36
+          <text class="day-label">第{{ day }}天</text>
37
+          <text class="reward">+{{ getReward(day) }}金币</text>
15 38
         </view>
16 39
       </view>
17 40
       <text class="tip">已连续签到 {{ signedDays }} 天</text>
18 41
     </view>
19 42
     
43
+    <!-- 合作任务 -->
44
+    <PartnerTask />
45
+    
20 46
     <!-- 任务中心 -->
21
-    <view class="tasks-section">
22
-      <text class="title">每日任务</text>
47
+    <view class="task-center">
48
+      <view class="section-header">
49
+        <text class="title">每日任务</text>
50
+        <text class="more">查看更多</text>
51
+      </view>
52
+      
23 53
       <view class="task-list">
24
-        <view v-for="task in dailyTasks" :key="task.id" class="task-item">
54
+        <view 
55
+          v-for="task in dailyTasks"
56
+          :key="task.id"
57
+          class="task-item"
58
+        >
25 59
           <view class="task-info">
26
-            <text class="task-name">{{ task.name }}</text>
27
-            <text class="task-reward">+{{ task.reward }}金币</text>
60
+            <uni-icons :type="task.icon" size="20" :color="task.completed ? '#52c41a' : '#666'"></uni-icons>
61
+            <text class="name">{{ task.name }}</text>
28 62
           </view>
29 63
           <button 
30
-            :class="['task-btn', { disabled: task.completed }]"
64
+            class="action-btn"
65
+            :class="{ completed: task.completed }"
31 66
             @click="completeTask(task)"
32 67
           >
33
-            {{ task.completed ? '已完成' : '去完成' }}
68
+            {{ task.completed ? '已完成' : '+'+task.reward+'金币' }}
34 69
           </button>
35 70
         </view>
36 71
       </view>
@@ -51,23 +86,39 @@
51 86
         </view>
52 87
       </view>
53 88
     </view>
54
-  </view>
89
+  </scroll-view>
55 90
 </template>
56 91
 
57 92
 <script setup>
58
-import { ref } from 'vue'
93
+import { ref, onMounted } from 'vue'
94
+import { useSignSystem } from '@/utils/signUtils'
95
+import PartnerTask from '@/components/PartnerTask.vue'
96
+import { useUserStore } from '@/stores/user'
97
+
98
+const userStore = useUserStore()
99
+const { signedDays, todaySigned, signRewards, loadSignStatus, doSign } = useSignSystem()
59 100
 
60
-const signedDays = ref(3)
61
-const currentDay = ref(3) // 0-6 表示周一到周日
101
+// 用户数据
102
+const userName = ref('哎呀用户')
103
+const userAvatar = ref('/static/avatar/default.png')
104
+const coins = ref(350)
105
+const isVIP = ref(true)
62 106
 
63 107
 // 每日任务
64 108
 const dailyTasks = ref([
65
-  { id: 1, name: '阅读30分钟', reward: 50, completed: false },
66
-  { id: 2, name: '分享给好友', reward: 30, completed: true },
67
-  { id: 3, name: '评论本章节', reward: 20, completed: false },
68
-  { id: 4, name: '完善个人资料', reward: 10, completed: false }
109
+  { id: 1, name: '阅读30分钟', icon: 'eye', reward: 50, completed: false },
110
+  { id: 2, name: '分享给好友', icon: 'redo', reward: 30, completed: true },
111
+  { id: 3, name: '评论本章节', icon: 'chat', reward: 20, completed: false },
112
+  { id: 4, name: '完善个人资料', icon: 'person', reward: 10, completed: false }
69 113
 ])
114
+// 当前星期几(0-6)
115
+const currentDay = ref(new Date().getDay() + 1)
70 116
 
117
+// 获取每日奖励
118
+const getReward = (day) => {
119
+  const reward = signRewards.value.find(r => r.day === day)
120
+  return reward ? reward.reward : 10 + day * 5
121
+}
71 122
 // 合作平台
72 123
 const partners = ref([
73 124
   { id: 1, name: '百度地图', icon: '/static/partners/baidu.png', url: 'https://map.baidu.com' },
@@ -76,29 +127,43 @@ const partners = ref([
76 127
   { id: 4, name: '饿了么', icon: '/static/partners/eleme.png', url: 'https://www.ele.me' }
77 128
 ])
78 129
 
79
-// 签到逻辑
80
-const signIn = (dayIndex) => {
81
-  if (dayIndex !== currentDay.value) return
82
-  if (signedDays.value > currentDay.value) return
130
+// 处理签到
131
+const handleSign = async (day) => {
132
+  if (day !== currentDay.value) {
133
+    uni.showToast({ title: '请先完成今日签到', icon: 'none' })
134
+    return
135
+  }
83 136
   
84
-  signedDays.value++
85
-  uni.showToast({ title: `签到成功!获得${calculateReward(dayIndex)}金币` })
86
-}
87
-
88
-// 计算签到奖励
89
-const calculateReward = (day) => {
90
-  const baseReward = 10
91
-  return baseReward * (day + 1)
137
+  if (todaySigned.value) {
138
+    uni.showToast({ title: '今日已签到', icon: 'none' })
139
+    return
140
+  }
141
+  
142
+  const reward = await doSign()
143
+  if (reward) {
144
+    coins.value += reward
145
+  }
92 146
 }
93 147
 
94 148
 // 完成任务
95 149
 const completeTask = (task) => {
96 150
   if (task.completed) return
97
-  
98 151
   task.completed = true
99
-  uni.showToast({ title: `任务完成!获得${task.reward}金币` })
152
+  coins.value += task.reward
153
+  uni.showToast({ title: `任务完成!获得${task.reward}金币`, icon: 'success' })
100 154
 }
101
-
155
+// 初始化
156
+onMounted(async () => {
157
+  await loadSignStatus()
158
+  
159
+  // 加载用户信息
160
+  if (userStore.userInfo) {
161
+    userName.value = userStore.userInfo.nickname || '哎呀用户'
162
+    userAvatar.value = userStore.userInfo.avatar || '/static/avatar/default.png'
163
+    coins.value = userStore.userInfo.coins || 0
164
+    isVIP.value = userStore.isVIP
165
+  }
166
+})
102 167
 // 跳转合作平台
103 168
 const goPartnerTask = (partner) => {
104 169
   uni.navigateTo({
@@ -109,117 +174,170 @@ const goPartnerTask = (partner) => {
109 174
 
110 175
 <style scoped>
111 176
 .welfare-page {
177
+  height: 100vh;
178
+  background-color: var(--bg-color);
112 179
   padding: 20px;
180
+  box-sizing: border-box;
113 181
 }
114 182
 
115
-.title {
183
+.user-header {
184
+  display: flex;
185
+  align-items: center;
186
+  margin-bottom: 25px;
187
+}
188
+
189
+.avatar {
190
+  width: 60px;
191
+  height: 60px;
192
+  border-radius: 50%;
193
+  margin-right: 15px;
194
+}
195
+
196
+.info {
197
+  flex: 1;
198
+}
199
+
200
+.name {
116 201
   font-size: 18px;
117 202
   font-weight: bold;
118
-  margin-bottom: 15px;
119 203
   display: block;
120 204
 }
121 205
 
122
-.signin-section {
123
-  margin-bottom: 25px;
206
+.coins {
207
+  font-size: 14px;
208
+  color: #faad14;
209
+  display: block;
210
+  margin-top: 5px;
211
+}
212
+
213
+.vip-tag {
214
+  background: linear-gradient(135deg, #ffd666 0%, #ffc53d 100%);
215
+  color: #1a3353;
216
+  padding: 4px 10px;
217
+  border-radius: 15px;
218
+  font-size: 12px;
219
+  display: flex;
220
+  align-items: center;
221
+}
222
+
223
+.sign-card {
224
+  background-color: var(--card-bg);
225
+  border-radius: 16px;
226
+  padding: 20px;
227
+  margin-bottom: 20px;
228
+}
229
+
230
+.sign-header {
231
+  display: flex;
232
+  justify-content: space-between;
233
+  align-items: center;
234
+  margin-bottom: 15px;
235
+}
236
+
237
+.title {
238
+  font-size: 18px;
239
+  font-weight: bold;
124 240
 }
125 241
 
126
-.signin-grid {
242
+.subtitle {
243
+  font-size: 14px;
244
+  color: #666;
245
+}
246
+
247
+.sign-calendar {
127 248
   display: grid;
128 249
   grid-template-columns: repeat(7, 1fr);
129
-  gap: 5px;
250
+  gap: 8px;
130 251
 }
131 252
 
132
-.day {
253
+.sign-day {
133 254
   border: 1px solid #eee;
134 255
   border-radius: 8px;
135
-  padding: 8px 5px;
256
+  padding: 10px 5px;
136 257
   text-align: center;
137
-  font-size: 12px;
138
-  
139
-  &.signed {
140
-    background-color: #e6fffb;
141
-    border-color: #36cfc9;
142
-  }
143
-  
144
-  &.today {
145
-    background-color: #fff7e6;
146
-    border-color: #ffc53d;
147
-  }
258
+  background-color: #fafafa;
148 259
 }
149 260
 
150
-.reward {
151
-  display: block;
152
-  color: #faad14;
153
-  font-weight: bold;
261
+.sign-day.signed {
262
+  background-color: #e6fffb;
263
+  border-color: #36cfc9;
264
+}
265
+
266
+.sign-day.today {
267
+  background-color: #fff7e6;
268
+  border-color: #ffc53d;
154 269
 }
155 270
 
156
-.tip {
271
+.sign-day.active {
272
+  background-color: #fff1b8;
273
+  border-color: #ffc53d;
274
+}
275
+
276
+.day-label {
277
+  font-size: 13px;
157 278
   display: block;
158
-  margin-top: 10px;
159
-  color: #8c8c8c;
279
+}
280
+
281
+.reward {
160 282
   font-size: 12px;
283
+  color: #faad14;
284
+  font-weight: bold;
285
+  display: block;
286
+  margin-top: 3px;
161 287
 }
162 288
 
163
-.task-list {
164
-  border-top: 1px solid #f0f0f0;
289
+.task-center {
290
+  background-color: var(--card-bg);
291
+  border-radius: 16px;
292
+  padding: 20px;
165 293
 }
166 294
 
167
-.task-item {
295
+.section-header {
168 296
   display: flex;
169 297
   justify-content: space-between;
170 298
   align-items: center;
171
-  padding: 12px 0;
172
-  border-bottom: 1px solid #f0f0f0;
173
-}
174
-
175
-.task-info {
176
-  flex: 1;
299
+  margin-bottom: 15px;
177 300
 }
178 301
 
179
-.task-name {
180
-  display: block;
302
+.more {
181 303
   font-size: 14px;
304
+  color: var(--primary-color);
182 305
 }
183 306
 
184
-.task-reward {
185
-  font-size: 12px;
186
-  color: #faad14;
187
-}
188
-
189
-.task-btn {
190
-  margin: 0;
191
-  padding: 0 10px;
192
-  height: 28px;
193
-  line-height: 28px;
194
-  font-size: 12px;
195
-  background-color: #1890ff;
196
-  color: white;
197
-  
198
-  &.disabled {
199
-    background-color: #bfbfbf;
200
-  }
307
+.task-list {
308
+  border-top: 1px solid #f0f0f0;
201 309
 }
202 310
 
203
-.partner-grid {
204
-  display: grid;
205
-  grid-template-columns: repeat(4, 1fr);
206
-  gap: 15px;
311
+.task-item {
312
+  display: flex;
313
+  justify-content: space-between;
314
+  align-items: center;
315
+  padding: 15px 0;
316
+  border-bottom: 1px solid #f0f0f0;
207 317
 }
208 318
 
209
-.partner-item {
319
+.task-info {
210 320
   display: flex;
211
-  flex-direction: column;
212 321
   align-items: center;
213 322
 }
214 323
 
215
-.partner-icon {
216
-  width: 50px;
217
-  height: 50px;
218
-  border-radius: 10px;
219
-  margin-bottom: 5px;
324
+.name {
325
+  font-size: 15px;
326
+  margin-left: 10px;
220 327
 }
221 328
 
222
-.partner-name {
223
-  font-size: 12px;
329
+.action-btn {
330
+  background-color: var(--primary-color);
331
+  color: white;
332
+  border: none;
333
+  border-radius: 16px;
334
+  height: 30px;
335
+  line-height: 30px;
336
+  padding: 0 12px;
337
+  font-size: 13px;
338
+}
339
+
340
+.action-btn.completed {
341
+  background-color: #bfbfbf;
224 342
 }
225 343
 </style>

+ 1
- 0
RuoYi-App/utils/adManager.js View File

@@ -1,5 +1,6 @@
1 1
 import { ref } from 'vue'
2 2
 import { useUserStore } from '@/stores/user'
3
+import { useVipStore } from '@/stores/vip'
3 4
 
4 5
 // 广告平台配置
5 6
 const AD_PLATFORMS = {

+ 95
- 0
RuoYi-App/utils/contentCleaner.js View File

@@ -0,0 +1,95 @@
1
+/**
2
+ * 小说内容清洗工具
3
+ * 移除原始网站信息、广告、干扰元素
4
+ */
5
+export const contentCleaner = {
6
+  // 主清洗函数
7
+  clean(content) {
8
+    if (!content) return ''
9
+    
10
+    // 执行清洗流水线
11
+    return this.pipeline(content, [
12
+      this.removeWebsiteInfo,
13
+      this.removeAds,
14
+      this.removeHtmlTags,
15
+      this.normalizeSpaces,
16
+      this.fixParagraphs,
17
+      this.removeSpecialChars
18
+    ])
19
+  },
20
+  
21
+  // 清洗流水线
22
+  pipeline(content, processors) {
23
+    return processors.reduce((result, processor) => {
24
+      return processor(result)
25
+    }, content)
26
+  },
27
+  
28
+  // 移除网站信息
29
+  removeWebsiteInfo(content) {
30
+    const patterns = [
31
+      /最新网址[::]?\s*[a-z0-9.-]+/gi,
32
+      /www\.[a-z0-9]+\.[a-z]{2,}/gi,
33
+      /请?收藏本站:https?:\/\/[^\s]+/gi,
34
+      /请?记住本站域名:\w+\.\w+/gi,
35
+      /(电脑版|手机版)?访问:m?\.\w+\.\w+/gi,
36
+      /【[^】]{0,10}更新快[^】]{0,10}】/g
37
+    ]
38
+    
39
+    return patterns.reduce((result, pattern) => {
40
+      return result.replace(pattern, '')
41
+    }, content)
42
+  },
43
+  
44
+  // 移除广告文本
45
+  removeAds(content) {
46
+    const adKeywords = [
47
+      '广告', '推广', '推荐票', '月票', 'QQ群', '微信号',
48
+      'VIP章节', '正版订阅', '盗版', '笔趣阁', '天才一秒记住',
49
+      '本站地址', '手机用户'
50
+    ]
51
+    
52
+    const adPattern = new RegExp(
53
+      `[【((]?(${adKeywords.join('|')})[^))】]*[))】]?`,
54
+      'gi'
55
+    )
56
+    
57
+    return content.replace(adPattern, '')
58
+  },
59
+  
60
+  // 移除HTML标签
61
+  removeHtmlTags(content) {
62
+    return content
63
+      .replace(/<br\s*\/?>/g, '\n') // 保留换行
64
+      .replace(/&nbsp;|&#160;/g, ' ') // 替换空格实体
65
+      .replace(/<[^>]+>/g, '') // 移除所有HTML标签
66
+  },
67
+  
68
+  // 标准化空格
69
+  normalizeSpaces(content) {
70
+    return content
71
+      .replace(/\s+/g, ' ') // 合并连续空格
72
+      .replace(/^\s+|\s+$/g, '') // 移除首尾空格
73
+  },
74
+  
75
+  // 修复段落格式
76
+  fixParagraphs(content) {
77
+    return content
78
+      .split('\n')
79
+      .map(line => line.trim())
80
+      .filter(line => line.length > 0)
81
+      .join('\n\n')
82
+  },
83
+  
84
+  // 移除特殊字符
85
+  removeSpecialChars(content) {
86
+    const specialChars = [
87
+      '\u200b', // 零宽空格
88
+      '\ufeff', // BOM头
89
+      '\u202a', '\u202b', '\u202c', '\u202d', '\u202e' // 方向控制符
90
+    ]
91
+    
92
+    const pattern = new RegExp(`[${specialChars.join('')}]`, 'g')
93
+    return content.replace(pattern, '')
94
+  }
95
+}

+ 74
- 0
RuoYi-App/utils/signUtils.js View File

@@ -0,0 +1,74 @@
1
+import { ref } from 'vue'
2
+import { request } from '@/utils/request'
3
+
4
+// 签到状态管理
5
+export function useSignSystem() {
6
+  const signedDays = ref(0)
7
+  const todaySigned = ref(false)
8
+  const signRewards = ref([])
9
+  
10
+  // 加载签到状态
11
+  const loadSignStatus = async () => {
12
+    try {
13
+      const res = await request({
14
+        url: '/sign/status',
15
+        method: 'GET',
16
+        headers: { 'Authorization': `Bearer ${uni.getStorageSync('token')}` }
17
+      })
18
+      
19
+      signedDays.value = res.data.signedDays || 0
20
+      todaySigned.value = res.data.todaySigned || false
21
+      signRewards.value = res.data.rewards || generateDefaultRewards()
22
+      return true
23
+    } catch (err) {
24
+      console.error('签到状态加载失败', err)
25
+      return false
26
+    }
27
+  }
28
+  
29
+  // 执行签到
30
+  const doSign = async () => {
31
+    if (todaySigned.value) return
32
+    
33
+    try {
34
+      uni.showLoading({ title: '签到中...' })
35
+      const res = await request({
36
+        url: '/sign/do',
37
+        method: 'POST',
38
+        headers: { 'Authorization': `Bearer ${uni.getStorageSync('token')}` }
39
+      })
40
+      
41
+      if (res.data.success) {
42
+        signedDays.value++
43
+        todaySigned.value = true
44
+        uni.showToast({ title: `签到成功!获得${res.data.reward}金币`, icon: 'success' })
45
+        return res.data.reward
46
+      }
47
+    } catch (err) {
48
+      uni.showToast({ title: '签到失败', icon: 'none' })
49
+    } finally {
50
+      uni.hideLoading()
51
+    }
52
+  }
53
+  
54
+  // 生成默认奖励规则
55
+  const generateDefaultRewards = () => {
56
+    return [
57
+      { day: 1, reward: 10 },
58
+      { day: 2, reward: 15 },
59
+      { day: 3, reward: 20 },
60
+      { day: 4, reward: 25 },
61
+      { day: 5, reward: 30 },
62
+      { day: 6, reward: 40 },
63
+      { day: 7, reward: 50, extra: '双倍金币卡' }
64
+    ]
65
+  }
66
+  
67
+  return {
68
+    signedDays,
69
+    todaySigned,
70
+    signRewards,
71
+    loadSignStatus,
72
+    doSign
73
+  }
74
+}

Loading…
Cancel
Save