Bladeren bron

tj

master
fzzj 10 maanden geleden
bovenliggende
commit
b5d44b3b19

+ 16
- 0
RuoYi-App/.env Bestand weergeven

@@ -0,0 +1,16 @@
1
+# 基础配置
2
+VUE_APP_NAME=哎呀电子小说
3
+VUE_APP_VERSION=1.0.0
4
+
5
+# 后端API地址
6
+VUE_APP_API_BASE=https://api.aiyadianzi.ltd
7
+VUE_APP_PHP_BASE=https://php-backend.aiyadianzi.ltd
8
+
9
+# 广告配置
10
+VUE_APP_WECHAT_AD_UNIT=wechat_ad123
11
+VUE_APP_DOUYIN_AD_UNIT=douyin_ad456
12
+VUE_APP_H5_AD_PID=h5_pid789
13
+
14
+# 多平台标识
15
+# 编译时会自动替换
16
+VUE_APP_PLATFORM=MP-WEIXIN

+ 21
- 0
RuoYi-App/App.vue Bestand weergeven

@@ -1,6 +1,18 @@
1
+<template>
2
+  <view :class="['app-container', `theme-${themeStore.currentTheme}`]">
3
+    <router-view />
4
+  </view>
5
+</template>
1 6
 <script>
2 7
   import config from './config'
3 8
   import { getToken } from '@/utils/auth'
9
+import { useThemeStore } from '@/stores/theme'
10
+import { onLaunch } from '@dcloudio/uni-app'
11
+// 初始化主题
12
+onLaunch(() => {
13
+  const themeStore = useThemeStore()
14
+  themeStore.initTheme()
15
+})
4 16
 
