xiansin hace 2 años
padre
commit
ccfad61f55
Se han modificado 41 ficheros con 879 adiciones y 85 borrados
  1. 8 3
      mini/App.vue
  2. 18 3
      mini/api/episode.js
  3. 10 3
      mini/api/setting.js
  4. 16 0
      mini/api/user/collect.js
  5. 16 0
      mini/api/user/consume.js
  6. 22 0
      mini/api/user/episode.js
  7. 11 1
      mini/api/user/index.js
  8. 16 0
      mini/api/user/recharge.js
  9. 11 2
      mini/components/Episode/index.vue
  10. 20 7
      mini/components/Recharge/index.vue
  11. 7 0
      mini/pages.json
  12. 202 0
      mini/pages/episode/play.vue
  13. 1 3
      mini/pages/index/components/EpisodeBox.vue
  14. 113 0
      mini/pages/index/components/Recent.vue
  15. 5 2
      mini/pages/index/index.vue
  16. 29 9
      mini/pages/my/consume.vue
  17. 28 8
      mini/pages/my/history.vue
  18. 11 3
      mini/pages/my/index.vue
  19. 17 6
      mini/pages/my/recharge.vue
  20. 24 11
      mini/pages/trace/index.vue
  21. BIN
      mini/static/image/video.png
  22. 0 9
      mini/utils/request/requestInterceptors.js
  23. 8 8
      mini/utils/request/responseInterceptors.js
  24. 1 1
      server/app/Http/Controllers/V1/AuthController.php
  25. 25 0
      server/app/Http/Controllers/V1/EpisodeController.php
  26. 8 0
      server/app/Http/Controllers/V1/SettingController.php
  27. 46 0
      server/app/Http/Controllers/V1/User/CollectController.php
  28. 34 0
      server/app/Http/Controllers/V1/User/ConsumeController.php
  29. 30 0
      server/app/Http/Controllers/V1/User/RechargeController.php
  30. 36 0
      server/app/Http/Controllers/V1/User/WatchRecordsController.php
  31. 3 0
      server/app/Models/Episode.php
  32. 10 0
      server/app/Models/EpisodesCategory.php
  33. 5 0
      server/app/Models/RechargeCombo.php
  34. 10 1
      server/app/Models/UserCollect.php
  35. 2 1
      server/app/Models/UserConsumeRecord.php
  36. 13 1
      server/app/Models/UserEpisodesRecord.php
  37. 10 1
      server/app/Models/UserRechargeRecord.php
  38. 12 2
      server/app/Models/UserWatchRecord.php
  39. 17 0
      server/app/Scopes/UserScope.php
  40. 24 0
      server/routes/api.php
  41. BIN
      server/server.zip

+ 8 - 3
mini/App.vue

xqd
@@ -8,10 +8,15 @@ export default {
         this.$store.dispatch('user/info', res.data)
       })
     } else {
-      await this.$api.user.login().then(res => {
+      this.$loading()
+      await this.$api.user.login().then(async res => {
+        this.$hideLoading()
         const { token, user_info } = res.data
-        this.$store.dispatch('user/token', token)
-        this.$store.dispatch('user/info', user_info)
+        await this.$store.dispatch('user/token', token)
+        await this.$store.dispatch('user/info', user_info)
+        uni.reLaunch({
+          url: options.path ? '/' + options.path : '/pages/index/index'
+        })
       })
     }
   },

+ 18 - 3
mini/api/episode.js

xqd
@@ -6,20 +6,35 @@ export function recommend() {
   )
 }
 
