浏览代码

ross抖音挑战赛版本提测

yuwenjun1997 2 年之前
父节点
当前提交
ee0dd227c2

+ 2 - 2
.env.development

@@ -6,8 +6,8 @@ EVN = 'development'
 LOCALHOSE = 'http://192.168.2.92:8888'
 
 # 接口api地址
-BASE_URL = 'https://zplma-b.caimei365.com'
-# BASE_URL = 'http://192.168.2.200:8012'
+# BASE_URL = 'https://zplma-b.caimei365.com'
+BASE_URL = 'http://192.168.2.200:8012'
 # BASE_URL = 'http://192.168.2.67:8012'
 
 # 静态资源文件地址

+ 28 - 8
apis/module/challenge.js

@@ -2,17 +2,37 @@
 export default ($axios) => {
   const challengeApis = {}
 
-  challengeApis.fetchVideoList = (params = {}) =>
-    $axios.get('/wx/auth/get/published/video/list', { params })
+  challengeApis.fetchVideoList = (params = {}) => $axios.get('/wx/auth/get/published/video/list', { params })
 
-  challengeApis.publishVideoSave = (data = {}) =>
-    $axios.post('/wx/auth/save/video/info', data)
+  challengeApis.publishVideoSave = (data = {}) => $axios.post('/wx/auth/save/video/info', data)
 
-  challengeApis.checkActivityPublish = (data = {}) =>
-    $axios.post('/wx/auth/check/video/info', data)
+  challengeApis.checkActivityPublish = (data = {}) => $axios.post('/wx/auth/check/video/info', data)
 
-  challengeApis.fetchActivityStatus = (params = {}) =>
-    $axios.get('/wx/auth/get/activitty', { params })
+  challengeApis.fetchActivityStatus = (params = {}) => $axios.get('/wx/auth/get/activitty', { params })
+
+  // 报名时新增参赛机构信息(资质上传)
+  challengeApis.clubSignUpActivity = (data = {}) => $axios.post('/wx/auth/save/ross/info', data)
+
+  // 获取挑战赛主页左上角已经报名的机构
+  challengeApis.fetchEnteredClubList = (params = {}) => $axios.get('/wx/auth/get/contested/info', { params })
+
+  // 获取当前机构已上传的视频
+  challengeApis.fetchOwnerVideoList = (params = {}) => $axios.get('/wx/auth/get/video/username', { params })
+
+  // 获取全部机构视频
+  challengeApis.fetchClubVideoList = (params = {}) => $axios.get('/wx/auth/get/video/all', { params })
+
+  // 验证机构是否还能上传视频
+  challengeApis.beforeUploadCheck = (params = {}) => $axios.get('/wx/auth/check/video/username', { params })
+
+  // 校验用户是否已报名
+  challengeApis.checkContestInfo = (params = {}) => $axios.get('/wx/auth/check/contest/info', { params })
+
+  // 保存抖音视频
+  challengeApis.publishVideo = (data = {}) => $axios.post('/wx/auth/save/video', data)
+
+  // 视频点赞
+  challengeApis.diggcountVideo = (data = {}) => $axios.post('/wx/auth/up/video/diggcount', data)
 
   return challengeApis
 }

+ 6 - 0
assets/css/global.css

@@ -12,3 +12,9 @@
     background-color: #ddd;
   }
 }
+
+.scroll-none {
+  width: 100vw;
+  height: 100vh;
+  overflow: hidden;
+}

+ 150 - 0
assets/css/iconfont.css