5 17
   export default {
6 18
     onLaunch: function() {
@@ -30,4 +42,13 @@
30 42
 
31 43
 <style lang="scss">
32 44
   @import '@/static/scss/index.scss'
45
+@import '@/styles/themes/default.scss';
46
+@import '@/styles/themes/aydz-blue.scss';
47
+
48
+.app-container {
49
+  font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
50
+  height: 100vh;
51
+  background-color: var(--bg-color);
52
+  color: var(--text-color);
53
+}
33 54
 </style>

+ 57
- 0
RuoYi-App/README.md Bestand weergeven

@@ -48,4 +48,61 @@ RuoYi App 移动解决方案,采用uniapp框架,一份代码多终端适配
48 48
         <td><img src="https://oscimg.oschina.net/oscnet/up-d77995cc00687cedd00d5ac7d68a07ea276.png"/></td>
49 49
 		<td><img src="https://oscimg.oschina.net/oscnet/up-fa8f5ab20becf59b4b38c1b92a9989e7109.png"/></td>
50 50
     </tr>
51
+src/
52
+├── api/                  # API请求封装
53
+│   ├── auth.js           # 认证接口
54
+│   ├── novel.js          # 小说接口
55
+│   └── vip.js            # VIP接口
56
+│
57
+├── components/           # 通用组件
58
+│   ├── NovelReader.vue   # 阅读器组件
59
+│   ├── AdBanner.vue      # 广告组件
60
+│   └── ThemePicker.vue   # 主题选择器
61
+│
62
+├── pages/                # 页面组件
63
+│   ├── index/            # 首页
64
+│   ├── bookshelf/        # 书架
65
+│   ├── reader/           # 阅读页
66
+│   ├── welfare/          # 福利页
67
+│   └── my/               # 个人中心
68
+│
69
+├── stores/               # Pinia状态管理
70
+│   ├── theme.js          # 主题管理
71
+│   ├── user.js           # 用户管理
72
+│   └── novel.js          # 小说状态
73
+│
74
+├── styles/               # 样式文件
75
+│   ├── variables.scss    # 全局变量
76
+│   └── themes/           # 主题文件
77
+│       ├── default.scss
78
+│       └── aydz-blue.scss
79
+│
80
+├── utils/                # 工具函数
81
+│   ├── adManager.js      # 广告管理
82
+│   ├── contentParser.js  # 内容解析
83
+│   └── storage.js        # 存储封装
84
+│
85
+├── App.vue               # 应用入口
86
+├── main.js               # 主入口文件
87
+└── manifest.json         # 多端配置文件
88
+
89
+运行开发环境:
90
+# 微信小程序
91
+npm run dev:mp-weixin
92
+
93
+# 抖音小程序
94
+npm run dev:mp-toutiao
95
+
96
+# H5
97
+npm run serve
98
+
99
+生产环境构建:
100
+# 微信小程序
101
+npm run build:mp-weixin
102
+
103
+# 抖音小程序
104
+npm run build:mp-toutiao
105
+
106
+# H5
107
+npm run build
51 108
 </table>

+ 184
- 0
RuoYi-App/components/NovelReader.vue Bestand weergeven

@@ -0,0 +1,184 @@
1
+<template>
2
+  <view class="reader-container">
3
+    <!-- 阅读模式选择器 -->
4
+    <view class="mode-selector">
5
+      <button @click="readingMode = 'scroll'" :class="{ active: readingMode === 'scroll' }">
6
+        滚动模式
7
+      </button>
8
+      <button @click="readingMode = 'page'" :class="{ active: readingMode === 'page' }">
9
+        翻页模式
10
+      </button>
11
+    </view>
12
+    
13
+    <!-- 滚动阅读模式 -->
14
+    <scroll-view 
15
+      v-if="readingMode === 'scroll'" 
16
+      scroll-y 
17
+      class="scroll-reader"
18
+      @scrolltolower="loadNextChapter"
19
+    >
20
+      <rich-text :nodes="formatContent(chapterContent)" />
21
+    </scroll-view>
22
+    
23
+    <!-- 翻页阅读模式 -->
24
+    <swiper 
25
+      v-else 
26
+      :current="currentPage" 
27
+      circular 
28
+      class="page-reader"
29
+      @change="onPageChange"
30
+    >
31
+      <swiper-item v-for="(page, index) in paginatedContent" :key="index">
32
+        <rich-text :nodes="page" />
33
+      </swiper-item>
34
+    </swiper>
35
+    
36
+    <!-- 底部广告 -->
37
+    <view v-if="feedAd" class="bottom-ad">
38
+      <ad :unit-id="feedAd.unitId" ad-type="feed" />
39
+    </view>
40
+  </view>
41
+</template>
42
+
43
+<script setup>
44
+import { ref, computed, onMounted, watch } from 'vue'
45
+import { useAdManager } from '@/utils/adManager'
46
+import { useUserStore } from '@/stores/user'
47
+
48
+const props = defineProps({
49
+  chapterId: Number,
50
+  novelId: Number
51
+})
52
+
53
+const userStore = useUserStore()
54
+const { showRewardAd, showFeedAd } = useAdManager()
55
+
56
+// 阅读状态
57
+const readingMode = ref('scroll') // 'scroll' | 'page'
58
+const currentPage = ref(0)
59
+const chapterContent = ref('')
60
+const paginatedContent = ref([])
61
+const lastReadPosition = ref(0)
62
+const feedAd = ref(null)
63
+
64
+// 获取章节内容
65
+const fetchChapterContent = async () => {
66
+  const res = await uni.request({
67
+    url: `https://php-backend.aiyadianzi.ltd/chapter/${props.chapterId}`,
68
+    method: 'GET'
69
+  })
70
+  
71
+  // 清洗内容
72
+  chapterContent.value = cleanContent(res.data.content)
73
+  
74
+  // 分页处理
75
+  paginatedContent.value = paginateContent(chapterContent.value)
76
+  
77
+  // 恢复阅读位置
78
+  if (userStore.isLoggedIn) {
79
+    const position = await getReadingPosition()
80
+    currentPage.value = position.page
81
+    lastReadPosition.value = position.scrollTop
82
+  }
83
+}
84
+
85
+// 内容清洗(移除原始网站信息)
86
+const cleanContent = (content) => {
87
+  return content
88
+    .replace(/最新网址[::]?\s*[a-z0-9.-]+/gi, '')
89
+    .replace(/www\.[a-z0-9]+\.[a-z]{2,}/gi, '')
90
+}
91
+
92
+// 内容分页(每页800字符)
93
+const paginateContent = (content) => {
94
+  const pages = []
95
+  const pageSize = 800
96
+  let start = 0
97
+  
98
+  while (start < content.length) {
99
+    pages.push(content.substring(start, start + pageSize))
100
+    start += pageSize
101
+  }
102
+  
103
+  return pages
104
+}
105
+
106
+// 翻页事件处理
107
+const onPageChange = (e) => {
108
+  currentPage.value = e.detail.current
109
+  saveReadingPosition()
110
+  showRewardAd(props.chapterId)
111
+}
112
+
113
+// 保存阅读位置
114
+const saveReadingPosition = () => {
115
+  if (!userStore.isLoggedIn) return
116
+  
117
+  uni.request({
118
+    url: 'https://api.aiyadianzi.ltd/reading/progress',
119
+    method: 'POST',
120
+    data: {
121
+      userId: userStore.userId,
122
+      novelId: props.novelId,
123
+      chapterId: props.chapterId,
124
+      page: currentPage.value,
125
+      scrollTop: lastReadPosition.value
126
+    }
127
+  })
128
+}
129
+
130
+// 初始化
131
+onMounted(async () => {
132
+  await fetchChapterContent()
133
+  feedAd.value = showFeedAd()
134
+})
135
+
136
+// VIP状态变化时更新广告
137
+watch(() => userStore.isVIP, (isVip) => {
138
+  if (isVip) {
139
+    feedAd.value = null
140
+  } else {
141
+    feedAd.value = showFeedAd()
142
+  }
143
+})
144
+</script>
145
+
146
+<style scoped>
147
+.reader-container {
148
+  height: 100vh;
149
+  display: flex;
150
+  flex-direction: column;
151
+}
152
+
153
+.mode-selector {
154
+  display: flex;
155
+  padding: 10px;
156
+  background: var(--header-bg);
157
+  
158
+  button {
159
+    flex: 1;
160
+    margin: 0 5px;
161
+    font-size: 14px;
162
+    
163
+    &.active {
164
+      background: var(--primary-color);
165
+      color: white;
166
+    }
167
+  }
168
+}
169
+
170
+.scroll-reader {
171
+  flex: 1;
172
+  padding: 15px;
173
+  overflow-y: auto;
174
+}
175
+
176
+.page-reader {
177
+  flex: 1;
178
+}
179
+
180
+.bottom-ad {
181
+  height: 100px;
182
+  background: #f5f5f5;
183
+}
184
+</style>

+ 45
- 6
RuoYi-App/manifest.json Bestand weergeven

@@ -1,8 +1,8 @@
1 1
 {
2
-    "name" : "若依移动端",
2
+    "name" : "哎呀免费小说",
3 3
     "appid" : "__UNI__25A9D80",
4
-    "description" : "",
5
-    "versionName" : "1.2.0",
4
+    "description" : "哎呀电子科技旗下小说阅读平台",
5
+    "versionName" : "1.0.0",
6 6
     "versionCode" : "100",
7 7
     "transformPx" : false,
8 8
     "app-plus" : {
@@ -41,7 +41,7 @@
41 41
     },
42 42
     "quickapp" : {},
43 43
     "mp-weixin" : {
44
-        "appid" : "wxccd7e2a0911b3397",
44
+        "appid" : "YOUR_WECHAT_APPID",
45 45
         "setting" : {
46 46
             "urlCheck" : false,
47 47
             "es6" : false,
@@ -53,17 +53,56 @@
53 53
         },
54 54
         "usingComponents" : true
55 55
     },
56
-    "vueVersion" : "2",
56
+    "usingComponents": true,
57
+    "permission": {
58
+      "scope.userLocation": {
59
+        "desc": "用于附近书店功能"
60
+      }
61
+    },
62
+    "plugins": {
63
+      "adPlugin": {
64
+        "version": "1.2.7",
65
+        "provider": "wx3f8e7a3d8a3d8a3d"
66
+      }
67
+    }
68
+  },
69
+   /*   "vueVersion" : "2", */
70
+   /*抖音小程序配置 */
71
+  "mp-toutiao": {
72
+    "appid": "YOUR_DOUYIN_APPID",
73
+    "setting": {
74
+      "urlCheck": false,
75
+      "minified": true
76
+    },
77
+    "ad": {
78
+      "rewardVideo": {
79
+        "unitId": "DOUYIN_AD_UNIT_ID"
80
+      },
81
+      "banner": {
82
+        "unitId": "DOUYIN_BANNER_ID"
83
+      }
84
+    }
85
+  },
57 86
     "h5" : {
87
+    "title": "哎呀免费小说",
58 88
         "template" : "static/index.html",
59 89
         "devServer" : {
60 90
             "port" : 9090,
61 91
             "https" : false
62 92
         },
63
-        "title" : "RuoYi-App",
64 93
         "router" : {
65 94
             "mode" : "hash",
66 95
             "base" : "./"
67 96
         }
97
+    "optimization": {
98
+      "treeShaking": {
99
+        "enable": true
100
+      }
101
+    },
102
+    "sdkConfig": {
103
+      "ad": {
104
+        "adpid": "H5_AD_PID"
105
+      }
106
+    }
68 107
     }
69 108
 }

+ 225
- 0
RuoYi-App/pages/welfare/index.vue Bestand weergeven

@@ -0,0 +1,225 @@
1
+<template>
2
+  <view class="welfare-page">
3
+    <!-- 签到区域 -->
4
+    <view class="signin-section">
5
+      <text class="title">每日签到</text>
6
+      <view class="signin-grid">
7
+        <view 
8
+          v-for="(day, index) in 7" 
9
+          :key="day" 
10
+          :class="['day', { signed: index < signedDays, today: index === currentDay }]"
11
+          @click="signIn(index)"
12
+        >
13
+          <text>第{{ day }}天</text>
14
+          <text class="reward">+{{ calculateReward(index) }}金币</text>
15
+        </view>
16
+      </view>
17
+      <text class="tip">已连续签到 {{ signedDays }} 天</text>
18
+    </view>
19
+    
20
+    <!-- 任务中心 -->
21
+    <view class="tasks-section">
22
+      <text class="title">每日任务</text>
23
+      <view class="task-list">
24
+        <view v-for="task in dailyTasks" :key="task.id" class="task-item">
25
+          <view class="task-info">
26
+            <text class="task-name">{{ task.name }}</text>
27
+            <text class="task-reward">+{{ task.reward }}金币</text>
28
+          </view>
29
+          <button 
30
+            :class="['task-btn', { disabled: task.completed }]"
31
+            @click="completeTask(task)"
32
+          >
33
+            {{ task.completed ? '已完成' : '去完成' }}
34
+          </button>
35
+        </view>
36
+      </view>
37
+    </view>
38
+    
39
+    <!-- 外部合作任务 -->
40
+    <view class="partner-tasks">
41
+      <text class="title">合作任务</text>
42
+      <view class="partner-grid">
43
+        <view 
44
+          v-for="partner in partners" 
45
+          :key="partner.id" 
46
+          class="partner-item"
47
+          @click="goPartnerTask(partner)"
48
+        >
49
+          <image :src="partner.icon" class="partner-icon" />
50
+          <text class="partner-name">{{ partner.name }}</text>
51
+        </view>
52
+      </view>
53
+    </view>
54
+  </view>
55
+</template>
56
+
57
+<script setup>
58
+import { ref } from 'vue'
59
+
60
+const signedDays = ref(3)
61
+const currentDay = ref(3) // 0-6 表示周一到周日
62
+
63
+// 每日任务
64
+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 }
69
+])
70
+
71
+// 合作平台
72
+const partners = ref([
73
+  { id: 1, name: '百度地图', icon: '/static/partners/baidu.png', url: 'https://map.baidu.com' },
74
+  { id: 2, name: '快手', icon: '/static/partners/kuaishou.png', url: 'https://www.kuaishou.com' },
75
+  { id: 3, name: '京东金融', icon: '/static/partners/jd.png', url: 'https://jr.jd.com' },
76
+  { id: 4, name: '饿了么', icon: '/static/partners/eleme.png', url: 'https://www.ele.me' }
77
+])
78
+
79
+// 签到逻辑
80
+const signIn = (dayIndex) => {
81
+  if (dayIndex !== currentDay.value) return
82
+  if (signedDays.value > currentDay.value) return
83
+  
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)
92
+}
93
+
94
+// 完成任务
95
+const completeTask = (task) => {
96
+  if (task.completed) return
97
+  
98
+  task.completed = true
99
+  uni.showToast({ title: `任务完成!获得${task.reward}金币` })
100
+}
101
+
102
+// 跳转合作平台
103
+const goPartnerTask = (partner) => {
104
+  uni.navigateTo({
105
+    url: `/pages/webview/webview?url=${encodeURIComponent(partner.url)}`
106
+  })
107
+}
108
+</script>
109
+
110
+<style scoped>
111
+.welfare-page {
112
+  padding: 20px;
113
+}
114
+
115
+.title {
116
+  font-size: 18px;
117
+  font-weight: bold;
118
+  margin-bottom: 15px;
119
+  display: block;
120
+}
121
+
122
+.signin-section {
123
+  margin-bottom: 25px;
124
+}
125
+
126
+.signin-grid {
127
+  display: grid;
128
+  grid-template-columns: repeat(7, 1fr);
129
+  gap: 5px;
130
+}
131
+
132
+.day {
133
+  border: 1px solid #eee;
134
+  border-radius: 8px;
135
+  padding: 8px 5px;
136
+  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
+  }
148
+}
149
+
150
+.reward {
151
+  display: block;
152
+  color: #faad14;
153
+  font-weight: bold;
154
+}
155
+
156
+.tip {
157
+  display: block;
158
+  margin-top: 10px;
159
+  color: #8c8c8c;
160
+  font-size: 12px;
161
+}
162
+
163
+.task-list {
164
+  border-top: 1px solid #f0f0f0;
165
+}
166
+
167
+.task-item {
168
+  display: flex;
169
+  justify-content: space-between;
170
+  align-items: center;
171
+  padding: 12px 0;
172
+  border-bottom: 1px solid #f0f0f0;
173
+}
174
+
175
+.task-info {
176
+  flex: 1;
177
+}
178
+
179
+.task-name {
180
+  display: block;
181
+  font-size: 14px;
182
+}
183
+
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
+  }
201
+}
202
+
203
+.partner-grid {
204
+  display: grid;
205
+  grid-template-columns: repeat(4, 1fr);
206
+  gap: 15px;
207
+}
208
+
209
+.partner-item {
210
+  display: flex;
211
+  flex-direction: column;
212
+  align-items: center;
213
+}
214
+
215
+.partner-icon {
216
+  width: 50px;
217
+  height: 50px;
218
+  border-radius: 10px;
219
+  margin-bottom: 5px;
220
+}
221
+
222
+.partner-name {
223
+  font-size: 12px;
224
+}
225
+</style>

