小说小程序前端代码仓库(小程序)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
9.5 KiB

  1. <template>
  2. <view class="page-container">
  3. <navbar title="任务中心" leftClick @leftClick="$utils.navigateBack" />
  4. <view class="task-center">
  5. <!-- 账户剩余 -->
  6. <view class="account-balance">
  7. <view class="balance-label">账户剩余</view>
  8. <view class="balance-value"><text class="num">{{ maxVote }}</text> <text class="unit"> 推荐票</text>
  9. </view>
  10. </view>
  11. <!-- 打卡得奖励 -->
  12. <view class="checkin-section">
  13. <view class="section-header">
  14. <text>打卡得奖励</text>
  15. <view class="record-btn" @click="$refs.signRecordPopup.open()">打卡记录</view>
  16. </view>
  17. <view class="checkin-grid">
  18. <view v-for="day in clockList" :key="day" class="checkin-day"
  19. :class="{ active: day.commonSignLog }">
  20. <view class="day-label" :class="{ bold: day.commonSignLog }">
  21. {{ day.title }}
  22. </view>
  23. <image class="ticket-img" :src="day.image" mode="aspectFit" />
  24. <view class="ticket-num">+ {{ day.num }}</view>
  25. </view>
  26. </view>
  27. <button class="checkin-btn" :class="{ 'checked-btn': !canSignToday }" :disabled="!canSignToday"
  28. @click="clickSignTask">
  29. {{ !canSignToday ? '已签到' : '签到得奖励' }}
  30. </button>
  31. </view>
  32. <!-- 更多任务 -->
  33. <view class="more-tasks">
  34. <view class="more-header">更多任务</view>
  35. <view class="task-list">
  36. <view class="task-item" v-for="(task, idx) in list" :key="idx"
  37. :class="{ 'no-border': idx == list.length - 1 }">
  38. <view class="task-info">
  39. <view class="task-title">{{ task.title }}</view>
  40. <view class="task-desc">推荐票 +{{ task.num }}</view>
  41. </view>
  42. <button class="get-btn"
  43. :class="{ 'received-btn': task.commonTaskLog || !task.isTask }"
  44. :disabled="task.commonTaskLog"
  45. @click="clickMoreTask(task)">
  46. {{ task.commonTaskLog ? '已领取' : (task.isTask ? '去领取' : '未达成') }}
  47. </button>
  48. </view>
  49. </view>
  50. </view>
  51. </view>
  52. <!-- 签到记录弹窗组件 -->
  53. <signRecordPopup ref="signRecordPopup" />
  54. </view>
  55. </template>
  56. <script>
  57. import mixinsList from '@/mixins/list.js'
  58. import signRecordPopup from '../components/novel/signRecordPopup.vue'
  59. export default {
  60. mixins: [mixinsList],
  61. components: {
  62. signRecordPopup,
  63. },
  64. data() {
  65. return {
  66. checkedDays: 3, // 已签到天数
  67. clockList: [],
  68. isChecked: false, // 新增:签到按钮状态
  69. maxVote: 0,
  70. mixinsListApi: '',
  71. canSignToday: false, // 今日是否可以签到
  72. }
  73. },
  74. computed: {
  75. },
  76. onShow() {
  77. this.getMyRecommendTicketNum()
  78. this.getSignTaskList()
  79. this.getMoreTaskList()
  80. this.checkTodaySignStatus()
  81. },
  82. methods: {
  83. getMyRecommendTicketNum() {
  84. this.$fetch('getMyRecommendTicketNum')
  85. .then(res => {
  86. this.maxVote = res
  87. })
  88. },
  89. getSignTaskList() {
  90. this.$fetch('getSignTaskList', {
  91. token: uni.getStorageSync('token')
  92. })
  93. .then(res => {
  94. this.clockList = res
  95. })
  96. },
  97. getMoreTaskList() {
  98. this.$fetch('getMoreTaskList', {
  99. token: uni.getStorageSync('token')
  100. })
  101. .then(res => {
  102. this.list = res
  103. })
  104. },
  105. async clickSignTask() {
  106. if (!this.canSignToday) {
  107. uni.showToast({
  108. title: '今日已签到',
  109. icon: 'none'
  110. })
  111. return
  112. }
  113. let taskId = 0
  114. for (var index = 0; index < this.clockList.length; index++) {
  115. var element = this.clockList[index];
  116. if (!element.commonSignLog) {
  117. taskId = element.id
  118. break
  119. }
  120. }
  121. if (!taskId) {
  122. uni.showToast({
  123. title: '已全部签到',
  124. icon: 'none'
  125. })
  126. return
  127. }
  128. await this.$fetch('clickSignTask', {
  129. taskId,
  130. })
  131. uni.showToast({
  132. title: '签到成功',
  133. icon: 'none'
  134. });
  135. // 签到成功后更新状态
  136. this.canSignToday = false;
  137. this.getSignTaskList()
  138. this.getMyRecommendTicketNum()
  139. },
  140. playTask(task){
  141. if ("key_comment" == task.keyName){
  142. //每日首条评论
  143. uni.showModal({
  144. title: '发布一条评论即可领取',
  145. showCancel: false,
  146. success: (res) => {
  147. }
  148. })
  149. }else if ("key_chapter" == task.keyName){
  150. //每日首阅三个章节
  151. uni.showModal({
  152. title: '阅读3个新的章节小说即可领取',
  153. showCancel: false,
  154. success: (res) => {
  155. }
  156. })
  157. return chapterCount >= 3;
  158. }else if ("key_advertisement" == task.keyName){
  159. // TODO 观看视频广告
  160. uni.showModal({
  161. title: '暂未开放',
  162. showCancel: false,
  163. success: (res) => {
  164. }
  165. })
  166. }
  167. },
  168. async clickMoreTask(task) {
  169. if (!task.isTask) {
  170. this.playTask(task)
  171. return
  172. }
  173. await this.$fetch('clickMoreTask', {
  174. taskId : task.id,
  175. })
  176. uni.showToast({
  177. title: '领取成功',
  178. icon: 'none'
  179. });
  180. this.getMoreTaskList()
  181. this.getMyRecommendTicketNum()
  182. },
  183. checkTodaySignStatus() {
  184. this.$fetch('getSignTaskToday')
  185. .then(res => {
  186. this.canSignToday = res;
  187. })
  188. .catch(err => {
  189. console.error('获取今日签到状态失败:', err);
  190. // 默认设置为不可签到(已签到状态)
  191. this.canSignToday = false;
  192. });
  193. },
  194. },
  195. }
  196. </script>
  197. <style scoped lang="scss">
  198. .page-container {
  199. height: 100vh;
  200. overflow: hidden;
  201. }
  202. .task-center {
  203. background: #f8f8f8;
  204. height: 100vh;
  205. padding-bottom: 40rpx;
  206. overflow: hidden;
  207. box-sizing: border-box;
  208. }
  209. .navbar-placeholder {
  210. height: 100rpx;
  211. }
  212. .account-balance {
  213. background: linear-gradient(90deg, #ffe16b, #ffd700);
  214. border-radius: 20rpx;
  215. margin: 24rpx 24rpx 0 24rpx;
  216. padding: 24rpx 32rpx;
  217. display: flex;
  218. align-items: center;
  219. justify-content: space-between;
  220. font-size: 30rpx;
  221. color: #333;
  222. .balance-label {
  223. font-weight: 500;
  224. }
  225. .balance-value {
  226. font-weight: bold;
  227. .num {
  228. font-size: 24rpx;
  229. }
  230. .unit {
  231. font-size: 22rpx;
  232. }
  233. }
  234. }
  235. .checkin-section {
  236. background: #fff;
  237. border-radius: 20rpx;
  238. margin: 24rpx;
  239. padding: 32rpx 24rpx 24rpx 24rpx;
  240. box-shadow: 0 2rpx 8rpx rgba(255, 215, 0, 0.08);
  241. border: 4rpx solid #ffe16b;
  242. .section-header {
  243. display: flex;
  244. justify-content: space-between;
  245. align-items: center;
  246. font-size: 28rpx;
  247. font-weight: 600;
  248. margin-bottom: 24rpx;
  249. .record-btn {
  250. background: #d6ff4b;
  251. color: #383938;
  252. border-radius: 24rpx;
  253. padding: 6rpx 28rpx;
  254. font-size: 24rpx;
  255. }
  256. }
  257. .checkin-grid {
  258. display: flex;
  259. flex-wrap: wrap;
  260. gap: 18rpx;
  261. margin-bottom: 32rpx;
  262. .checkin-day {
  263. width: 22%;
  264. background: #f7f7f7;
  265. border-radius: 12rpx;
  266. display: flex;
  267. flex-direction: column;
  268. align-items: center;
  269. padding: 18rpx 0 10rpx 0;
  270. border: 2rpx solid #f0f0f0;
  271. &.active {
  272. background: #d6ff4b !important;
  273. border-color: #b6e900 !important;
  274. .day-label,
  275. .ticket-num {
  276. color: #222 !important;
  277. font-weight: bold;
  278. }
  279. }
  280. .day-label {
  281. font-size: 24rpx;
  282. font-weight: 600;
  283. height: 90rpx;
  284. margin-bottom: 8rpx;
  285. color: #333;
  286. &.bold {
  287. font-weight: bold;
  288. }
  289. }
  290. .ticket-img {
  291. width: 48rpx;
  292. height: 36rpx;
  293. margin-bottom: 6rpx;
  294. display: flex;
  295. }
  296. .ticket-num {
  297. font-size: 22rpx;
  298. color: #bfa100;
  299. }
  300. }
  301. }
  302. .checkin-btn {
  303. width: 600rpx;
  304. background: linear-gradient(90deg, #ffe16b, #ffd700);
  305. color: #333;
  306. font-size: 30rpx;
  307. border-radius: 46rpx;
  308. padding: 18rpx 0;
  309. font-weight: bold;
  310. margin-top: 8rpx;
  311. transition: background 0.2s;
  312. }
  313. }
  314. .checked-btn {
  315. background: linear-gradient(90deg, #e0e0e0, #bdbdbd) !important;
  316. color: #444 !important;
  317. cursor: not-allowed;
  318. }
  319. .more-tasks {
  320. background: #faffea;
  321. border-radius: 20rpx;
  322. margin: 0 24rpx;
  323. padding: 24rpx 24rpx 8rpx 24rpx;
  324. margin-top: 100rpx;
  325. .more-header {
  326. font-size: 28rpx;
  327. color: #bfa100;
  328. font-weight: 600;
  329. margin-bottom: 18rpx;
  330. }
  331. .task-list {
  332. .task-item {
  333. display: flex;
  334. align-items: center;
  335. justify-content: space-between;
  336. background: #fff;
  337. border-radius: 0;
  338. margin: 0;
  339. padding: 18rpx 20rpx 10rpx 20rpx;
  340. border-bottom: 1rpx solid #f2f2f2;
  341. .task-info {
  342. display: flex;
  343. flex-direction: column;
  344. align-items: flex-start;
  345. flex: 1;
  346. min-width: 0;
  347. margin-top: 30rpx;
  348. .task-title {
  349. font-size: 26rpx;
  350. color: #222;
  351. font-weight: bold;
  352. }
  353. .task-desc {
  354. font-size: 20rpx;
  355. color: #bbb;
  356. margin-top: 4rpx;
  357. }
  358. }
  359. .get-btn {
  360. background: #ffd700;
  361. color: #fff;
  362. font-size: 24rpx;
  363. border-radius: 24rpx;
  364. padding: 0 28rpx;
  365. font-weight: bold;
  366. border: none;
  367. height: 48rpx;
  368. display: flex;
  369. align-items: center;
  370. justify-content: center;
  371. box-shadow: none;
  372. white-space: nowrap;
  373. margin-left: 18rpx;
  374. transition: background 0.2s;
  375. }
  376. .received-btn {
  377. background: linear-gradient(90deg, #e0e0e0, #bdbdbd) !important;
  378. color: #444 !important;
  379. cursor: not-allowed;
  380. }
  381. &.no-border {
  382. border-bottom: none;
  383. }
  384. }
  385. }
  386. }
  387. </style>