-export async function news() {
+export function news() {
   return request.get(
     'episode/news'
   )
 }
 
-export async function rank() {
+export function rank() {
   return request.get(
     'episode/rank'
   )
 }
 
+export function info(id) {
+  return request.get(
+    'episode/info',
+    { id }
+  )
+}
+
+export function trace() {
+  return request.get(
+    'episode/trace'
+  )
+}
+
 export default {
   recommend,
   news,
-  rank
+  rank,
+  info,
+  trace
 }

+ 10 - 3
mini/api/setting.js

xqd
@@ -6,20 +6,27 @@ export function tabBar() {
   )
 }
 
-export async function navBar() {
+export function navBar() {
   return request.get(
     'setting/navBar'
   )
 }
 
-export async function banner() {
+export function banner() {
   return request.get(
     'setting/banner'
   )
 }
 
+export function rechargeCombo() {
+  return request.get(
+    'setting/rechargeCombo'
+  )
+}
+
 export default {
   tabBar,
   navBar,
-  banner
+  banner,
+  rechargeCombo
 }

+ 16 - 0
mini/api/user/collect.js

xqd
@@ -0,0 +1,16 @@
+
+/**
+ * Created by JianJia.Zhou<jianjia.zhou> on 2022/8/22.
+ */
+const request = uni.$u.http
+
+export function record(params) {
+  return request.get(
+    'user/collect/record',
+    { params }
+  )
+}
+
+export default {
+  record
+}

+ 16 - 0
mini/api/user/consume.js

xqd
@@ -0,0 +1,16 @@
+
+/**
+ * Created by JianJia.Zhou<jianjia.zhou> on 2022/8/22.
+ */
+const request = uni.$u.http
+
+export function record(params) {
+  return request.get(
+    'user/consume/record',
+    { params }
+  )
+}
+
+export default {
+  record
+}

+ 22 - 0
mini/api/user/episode.js

xqd
@@ -0,0 +1,22 @@
+
+/**
+ * Created by JianJia.Zhou<jianjia.zhou> on 2022/8/22.
+ */
+const request = uni.$u.http
+
+export function record(params) {
+  return request.get(
+    'user/watch/record',
+    params
+  )
+}
+export function recent() {
+  return request.get(
+    'user/watch/recent'
+  )
+}
+
+export default {
+  recent,
+  record
+}

+ 11 - 1
mini/api/user/index.js

xqd xqd
@@ -2,8 +2,14 @@
  * Created by JianJia.Zhou<jianjia.zhou> on 2022/8/14.
  */
 import { getToken } from '../../utils/auth'
+
 const request = uni.$u.http
 
+import episode from './episode'
+import consume from './consume'
+import recharge from './recharge'
+import collect from './collect'
+
 export async function login() {
   return new Promise(resolve => {
     uni.showLoading({
@@ -47,5 +53,9 @@ export default {
   login,
   update,
   info,
-  isLogin
+  isLogin,
+  episode,
+  consume,
+  recharge,
+  collect
 }

+ 16 - 0
mini/api/user/recharge.js

xqd
@@ -0,0 +1,16 @@
+
+/**
+ * Created by JianJia.Zhou<jianjia.zhou> on 2022/8/22.
+ */
+const request = uni.$u.http
+
+export function record(params) {
+  return request.get(
+    'user/recharge/record',
+    { params }
+  )
+}
+
+export default {
+  record
+}

+ 11 - 2
mini/components/Episode/index.vue

xqd xqd
@@ -1,5 +1,5 @@
 <template>
-  <view class="episode" :style="[customStyle]">
+  <view class="episode" :style="[customStyle]" @click="handlePlay">
     <view class="cover-image" :style="[imageStyle]">
       <view v-if="rank" class="rank" :class="{first: rank === 1}">
         <text>1</text>
@@ -72,7 +72,16 @@ export default {
     return {}
   },
   computed: {},
-  methods: {}
+  methods: {
+    handlePlay() {
+      this.$u.route({
+        url: '/pages/episode/play',
+        params: {
+          id: this.episode.id
+        }
+      })
+    }
+  }
 }
 </script>
 

+ 20 - 7
mini/components/Recharge/index.vue

xqd xqd xqd
@@ -12,19 +12,19 @@
           <text>充值金币</text>
           <u-icon name="close-circle" size="52rpx" color="#BEBDBB" @click="close" />
         </view>
-        <view class="overage">账户余额:<text>3金币</text></view>
+        <view class="overage">账户余额:<text>{{ userInfo.info.integral }}金币</text></view>
 
         <view class="recharge-group dir-left-wrap">
           <view
-            v-for="(item,index) in recharge"
+            v-for="(combo,index) in combos"
             :key="index"
             class="recharge-item dir-top-wrap main-center cross-center"
             :class="{active: rechargeActive === index}"
             @click="rechargeActive = index"
           >
-            <text class="price">49.99元</text>
-            <text class="gold">500+300金币</text>
-            <text class="gift">多送350金币</text>
+            <text class="price">{{ combo.price }}元</text>
+            <text class="gold">{{ combo.gold }}+{{ combo.gift }}金币</text>
+            <text class="gift">多送{{ combo.gift }}金币</text>
           </view>
         </view>
 
@@ -35,6 +35,7 @@
 </template>
 
 <script>
+import { mapState } from 'vuex'
 export default {
   name: 'Recharge',
   props: {
@@ -52,17 +53,29 @@ export default {
       modal: {
         show: false
       },
-      recharge: [1, 2, 3, 4],
+      combos: [1, 2, 3, 4],
       rechargeActive: 1
     }
   },
-  computed: {},
+  computed: {
+    ...mapState({
+      userInfo: seate => seate.user.info
+    })
+  },
   watch: {
     show(val) {
       this.modal.show = val
     }
   },
+  created() {
+    this.getCombo()
+  },
   methods: {
+    getCombo() {
+      this.$api.setting.rechargeCombo().then(res => {
+        this.combos = res.data
+      })
+    },
     close() {
       this.$emit('update:show', false)
     }

+ 7 - 0
mini/pages.json

xqd
@@ -88,6 +88,13 @@
         "navigationBarTitleText": "会员免费片单",
         "enablePullDownRefresh": false
       }
+    },
+    {
+      "path": "pages/episode/play",
+      "style": {
+        "navigationBarTitleText": "播放",
+        "enablePullDownRefresh": false
+      }
     }
   ],
   "globalStyle": {

+ 202 - 0
mini/pages/episode/play.vue

xqd
@@ -0,0 +1,202 @@
+<template>
+  <view class="play-container">
+    <!--播放数据-->
+    <view class="view-num main-left cross-center">
+      <u-icon name="eye-fill" :color="$colors.primaryColor" size="26rpx" />
+      <text>321</text>
+    </view>
+    <!--按钮-->
+    <view class="status-bar dir-top-wrap main-center">
+      <view class="item fav dir-top-wrap main-center cross-center">
+        <u-icon name="heart-fill" size="58rpx" :color="$colors.primaryColor" />
+        <text class="active">3.2k</text>
+      </view>
+      <view class="item collect dir-top-wrap main-center cross-center">
+        <u-icon name="star-fill" size="58rpx" :color="$colors.defaultColor" />
+        <text>3.2k</text>
+      </view>
+
+      <view class="item share dir-top-wrap main-center cross-center">
+        <u-icon name="share-fill" size="58rpx" :color="$colors.defaultColor" />
+        <text>3.2k</text>
+      </view>
+    </view>
+    <!--底部-->
+    <view class="footer main-between cross-center">
+      <view class="icon" />
+      <view class="name">
+        <u-text text="jldjklsjgkldjglkdfhgklfdhgkldhgjklfhjkfhgjkfdhgjkdfhgk" :color="$colors.infoColor" :lines="1" />
+      </view>
+      <view class="arrow">
+        <u-icon name="arrow-up" :color="$colors.infoColor" size="32rpx" />
+      </view>
+    </view>
+    <!--视频播放-->
+    <view class="video-box main-center cross-center">
+      <!-- 控制按钮 - 播放 -->
+      <view v-if="!isPlaying" class="play-btn main-center cross-center" @tap="handlePlay">
+        <u-icon name="play-right-fill" size="100rpx" :color="$colors.defaultColor" />
+      </view>
+      <!-- 控制按钮 - 暂停 -->
+      <view v-if="isPlaying" class="pause-layer" @tap="handlePause" />
+      <!--进度条-->
+      <view v-if="isPlaying" class="progress-container">
+        <view class="progress" :style="{width: progress+'%'}" />
+      </view>
+      <video
+        id="video"
+        :show-play-btn="playBtn"
+        :show-fullscreen-btn="fullscreenBtn"
+        :controls="controls"
+        object-fit="contain"
+        style="width: 100%;height: 100%;"
+        poster="http://zhengda.oss-cn-chengdu.aliyuncs.com/zhangsiye/images/17ec18c234d9f808da0a83cd46749b32.jpg"
+        src="http://zhengda.oss-cn-chengdu.aliyuncs.com/zhangsiye/files/002fa937509b428ca12c826c28aba7c4.mp4"
+        @timeupdate="timeupdate"
+      />
+    </view>
+
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'Play',
+  data() {
+    return {
+      id: null,
+      isPlaying: false,
+      videoContext: null,
+      controls: false,
+      fullscreenBtn: false,
+      playBtn: false,
+      progress: 0
+    }
+  },
+  computed: {},
+  methods: {
+    timeupdate({ detail }) {
+      // currentTime, duration
+      if (detail.duration) {
+        this.progress = (detail.currentTime / detail.duration * 100).toFixed(2)
+      }
+      console.log('-->data', detail)
+      // this.progress =
+    },
+    handlePlay() {
+      this.isPlaying = true
+      this.videoContext.play()
+    },
+    handlePause() {
+      if (!this.isPlaying) return
+      this.isPlaying = false
+      this.videoContext.pause()
+    }
+  },
+  onLoad(options) {
+    this.videoContext = uni.createVideoContext('video', this)
+    this.id = options.id
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+    .play-container {
+      font-size: 28rpx;
+      .video-box{
+        position: fixed;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        .play-btn{
+          position: absolute;
+          top: 0;
+          left: 0;
+          bottom: 0;
+          right: 0;
+          background: rgba(255,255,255,0);
+          z-index: 99;
+        }
+        .pause-layer{
+          position: absolute;
+          top: 0;
+          left: 0;
+          bottom: 0;
+          right: 0;
+          background: transparent;
+          z-index: 99;
+        }
+        video{
+          position: absolute;
+          z-index: 98;
+        }
+        .progress-container{
+          width: 93vw;
+          background: #fff;
+          height: 10rpx;
+          position: fixed;
+          z-index: 100;
+          bottom: 160rpx;
+          .progress{
+            height: 10rpx;
+            background: linear-gradient(270deg, #6EEBE8 0%, #FF74B9 100%);
+          }
+        }
+      }
+      .view-num{
+        position: fixed;
+        right: 20rpx;
+        top: 40rpx;
+        color: #fff;
+        font-size: 26rpx;
+        z-index: 100;
+        text{
+          margin-left: 10rpx;
+        }
+      }
+
+      .status-bar{
+        position: fixed;
+        bottom: 300rpx;
+        right: 40rpx;
+        color: $default-color;
+        z-index: 100;
+        .item{
+          margin-bottom: 40rpx;
+          text{
+            margin-top: 10rpx;
+            &.active{
+              color: $primary-color;
+            }
+          }
+        }
+      }
+
+      .footer{
+        position: fixed;
+        width: 95vw;
+        height: 76rpx;
+        background: rgba(24, 28, 47, 0.8);
+        bottom: 50rpx;
+        left: 50%;
+        transform: translateX(-50%);
+        font-size: 26rpx;
+        color: $info-color;
+        border-radius: 20rpx;
+        padding: 0 20rpx;
+        z-index: 100;
+        .icon{
+          background: url("/static/image/video.png") no-repeat center;
+          background-size: 70%;
+          width: 80rpx;
+          height: 80rpx;
+        }
+        .name{
+          text-align: left;
+          flex: 1;
+          padding: 0 30rpx;
+        }
+      }
+    }
+</style>

+ 1 - 3
mini/pages/index/components/EpisodeBox.vue

xqd
@@ -34,12 +34,10 @@
 </template>
 
 <script>
-import UIcon from '../../../uni_modules/uview-ui/components/u-icon/u-icon'
 import Episode from '../../../components/Episode/index'
-import { recommend } from '../../../api/episode'
 export default {
   name: 'EpisodeBox',
-  components: { Episode, UIcon },
+  components: { Episode },
   props: {
     title: {
       type: String,

+ 113 - 0
mini/pages/index/components/Recent.vue

xqd
@@ -0,0 +1,113 @@
+<template>
+  <view v-if="show" class="recent-container main-between cross-center">
+    <view class="close" @click="handleClose">
+      <u-icon name="close-circle" :color="$colors.primaryColor" size="54rpx" />
+    </view>
+    <view class="cover-image">
+      <image src="@/static/image/default-movie.png" mode="aspectFill" />
+    </view>
+    <view class="info">
+      <text class="name">{{ recent.detail.episode.name }}</text>
+      <view class="status-box">
+        <text class="status">{{ recent.detail.episode.status_text }}</text>
+        <text>共{{ recent.detail.episode.total }}集</text>
+      </view>
+      <view class="recent">上次观看至 <text>第{{ recent.detail.sort }}集</text></view>
+    </view>
+    <view class="play-btn">继续观看</view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'Recent',
+  data() {
+    return {
+      show: false,
+      recent: []
+    }
+  },
+  created() {
+    this.getRecent()
+  },
+  methods: {
+    getRecent() {
+      this.$api.user.episode.recent().then(res => {
+        if (res.data) {
+          this.recent = res.data
+          setTimeout(() => {
+            this.show = true
+          }, 500)
+        }
+      })
+    },
+    handleClose() {
+      this.show = false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+    .recent-container {
+      position: fixed;
+      bottom: 140rpx;
+      width: 96vw;
+      left: 50%;
+      transform: translateX(-50%);
+      border: 1rpx solid $primary-color;
+      height: 300rpx;
+      border-radius: 20rpx;
+      background: rgba(24, 28, 47, 0.8);
+      padding: 20rpx 26rpx;
+      font-size: 32rpx;
+      .close{
+        position: absolute;
+        top: -70rpx;
+        right: 0;
+      }
+      .cover-image{
+        image{
+          width: 200rpx;
+          height: 240rpx;
+        }
+      }
+      .info{
+        flex: 1;
+        margin-left: 20rpx;
+        color: #fff;
+        .name{
+          font-weight: 800;
+          font-size: 34rpx;
+        }
+        .status-box{
+          margin-bottom: 10rpx;
+          font-size: 24rpx;
+          margin-top: 60rpx;
+          .status{
+            color: $primary-color;
+            margin-right: 14rpx;
+          }
+        }
+        .recent{
+          color: $info-color;
+          font-size: 24rpx;
+          text{
+            color: #FB3651;
+            margin-left: 8rpx;
+          }
+        }
+      }
+      .play-btn{
+        width: 180rpx;
+        border-radius: 30rpx;
+        padding: 20rpx 0;
+        background: linear-gradient(270deg, #6EEBE8 0%, #FF74B9 100%);
+        text-align: center;
+        flex-shrink: 0;
+        font-size: 32rpx;
+        color: $default-color;
+
+      }
+    }
+</style>

+ 5 - 2
mini/pages/index/index.vue

xqd xqd xqd
@@ -33,6 +33,8 @@
     <episode-box title="排行榜" type="rank" />
     <!--tab bar-->
     <tab-bar />
+    <!--最近播放-->
+    <recent />
   </view>
 </template>
 
@@ -41,8 +43,9 @@ import SwiperBox from '../../components/SwiperBox/index'
 import EpisodeBox from './components/EpisodeBox'
 import TabBar from '../../components/TabBar/index'
 import NavBar from '../../components/NavBar/index'
+import Recent from './components/Recent'
 export default {
-  components: { NavBar, TabBar, EpisodeBox, SwiperBox },
+  components: { Recent, NavBar, TabBar, EpisodeBox, SwiperBox },
   data() {
     return {
       keywords: ''
@@ -60,7 +63,7 @@ export default {
 
 <style lang="scss" scoped>
   .container{
-    padding: 0 20rpx;
+    padding: 0 20rpx 160px;
     .search-box{
       margin-top: 30rpx;
     }

+ 29 - 9
mini/pages/my/consume.vue

xqd xqd xqd
@@ -13,19 +13,19 @@
         class="consume-item dir-left-nowrap cross-center"
       >
         <view class="cover-image">
-          <image src="@/static/image/default-movie.png" />
+          <image :src="item.detail.episode.cover_img" />
         </view>
         <view class="episode-box dir-top-wrap ">
-          <view class="name">毒液</view>
+          <view class="name">{{ item.detail.episode.name }}</view>
           <view class="status-box main-left cross-center">
-            <text class="status">已完结</text>
-            <text>共18集</text>
+            <text class="status">{{ item.detail.episode.status_text }}</text>
+            <text>共{{ item.detail.episode.total }}集</text>
           </view>
-          <view class="detail">5集</view>
-          <text class="time">2022-07-21 12:45:21</text>
+          <view class="detail">{{ item.detail.sort }}集</view>
+          <text class="time">{{ item.created_at }}</text>
         </view>
         <view class="gold">
-          <text class="number">-150</text>
+          <text class="number">-{{ item.price }}</text>
           <text>金币</text>
         </view>
       </view>
@@ -46,7 +46,7 @@
 export default {
   data() {
     return {
-      consume: [1, 2],
+      consume: [],
       datePicker: {
         value: (new Date()).getTime(),
         date: uni.$u.timeFormat((new Date()).getTime(), 'yyyy-mm'),
@@ -71,9 +71,29 @@ export default {
     }
   },
   methods: {
-    handleDateConfirm() {
+    handlePlay(detail) {
+      this.$u.route({
+        url: '/pages/episode/play',
+        params: {
+          id: detail.episode.id,
+          list_id: detail.id
+        }
+      })
+    },
+    handleDateConfirm(date) {
       this.datePicker.show = false
+      this.getConsume()
+    },
+    getConsume() {
+      this.$loading()
+      this.$api.user.consume.record({ date: this.datePicker.date }).then(res => {
+        this.$hideLoading()
+        this.consume = res.data
+      })
     }
+  },
+  onLoad() {
+    this.getConsume()
   }
 }
 </script>

+ 28 - 8
mini/pages/my/history.vue

xqd xqd
@@ -6,20 +6,20 @@
       class="item main-left"
     >
       <view class="cover-image">
-        <image src="/static/image/default-movie.png" />
+        <image :src="item.detail.episode.cover_img" />
       </view>
       <view class="right-box">
         <view class="op-group main-right cross-center">
           <view class="delete">删除</view>
-          <view class="play">播放</view>
+          <view class="play" @click="handlePlay(item.detail)">播放</view>
         </view>
-        <view class="name">毒液</view>
+        <view class="name">{{ item.detail.episode.name }}</view>
         <view class="status-box">
-          <text class="status">已完结</text>
-          <text>共18集</text>
+          <text class="status">{{ item.detail.episode.status_text }}</text>
+          <text>共{{ item.detail.episode.total }}集</text>
         </view>
         <view class="record">
-          上次看至 <text>第集</text>
+          上次看至 <text>第{{ item.detail.sort }}集</text>
         </view>
       </view>
     </view>
@@ -30,11 +30,31 @@
 export default {
   data() {
     return {
-      history: [1, 2, 3, 4, 5]
+      history: []
     }
   },
   computed: {},
-  methods: {}
+  methods: {
+    handlePlay(detail) {
+      this.$u.route({
+        url: '/pages/episode/play',
+        params: {
+          id: detail.episode.id,
+          list_id: detail.id
+        }
+      })
+    },
+    getHistory() {
+      this.$loading()
+      this.$api.user.episode.record().then(res => {
+        this.$hideLoading()
+        this.history = res.data
+      })
+    }
+  },
+  onLoad() {
+    this.getHistory()
+  }
 }
 </script>
 

+ 11 - 3
mini/pages/my/index.vue

xqd xqd xqd xqd xqd
@@ -34,7 +34,7 @@
           class="episode"
         >
           <view class="cover-image">
-            <image src="@/static/image/default-movie.png" mode="aspectFill" />
+            <image :src="item.detail.episode.cover_img" mode="aspectFill" />
           </view>
         </view>
       </view>
@@ -70,7 +70,7 @@ export default {
   components: { TabBar },
   data() {
     return {
-      history: [1, 2, 3, 4],
+      history: [],
       menus: [
         { icon: '/static/image/my-page/order.png', name: '消费记录', href: '/pages/my/consume' },
         { icon: '/static/image/my-page/recharge.png', name: '充值记录', href: '/pages/my/recharge' },
@@ -86,6 +86,11 @@ export default {
     })
   },
   methods: {
+    getHistory() {
+      this.$api.user.episode.record().then(res => {
+        this.history = res.data
+      })
+    },
     handleMenu(menu) {
       if (menu.href) {
         this.$u.route(menu.href)
@@ -107,6 +112,9 @@ export default {
         }
       })
     }
+  },
+  onLoad() {
+    this.getHistory()
   }
 }
 </script>
@@ -193,7 +201,7 @@ export default {
           }
           .cover-image{
             image{
-              width: 100%;
+              width: calc(100%/4);
               height: 200rpx;
             }
           }

+ 17 - 6
mini/pages/my/recharge.vue

xqd xqd xqd
@@ -15,17 +15,17 @@
         <view class="gold-box main-between cross-center">
           <view class="left-box main-left cross-center">
             <view class="gold">
-              <text class="number">+150</text>
+              <text class="number">+{{ item.gold + item.gift }}</text>
               <text>金币</text>
             </view>
-            <view class="gift">充100赠50</view>
+            <view class="gift">充{{ item.gold }}赠{{ item.gift }}</view>
           </view>
           <view class="right-box">
-            <text class="number">49.99</text>
-            <text>金币</text>
+            <text class="number">{{ item.price }}</text>
+            <text></text>
           </view>
         </view>
-        <text class="time">2022.07.21 12:55</text>
+        <text class="time">{{ item.created_at }}</text>
       </view>
     </view>
     <!--时间选择-->
@@ -44,7 +44,7 @@
 export default {
   data() {
     return {
-      recharge: [1, 2, 3, 4, 5],
+      recharge: [],
       datePicker: {
         value: (new Date()).getTime(),
         date: uni.$u.timeFormat((new Date()).getTime(), 'yyyy-mm'),
@@ -71,7 +71,18 @@ export default {
   methods: {
     handleDateConfirm() {
       this.datePicker.show = false
+      this.getRecharge()
+    },
+    getRecharge() {
+      this.$loading()
+      this.$api.user.recharge.record({ date: this.datePicker.date }).then(res => {
+        this.$hideLoading()
+        this.recharge = res.data
+      })
     }
+  },
+  onLoad() {
+    this.getRecharge()
   }
 }
 </script>

+ 24 - 11
mini/pages/trace/index.vue

xqd xqd xqd
@@ -11,9 +11,9 @@
       <!--剧集-->
       <view class="content main-left cross-center">
         <episode
-          v-for="(episode,index) in collect"
+          v-for="(item,index) in collect"
           :key="index"
-          :episode="episode"
+          :episode="item.episode"
           :custom-style="{
             marginRight: ((index+1) % 4 !== 0 ? '10rpx' :''),
             width: ((710 - 60) / 4 )+'rpx' // 60为间隔 710为 collect-container宽度
@@ -31,10 +31,10 @@
       :key="index"
       class="category main-left"
     >
-      <view class="name">动作</view>
+      <view class="name">{{ item.name }}</view>
       <view class="episode-box main-left">
         <episode
-          v-for="(episode,episodeIndex) in collect"
+          v-for="(episode,episodeIndex) in item.episodes"
           :key="episodeIndex"
           :episode="episode"
           :custom-style="{
@@ -60,16 +60,29 @@ export default {
   components: { TabBar, Episode },
   data() {
     return {
-      collect: [
-        { name: '毒液', status: '已完结', total: 40 },
-        { name: '毒液', status: '已完结', total: 40 },
-        { name: '毒液', status: '已完结', total: 40 }
-      ],
-      category: [1, 2, 3]
+      collect: [],
+      category: []
     }
   },
   computed: {},
-  methods: {}
+  methods: {
+    getCollect() {
+      this.$api.user.collect.record({ limit: 4 }).then(res => {
+        this.collect = res.data
+      })
+    },
+    getTrace() {
+      this.$loading()
+      this.$api.episode.trace().then(res => {
+        this.$hideLoading()
+        this.category = res.data
+      })
+    }
+  },
+  onLoad() {
+    this.getCollect()
+    this.getTrace()
+  }
 }
 </script>
 

BIN
mini/static/image/video.png


+ 0 - 9
mini/utils/request/requestInterceptors.js

xqd xqd
@@ -1,4 +1,3 @@
-import Cache from '../cache'
 import { getToken } from '../auth'
 
 /**
@@ -9,14 +8,6 @@ module.exports = vm => {
   uni.$u.http.interceptors.request.use(
     config => { // 可使用async await 做异步操作
       config.data = config.data || {}
-      const brandId = Cache.get('brand_id') ? Cache.get('brand_id') : 18
-      if (brandId) {
-        config.header['Brand-Id'] = brandId
-      }
-
-      if (Cache.get('store_id')) {
-        config.header['Store-Id'] = Cache.get('store_id')
-      }
 
       if (getToken()) {
         config.header['Authorization'] = getToken()

+ 8 - 8
mini/utils/request/responseInterceptors.js

xqd xqd
@@ -19,10 +19,10 @@ module.exports = vm => {
           showCancel: false
         })
         // 401 登录超时 402 需要登录
-        if (data.code === 401 || data.code === 402) {
-          uni.reLaunch({
+        if (data.status_code === 401 || data.status_code === 402) {
+          /* uni.reLaunch({
             url: 'pages/index/index'
-          })
+          })*/
         }
         return Promise.reject(data.message)
       }
@@ -30,11 +30,11 @@ module.exports = vm => {
       return data
     }, (error) => {
       console.error('-->error', error)
-      uni.showModal({
-        title: '提示',
-        content: error.message,
-        showCancel: false
-      })
+      // uni.showModal({
+      //   title: '提示',
+      //   content: error.message,
+      //   showCancel: false
+      // })
       return Promise.reject(error)
     })
 }

+ 1 - 1
server/app/Http/Controllers/V1/AuthController.php

xqd
@@ -165,7 +165,7 @@ class AuthController extends Controller
             $code = $request->input('code');
             $anonymousCode = $request->input('anonymousCode');
             $app = $this->getByteDanceFactory();
-            $res = $app::login($code, $anonymousCode);
+            $res = $app->login($code, $anonymousCode);
 
             $openId = $res['openid'];
             $user = User::where('open_id', $openId)->first();

+ 25 - 0
server/app/Http/Controllers/V1/EpisodeController.php

xqd xqd
@@ -2,11 +2,13 @@
 namespace App\Http\Controllers\V1;
 use App\Models\Banner;
 use App\Models\Episode;
+use App\Models\EpisodesCategory;
 use App\Models\NavBar;
 use App\Models\Tabbar;
 use App\Models\User;
 use App\Models\UserWatchRecord;
 use Carbon\Carbon;
+use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Support\Collection;
 
 class EpisodeController extends Controller
@@ -87,6 +89,29 @@ class EpisodeController extends Controller
             $lists[] = $episode;
         }
 
+        return $this->success($lists);
+    }
+
+    public function trace()
+    {
+        $lists = EpisodesCategory::with(['episodes' =>function($query){
+            /* @var Builder  $query*/
+            return $query->limit(3);
+        }])->has('episodes')
+            ->orderByDesc('sort')
+            ->get();
+
+        /* @var EpisodesCategory $list*/
+        foreach ($lists as $list){
+            /* @var Episode $episode*/
+            foreach ($list->episodes as $episode){
+                $count = $episode->withCount('lists')->first()->toArray();
+                $episode->total = $count['lists_count'];
+                $episode->status_text = $episode->status;
+            }
+        }
+
+
         return $this->success($lists);
     }
 }

+ 8 - 0
server/app/Http/Controllers/V1/SettingController.php

xqd xqd
@@ -2,6 +2,7 @@
 namespace App\Http\Controllers\V1;
 use App\Models\Banner;
 use App\Models\NavBar;
+use App\Models\RechargeCombo;
 use App\Models\Tabbar;
 
 class SettingController extends Controller
@@ -44,4 +45,11 @@ class SettingController extends Controller
 
         return $this->success($all);
     }
+
+    public function rechargeCombo()
+    {
+        $lists = RechargeCombo::all();
+
+        return $this->success($lists);
+    }
 }

+ 46 - 0
server/app/Http/Controllers/V1/User/CollectController.php

xqd
@@ -0,0 +1,46 @@
+<?php
+namespace App\Http\Controllers\V1\User;
+
+use App\Http\Controllers\V1\Controller;
+use App\Models\UserCollect;
+use App\Models\UserConsumeRecord;
+use App\Models\UserEpisodesRecord;
+use App\Models\UserWatchRecord;
+use Carbon\Carbon;
+use Dingo\Api\Http\Request;
+use Illuminate\Database\Eloquent\Builder;
+
+class CollectController extends Controller
+{
+    public function record(Request $request)
+    {
+        $limit = $request->input('limit', 10);
+        $lists  = UserCollect::filterUser()
+            ->with(['episode','watchRecord.detail'])
+            ->limit($limit)
+            ->orderByDesc('id')->get();
+
+        foreach ($lists as $list){
+            $count = $list->episode->withCount('lists')->first()->toArray();
+            $list->episode->total = $count['lists_count'];
+            $list->episode->status_text = $list->episode->status;
+        }
+
+        return $this->success($lists);
+    }
+
+    public function check()
+    {
+        
+    }
+
+    public function add()
+    {
+        
+    }
+
+    public function destory()
+    {
+
+    }
+}

+ 34 - 0
server/app/Http/Controllers/V1/User/ConsumeController.php

xqd
@@ -0,0 +1,34 @@
+<?php
+namespace App\Http\Controllers\V1\User;
+
+use App\Http\Controllers\V1\Controller;
+use App\Models\UserConsumeRecord;
+use App\Models\UserEpisodesRecord;
+use App\Models\UserWatchRecord;
+use Carbon\Carbon;
+use Dingo\Api\Http\Request;
+use Illuminate\Database\Eloquent\Builder;
+
+class ConsumeController extends Controller
+{
+    public function record(Request $request)
+    {
+        $date = $request->input('date');
+        $date= $date??Carbon::now()->format('Y-m');
+        $lists  = UserEpisodesRecord::filterUser()
+            ->when($date,function ($query,$date){
+                /* @var Builder  $query*/
+                return $query->where('created_at', 'like', "$date%");
+            })
+            ->with(['detail.episode'])
+            ->orderByDesc('id')->get();
+
+        foreach ($lists as $list){
+            $count = $list->detail->episode->withCount('lists')->first()->toArray();
+            $list->detail->episode->total = $count['lists_count'];
+            $list->detail->episode->status_text = $list->detail->episode->status;
+        }
+
+        return $this->success($lists);
+    }
+}

+ 30 - 0
server/app/Http/Controllers/V1/User/RechargeController.php

xqd
@@ -0,0 +1,30 @@
+<?php
+namespace App\Http\Controllers\V1\User;
+
+use App\Http\Controllers\V1\Controller;
+use App\Models\UserConsumeRecord;
+use App\Models\UserEpisodesRecord;
+use App\Models\UserRechargeRecord;
+use App\Models\UserWatchRecord;
+use Carbon\Carbon;
+use Dingo\Api\Http\Request;
+use Illuminate\Database\Eloquent\Builder;
+
+class RechargeController extends Controller
+{
+    public function record(Request $request)
+    {
+        $date = $request->input('date');
+        $date= $date??Carbon::now()->format('Y-m');
+        $lists  = UserRechargeRecord::filterUser()
+            ->when($date,function ($query,$date){
+                /* @var Builder  $query*/
+                return $query->where('created_at', 'like', "$date%");
+            })
+            ->orderByDesc('id')
+            ->get();
+
+
+        return $this->success($lists);
+    }
+}

+ 36 - 0
server/app/Http/Controllers/V1/User/WatchRecordsController.php

xqd
@@ -0,0 +1,36 @@
+<?php
+namespace App\Http\Controllers\V1\User;
+
+use App\Http\Controllers\V1\Controller;
+use App\Models\UserWatchRecord;
+
+class WatchRecordsController extends Controller
+{
+    public function lists()
+    {
+        $lists  = UserWatchRecord::filterUser()
+            ->with(['detail.episode'])
+            ->orderByDesc('id')->get();
+
+        foreach ($lists as $list){
+            $count = $list->detail->episode->withCount('lists')->first()->toArray();
+            $list->detail->episode->total = $count['lists_count'];
+            $list->detail->episode->status_text = $list->detail->episode->status;
+        }
+
+        return $this->success($lists);
+    }
+
+
+    public function recent()
+    {
+        $info = UserWatchRecord::filterUser()
+            ->with(['detail.episode'])
+            ->orderByDesc('id')->first();
+        $count = $info->detail->episode->withCount('lists')->first()->toArray();
+        $info->detail->episode->total = $count['lists_count'];
+        $info->detail->episode->status_text = $info->detail->episode->status;
+
+        return $this->success($info);
+    }
+}

+ 3 - 0
server/app/Models/Episode.php

xqd
@@ -58,6 +58,9 @@ class Episode extends Model
 {
 	use HasDateTimeFormatter,SoftDeletes;
 
+    protected $hidden = [
+        'updated_at','deleted_at'
+    ];
 
     public function category()
     {

+ 10 - 0
server/app/Models/EpisodesCategory.php

xqd xqd
@@ -33,6 +33,7 @@ use Illuminate\Database\Eloquent\Model;
  * @method static \Illuminate\Database\Query\Builder|EpisodesCategory withoutTrashed()
  * @mixin \Eloquent
  * @property-read EpisodesCategory|null $parent
+ * @property-read \App\Models\Episode|null $episodes
  */
 class EpisodesCategory extends Model
 {
@@ -41,9 +42,18 @@ class EpisodesCategory extends Model
 
     protected $table = 'episodes_category';
 
+    protected $hidden = [
+        'updated_at','deleted_at','path'
+    ];
+
 
     public function parent()
     {
         return $this->belongsTo(EpisodesCategory::class,'pid','id');
     }
+
+    public function episodes()
+    {
+        return $this->hasMany(Episode::class,'category_id','id');
+    }
 }

+ 5 - 0
server/app/Models/RechargeCombo.php

xqd
@@ -40,4 +40,9 @@ class RechargeCombo extends Model
     use HasFactory,SoftDeletes;
 
     protected $table = 'recharge_combo';
+
+    protected $casts = [
+        'gold' => 'integer',
+        'gift' => 'integer',
+    ];
 }

+ 10 - 1
server/app/Models/UserCollect.php

xqd xqd
@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Scopes\UserScope;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\SoftDeletes;
@@ -29,14 +30,22 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static \Illuminate\Database\Query\Builder|UserCollect withoutTrashed()
  * @mixin \Eloquent
  * @property-read \App\Models\Episode|null $episode
+ * @method static \Illuminate\Database\Eloquent\Builder|UserCollect filterUser()
+ * @property-read \App\Models\UserWatchRecord|null $watchRecord
  */
 class UserCollect extends Model
 {
-    use HasFactory,SoftDeletes;
+    use HasFactory,SoftDeletes,UserScope;
+
     protected $table = 'user_collect';
 
     public function episode()
     {
         return $this->belongsTo(Episode::class,'episode_id','id');
     }
+
+    public function watchRecord()
+    {
+        return $this->belongsTo(UserWatchRecord::class,'episode_id','episode_id');
+    }
 }

+ 2 - 1
server/app/Models/UserConsumeRecord.php

xqd xqd
@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Scopes\UserScope;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 
@@ -38,7 +39,7 @@ use Illuminate\Database\Eloquent\Model;
  */
 class UserConsumeRecord extends Model
 {
-    use HasFactory;
+    use HasFactory, UserScope;
 
     public function user()
     {

+ 13 - 1
server/app/Models/UserEpisodesRecord.php

xqd xqd xqd
@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Scopes\UserScope;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 
@@ -34,10 +35,16 @@ use Illuminate\Database\Eloquent\Model;
  * @property-read \App\Models\User|null $user
  * @method static \Illuminate\Database\Eloquent\Builder|UserEpisodesRecord whereStatust($value)
  * @property-read \App\Models\Episode|null $episodes
+ * @property-read \App\Models\EpisodesList|null $detail
+ * @method static \Illuminate\Database\Eloquent\Builder|UserEpisodesRecord filterUser()
  */
 class UserEpisodesRecord extends Model
 {
-    use HasFactory;
+    use HasFactory,UserScope;
+
+    protected $casts = [
+        'price' => 'integer',
+    ];
 
     protected function serializeDate(\DateTimeInterface $date)
     {
@@ -53,4 +60,9 @@ class UserEpisodesRecord extends Model
     {
         return $this->belongsTo(Episode::class,'episodes_id','id');
     }
+
+    public function detail()
+    {
+        return $this->belongsTo(EpisodesList::class,'list_id','id');
+    }
 }

+ 10 - 1
server/app/Models/UserRechargeRecord.php

xqd xqd
@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Scopes\UserScope;
 use Carbon\Traits\Timestamp;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
@@ -38,10 +39,18 @@ use Illuminate\Database\Eloquent\Model;
  * @method static \Illuminate\Database\Eloquent\Builder|UserRechargeRecord whereGold($value)
  * @property int $status 充值状态
  * @method static \Illuminate\Database\Eloquent\Builder|UserRechargeRecord whereStatus($value)
+ * @property string $desc 说明
+ * @method static \Illuminate\Database\Eloquent\Builder|UserRechargeRecord filterUser()
+ * @method static \Illuminate\Database\Eloquent\Builder|UserRechargeRecord whereDesc($value)
  */
 class UserRechargeRecord extends Model
 {
-    use HasFactory;
+    use HasFactory,UserScope;
+
+    protected $casts = [
+        'gold' => 'integer',
+        'gift' => 'integer',
+    ];
 
     protected function serializeDate(\DateTimeInterface $date)
     {

+ 12 - 2
server/app/Models/UserWatchRecord.php

xqd xqd xqd
@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use App\Scopes\UserScope;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\SoftDeletes;
@@ -31,10 +32,13 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static \Illuminate\Database\Query\Builder|UserWatchRecord onlyTrashed()
  * @method static \Illuminate\Database\Query\Builder|UserWatchRecord withTrashed()
  * @method static \Illuminate\Database\Query\Builder|UserWatchRecord withoutTrashed()
+ * @property-read \App\Models\EpisodesList|null $detail
+ * @method static \Illuminate\Database\Eloquent\Builder|UserWatchRecord user(\Illuminate\Database\Eloquent\Model $model)
+ * @method static \Illuminate\Database\Eloquent\Builder|UserWatchRecord filterUser()
  */
 class UserWatchRecord extends Model
 {
-    use HasFactory,SoftDeletes;
+    use HasFactory,SoftDeletes, UserScope;
 
     protected function serializeDate(\DateTimeInterface $date)
     {
@@ -44,6 +48,12 @@ class UserWatchRecord extends Model
 
     public function episode()
     {
-        return $this->belongsTo(Episode::class,'id','episode_id');
+        return $this->belongsTo(Episode::class,'episode_id','id');
     }
+
+    public function detail()
+    {
+        return $this->belongsTo(EpisodesList::class,'list_id','id');
+    }
+
 }

+ 17 - 0
server/app/Scopes/UserScope.php

xqd
@@ -0,0 +1,17 @@
+<?php
+namespace App\Scopes;
+
+trait UserScope
+{
+    /**
+     * @param  \Illuminate\Database\Eloquent\Builder  $query
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function scopeFilterUser($query)
+    {
+        $user = \user();
+        if($user){
+            $query->where('user_id', $user->id);
+        }
+    }
+}

+ 24 - 0
server/routes/api.php

xqd xqd xqd
@@ -74,6 +74,28 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\V1'], function ($api)
             $api->post('feedback', 'UserController@feedback'); //问题反馈
             $api->get('info', 'UserController@info'); //用户信息
             $api->post('update', 'UserController@update'); //更新用户信息
+
+
+            $api->group(['prefix' => 'watch','namespace' => 'User'], function ($api){
+                /* @var Dingo\Api\Routing\Router $api*/
+                $api->get('record', 'WatchRecordsController@lists');
+                $api->get('recent', 'WatchRecordsController@recent');
+            });
+
+            $api->group(['prefix' => 'consume','namespace' => 'User'], function ($api){
+                /* @var Dingo\Api\Routing\Router $api*/
+                $api->get('record', 'ConsumeController@record');
+            });
+
+            $api->group(['prefix' => 'recharge','namespace' => 'User'], function ($api){
+                /* @var Dingo\Api\Routing\Router $api*/
+                $api->get('record', 'RechargeController@record');
+            });
+
+            $api->group(['prefix' => 'collect','namespace' => 'User'], function ($api){
+                /* @var Dingo\Api\Routing\Router $api*/
+                $api->get('record', 'CollectController@record');
+            });
         });
 
         // 相关设置
@@ -82,6 +104,7 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\V1'], function ($api)
             $api->get('tabBar', 'SettingController@tabBar'); // 底部导航
             $api->get('navBar', 'SettingController@navBar'); // 首页顶部分类
             $api->get('banner', 'SettingController@banner'); // banner
+            $api->get('rechargeCombo', 'SettingController@rechargeCombo'); // 充值
         });
 
         // 短剧
@@ -90,6 +113,7 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\V1'], function ($api)
             $api->get('recommend', 'EpisodeController@recommend'); // 推荐
             $api->get('news', 'EpisodeController@news'); // 最新
             $api->get('rank', 'EpisodeController@rank'); // 排行
+            $api->get('trace', 'EpisodeController@trace'); // 追剧
         });
 
     });

BIN
server/server.zip