+ 49
- 0
RuoYi-App/store/theme.js Bestand weergeven

@@ -0,0 +1,49 @@
1
+import { defineStore } from 'pinia'
2
+import { ref } from 'vue'
3
+
4
+export const useThemeStore = defineStore('theme', () => {
5
+  const themes = ref({
6
+    default: {
7
+      '--primary-color': '#1890ff',
8
+      '--bg-color': '#f8f9fa',
9
+      '--text-color': '#333',
10
+      '--header-bg': '#ffffff'
11
+    },
12
+    aydzBlue: {
13
+      '--primary-color': '#2a5caa',
14
+      '--bg-color': '#e6f7ff',
15
+      '--text-color': '#1a3353',
16
+      '--header-bg': '#2a5caa'
17
+    },
18
+    darkMode: {
19
+      '--primary-color': '#52c41a',
20
+      '--bg-color': '#1a1a1a',
21
+      '--text-color': '#e6e6e6',
22
+      '--header-bg': '#2a2a2a'
23
+    }
24
+  })
25
+  
26
+  const currentTheme = ref('default')
27
+  
28
+  // 初始化主题
29
+  const initTheme = () => {
30
+    const savedTheme = uni.getStorageSync('selectedTheme') || 'default'
31
+    setTheme(savedTheme)
32
+  }
33
+  
34
+  // 设置主题
35
+  const setTheme = (themeName) => {
36
+    if (!themes.value[themeName]) return
37
+    
38
+    currentTheme.value = themeName
39
+    uni.setStorageSync('selectedTheme', themeName)
40
+    
41
+    // 应用CSS变量
42
+    const themeVars = themes.value[themeName]
43
+    Object.keys(themeVars).forEach(key => {
44
+      document.documentElement.style.setProperty(key, themeVars[key])
45
+    })
46
+  }
47
+  
48
+  return { themes, currentTheme, initTheme, setTheme }
49
+})