@@ -0,0 +1,150 @@
+@font-face {
+  font-family: 'iconfont'; /* Project id 2996139 */
+  src: url('../fonts/iconfont.woff2?t=1670468041210') format('woff2'),
+    url('../fonts/iconfont.woff?t=1670468041210') format('woff'),
+    url('../fonts/iconfont.ttf?t=1670468041210') format('truetype');
+}
+
+.iconfont {
+  font-family: 'iconfont' !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-profile:before {
+  content: '\e6a2';
+}
+
+.icon-eye-open:before {
+  content: '\e666';
+}
+
+.icon-eye-close:before {
+  content: '\e66f';
+}
+
+.icon-arrow-left:before {
+  content: '\e601';
+}
+
+.icon-arrow-right:before {
+  content: '\e605';
+}
+
+.icon-arrow-left-block:before {
+  content: '\e609';
+}
+
+.icon-arrow-top-block:before {
+  content: '\e60a';
+}
+
+.icon-arrow-right-block:before {
+  content: '\e60b';
+}
+
+.icon-arrow-down-block:before {
+  content: '\e60c';
+}
+
+.icon-close:before {
+  content: '\e616';
+}
+
+.icon-refresh:before {
+  content: '\e61a';
+}
+
+.icon-switch:before {
+  content: '\e61e';
+}
+
+.icon-fangda:before {
+  content: '\e625';
+}
+
+.icon-suoxiao:before {
+  content: '\e626';
+}
+
+.icon-error:before {
+  content: '\e627';
+}
+
+.icon-right:before {
+  content: '\e628';
+}
+
+.icon-home:before {
+  content: '\e62c';
+}
+
+.icon-location:before {
+  content: '\e633';
+}
+
+.icon-choose:before {
+  content: '\e635';
+}
+
+.icon-play:before {
+  content: '\e63b';
+}
+
+.icon-link:before {
+  content: '\e63e';
+}
+
+.icon-serach:before {
+  content: '\e63f';
+}
+
+.icon-heart:before {
+  content: '\e644';
+}
+
+.icon-upload:before {
+  content: '\e646';
+}
+
+.icon-download:before {
+  content: '\e647';
+}
+
+.icon-delete:before {
+  content: '\e652';
+}
+
+.icon-praise-linear:before {
+  content: '\e65d';
+}
+
+.icon-praise:before {
+  content: '\e65e';
+}
+
+.icon-star-linear:before {
+  content: '\e661';
+}
+
+.icon-star:before {
+  content: '\e662';
+}
+
+.icon-add:before {
+  content: '\e664';
+}
+
+.icon-sub:before {
+  content: '\e665';
+}
+
+.icon-arrow-up:before {
+  content: '\e602';
+}
+
+.icon-arrow-down:before {
+  content: '\e604';
+}

二进制
assets/fonts/iconfont.ttf


二进制
assets/fonts/iconfont.woff


二进制
assets/fonts/iconfont.woff2


二进制
assets/theme-images/common/icon-praise-active.png


+ 237 - 0
components/GeneralDialog/index.vue

@@ -0,0 +1,237 @@
+<template>
+  <div class="general-dialog" v-if="value">
+    <!-- 内容区域 -->
+    <div class="dialog__container">
+      <div class="dialog__close iconfont icon-close" @click="$emit('change', false)"></div>
+      <div class="dialog__title">
+        <slot name="title"></slot>
+        <span v-text="title" v-if="!$slots.title"></span>
+      </div>
+      <div class="dialog__content">
+        <slot></slot>
+        <div class="dialog__content_text" v-text="content" v-if="!$slots.default"></div>
+      </div>
+      <div class="dialog__footer">
+        <slot name="footer"></slot>
+        <template v-if="!$slots.footer">
+          <div class="btn confirm" @click="$emit('confirm')" v-if="confirm">
+            {{ confirmText ? confirmText : '确定' }}
+          </div>
+          <div class="btn cancel" @click="$emit('cancel')" v-if="cancel">
+            {{ cancelText ? cancelText : '取消' }}
+          </div>
+        </template>
+      </div>
+    </div>
+    <!-- 遮罩层 -->
+    <div class="dialog__mask"></div>
+  </div>
+</template>
+
+<script>
+export default {
+  model: {
+    prop: 'value',
+    event: 'change',
+  },
+  props: {
+    title: {
+      type: String,
+      default: '提示',
+    },
+    content: String,
+    value: {
+      type: Boolean,
+      default: false,
+    },
+    confirm: {
+      type: Boolean,
+      default: true,
+    },
+    cancel: {
+      type: Boolean,
+      default: false,
+    },
+    confirmText: String,
+    cancelText: String,
+  },
+  watch: {
+    value(val) {
+      this.$nextTick(() => {
+        val ? document.body.classList.add('scroll-none') : document.body.classList.remove('scroll-none')
+      })
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@media screen and (min-width: 768px) {
+  .general-dialog {
+    position: relative;
+    z-index: 999;
+    .dialog__container {
+      position: fixed;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      min-width: 400px;
+      background: #fff;
+      box-sizing: border-box;
+      padding: 40px 32px 32px;
+      z-index: 1;
+      border-radius: 4px;
+
+      .dialog__close {
+        position: absolute;
+        width: 36px;
+        height: 36px;
+        right: 8px;
+        top: 8px;
+        text-align: center;
+        line-height: 36px;
+        font-size: 24px;
+        color: #b2b2b2;
+        transition: all 0.2s;
+        cursor: pointer;
+
+        &:hover {
+          color: #666;
+        }
+      }
+
+      .dialog__title {
+        font-size: 24px;
+        color: #282828;
+        text-align: center;
+      }
+      .dialog__content {
+        padding: 32px 0;
+        .dialog__content_text {
+          font-size: 16px;
+          color: #282828;
+          text-align: center;
+        }
+      }
+      .dialog__footer {
+        .btn {
+          height: 46px;
+          border-radius: 4px;
+          text-align: center;
+          box-sizing: border-box;
+          font-size: 14px;
+          margin: 0 auto;
+          cursor: pointer;
+
+          &.confirm {
+            line-height: 46px;
+            background: #f3920d;
+            color: #fff;
+          }
+
+          &.cancel {
+            line-height: 44px;
+            border: 1px solid #f3920d;
+            color: #f3920d;
+            margin-top: 16px;
+          }
+        }
+      }
+    }
+    .dialog__mask {
+      position: fixed;
+      width: 100vw;
+      height: 100vh;
+      left: 0;
+      top: 0;
+      background: #000;
+      opacity: 0.4;
+    }
+  }
+}
+
+@media screen and (max-width: 768px) {
+  .general-dialog {
+    position: relative;
+    z-index: 999;
+    .dialog__container {
+      position: fixed;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      min-width: 76vw;
+      background: #fff;
+      box-sizing: border-box;
+      padding: 7vw 7vw 6.4vw;
+      z-index: 1;
+      border-radius: 0.4vw;
+
+      .dialog__close {
+        position: absolute;
+        width: 5.6vw;
+        height: 5.6vw;
+        right: 3.2vw;
+        top: 3.2vw;
+        text-align: center;
+        line-height: 5.6vw;
+        font-size: 4.8vw;
+        color: #b2b2b2;
+        transition: all 0.2s;
+        cursor: pointer;
+
+        &:hover {
+          color: #666;
+        }
+      }
+
+      .dialog__title {
+        font-size: 4.8vw;
+        color: #282828;
+        text-align: center;
+      }
+      .dialog__content {
+        padding: 3.2vw 0;
+        .dialog__content_text {
+          font-size: 3.6vw;
+          color: #282828;
+          line-height: 5.6vw;
+          text-align: center;
+        }
+      }
+      .dialog__footer {
+        .btn {
+          height: 8.8vw;
+          border-radius: 0.4vw;
+          text-align: center;
+          box-sizing: border-box;
+          font-size: 3.6vw;
+          margin: 0 auto;
+          cursor: pointer;
+
+          &.confirm {
+            line-height: 8.8vw;
+            background: #f3920d;
+            color: #fff;
+          }
+
+          &.cancel {
+            line-height: 8.8vw;
+            border: 0.1vw solid #f3920d;
+            color: #f3920d;
+            margin-top: 2.4vw;
+          }
+        }
+      }
+    }
+    .dialog__mask {
+      position: fixed;
+      width: 100vw;
+      height: 100vh;
+      left: 0;
+      top: 0;
+      background: #000;
+      opacity: 0.4;
+    }
+  }
+}
+</style>

+ 192 - 0
components/GeneralUploadImage/index.vue

@@ -0,0 +1,192 @@
+<template>
+  <div class="upload-control">
+    <div class="section">
+      <div class="upload" @click="chooseFile">
+        <input type="file" v-show="false" ref="fileInput" :accept="accept" @change="onFileInputChange" />
+        <span class="iconfont icon-add"></span>
+      </div>
+      <template v-if="showImage">
+        <div class="remove"><span class="iconfont icon-delete"></span></div>
+        <div class="preview"><img src="" alt="" /></div>
+      </template>
+    </div>
+    <div class="tip" v-if="tipText" v-text="tipText"></div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    tipText: String,
+    action: {
+      type: String,
+      default: () => process.env.BASE_URL + '/wx/upload/image',
+    },
+    limit: {
+      type: Number,
+      default: 1,
+    },
+    accept: {
+      type: String,
+      default: '.jpg,.png,.gif',
+    },
+    fileList: {
+      type: Array,
+      default: () => [],
+    },
+  },
+  computed: {
+    showImage() {
+      return this.fileList.length > 0
+    },
+  },
+  methods: {
+    chooseFile() {
+      this.$nextTick(() => {
+        this.$refs.fileInput.click()
+      })
+    },
+    onFileInputChange(e) {
+      console.log(e.target.files)
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@media screen and (min-width: 768px) {
+  .upload-control {
+    .section {
+      width: 180px;
+      height: 180px;
+      position: relative;
+      margin: 0 auto;
+
+      .remove {
+        position: absolute;
+        right: 0;
+        top: 0;
+        width: 28px;
+        height: 28px;
+        line-height: 28px;
+        text-align: center;
+        background: rgba(0, 0, 0, 0.4);
+        z-index: 1;
+        cursor: pointer;
+        .icon-delete {
+          font-size: 18px;
+          color: #f1f1f1;
+        }
+      }
+
+      .preview {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        top: 0;
+
+        img {
+          display: block;
+          width: 100%;
+          height: 100%;
+          object-fit: contain;
+        }
+      }
+
+      .upload {
+        width: 100%;
+        height: 100%;
+        border: 1px dashed #bec3cc;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        background: #fbfbfb;
+        box-sizing: border-box;
+        .icon-add {
+          font-size: 36px;
+          color: #bec3cc;
+        }
+      }
+    }
+
+    .tip {
+      width: 266px;
+      margin: 0 auto;
+      margin-top: 36px;
+      line-height: 24px;
+      font-size: 14px;
+      color: #b2b2b2;
+      text-align: center;
+    }
+  }
+}
+
+@media screen and (max-width: 768px) {
+  .upload-control {
+    .section {
+      width: 42vw;
+      height: 42vw;
+      position: relative;
+      margin: 0 auto;
+
+      .remove {
+        position: absolute;
+        right: 0;
+        top: 0;
+        width: 5vw;
+        height: 5vw;
+        line-height: 5vw;
+        text-align: center;
+        background: rgba(0, 0, 0, 0.4);
+        z-index: 1;
+        cursor: pointer;
+        .icon-delete {
+          font-size: 3.6vw;
+          color: #f1f1f1;
+        }
+      }
+
+      .preview {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        top: 0;
+
+        img {
+          display: block;
+          width: 100%;
+          height: 100%;
+          object-fit: contain;
+        }
+      }
+
+      .upload {
+        width: 100%;
+        height: 100%;
+        border: 0.1vw dashed #bec3cc;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        background: #fbfbfb;
+        box-sizing: border-box;
+        .icon-add {
+          font-size: 7.2vw;
+          color: #bec3cc;
+        }
+      }
+    }
+
+    .tip {
+      width: 57vw;
+      margin: 0 auto;
+      margin-top: 4.8vw;
+      line-height: 5.6vw;
+      font-size: 3.2vw;
+      color: #b2b2b2;
+      text-align: center;
+    }
+  }
+}
+</style>

+ 212 - 128
components/SimpleVideoList/index.vue

@@ -1,66 +1,76 @@
 <template>
-  <div class="video-list">
-    <div
-      class="video"
-      v-for="(item, index) in list"
-      :key="index"
-      @click="onPlay(item)"
-    >
-      <div class="cover">
-        <img :src="item.cover" alt="" />
-        <div class="name">{{ item.title }}</div>
-        <div class="rank" :class="'rank-0' + (index + 1)">
-          {{ index + 1 }}
+  <div class="video-container">
+    <div class="video-list">
+      <div class="video" v-for="(item, index) in list" :key="index" @click="onPlay(item)">
+        <div class="cover">
+          <video :src="item.ossUrl"></video>
+          <div class="name">
+            <span>{{ item.title }}</span>
+          </div>
+          <div class="play"></div>
+        </div>
+        <div class="info">
+          <div class="club-name">{{ item.authParty }}</div>
+          <div class="mobile">{{ item.userName | mobileFormat }}</div>
+        </div>
+        <div class="foot">
+          <div class="date">{{ item.releaseTime | dateFormat }}</div>
+          <div class="praise" :class="{ active: item.diggStatus === 1 }" @click.stop="$emit('praise', item)">
+            {{ item.diggCount }}
+          </div>
         </div>
-        <div class="play" @click="onPlay(item)"></div>
-      </div>
-      <div class="info">
-        <div class="club-name">{{ item.authParty }}</div>
-        <div class="mobile">{{ item.userName | mobileFormat }}</div>
-      </div>
-      <div class="foot">
-        <div class="date">{{ item.releaseTime | dateFormat }}</div>
-        <div class="praise">{{ item.diggCount }}</div>
-        <div class="pv">{{ item.playCount }}</div>
       </div>
     </div>
+    <!-- 视频播放组件 -->
+    <SimpleVideoPlayer :videoSrc="videoUrl" :description="description" ref="videoPlayer"></SimpleVideoPlayer>
   </div>
 </template>
 
 <script>
 export default {
+  filters: {
+    mobileFormat(mobile) {
+      return mobile ? mobile.replace(/^(\w{3})\w+(\w{4})$/, '$1****$2') : ''
+    },
+  },
   props: {
     list: {
       type: Array,
       default: () => [],
     },
   },
+  data() {
+    return {
+      videoUrl: '',
+      description: '',
+    }
+  },
+  methods: {
+    // 播放视频
+    onPlay(row) {
+      this.videoUrl = row.ossUrl
+      this.description = row.title
+      this.$refs.videoPlayer.open()
+    },
+  },
 }
 </script>
 
 <style lang="scss" scoped>
 @media screen and (min-width: 768px) {
   .video-list {
-    &::after {
-      content: '';
-      display: block;
-      clear: both;
-    }
+    display: grid;
+    grid-template-columns: repeat(6, 1fr);
+    grid-column-gap: 16px;
+    grid-row-gap: 16px;
 
     .video {
-      float: left;
-      width: 288px;
-      height: 356px;
+      height: 358px;
       background: #fff;
       box-sizing: border-box;
       border: 1px solid #efefef;
-      margin: 0 16px 16px 0;
-      transition: all 0.4s;
 
       &:hover {
-        transform: translateY(-10px);
-        box-shadow: 0px 6px 16px 1px rgba(0, 0, 0, 0.1);
-
         .info {
           .club-name {
             color: #f3920d;
@@ -73,117 +83,196 @@ export default {
       }
 
       .cover {
-        width: 100%;
-        height: 198px;
+        height: 248px;
         position: relative;
 
-        img {
-          display: block;
-          width: 100%;
-          height: 100%;
-        }
-
-        &::after {
-          content: '';
+        video {
           display: block;
-          position: absolute;
-          left: 0;
-          top: 0;
-          z-index: 1;
           width: 100%;
           height: 100%;
-          background: #000;
-          opacity: 0;
-          transition: opacity 0.2s;
+          background: #fff;
         }
 
         .play {
           position: absolute;
           z-index: 2;
-          width: 48px;
-          height: 48px;
-          background: url(~assets/theme-images/common/pc-icon-play.png)
-            no-repeat center;
-          background-size: 48px;
+          width: 32px;
+          height: 32px;
+          background: url(~assets/theme-images/common/pc-icon-play.png) no-repeat center;
+          background-size: 32px;
           left: 50%;
           top: 50%;
           transform: translate(-50%, -50%);
-          opacity: 0;
-          transition: opacity 0.2s;
           cursor: pointer;
         }
 
-        &:hover {
-          .play {
-            opacity: 1;
-          }
+        .name {
+          display: flex;
+          align-items: flex-end;
+          width: 100%;
+          height: 75px;
+          padding: 17px 16px;
+          box-sizing: border-box;
+          position: absolute;
+          left: 0;
+          bottom: 0;
+          background: linear-gradient(180deg, rgba(51, 51, 51, 0) 0%, #333333 100%);
 
-          .rank,
-          .name {
-            opacity: 0;
+          span {
+            color: #fff;
+            font-size: 14px;
+            line-height: 20px;
+            max-height: 40px;
+            @include ellipsis(2);
           }
+        }
+      }
+
+      .info {
+        padding: 10px;
+        .club-name {
+          font-size: 14px;
+          color: #282828;
+          @include ellipsis(1);
+          cursor: pointer;
+          height: 18px;
+        }
+        .mobile {
+          font-size: 14px;
+          color: #666;
+          margin-top: 8px;
+        }
+      }
+
+      .foot {
+        color: #999999;
+        font-size: 14px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        border-top: 1px solid #efefef;
+        margin: 0 10px;
+        padding-top: 8px;
+
+        .praise {
+          flex-shrink: 0;
+          line-height: 16px;
+          cursor: pointer;
+        }
+
+        .praise {
+          position: relative;
+          padding-left: 22px;
 
           &::after {
-            opacity: 0.4;
+            content: '';
+            display: block;
+            width: 16px;
+            height: 16px;
+            background-size: 16px;
+            position: absolute;
+            left: 0;
+            top: 50%;
+            transform: translateY(-50%);
+            background-repeat: no-repeat;
+            background-position: center;
+            background-image: url(~assets/theme-images/common/icon-praise.png);
+          }
+          &.active {
+            color: #f3920d;
+            &::after {
+              background-image: url(~assets/theme-images/common/icon-praise-active.png);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+@media screen and (max-width: 768px) {
+  .video-list {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    grid-column-gap: 2.4vw;
+    grid-row-gap: 2.4vw;
+    .video {
+      height: 89.2vw;
+      background: #fff;
+      box-sizing: border-box;
+      border: 0.1vw solid #efefef;
+
+      &:hover {
+        .info {
+          .club-name {
+            color: #f3920d;
           }
         }
+      }
+
+      &:nth-child(4n) {
+        margin-right: 0;
+      }
+
+      .cover {
+        height: 64.4vw;
+        position: relative;
+
+        video {
+          display: block;
+          width: 100%;
+          height: 100%;
+          background: #fff;
+        }
+
+        .play {
+          position: absolute;
+          z-index: 2;
+          width: 7.2vw;
+          height: 7.2vw;
+          background: url(~assets/theme-images/common/h5-icon-play.png) no-repeat center;
+          background-size: 7.2vw;
+          left: 50%;
+          top: 50%;
+          transform: translate(-50%, -50%);
+          cursor: pointer;
+        }
 
         .name {
-          color: #fff;
-          font-size: 16px;
-          text-align: center;
+          display: flex;
+          align-items: flex-end;
           width: 100%;
-          line-height: 40px;
-          @include ellipsis(1);
+          height: 16vw;
+          padding: 4.4vw 3vw;
           box-sizing: border-box;
-          padding: 0 16px;
           position: absolute;
           left: 0;
           bottom: 0;
-          background: rgba(0, 0, 0, 0.5);
-          transition: opacity 0.2s;
-        }
+          background: linear-gradient(180deg, rgba(51, 51, 51, 0) 0%, #333333 100%);
 
-        .rank {
-          position: absolute;
-          left: 10px;
-          top: 0;
-          width: 43px;
-          height: 45px;
-          background: url(~assets/theme-images/ross/pc-rank.png) no-repeat
-            center;
-          background-size: 43px;
-          text-align: center;
-          box-sizing: border-box;
-          padding-top: 13px;
-          font-weight: bold;
-          color: #fff;
-          font-size: 13px;
-          transition: opacity 0.2s;
-
-          &.rank-01 {
-            background-image: url(~assets/theme-images/ross/pc-rank-01.png);
-          }
-          &.rank-02 {
-            background-image: url(~assets/theme-images/ross/pc-rank-02.png);
-          }
-          &.rank-03 {
-            background-image: url(~assets/theme-images/ross/pc-rank-03.png);
+          span {
+            color: #fff;
+            font-size: 3vw;
+            line-height: 3.6vw;
+            max-height: 7.2vw;
+            @include ellipsis(2);
           }
         }
       }
 
       .info {
-        padding: 24px 16px;
+        padding: 2.4vw;
         .club-name {
-          font-size: 18px;
+          font-size: 3.4vw;
           color: #282828;
           @include ellipsis(1);
+          cursor: pointer;
+          height: 3.6vw;
         }
         .mobile {
-          font-size: 16px;
+          font-size: 3vw;
           color: #666;
-          margin-top: 12px;
+          margin-top: 1.6vw;
         }
       }
 
@@ -193,47 +282,42 @@ export default {
         display: flex;
         justify-content: space-between;
         align-items: center;
-        margin: 0 16px;
         border-top: 1px solid #efefef;
-        padding-top: 12px;
+        margin: 0 2.4vw;
+        padding-top: 2.4vw;
+        line-height: 3.6vw;
 
-        .date,
-        .praise,
-        .pv {
+        .praise {
           flex-shrink: 0;
-          line-height: 24px;
+          cursor: pointer;
         }
 
-        .praise,
-        .pv {
+        .praise {
           position: relative;
-          margin-left: 16px;
-          padding-left: 30px;
+          padding-left: 4.4vw;
 
           &::after {
             content: '';
             display: block;
-            width: 24px;
-            height: 24px;
-            background: url(~assets/theme-images/common/icon-praise.png)
-              no-repeat center;
-            background-size: 24px;
+            width: 3.6vw;
+            height: 3.6vw;
+            background-size: 3.6vw;
             position: absolute;
             left: 0;
             top: 50%;
             transform: translateY(-50%);
+            background-image: url(~assets/theme-images/common/icon-praise.png);
+            background-position: center;
+            background-repeat: no-repeat;
           }
-        }
 
-        .pv {
-          &::after {
-            background-image: url(~assets/theme-images/common/icon-pv.png);
+          &.active {
+            color: #f3920d;
+            &::after {
+              background-image: url(~assets/theme-images/common/icon-praise-active.png);
+            }
           }
         }
-
-        .date {
-          margin-right: 44px;
-        }
       }
     }
   }

+ 7 - 28
layouts/app-ross.vue

@@ -20,27 +20,6 @@
               </template>
             </div>
             <div class="user-info">
-              <!-- <template v-if="accessToken">
-                <span v-text="userInfo.mobile"></span>
-                <span class="underline logout" @click="logout">退出登录</span>
-              </template>
-              <template v-else>
-                <div class="flex justify-center">
-                  <div
-                    class="login mx-3 rounded-sm border-white md:leading-6"
-                    @click="onLogin"
-                  >
-                    登录
-                  </div>
-                  |
-                  <div
-                    class="register mx-3 rounded-sm border-white md:leading-6"
-                    @click="onRegister"
-                  >
-                    注册
-                  </div>
-                </div>
-              </template> -->
               <template v-if="accessToken">
                 <div class="user-center">
                   <span class="icon el-icon-user-solid"></span>
@@ -124,13 +103,13 @@ export default {
       drawer: false,
       isMounted: false,
       list: [
-        // {
-        //   id: 4,
-        //   name: '挑战赛',
-        //   path: '/activity/challenge',
-        //   icon: 'icon-challenge',
-        //   auth: false,
-        // },
+        {
+          id: 4,
+          name: '挑战赛',
+          path: '/activity/challenge',
+          icon: 'icon-challenge',
+          auth: false,
+        },
         {
           id: 1,
           name: '授权申请',

+ 3 - 2
nuxt.config.js

@@ -44,8 +44,9 @@ export default {
 
   // Global CSS: https://go.nuxtjs.dev/config-css
   css: [
-    'vant/lib/index.css',
-    'element-ui/lib/theme-chalk/index.css',
+    // 'vant/lib/index.css',
+    // 'element-ui/lib/theme-chalk/index.css',
+    '~/assets/css/iconfont.css',
     'swiper/css/swiper.css',
     'animate.css/animate.min.css',
     '~/assets/css/global.css',

+ 6 - 6
package-lock.json

@@ -5308,9 +5308,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001327",
-      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001327.tgz",
-      "integrity": "sha512-1/Cg4jlD9qjZzhbzkzEaAC2JHsP0WrOc8Rd/3a3LuajGzGWR/hD7TVyvq99VqmTy99eVh8Zkmdq213OgvgXx7w=="
+      "version": "1.0.30001436",
+      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz",
+      "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg=="
     },
     "node_modules/caseless": {
       "version": "0.12.0",
@@ -24114,9 +24114,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001327",
-      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001327.tgz",
-      "integrity": "sha512-1/Cg4jlD9qjZzhbzkzEaAC2JHsP0WrOc8Rd/3a3LuajGzGWR/hD7TVyvq99VqmTy99eVh8Zkmdq213OgvgXx7w=="
+      "version": "1.0.30001436",
+      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz",
+      "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg=="
     },
     "caseless": {
       "version": "0.12.0",

+ 498 - 145
pages/_template/ross/activity/challenge/index.vue

@@ -1,14 +1,33 @@
 <template>
   <div class="page">
-    <div class="page-top"></div>
+    <div class="page-top">
+      <div class="top-tip-container">
+        <transition name="fade">
+          <div class="top-tip" v-show="showMobileStatus">{{ showMobile }} 已报名参赛!</div>
+        </transition>
+      </div>
+    </div>
     <div class="page-content">
-      <div class="content">
+      <!-- 视频列表 -->
+      <template v-if="videoList.length > 0">
+        <div class="section-title">
+          <div class="title">视频排名</div>
+          <div class="more" @click="onMoreClick">
+            <span>更多</span>
+            <span class="iconfont icon-arrow-right"></span>
+          </div>
+        </div>
+        <SimpleVideoList :list="videoList" @praise="onPraise" />
+      </template>
+      <!-- 活动介绍 -->
+      <div class="section-title">
+        <div class="title">活动介绍</div>
+      </div>
+      <div class="activity-content">
         <div class="title">赛事活动介绍</div>
         <dl>
           <dt><div>活动介绍</div></dt>
-          <dd>
-            采美平台和云进通在跨境物流领域达成战略合作,为广大采美客户提供轻松无忧的跨境体验!
-          </dd>
+          <dd>采美平台和云进通在跨境物流领域达成战略合作,为广大采美客户提供轻松无忧的跨境体验!</dd>
           <dt><div>活动时间</div></dt>
           <dd>2022.10月10日——2022.12月31日</dd>
           <dt><div>活动奖励</div></dt>
@@ -20,26 +39,36 @@
             相关名称、图片、相关购买凭证,进行设备认证。完成后登录上传视频且抖音分享成功即可!
           </dd>
           <dt><div>评奖规则</div></dt>
-          <dd>
-            用户登录商城注册成功后,按在规定的时间内完成点赞数最高的评定等级
-          </dd>
+          <dd>用户登录商城注册成功后,按在规定的时间内完成点赞数最高的评定等级</dd>
           <dt><div>奖项公布</div></dt>
           <dd>用户在规定时间内完成后10个工作日将公布排名以及颁发奖品</dd>
           <dt><div>注意事项</div></dt>
           <dd>一个抖音账号只能参与一次</dd>
         </dl>
       </div>
-      <div class="entry">
-        <div class="title">活动入口</div>
-        <div class="list">
-          <div class="cover" @click="toDetail">
-            <span class="status" :class="activity.type">
-              {{ activity.text }}
-            </span>
-          </div>
-        </div>
+
+      <div class="control">
+        <div class="btn type1" @click="onToSingUp">点击报名参赛</div>
+        <div class="btn type2" @click="onToUpload">上传抖音视频</div>
       </div>
     </div>
+
+    <!-- 资质上传框 -->
+    <GeneralDialog v-model="showDialog" title="上传营业执照" @confirm="onConfirm">
+      <div class="general-content">
+        <SimpleUploadImage @success="onImageSuccess" @remove="onImageRemove" :imageList="imageList" />
+        <div class="tip">提示:请上传您的真实营业执照,以免影响 您的评奖结果!</div>
+      </div>
+    </GeneralDialog>
+
+    <!-- 提示框 -->
+    <GeneralDialog
+      v-model="promptDialog"
+      title="提示"
+      :content="promptContent"
+      :confirmText="promptConfirmText"
+      @confirm="onPromptConfirm"
+    ></GeneralDialog>
   </div>
 </template>
 
@@ -48,29 +77,309 @@ import { mapGetters } from 'vuex'
 export default {
   layout: 'app-ross',
   data() {
+    const promptContentMap = {
+      0: '恭喜您,报名成功,请去上传您的抖音视频!',
+      1: '抱歉!活动还未开始,暂无法报名!',
+      2: '抱歉!活动已结束,暂无法报名!',
+      3: '抱歉!您已报名,请勿重复操作!',
+      4: '抱歉!请完善您的营业执照!',
+      5: '抱歉!您还未报名,请先报名后再来上传视频!',
+      6: '抱歉!活动还未开始,暂无法上传视频!',
+      7: '抱歉!活动已结束,暂无法上传视频!',
+    }
+
     return {
+      dialog: true,
       activityState: 0,
+      videoList: [],
+      // 用户报名状态
+      signUpStatus: 0, // 0:未报名 1:已报名
+      // 营业执照
+      showDialog: false,
+      imageList: [],
+      qualificationUrl: '',
+      // 提示框信息
+      promptDialog: false,
+      promptContentMap,
+      promptStatus: -1,
+      // 非认证机构报名提交资质报名表单
+      formData: {
+        mobile: '',
+        licenseOssUrl: '',
+        authUserId: '',
+        licenseOssName: '',
+        authId: '',
+      },
+      listQuery: {
+        clubUserId: '',
+        clubUserName: '',
+        authUserId: '',
+        pageNum: 1,
+        pageSize: 12,
+        status: 1,
+      },
+      // 参赛用户列表
+      singInClubList: [],
+      showMobile: '',
+      showMobileStatus: false,
     }
   },
   computed: {
-    ...mapGetters(['routePrefix', 'authUserId']),
-    // 活动状态
-    activity() {
-      const result = {
-        2: { type: 'end', text: '已结束' },
-        1: { type: 'start', text: '进行中' },
-        0: { type: 'wait', text: '未开始' },
-      }
-      return result[this.activityState]
+    ...mapGetters(['routePrefix', 'authUserId', 'accessToken', 'userInfo', 'authId']),
+    // 提示框内容
+    promptContent() {
+      return this.promptContentMap[this.promptStatus]
+    },
+    // 确定按钮内容
+    promptConfirmText() {
+      return this.promptStatus === 2 ? '上传抖音视频' : '确定'
+    },
+    // 参赛机构手机号
+    signInClubMobileList() {
+      return this.singInClubList.map((item) => item.userName.replace(/^(\w{3})\w+(\w{4})$/, '$1****$2'))
     },
   },
   created() {
     this.fetchActivityStatus()
+    this.fetchVideoList()
+    this.fetchSignInClubList()
   },
   mounted() {
+    // 分享当前页面
     this.initAppMessageShareData()
   },
   methods: {
+    // 参赛机构手机号滚动
+    mobileLoop() {
+      if (this.signInClubMobileList.length > 0) {
+        let i = 0
+        this.showMobile = this.signInClubMobileList[i]
+        this.showMobileStatus = true
+        console.log('显示')
+        setTimeout(() => {
+          this.showMobileStatus = false
+          console.log('隐藏')
+        }, 1500)
+        const timer = setInterval(() => {
+          i++
+          setTimeout(() => {
+            this.showMobileStatus = false
+            console.log('隐藏')
+          }, 1500)
+          if (i > this.signInClubMobileList.length - 1) {
+            clearInterval(timer)
+            console.log('结束')
+            return
+          }
+          console.log('显示')
+          this.showMobile = this.signInClubMobileList[i]
+          this.showMobileStatus = true
+        }, 2000)
+      }
+    },
+
+    // 查看更多视频
+    onMoreClick() {
+      this.$router.push(`${this.routePrefix}/activity/challenge/list`)
+    },
+
+    // 点击报名
+    onToSingUp() {
+      // 活动未开启
+      if (this.activityState === 0) {
+        this.promptStatus = 1
+        this.promptDialog = true
+        return
+      }
+      // 活动已结束
+      if (this.activityState === 2) {
+        this.promptStatus = 2
+        this.promptDialog = true
+        return
+      }
+      // 未登录
+      if (!this.accessToken) {
+        this.$store.commit('app/SHOW_LOGIN')
+        return
+      }
+      // 已经报名
+      if (this.signUpStatus === 1) {
+        this.promptStatus = 3
+        this.promptDialog = true
+        return
+      }
+      // 未认证机构
+      if (!this.authId) {
+        this.promptStatus = 4
+        this.promptDialog = true
+        return
+      }
+
+      // 已认证机构
+      if (this.authId) {
+        this.clubSignUpActivity()
+      }
+    },
+
+    // 上传抖音视频
+    onToUpload() {
+      // 活动未开启
+      if (this.activityState === 0) {
+        this.promptStatus = 6
+        this.promptDialog = true
+        return
+      }
+      // 活动已结束
+      if (this.activityState === 2) {
+        this.promptStatus = 7
+        this.promptDialog = true
+        return
+      }
+      // 未登录
+      if (!this.accessToken) {
+        this.$store.commit('app/SHOW_LOGIN')
+        return
+      }
+      // 未报名
+      if (this.signUpStatus === 0) {
+        this.promptDialog = true
+        this.promptStatus = 5
+        return
+      }
+
+      this.$router.push(`${this.routePrefix}/activity/challenge/publish`)
+    },
+
+    // 机构用户报名
+    async clubSignUpActivity() {
+      try {
+        this.formData.mobile = this.userInfo.mobile
+        this.formData.authUserId = this.authUserId
+        this.formData.authId = this.userInfo.authId
+        await this.$http.api.clubSignUpActivity(this.formData)
+        // 报名成功
+        this.promptStatus = 0
+        this.signUpStatus = 1
+        this.promptDialog = true
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 提示框确定事件
+    onPromptConfirm() {
+      this.promptDialog = false
+      // 上传资质
+      if (this.promptStatus === 4) {
+        this.showDialog = true
+        return
+      }
+      // 去上传抖音视频
+      if (this.promptStatus === 0) {
+        this.$router.push(`${this.routePrefix}/activity/challenge/publish`)
+        return
+      }
+    },
+
+    // 提交资质文件
+    onConfirm() {
+      this.showDialog = false
+      this.clubSignUpActivity()
+    },
+
+    // 文件上传成功
+    onImageSuccess({ response, fileList }) {
+      this.imageList = fileList
+      this.formData.licenseOssUrl = response.data
+    },
+
+    // 文件移除
+    onImageRemove() {
+      this.imageList = []
+      this.formData.licenseOssUrl = ''
+    },
+
+    // 视频点赞
+    async onPraise(video) {
+      try {
+        if (!this.accessToken) {
+          this.$toast('请先登录')
+          this.$store.commit('app/SHOW_LOGIN')
+          return
+        }
+        const status = video.diggStatus === 0 ? 1 : 0
+        const { clubUserId } = this.userInfo
+        await this.$http.api.diggcountVideo({
+          id: video.id,
+          diggStatus: status,
+          clubUserId,
+          clubUserIds: video.clubUserIds,
+        })
+        video.diggStatus = status
+        let tip = ''
+        if (video.diggStatus === 0) {
+          tip = '已取消点赞'
+          video.diggCount--
+        } else {
+          tip = '点赞成功'
+          video.diggCount++
+        }
+        this.$toast.success(tip)
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取视频列表
+    async fetchVideoList() {
+      try {
+        this.listQuery.authUserId = this.authUserId
+        this.listQuery.clubUserId = this.userInfo.clubUserId
+        const res = await this.$http.api.fetchClubVideoList(this.listQuery)
+        console.log(res)
+        this.videoList = res.data.list
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取用户活动报名状态
+    async fetchContestInfo() {
+      try {
+        const res = await this.$http.api.checkContestInfo({ mobile: this.userInfo.mobile })
+        this.signUpStatus = res.data.contestStatus
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取参赛的用户
+    async fetchSignInClubList() {
+      try {
+        const res = await this.$http.api.fetchEnteredClubList({ authUserId: this.authUserId })
+        this.singInClubList = res.data
+        this.mobileLoop()
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取活动状态
+    async fetchActivityStatus() {
+      try {
+        const res = await this.$http.api.fetchActivityStatus({
+          authUserId: this.authUserId,
+        })
+        if (!res.data) return
+        this.activityState = res.data.activityState
+        if (!this.accessToken) return
+        this.fetchContestInfo()
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 分享当前页面
     initAppMessageShareData() {
       //  重试次数
       let retryCount = 1
@@ -89,48 +398,91 @@ export default {
         })
       })
     },
-
-    // 活动详情
-    toDetail() {
-      const url = `${this.routePrefix}/activity/challenge/list`
-      this.$router.push(url)
-    },
-
-    // 获取活动状态
-    async fetchActivityStatus() {
-      try {
-        const res = await this.$http.api.fetchActivityStatus({
-          authUserId: this.authUserId,
-        })
-        if (!res.data) return
-        this.activityState = res.data.activityState
-      } catch (error) {
-        console.log(error)
-      }
-    },
   },
 }
 </script>
 
 <style lang="scss" scoped>
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.5s;
+}
+.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
+  opacity: 0;
+}
+
 @media screen and (min-width: 768px) {
+  .general-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .tip {
+      width: 266px;
+      margin: 0 auto;
+      margin-top: 36px;
+      line-height: 24px;
+      font-size: 14px;
+      color: #b2b2b2;
+      text-align: center;
+    }
+  }
+
   .page {
     padding-bottom: 120px;
     background: #fff;
     .page-top {
       width: 100%;
       height: 530px;
-      background: url(~assets/theme-images/ross/pc-banner-activity.png)
-        no-repeat center;
+      background: url(~assets/theme-images/ross/pc-banner-activity.png) no-repeat center;
       background-size: auto 530px;
+
+      .top-tip-container {
+        position: relative;
+        width: 870px;
+        margin: 0 auto;
+
+        .top-tip {
+          position: absolute;
+          left: 0;
+          top: 32px;
+          width: 200px;
+          height: 32px;
+          background: rgba(0, 0, 0, 0.4);
+          border-radius: 2px;
+          font-size: 14px;
+          color: #fff;
+          text-align: center;
+          line-height: 32px;
+        }
+      }
     }
 
     .page-content {
-      width: 872px;
+      width: 1196px;
       margin: 0 auto;
-      margin-top: 16px;
 
-      .content {
+      .section-title {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 32px 0 16px;
+        .title {
+          font-size: 16px;
+          font-weight: bold;
+          color: #282828;
+        }
+        .more {
+          font-size: 14px;
+          color: #999;
+          cursor: pointer;
+
+          &:hover {
+            color: #f3920d;
+          }
+        }
+      }
+
+      .activity-content {
         min-height: 1170px;
 
         .title {
@@ -142,12 +494,13 @@ export default {
           margin-bottom: 70px;
           padding-top: 20px;
           font-weight: bold;
-          background: url(~assets/theme-images/ross/pc_activity_title_01.png)
-            no-repeat center;
+          background: url(~assets/theme-images/ross/pc_activity_title_01.png) no-repeat center;
           background-size: auto 80px;
         }
 
         dl {
+          width: 872px;
+          margin: 0 auto;
           dt {
             font-size: 30px;
             text-align: center;
@@ -171,52 +524,26 @@ export default {
         }
       }
 
-      .entry {
-        margin-top: 120px;
-        .title {
-          font-size: 40px;
-          color: #333;
-          height: 80px;
-          box-sizing: border-box;
+      .control {
+        .btn {
+          width: 295px;
+          height: 50px;
+          line-height: 50px;
+          font-size: 16px;
           text-align: center;
-          margin-bottom: 70px;
-          padding-top: 20px;
-          font-weight: bold;
-          background: url(~assets/theme-images/ross/pc_activity_title_02.png)
-            no-repeat center;
-          background-size: auto 80px;
-        }
-
-        .cover {
-          position: relative;
-          width: 856px;
-          height: 340px;
-          margin: 0 auto;
+          margin: 16px auto 0;
           cursor: pointer;
-          background: url(~assets/theme-images/ross/activity-entry-bg.png)
-            no-repeat center;
-          background-size: 856px 340px;
-
-          .status {
-            position: absolute;
-            left: 0;
-            top: 0;
-            width: 72px;
-            height: 32px;
-            line-height: 32px;
-            border-radius: 8px 0 8px 0;
-            color: #fff;
-            text-align: center;
+          border-radius: 4px;
 
-            &.wait {
-              background: #f94b4b;
-            }
-            &.start {
-              background: #f3920d;
-            }
-            &.end {
-              background: rgba(0, 0, 0, 0.3);
-            }
+          &.type1 {
+            background: #f3920d;
+            color: #ffffff;
+          }
+          &.type2 {
+            border: 1px solid #f3920d;
+            box-sizing: border-box;
+            line-height: 48px;
+            color: #f3920d;
           }
         }
       }
@@ -225,14 +552,48 @@ export default {
 }
 
 @media screen and (max-width: 768px) {
+  .general-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .tip {
+      width: 57vw;
+      margin: 0 auto;
+      margin-top: 4.8vw;
+      line-height: 5.6vw;
+      font-size: 3.2vw;
+      color: #b2b2b2;
+      text-align: center;
+    }
+  }
+
   .page {
     padding-bottom: 10vw;
     .page-top {
       width: 100%;
       height: 100vw;
-      background: url(~assets/theme-images/ross/h5-banner-activity.png)
-        no-repeat center;
+      background: url(~assets/theme-images/ross/h5-banner-activity.png) no-repeat center;
       background-size: auto 100vw;
+
+      .top-tip-container {
+        position: relative;
+        width: 100%;
+        margin: 0 auto;
+
+        .top-tip {
+          position: absolute;
+          left: 4vw;
+          top: 6.4vw;
+          width: 46vw;
+          height: 7.2vw;
+          background: rgba(0, 0, 0, 0.4);
+          border-radius: 0.2vw;
+          font-size: 3.2vw;
+          color: #fff;
+          text-align: center;
+          line-height: 7.2vw;
+        }
+      }
     }
 
     .page-content {
@@ -240,7 +601,28 @@ export default {
       margin: 0 auto;
       margin-top: 3.2vw;
 
-      .content {
+      .section-title {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 32px 0 16px;
+        .title {
+          font-size: 16px;
+          font-weight: bold;
+          color: #282828;
+        }
+        .more {
+          font-size: 14px;
+          color: #999;
+          cursor: pointer;
+
+          &:hover {
+            color: #f3920d;
+          }
+        }
+      }
+
+      .activity-content {
         min-height: 116.3vw;
         margin-top: 8vw;
 
@@ -253,8 +635,7 @@ export default {
           margin-bottom: 8vw;
           padding-top: 2.4vw;
           font-weight: bold;
-          background: url(~assets/theme-images/ross/h5_activity_title_01.png)
-            no-repeat center;
+          background: url(~assets/theme-images/ross/h5_activity_title_01.png) no-repeat center;
           background-size: auto 9vw;
         }
 
@@ -282,54 +663,26 @@ export default {
         }
       }
 
-      .entry {
-        margin-top: 15vw;
-        .title {
-          font-size: 5vw;
-          color: #333;
-          height: 9vw;
-          box-sizing: border-box;
+      .control {
+        .btn {
+          width: 85.5vw;
+          height: 12vw;
+          line-height: 12vw;
+          font-size: 3.6vw;
           text-align: center;
-          margin-bottom: 8vw;
-          padding-top: 2.4vw;
-          font-weight: bold;
-          background: url(~assets/theme-images/ross/h5_activity_title_02.png)
-            no-repeat center;
-          background-size: auto 9vw;
-        }
-
-        .cover {
-          position: relative;
-          width: 85.6vw;
-          height: 34vw;
-          margin: 0 auto;
-          // background: pink;
+          margin: 2.4vw auto 0;
           cursor: pointer;
-          background: url(~assets/theme-images/ross/activity-entry-bg.png)
-            no-repeat center;
-          background-size: 85.6vw 34vw;
-
-          .status {
-            position: absolute;
-            left: 0;
-            top: 0;
-            width: 12.8vw;
-            height: 5.6vw;
-            line-height: 5.6vw;
-            border-radius: 0.8vw 0 0.8vw 0;
-            color: #fff;
-            text-align: center;
-            font-size: 3vw;
+          border-radius: 0.4vw;
 
-            &.wait {
-              background: #f94b4b;
-            }
-            &.start {
-              background: #f3920d;
-            }
-            &.end {
-              background: rgba(0, 0, 0, 0.3);
-            }
+          &.type1 {
+            background: #f3920d;
+            color: #ffffff;
+          }
+          &.type2 {
+            border: 0.1vw solid #f3920d;
+            box-sizing: border-box;
+            line-height: 11.8vw;
+            color: #f3920d;
           }
         }
       }

+ 157 - 339
pages/_template/ross/activity/challenge/list.vue

@@ -1,19 +1,19 @@
 <template>
-  <div class="page">
-    <van-list
-      v-model="loadingMore"
-      :finished="finished"
-      :immediate-check="false"
-      :finished-text="total ? '没有更多了' : ''"
-      @load="fetchVideoList"
-    >
-      <div class="page-top"></div>
+  <van-list
+    v-model="loadingMore"
+    :finished="finished"
+    :immediate-check="false"
+    :finished-text="total ? '没有更多了' : ''"
+    @load="fetchVideoList"
+    class="vant-list"
+  >
+    <div class="page">
       <div class="page-content">
         <!-- 操作区域 -->
         <div class="control">
           <div class="search">
             <el-input
-              placeholder="搜索登录账户后四位或所属机构"
+              placeholder="搜索手机号后四位或机构名称"
               @keyup.enter.native="getList"
               v-model="listQuery.clubUserName"
               clearable
@@ -26,62 +26,24 @@
         </div>
         <!-- 视频列表区域 -->
         <div class="video-content">
-          <div class="title">视频排名</div>
-          <div class="video-list">
-            <div
-              class="video"
-              v-for="(item, index) in list"
-              :key="index"
-              @click="onPlay(item)"
-            >
-              <div class="cover">
-                <img :src="item.cover" alt="" />
-                <div class="name">{{ item.title }}</div>
-                <div class="rank" :class="'rank-0' + (index + 1)">
-                  {{ index + 1 }}
-                </div>
-                <div class="play" @click="onPlay(item)"></div>
-              </div>
-              <div class="info">
-                <div class="club-name">{{ item.authParty }}</div>
-                <div class="mobile">{{ item.userName | mobileFormat }}</div>
-              </div>
-              <div class="foot">
-                <div class="date">{{ item.releaseTime | dateFormat }}</div>
-                <div class="praise">{{ item.diggCount }}</div>
-                <div class="pv">{{ item.playCount }}</div>
-              </div>
-            </div>
-          </div>
+          <div class="title">参赛视频</div>
+          <SimpleVideoList :list="list" @praise="onPraise" />
           <!-- 列表为空 -->
-          <SimpleEmpty
-            v-if="!total && !isRequest"
-            name="icon-empty-video.png"
-            description="暂无视频~"
-          ></SimpleEmpty>
+          <SimpleEmpty v-if="!total && !isRequest" name="icon-empty-video.png" description="暂无视频~"></SimpleEmpty>
         </div>
       </div>
-    </van-list>
-
-    <div class="publish" @click="onPublish">发布视频</div>
-
-    <!-- 发布提示对话框 -->
-    <SimpleDialog
-      v-model="dialog"
-      :confirmText="dialogOption.confirmText"
-      :description="dialogOption.description"
-      :cancel="dialogOption.cancel"
-      @confirm="onConfirm"
-      @cancel="onCancel"
-    ></SimpleDialog>
-
-    <!-- 视频播放组件 -->
-    <SimpleVideoPlayer
-      :videoSrc="videoUrl"
-      :description="description"
-      ref="videoPlayer"
-    ></SimpleVideoPlayer>
-  </div>
+
+      <div class="publish" @click="onPublish">发布视频</div>
+      <!-- 提示框 -->
+      <GeneralDialog
+        v-model="promptDialog"
+        title="提示"
+        :content="promptContent"
+        :confirmText="promptConfirmText"
+        @confirm="onPromptConfirm"
+      ></GeneralDialog>
+    </div>
+  </van-list>
 </template>
 
 <script>
@@ -89,18 +51,26 @@ import { mapGetters } from 'vuex'
 import { debounce } from '~/utils'
 export default {
   layout: 'app-ross',
-  filters: {
-    mobileFormat(mobile) {
-      return mobile ? mobile.replace(/^(\w{3})\w+(\w{4})$/, '$1****$2') : ''
-    },
-  },
   data() {
+    const promptContentMap = {
+      0: '恭喜您,报名成功,请去上传您的抖音视频!',
+      1: '抱歉!活动还未开始,暂无法报名!',
+      2: '抱歉!活动已结束,暂无法报名!',
+      3: '抱歉!您已报名,请勿重复操作!',
+      4: '抱歉!请完善您的营业执照!',
+      5: '抱歉!您还未报名,请先报名后再来上传视频!',
+      6: '抱歉!活动还未开始,暂无法上传视频!',
+      7: '抱歉!活动已结束,暂无法上传视频!',
+    }
+
     return {
       isRequest: true,
       finished: true, // 列表加载是否完毕(加载完了所有数据)
       loadingMore: false, // 是否正在加载
       listQuery: {
+        clubUserId: '',
         clubUserName: '',
+        authUserId: '',
         pageNum: 1,
         pageSize: 20,
         status: 1,
@@ -114,15 +84,28 @@ export default {
         cancel: false,
       },
       publishInfo: {},
-      videoUrl: '',
-      description: '',
+
+      // 提示框信息
+      promptDialog: false,
+      promptContentMap,
+      promptStatus: -1,
+      activityState: 0,
+      // 用户报名状态
+      signUpStatus: 0, // 0:未报名 1:已报名
     }
   },
   computed: {
     ...mapGetters(['routePrefix', 'accessToken', 'userInfo', 'authUserId']),
+    promptContent() {
+      return this.promptContentMap[this.promptStatus]
+    },
+    promptConfirmText() {
+      return this.promptStatus === 2 ? '上传抖音视频' : '确定'
+    },
   },
   created() {
     this.getList()
+    this.fetchActivityStatus()
   },
   beforeDestroy() {
     this.$toast.clear()
@@ -146,7 +129,8 @@ export default {
         this.loadingMore = true
         this.isRequest = true
         this.listQuery.authUserId = this.authUserId
-        const res = await this.$http.api.fetchVideoList(this.listQuery)
+        this.listQuery.clubUserId = this.userInfo.clubUserId
+        const res = await this.$http.api.fetchClubVideoList(this.listQuery)
         this.list = [...this.list, ...res.data.list]
         this.total = res.data.total
         this.listQuery.pageNum++
@@ -160,102 +144,135 @@ export default {
       }
     }, 500),
 
-    // 提示框确定
-    onConfirm() {
-      this.dialog = false
-      if (this.dialogOption.confirmText === '去认证') {
-        const path = `${this.routePrefix}/form/club-register`
-        this.$router.push(path)
+    // 视频点赞
+    async onPraise(video) {
+      try {
+        if (!this.accessToken) {
+          this.$toast('请先登录')
+          this.$store.commit('app/SHOW_LOGIN')
+          return
+        }
+        const status = video.diggStatus === 0 ? 1 : 0
+        const { clubUserId } = this.userInfo
+        await this.$http.api.diggcountVideo({
+          id: video.id,
+          diggStatus: status,
+          clubUserId,
+          clubUserIds: video.clubUserIds,
+        })
+        video.diggStatus = status
+        let tip = ''
+        if (video.diggStatus === 0) {
+          tip = '已取消点赞'
+          video.diggCount--
+        } else {
+          tip = '点赞成功'
+          video.diggCount++
+        }
+        this.$toast.success(tip)
+      } catch (error) {
+        console.log(error)
       }
     },
 
-    // 提示框取消
-    onCancel() {
-      this.dialog = false
-    },
-
     // 去发布视频
     async onPublish() {
+      // 活动未开启
+      if (this.activityState === 0) {
+        this.promptStatus = 6
+        this.promptDialog = true
+        return
+      }
+      // 活动已结束
+      if (this.activityState === 2) {
+        this.promptStatus = 7
+        this.promptDialog = true
+        return
+      }
+      // 未登录
       if (!this.accessToken) {
-        this.$toast('请先登录')
-        this.formType = 'login'
         this.$store.commit('app/SHOW_LOGIN')
         return
       }
-
-      if (!this.userInfo.authId) {
-        this.dialogOption.description = '抱歉,由于您未认证机构,无法参与!'
-        this.dialogOption.confirmText = '去认证'
-        this.dialog = true
+      // 未报名
+      if (this.signUpStatus === 0) {
+        this.promptDialog = true
+        this.promptStatus = 5
         return
       }
 
-      // 查询活动状态
-      await this.checkActivityPublish()
+      this.$router.push(`${this.routePrefix}/activity/challenge/publish`)
+    },
 
-      if (this.publishInfo.activityState === 0) {
-        this.dialogOption.description = '抱歉,活动未开始!'
-        this.dialog = true
+    // 提示框确定事件
+    onPromptConfirm() {
+      this.promptDialog = false
+      // 上传资质
+      if (this.promptStatus === 4) {
+        this.showDialog = true
         return
-      } else if (this.publishInfo.activityState === 2) {
-        this.dialogOption.description = '抱歉,活动已结束,暂无法发布视频!'
-        this.dialog = true
-        return
-      } else if (this.publishInfo.releaseStatus === 1) {
-        this.dialogOption.description =
-          '抱歉,平台规定每个用户只能发布一个视频,由于您已发布过视频,请勿再次发布!'
-        this.dialog = true
+      }
+      // 去上传抖音视频
+      if (this.promptStatus === 0) {
+        this.$router.push(`${this.routePrefix}/activity/challenge/publish`)
         return
       }
-      const url = `${this.routePrefix}/activity/challenge/publish`
-      this.$router.push(url)
     },
 
-    // 验证发布状态
-    async checkActivityPublish() {
-      if (!this.accessToken) return
-      const { authId, mobile } = this.userInfo
+    // 获取用户活动报名状态
+    async fetchContestInfo() {
       try {
-        const res = await this.$http.api.checkActivityPublish({
-          authUserId: this.authUserId,
-          authId,
-          userName: mobile,
-        })
-        this.publishInfo = res.data
+        const { mobile } = this.userInfo
+        const res = await this.$http.api.checkContestInfo({ mobile })
+        this.signUpStatus = res.data.contestStatus
       } catch (error) {
         console.log(error)
       }
     },
 
-    // 播放视频
-    onPlay(row) {
-      this.videoUrl = row.ossUrl
-      this.description = row.title
-      this.$refs.videoPlayer.open()
+    // 获取活动状态
+    async fetchActivityStatus() {
+      try {
+        const res = await this.$http.api.fetchActivityStatus({
+          authUserId: this.authUserId,
+        })
+        if (!res.data) return
+        this.activityState = res.data.activityState
+        if (!this.accessToken) return
+        this.fetchContestInfo()
+      } catch (error) {
+        console.log(error)
+      }
     },
   },
 }
 </script>
 
 <style lang="scss" scoped>
-::v-deep {
-  .el-input.is-active .el-input__inner,
-  .el-input__inner:focus {
-    border-color: #f3920d;
+@media screen and (min-width: 768px) {
+  ::v-deep {
+    .el-input.is-active .el-input__inner,
+    .el-input__inner:focus {
+      border-color: #f3920d;
+    }
+    .van-list__finished-text,
+    .van-list__placeholder {
+      background: #fff;
+    }
+  }
+
+  .vant-list {
+    background: #fff;
   }
-}
 
-@media screen and (min-width: 768px) {
   .page {
     background: #fff;
-    min-height: calc(100vh - 80px - 80px);
-    padding-bottom: 20px;
+    min-height: calc(100vh - 80px - 80px - 50px);
 
     .page-top {
       width: 100%;
       height: 530px;
-      background: url(~assets/theme-images/ross/pc-banner-activity.png)
-        no-repeat center;
+      background: url(~assets/theme-images/ross/pc-banner-activity.png) no-repeat center;
       background-size: auto 530px;
     }
 
@@ -319,210 +336,16 @@ export default {
           font-weight: bold;
           margin: 16px 0;
         }
-
-        .video-list {
-          &::after {
-            content: '';
-            display: block;
-            clear: both;
-          }
-
-          .video {
-            float: left;
-            width: 288px;
-            height: 356px;
-            background: #fff;
-            box-sizing: border-box;
-            border: 1px solid #efefef;
-            margin: 0 16px 16px 0;
-            transition: all 0.4s;
-
-            &:hover {
-              transform: translateY(-10px);
-              box-shadow: 0px 6px 16px 1px rgba(0, 0, 0, 0.1);
-
-              .info {
-                .club-name {
-                  color: #f3920d;
-                }
-              }
-            }
-
-            &:nth-child(4n) {
-              margin-right: 0;
-            }
-
-            .cover {
-              width: 100%;
-              height: 198px;
-              position: relative;
-
-              img {
-                display: block;
-                width: 100%;
-                height: 100%;
-              }
-
-              &::after {
-                content: '';
-                display: block;
-                position: absolute;
-                left: 0;
-                top: 0;
-                z-index: 1;
-                width: 100%;
-                height: 100%;
-                background: #000;
-                opacity: 0;
-                transition: opacity 0.2s;
-              }
-
-              .play {
-                position: absolute;
-                z-index: 2;
-                width: 48px;
-                height: 48px;
-                background: url(~assets/theme-images/common/pc-icon-play.png)
-                  no-repeat center;
-                background-size: 48px;
-                left: 50%;
-                top: 50%;
-                transform: translate(-50%, -50%);
-                opacity: 0;
-                transition: opacity 0.2s;
-                cursor: pointer;
-              }
-
-              &:hover {
-                .play {
-                  opacity: 1;
-                }
-
-                .rank,
-                .name {
-                  opacity: 0;
-                }
-
-                &::after {
-                  opacity: 0.4;
-                }
-              }
-
-              .name {
-                color: #fff;
-                font-size: 16px;
-                text-align: center;
-                width: 100%;
-                line-height: 40px;
-                @include ellipsis(1);
-                box-sizing: border-box;
-                padding: 0 16px;
-                position: absolute;
-                left: 0;
-                bottom: 0;
-                background: rgba(0, 0, 0, 0.5);
-                transition: opacity 0.2s;
-              }
-
-              .rank {
-                position: absolute;
-                left: 10px;
-                top: 0;
-                width: 43px;
-                height: 45px;
-                background: url(~assets/theme-images/ross/pc-rank.png) no-repeat
-                  center;
-                background-size: 43px;
-                text-align: center;
-                box-sizing: border-box;
-                padding-top: 13px;
-                font-weight: bold;
-                color: #fff;
-                font-size: 13px;
-                transition: opacity 0.2s;
-
-                &.rank-01 {
-                  background-image: url(~assets/theme-images/ross/pc-rank-01.png);
-                }
-                &.rank-02 {
-                  background-image: url(~assets/theme-images/ross/pc-rank-02.png);
-                }
-                &.rank-03 {
-                  background-image: url(~assets/theme-images/ross/pc-rank-03.png);
-                }
-              }
-            }
-
-            .info {
-              padding: 24px 16px;
-              .club-name {
-                font-size: 18px;
-                color: #282828;
-                @include ellipsis(1);
-              }
-              .mobile {
-                font-size: 16px;
-                color: #666;
-                margin-top: 12px;
-              }
-            }
-
-            .foot {
-              color: #999999;
-              font-size: 14px;
-              display: flex;
-              justify-content: space-between;
-              align-items: center;
-              margin: 0 16px;
-              border-top: 1px solid #efefef;
-              padding-top: 12px;
-
-              .date,
-              .praise,
-              .pv {
-                flex-shrink: 0;
-                line-height: 24px;
-              }
-
-              .praise,
-              .pv {
-                position: relative;
-                margin-left: 16px;
-                padding-left: 30px;
-
-                &::after {
-                  content: '';
-                  display: block;
-                  width: 24px;
-                  height: 24px;
-                  background: url(~assets/theme-images/common/icon-praise.png)
-                    no-repeat center;
-                  background-size: 24px;
-                  position: absolute;
-                  left: 0;
-                  top: 50%;
-                  transform: translateY(-50%);
-                }
-              }
-
-              .pv {
-                &::after {
-                  background-image: url(~assets/theme-images/common/icon-pv.png);
-                }
-              }
-
-              .date {
-                margin-right: 44px;
-              }
-            }
-          }
-        }
       }
     }
   }
 }
 
 @media screen and (max-width: 768px) {
+  .vant-list {
+    padding-bottom: 28vw;
+  }
+
   .page {
     background: #fff;
     position: relative;
@@ -531,17 +354,15 @@ export default {
     .page-top {
       width: 100%;
       height: 100vw;
-      background: url(~assets/theme-images/ross/h5-banner-activity.png)
-        no-repeat center;
+      background: url(~assets/theme-images/ross/h5-banner-activity.png) no-repeat center;
       background-size: auto 100vw;
     }
 
     .publish {
-      // position: fixed;
-      // bottom: 14vw;
-      // left: 7.4vw;
+      position: fixed;
+      bottom: 16vw;
+      left: 7.4vw;
       width: 85.6vw;
-      margin: 4vw auto;
       height: 12vw;
       background: #f3920d;
       color: #fff;
@@ -643,8 +464,7 @@ export default {
                 z-index: 2;
                 width: 48px;
                 height: 48px;
-                background: url(~assets/theme-images/common/pc-icon-play.png)
-                  no-repeat center;
+                background: url(~assets/theme-images/common/pc-icon-play.png) no-repeat center;
                 background-size: 48px;
                 left: 50%;
                 top: 50%;
@@ -676,8 +496,7 @@ export default {
                 top: 0;
                 width: 7.2vw;
                 height: 7.5vw;
-                background: url(~assets/theme-images/ross/h5-rank.png) no-repeat
-                  center;
+                background: url(~assets/theme-images/ross/h5-rank.png) no-repeat center;
                 background-size: 7.2vw;
                 text-align: center;
                 box-sizing: border-box;
@@ -739,8 +558,7 @@ export default {
                   display: block;
                   width: 3.6vw;
                   height: 3.6vw;
-                  background: url(~assets/theme-images/common/icon-praise.png)
-                    no-repeat center;
+                  background: url(~assets/theme-images/common/icon-praise.png) no-repeat center;
                   background-size: 3.6vw;
                   position: absolute;
                   left: 0;

+ 16 - 15
pages/_template/ross/activity/challenge/message.vue

@@ -2,16 +2,9 @@
   <div class="page">
     <div class="page-top">
       <div class="icon-submit-succsss"></div>
-      <div class="tip">视频发布成功&nbsp;!</div>
+      <div class="tip">上传成功</div>
     </div>
-    <div class="page-content">
-      <div class="title">温馨提示:</div>
-      <div class="content">
-        视频发布成功后,您可去首页查看您的视频排名,同时,平台会在1-2个工作日内将您的视频上传至抖音平台。上传成功后,平台会以短信的形式,将抖音口令发送到您的手机上,您可复制该抖音口令在抖音平台打开视频或直接在抖音平台搜索“ROSS”抖音账号或“视频标题”找到自己的视频,再将视频通过抖音分享方式分享给更多的好友来帮您点赞获得排名。如1-2工日后,您还未收到抖音口令或在抖音平台找不到自己发布的视频,可致电客服咨询,电话:
-        <a href="tel:0755-85885625">0755-85885625</a>
-      </div>
-    </div>
-    <div class="btn back" @click="onBack">返回</div>
+    <div class="btn back" @click="onBack">返回首页</div>
   </div>
 </template>
 
@@ -35,18 +28,27 @@ export default {
 <style lang="scss" scoped>
 @media screen and (min-width: 768px) {
   .btn.back {
-    display: none;
+    width: 295px;
+    height: 50px;
+    background: #f3920d;
+    border-radius: 4px 4px 4px 4px;
+    opacity: 1;
+    color: #fff;
+    font-size: 16px;
+    text-align: center;
+    line-height: 50px;
+    margin: 0 auto;
+    cursor: pointer;
   }
   .page-top {
     display: flex;
     align-items: center;
     flex-direction: column;
-    padding: 150px 0 80px;
+    padding: 150px 0 64px;
     .icon-submit-succsss {
       width: 64px;
       height: 64px;
-      background: url(~assets/theme-images/common/pc-icon-submit-success.png)
-        no-repeat center;
+      background: url(~assets/theme-images/common/pc-icon-submit-success.png) no-repeat center;
       background-size: 64px;
     }
 
@@ -86,8 +88,7 @@ export default {
     .icon-submit-succsss {
       width: 12vw;
       height: 12vw;
-      background: url(~assets/theme-images/common/pc-icon-submit-success.png)
-        no-repeat center;
+      background: url(~assets/theme-images/common/pc-icon-submit-success.png) no-repeat center;
       background-size: 12vw;
     }
 

+ 258 - 85
pages/_template/ross/activity/challenge/publish.vue

@@ -2,49 +2,63 @@
   <div class="page">
     <div class="page-content">
       <el-form label-position="top" :model="formData" :rules="rules" ref="form">
-        <el-form-item label="发布抖音视频:" prop="description">
+        <el-form-item label="作品描述:" prop="title">
           <el-input
             type="textarea"
-            rows="10"
-            v-model="formData.description"
+            rows="6"
+            v-model="formData.title"
             placeholder="添加作品描述,能让更多的人看到..."
             maxlength="300"
             show-word-limit
           ></el-input>
         </el-form-item>
-        <el-form-item label="视频封面:" prop="coverImage">
-          <el-input v-show="false" v-model="formData.coverImage"></el-input>
-          <div>
-            <SimpleUploadImage
-              :limit="1"
-              :image-list="coverList"
-              :before-upload="beforeCoverImageUpload"
-              tip="建议尺寸 286 * 198px"
-              @success="uploadCoverImageSuccess"
-              @remove="handleCoverImageRemove"
-            ></SimpleUploadImage>
-          </div>
+        <el-form-item label="上传视频:" prop="ossUrl">
+          <SimpleOssUpload @success="onVideoUploadSuccess" :limit="1"></SimpleOssUpload>
+          <el-input v-show="false" v-model="formData.ossUrl"></el-input>
         </el-form-item>
-        <el-form-item label="视频文件:" prop="fileUrl">
-          <SimpleOssUpload
-            @success="onVideoUploadSuccess"
-            :limit="1"
-          ></SimpleOssUpload>
-          <el-input v-show="false" v-model="formData.fileUrl"></el-input>
+        <el-form-item label="抖音视频链接:" prop="linked">
+          <el-input type="textarea" rows="3" v-model="formData.linked" placeholder="请添加抖音视频链接"></el-input>
         </el-form-item>
       </el-form>
       <div class="tip">
         <div class="title">温馨提示:</div>
         <div class="content">
-          视频文件大小不超过8G,时长在30分钟以内;分辨率为720p(720x1280)及以上;
-          支持常用视频格式,推荐使用mp4、webm
+          视频文件大小不超过8G,时长在30分钟以内;分辨率为720p(720x1280)及以上; 支持常用视频格式,推荐使用mp4、webm
         </div>
       </div>
       <div class="control">
+        <div class="button publish" @click="onPublish">提交</div>
         <div class="button cancel" @click="onBack">取消</div>
-        <div class="button publish" @click="onPublish">发布</div>
       </div>
     </div>
+
+    <!-- 已上传视频列表 -->
+    <div class="line"></div>
+    <div class="video-container" v-if="videoList.length > 0">
+      <div class="title"><span>已上传</span>({{ videoList.length }}个)</div>
+      <div class="video-list">
+        <div class="video" v-for="(item, index) in videoList" :key="index" @click="onPlay(item)">
+          <div class="cover">
+            <video :src="item.ossUrl"></video>
+            <div class="name">
+              <span>{{ item.title }}</span>
+            </div>
+            <div class="play"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 提示框 -->
+    <GeneralDialog
+      v-model="promptDialog"
+      title="提示"
+      content="抱歉,最多只能上传3个视频!!"
+      @confirm="promptDialog = false"
+    ></GeneralDialog>
+
+    <!-- 视频播放组件 -->
+    <SimpleVideoPlayer :videoSrc="videoUrl" :description="description" ref="videoPlayer"></SimpleVideoPlayer>
   </div>
 </template>
 
@@ -54,44 +68,50 @@ export default {
   layout: 'app-ross',
   data() {
     return {
+      promptDialog: false,
       formData: {
-        coverImage: '',
-        fileUrl: '',
-        description: '',
+        authUserId: '',
+        authId: '',
+        userName: '',
+        title: '',
         ossName: '',
+        cover: '',
+        ossUrl: '',
+        linked: '',
       },
       rules: {
-        coverImage: [
-          {
-            required: true,
-            message: '请上传视频封面图片',
-            trigger: ['change'],
-          },
-        ],
-        fileUrl: [
-          {
-            required: true,
-            message: '请上传视频',
-            trigger: ['change'],
-          },
-        ],
-        description: [
-          {
-            required: true,
-            message: '请上传视频',
-            trigger: ['blur'],
-          },
-        ],
+        ossUrl: [{ required: true, message: '请上传视频', trigger: ['change'] }],
+        linked: [{ required: true, message: '请填写抖音链接', trigger: ['blur'] }],
       },
-      coverList: [],
+      videoList: [],
+      videoUrl: '',
+      description: '',
     }
   },
   computed: {
     ...mapGetters(['routePrefix', 'userInfo', 'authUserId']),
   },
+  created() {
+    this.fetchVideoList()
+  },
   methods: {
+    // 获取当前机构已发布的视频列表
+    async fetchVideoList() {
+      try {
+        const { clubUserId, mobile } = this.userInfo
+        const res = await this.$http.api.fetchOwnerVideoList({ mobile: mobile, clubUserId: clubUserId })
+        this.videoList = res.data
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
     // 发布视频
     async onPublish() {
+      if (this.videoList.length >= 3) {
+        this.promptDialog = true
+        return
+      }
       try {
         await this.$refs.form.validate()
         this.publishVideoSave()
@@ -104,41 +124,19 @@ export default {
     async publishVideoSave() {
       try {
         const { authId, mobile } = this.userInfo
-        await this.$http.api.publishVideoSave({
-          authId,
-          authUserId: this.authUserId,
-          userName: mobile,
-          title: this.formData.description,
-          ossName: this.formData.ossName,
-          cover: this.formData.coverImage,
-          ossUrl: this.formData.fileUrl,
-        })
+        this.formData.authId = authId
+        this.formData.userName = mobile
+        this.formData.authUserId = this.authUserId
+        await this.$http.api.publishVideo(this.formData)
         this.$router.replace(`${this.routePrefix}/activity/challenge/message`)
       } catch (error) {
         console.log(error)
       }
     },
 
-    // 视频封面上传
-    beforeCoverImageUpload(file) {
-      const flag = file.size / 1024 / 1024 < 5
-      if (!flag) {
-        this.$message.error('视屏封面图片大小不能超过 5MB!')
-      }
-      return flag
-    },
-    uploadCoverImageSuccess({ response, file, fileList }) {
-      this.coverList = fileList
-      this.formData.coverImage = response.data
-    },
-    handleCoverImageRemove() {
-      this.coverList = []
-      this.formData.coverImage = ''
-    },
-
     // 视频上传成功
     onVideoUploadSuccess({ file, fileList }) {
-      this.formData.fileUrl = file.url
+      this.formData.ossUrl = file.url
       this.formData.ossName = file.uuid
     },
 
@@ -146,6 +144,13 @@ export default {
     onBack() {
       this.$router.back()
     },
+
+    // 播放视频
+    onPlay(row) {
+      this.videoUrl = row.ossUrl
+      this.description = row.title
+      this.$refs.videoPlayer.open()
+    },
   },
 }
 </script>
@@ -175,6 +180,89 @@ export default {
   .page {
     background: #fff;
     padding: 40px 0;
+
+    .video-container {
+      width: 568px;
+      margin: 0 auto;
+      margin-top: 100px;
+      .title {
+        font-size: 16px;
+        color: #282828;
+        margin-bottom: 16px;
+      }
+
+      .video-list {
+        display: grid;
+        grid-template-columns: repeat(3, 1fr);
+        grid-column-gap: 16px;
+        grid-row-gap: 16px;
+
+        .video {
+          background: #fff;
+          box-sizing: border-box;
+          border: 1px solid #efefef;
+
+          &:hover {
+            .info {
+              .club-name {
+                color: #f3920d;
+              }
+            }
+          }
+
+          &:nth-child(4n) {
+            margin-right: 0;
+          }
+
+          .cover {
+            height: 248px;
+            position: relative;
+
+            video {
+              display: block;
+              width: 100%;
+              height: 100%;
+              background: #fff;
+            }
+
+            .play {
+              position: absolute;
+              z-index: 2;
+              width: 32px;
+              height: 32px;
+              background: url(~assets/theme-images/common/pc-icon-play.png) no-repeat center;
+              background-size: 32px;
+              left: 50%;
+              top: 50%;
+              transform: translate(-50%, -50%);
+              cursor: pointer;
+            }
+
+            .name {
+              display: flex;
+              align-items: flex-end;
+              width: 100%;
+              height: 75px;
+              padding: 17px 16px;
+              box-sizing: border-box;
+              position: absolute;
+              left: 0;
+              bottom: 0;
+              background: linear-gradient(180deg, rgba(51, 51, 51, 0) 0%, #333333 100%);
+
+              span {
+                color: #fff;
+                font-size: 14px;
+                line-height: 20px;
+                max-height: 40px;
+                @include ellipsis(2);
+              }
+            }
+          }
+        }
+      }
+    }
+
     .page-content {
       width: 568px;
       margin: 0 auto;
@@ -188,7 +276,7 @@ export default {
           margin-bottom: 8px;
         }
         .content {
-          color: #999999;
+          color: #f94b4b;
         }
       }
 
@@ -206,11 +294,11 @@ export default {
           &.cancel {
             border: 1px solid #c2c2c2;
             color: #666666;
+            margin-top: 16px;
           }
           &.publish {
             background: #f3920d;
             color: #fff;
-            margin-top: 16px;
           }
         }
       }
@@ -229,10 +317,97 @@ export default {
   .page {
     background: #fff;
     padding: 8vw 0;
-    .page-content {
-      width: 85.6vw;
-      margin: 0 auto;
 
+    .line {
+      width: 100%;
+      height: 3.2vw;
+      background: #f1f1f1;
+      margin: 7.2vw 0;
+    }
+
+    .video-container {
+      padding: 0 3.2vw;
+      .title {
+        font-size: 3.4vw;
+        color: #282828;
+        margin-bottom: 4vw;
+        span {
+          font-weight: bold;
+        }
+      }
+      .video-list {
+        display: grid;
+        grid-template-columns: repeat(2, 1fr);
+        grid-column-gap: 2.4vw;
+        grid-row-gap: 2.4vw;
+        .video {
+          background: #fff;
+          box-sizing: border-box;
+          border: 0.1vw solid #efefef;
+
+          &:hover {
+            .info {
+              .club-name {
+                color: #f3920d;
+              }
+            }
+          }
+
+          &:nth-child(4n) {
+            margin-right: 0;
+          }
+
+          .cover {
+            height: 64.4vw;
+            position: relative;
+
+            video {
+              display: block;
+              width: 100%;
+              height: 100%;
+              background: #fff;
+            }
+
+            .play {
+              position: absolute;
+              z-index: 2;
+              width: 7.2vw;
+              height: 7.2vw;
+              background: url(~assets/theme-images/common/h5-icon-play.png) no-repeat center;
+              background-size: 7.2vw;
+              left: 50%;
+              top: 50%;
+              transform: translate(-50%, -50%);
+              cursor: pointer;
+            }
+
+            .name {
+              display: flex;
+              align-items: flex-end;
+              width: 100%;
+              height: 16vw;
+              padding: 4.4vw 3vw;
+              box-sizing: border-box;
+              position: absolute;
+              left: 0;
+              bottom: 0;
+              background: linear-gradient(180deg, rgba(51, 51, 51, 0) 0%, #333333 100%);
+
+              span {
+                color: #fff;
+                font-size: 3vw;
+                line-height: 3.6vw;
+                max-height: 7.2vw;
+                @include ellipsis(2);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .page-content {
+      padding: 0 3.2vw;
       .tip {
         margin: 20vw 0 12vw;
         font-size: 3vw;
@@ -242,15 +417,13 @@ export default {
           margin-bottom: 8px;
         }
         .content {
-          color: #999999;
+          color: #f94b4b;
         }
       }
 
       .control {
         .button {
-          width: 85.6vw;
           height: 12vw;
-          margin: 0 auto;
           cursor: pointer;
           display: flex;
           justify-content: center;
@@ -259,11 +432,11 @@ export default {
           &.cancel {
             border: 0.1vw solid #c2c2c2;
             color: #666666;
+            margin-top: 3.2vw;
           }
           &.publish {
             background: #f3920d;
             color: #fff;
-            margin-top: 3.2vw;
           }
         }
       }

+ 0 - 0
pages/_template/ross/activity/challenge_new/index.vue → pages/_template/ross/activity/challenge_old/index.vue


+ 224 - 1
pages/_template/ross/activity/challenge_new/list.vue → pages/_template/ross/activity/challenge_old/list.vue

@@ -27,7 +27,32 @@
         <!-- 视频列表区域 -->
         <div class="video-content">
           <div class="title">视频排名</div>
-          <SimpleVideoList :list="list" />
+          <div class="video-list">
+            <div
+              class="video"
+              v-for="(item, index) in list"
+              :key="index"
+              @click="onPlay(item)"
+            >
+              <div class="cover">
+                <img :src="item.cover" alt="" />
+                <div class="name">{{ item.title }}</div>
+                <div class="rank" :class="'rank-0' + (index + 1)">
+                  {{ index + 1 }}
+                </div>
+                <div class="play" @click="onPlay(item)"></div>
+              </div>
+              <div class="info">
+                <div class="club-name">{{ item.authParty }}</div>
+                <div class="mobile">{{ item.userName | mobileFormat }}</div>
+              </div>
+              <div class="foot">
+                <div class="date">{{ item.releaseTime | dateFormat }}</div>
+                <div class="praise">{{ item.diggCount }}</div>
+                <div class="pv">{{ item.playCount }}</div>
+              </div>
+            </div>
+          </div>
           <!-- 列表为空 -->
           <SimpleEmpty
             v-if="!total && !isRequest"
@@ -294,6 +319,204 @@ export default {
           font-weight: bold;
           margin: 16px 0;
         }
+
+        .video-list {
+          &::after {
+            content: '';
+            display: block;
+            clear: both;
+          }
+
+          .video {
+            float: left;
+            width: 288px;
+            height: 356px;
+            background: #fff;
+            box-sizing: border-box;
+            border: 1px solid #efefef;
+            margin: 0 16px 16px 0;
+            transition: all 0.4s;
+
+            &:hover {
+              transform: translateY(-10px);
+              box-shadow: 0px 6px 16px 1px rgba(0, 0, 0, 0.1);
+
+              .info {
+                .club-name {
+                  color: #f3920d;
+                }
+              }
+            }
+
+            &:nth-child(4n) {
+              margin-right: 0;
+            }
+
+            .cover {
+              width: 100%;
+              height: 198px;
+              position: relative;
+
+              img {
+                display: block;
+                width: 100%;
+                height: 100%;
+              }
+
+              &::after {
+                content: '';
+                display: block;
+                position: absolute;
+                left: 0;
+                top: 0;
+                z-index: 1;
+                width: 100%;
+                height: 100%;
+                background: #000;
+                opacity: 0;
+                transition: opacity 0.2s;
+              }
+
+              .play {
+                position: absolute;
+                z-index: 2;
+                width: 48px;
+                height: 48px;
+                background: url(~assets/theme-images/common/pc-icon-play.png)
+                  no-repeat center;
+                background-size: 48px;
+                left: 50%;
+                top: 50%;
+                transform: translate(-50%, -50%);
+                opacity: 0;
+                transition: opacity 0.2s;
+                cursor: pointer;
+              }
+
+              &:hover {
+                .play {
+                  opacity: 1;
+                }
+
+                .rank,
+                .name {
+                  opacity: 0;
+                }
+
+                &::after {
+                  opacity: 0.4;
+                }
+              }
+
+              .name {
+                color: #fff;
+                font-size: 16px;
+                text-align: center;
+                width: 100%;
+                line-height: 40px;
+                @include ellipsis(1);
+                box-sizing: border-box;
+                padding: 0 16px;
+                position: absolute;
+                left: 0;
+                bottom: 0;
+                background: rgba(0, 0, 0, 0.5);
+                transition: opacity 0.2s;
+              }
+
+              .rank {
+                position: absolute;
+                left: 10px;
+                top: 0;
+                width: 43px;
+                height: 45px;
+                background: url(~assets/theme-images/ross/pc-rank.png) no-repeat
+                  center;
+                background-size: 43px;
+                text-align: center;
+                box-sizing: border-box;
+                padding-top: 13px;
+                font-weight: bold;
+                color: #fff;
+                font-size: 13px;
+                transition: opacity 0.2s;
+
+                &.rank-01 {
+                  background-image: url(~assets/theme-images/ross/pc-rank-01.png);
+                }
+                &.rank-02 {
+                  background-image: url(~assets/theme-images/ross/pc-rank-02.png);
+                }
+                &.rank-03 {
+                  background-image: url(~assets/theme-images/ross/pc-rank-03.png);
+                }
+              }
+            }
+
+            .info {
+              padding: 24px 16px;
+              .club-name {
+                font-size: 18px;
+                color: #282828;
+                @include ellipsis(1);
+              }
+              .mobile {
+                font-size: 16px;
+                color: #666;
+                margin-top: 12px;
+              }
+            }
+
+            .foot {
+              color: #999999;
+              font-size: 14px;
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+              margin: 0 16px;
+              border-top: 1px solid #efefef;
+              padding-top: 12px;
+
+              .date,
+              .praise,
+              .pv {
+                flex-shrink: 0;
+                line-height: 24px;
+              }
+
+              .praise,
+              .pv {
+                position: relative;
+                margin-left: 16px;
+                padding-left: 30px;
+
+                &::after {
+                  content: '';
+                  display: block;
+                  width: 24px;
+                  height: 24px;
+                  background: url(~assets/theme-images/common/icon-praise.png)
+                    no-repeat center;
+                  background-size: 24px;
+                  position: absolute;
+                  left: 0;
+                  top: 50%;
+                  transform: translateY(-50%);
+                }
+              }
+
+              .pv {
+                &::after {
+                  background-image: url(~assets/theme-images/common/icon-pv.png);
+                }
+              }
+
+              .date {
+                margin-right: 44px;
+              }
+            }
+          }
+        }
       }
     }
   }

+ 0 - 0
pages/_template/ross/activity/challenge_new/message.vue → pages/_template/ross/activity/challenge_old/message.vue


+ 0 - 0
pages/_template/ross/activity/challenge_new/publish.vue → pages/_template/ross/activity/challenge_old/publish.vue


+ 1 - 0
plugins/element-ui.js

@@ -1,4 +1,5 @@
 import Vue from 'vue'
 import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
 
 Vue.use(ElementUI)

+ 1 - 0
plugins/vant.js

@@ -1,6 +1,7 @@
 import Vue from 'vue'
 import Vant from 'vant'
 import '@vant/touch-emulator'
+import 'vant/lib/index.css'
 
 import { Toast, Dialog, Notify } from 'vant'