+ 77
- 0
RuoYi-App/store/user.js Bestand weergeven

@@ -0,0 +1,77 @@
1
+import { defineStore } from 'pinia'
2
+import { ref, computed } from 'vue'
3
+
4
+export const useUserStore = defineStore('user', () => {
5
+  const userId = ref(null)
6
+  const userInfo = ref(null)
7
+  const vipInfo = ref(null)
8
+  
9
+  // 获取VIP状态
10
+  const isVIP = computed(() => {
11
+    if (!vipInfo.value) return false
12
+    return new Date(vipInfo.value.expireTime) > new Date()
13
+  })
14
+  
15
+  // 用户登录
16
+  const login = async (username, password) => {
17
+    const res = await uni.request({
18
+      url: 'https://api.aiyadianzi.ltd/auth/login',
19
+      method: 'POST',
20
+      data: { username, password }
21
+    })
22
+    
23
+    userId.value = res.data.userId
24
+    userInfo.value = res.data.userInfo
25
+    vipInfo.value = res.data.vipInfo
26
+    
27
+    uni.setStorageSync('token', res.data.token)
28
+  }
29
+  
30
+  // 购买VIP
31
+  const purchaseVIP = async (plan) => {
32
+    const res = await uni.request({
33
+      url: 'https://api.aiyadianzi.ltd/vip/purchase',
34
+      method: 'POST',
35
+      data: {
36
+        userId: userId.value,
37
+        planId: plan.id
38
+      },
39
+      header: { Authorization: `Bearer ${uni.getStorageSync('token')}` }
40
+    })
41
+    
42
+    vipInfo.value = {
43
+      type: plan.type,
44
+      expireTime: res.data.expireTime
45
+    }
46
+  }
47
+  
48
+  // 初始化用户
49
+  const initUser = async () => {
50
+    const token = uni.getStorageSync('token')
51
+    if (!token) return
52
+    
53
+    try {
54
+      const res = await uni.request({
55
+        url: 'https://api.aiyadianzi.ltd/auth/profile',
56
+        header: { Authorization: `Bearer ${token}` }
57
+      })
58
+      
59
+      userId.value = res.data.userId
60
+      userInfo.value = res.data.userInfo
61
+      vipInfo.value = res.data.vipInfo
62
+    } catch (err) {
63
+      console.error('用户初始化失败', err)
64
+      uni.removeStorageSync('token')
65
+    }
66
+  }
67
+  
68
+  return { 
69
+    userId, 
70
+    userInfo, 
71
+    vipInfo, 
72
+    isVIP, 
73
+    login, 
74
+    purchaseVIP, 
75
+    initUser 
76
+  }
77
+})

+ 82
- 0
RuoYi-App/utils/adManager.js Bestand weergeven

@@ -0,0 +1,82 @@
1
+import { ref } from 'vue'
2
+import { useUserStore } from '@/stores/user'
3
+
4
+// 广告平台配置
5
+const AD_PLATFORMS = {
6
+  WECHAT: {
7
+    reward: adUnitId => wx.createRewardedVideoAd({ adUnitId }),
8
+    feed: adUnitId => wx.createBannerAd({ adUnitId })
9
+  },
10
+  DOUYIN: {
11
+    reward: adUnitId => tt.createRewardedVideoAd({ adUnitId }),
12
+    feed: adUnitId => tt.createBannerAd({ adUnitId })
13
+  },
14
+  H5: {
15
+    reward: adUnitId => {
16
+      console.log('H5激励广告:', adUnitId)
17
+      return { show: () => Promise.resolve() }
18
+    },
19
+    feed: adUnitId => {
20
+      return { show: () => console.log('H5信息流广告展示') }
21
+    }
22
+  }
23
+}
24
+
25
+// 广告触发控制器
26
+export function useAdManager() {
27
+  const userStore = useUserStore()
28
+  const chapterCount = ref(0)
29
+  
30
+  // 获取当前平台适配器
31
+  const getPlatformAdapter = () => {
32
+    // #ifdef MP-WEIXIN
33
+    return AD_PLATFORMS.WECHAT
34
+    // #endif
35
+    // #ifdef MP-DOUYIN
36
+    return AD_PLATFORMS.DOUYIN
37
+    // #endif
38
+    return AD_PLATFORMS.H5
39
+  }
40
+
41
+  // 展示激励广告(每5章触发)
42
+  const showRewardAd = async (chapterId) => {
43
+    if (userStore.isVIP) return
44
+    
45
+    chapterCount.value++
46
+    if (chapterCount.value % 5 !== 0) return
47
+    
48
+    const adapter = getPlatformAdapter()
49
+    const ad = adapter.reward('your_ad_unit_id')
50
+    
51
+    try {
52
+      await ad.show()
53
+      logAdView(chapterId, 'reward')
54
+    } catch (err) {
55
+      console.error('广告展示失败:', err)
56
+    }
57
+  }
58
+
59
+  // 展示底部信息流广告
60
+  const showFeedAd = () => {
61
+    if (userStore.isVIP) return null
62
+    
63
+    const adapter = getPlatformAdapter()
64
+    return adapter.feed('your_feed_ad_id')
65
+  }
66
+
67
+  // 广告日志记录(对接Java接口)
68
+  const logAdView = (chapterId, adType) => {
69
+    uni.request({
70
+      url: 'https://api.aiyadianzi.ltd/ad/log',
71
+      method: 'POST',
72
+      data: {
73
+        userId: userStore.userId,
74
+        chapterId,
75
+        adType,
76
+        platform: process.env.VUE_APP_PLATFORM
77
+      }
78
+    })
79
+  }
80
+
81
+  return { showRewardAd, showFeedAd }
82
+}

Laden…
Annuleren
Opslaan