Jelajahi Sumber

认证通v1.7.5版本页面绘制

yuwenjun1997 2 tahun lalu
induk
melakukan
fffdf558d7

+ 4 - 0
.env.development

@@ -26,3 +26,7 @@ PORT = '8888'
 
 # HTTPS flag
 HTTPS = false
+
+# OSS 上传目录
+# UPLOAD_DIR = 'dev/authFile/'
+UPLOAD_DIR = 'beta/authFile/'

+ 4 - 1
.env.production

@@ -21,4 +21,7 @@ HOST = '172.31.165.23'
 PORT = '8105'
 
 # HTTPS flag
-HTTPS = false
+HTTPS = false
+
+# OSS 上传目录
+UPLOAD_DIR = 'prod/authFile/'

+ 4 - 1
.env.staging

@@ -20,4 +20,7 @@ HOST = '172.31.165.28'
 PORT = '8105'
 
 # HTTPS flag
-HTTPS = false
+HTTPS = false
+
+# OSS 上传目录
+UPLOAD_DIR = 'beta/authFile/'

+ 4 - 0
apis/module/doc.js

@@ -1,6 +1,10 @@
 export default ($axios) => {
   const docApis = {}
 
+  // 阿里云oss初始化
+  docApis.fetchOssInit = (params = {}) =>
+    $axios.get('/database/oss/token', { params })
+
   // 获取文章列表
   docApis.getArticleList = (params = {}) =>
     $axios.get('/wx/data/article/list', { params })

TEMPAT SAMPAH
assets/theme-images/common/icon-praise.png


TEMPAT SAMPAH
assets/theme-images/common/icon-pv.png


TEMPAT SAMPAH
assets/theme-images/ross/pc-link-entry-challenge.png


TEMPAT SAMPAH
assets/theme-images/ross/pc-rank-01.png


TEMPAT SAMPAH
assets/theme-images/ross/pc-rank-02.png


TEMPAT SAMPAH
assets/theme-images/ross/pc-rank-03.png


TEMPAT SAMPAH
assets/theme-images/ross/pc-rank.png


+ 223 - 0
components/SimpleOssUpload/index.vue

@@ -0,0 +1,223 @@
+<template>
+  <div class="simple-oss-upload">
+    <template v-if="fileList.length < limit">
+      <div class="button" @click="onChooseFile">本地上传</div>
+      <input
+        v-show="false"
+        id="file"
+        ref="ossFileInput"
+        type="file"
+        :accept="accept"
+        multiple
+        @change="onFileChange"
+      />
+    </template>
+    <div class="file-list">
+      <template v-for="(file, index) in fileList">
+        <div class="file" :key="file.uuid">
+          <div class="name">{{ file.name }}</div>
+          <div class="control">
+            <span class="play" @click="onPlay(file)">播放</span>
+            <span class="remove" @click="onAbortUpload(file, index)">删除</span>
+          </div>
+          <div class="progress">
+            <el-progress
+              :percentage="file.percentage"
+              :show-text="false"
+              color="#F3920D"
+            ></el-progress>
+            <div class="status">
+              <span>{{ file.percentage }}%</span>
+              <span>上传中</span>
+            </div>
+          </div>
+        </div>
+      </template>
+    </div>
+    <!-- <template v-if="videoDialog">
+      <div class="video-dialog">
+        <video :src="currentVideoUrl"></video>
+      </div>
+      <div class="mask"></div>
+    </template> -->
+    <SimpleVideoPlayer
+      :videoSrc="currentVideoUrl"
+      ref="videoPlayer"
+    ></SimpleVideoPlayer>
+  </div>
+</template>
+
+<script>
+import OssUploadUtils from './upload'
+export default {
+  props: {
+    accept: {
+      type: String,
+      default: '.mp4',
+    },
+    limit: {
+      type: Number,
+      default: 9,
+    },
+  },
+  data() {
+    return {
+      videoDialog: true,
+      currentVideoUrl: '',
+      ossUpload: null,
+      fileList: [{ percentage: 90, name: 'a8b25605e0db0c9be8362c38d4.mp4' }],
+    }
+  },
+  methods: {
+    // 播放视频
+    onPlay(rawFile) {
+      this.currentVideoUrl = URL.createObjectURL(rawFile.file)
+      this.$refs.videoPlayer.open()
+    },
+    // 选择文件
+    onChooseFile() {
+      this.$nextTick(() => {
+        this.$refs.ossFileInput.click()
+      })
+    },
+    // 文件选中
+    onFileChange(e) {
+      this.ossUpload = new OssUploadUtils(this, {
+        success: this.onSuccess,
+        progress: this.onProgress,
+        faild: this.onFaild,
+      })
+      this.ossUpload.upload(e.target.files[0])
+    },
+    // 取消上传
+    async onAbortUpload(file, index) {
+      try {
+        await this.$confirm('确定删除该视频?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+        })
+        this.fileList.splice(index, 1)
+        const { checkpoint } = file
+        this.ossUpload.abortMultipartUpload(checkpoint)
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 上传进度
+    onProgress(file, fileList) {
+      this.fileList = fileList
+      this.$emit('progress', { file, fileList })
+    },
+    // 上传成功
+    onSuccess(file, fileList) {
+      console.log(file)
+      this.fileList = fileList
+      this.$emit('success', { file, fileList })
+    },
+    // 上传失败
+    onFaild(file, fileList) {
+      this.fileList = fileList
+      this.$emit('faild', { file, fileList })
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@mixin ellipsis($line: 1) {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: $line;
+  -webkit-box-orient: vertical;
+}
+
+@media screen and (min-width: 768px) {
+  .simple-oss-upload {
+    .button {
+      width: 104px;
+      height: 36px;
+      text-align: center;
+      line-height: 36px;
+      background: #f3920d;
+      color: #fff;
+      border-radius: 4px;
+      cursor: pointer;
+    }
+
+    .video-dialog {
+      position: fixed;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      width: 800px;
+      height: 600px;
+      z-index: 11;
+      background: #fff;
+      border-radius: 4px;
+
+      video {
+        display: block;
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .mask {
+      position: fixed;
+      left: 0;
+      top: 0;
+      width: 100vw;
+      height: 100vh;
+      background: #000;
+      opacity: 0.5;
+      z-index: 10;
+    }
+
+    .file-list {
+      line-height: initial;
+      margin-top: 16px;
+      .file {
+        padding: 8px 0;
+        position: relative;
+        .name {
+          font-size: 16px;
+          margin-bottom: 18px;
+          margin-right: 80px;
+          color: #666;
+        }
+        .control {
+          position: absolute;
+          top: 8px;
+          right: 0;
+          span {
+            font-size: 16px;
+            cursor: pointer;
+
+            &.play {
+              color: #1890ff;
+              margin-right: 12px;
+            }
+
+            &.remove {
+              color: #f3920d;
+            }
+          }
+        }
+        .progress {
+          padding-right: 120px;
+          position: relative;
+          .status {
+            position: absolute;
+            right: 0;
+            top: 50%;
+            transform: translateY(-53%);
+            font-size: 16px;
+            color: #f3920d;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 151 - 0
components/SimpleOssUpload/upload.js

@@ -0,0 +1,151 @@
+import OSS from 'ali-oss'
+import { v4 as uuidv4 } from 'uuid'
+
+class OssUploadUtils {
+  constructor(app, callback = {}) {
+    this.app = app // vue实例
+    this.client = null // ossClient
+    this.baseFileUrl = '' // 文件上传根路径
+    this.callback = callback
+    this.fileList = [] // 上传文件列表
+  }
+
+  // 初始化oss client实例
+  async initOssClient() {
+    try {
+      const res = await this.app.$http.api.fetchOssInit()
+      if (res.code) return
+      const { accessKeyId, securityToken, bucket, accessKeySecret } = res.data
+      const config = {
+        // 以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
+        region: 'oss-cn-shenzhen',
+        accessKeyId,
+        accessKeySecret,
+        stsToken: securityToken,
+        bucket,
+      }
+      this.client = new OSS(config)
+      this.baseFileUrl = `https://${config.bucket}.${config.region}.aliyuncs.com/`
+
+      setInterval(() => {
+        this.initOssClient()
+      }, 1000 * 60 * 60)
+      return res
+    } catch (error) {
+      return error
+    }
+  }
+
+  // 取消上传
+  abortMultipartUpload(abortCheckpoint) {
+    this.client.abortMultipartUpload(
+      abortCheckpoint.name,
+      abortCheckpoint.uploadId
+    )
+  }
+
+  // 上传文件
+  async multipartUpload(rawFile) {
+    if (!this.client) {
+      await this.initOssClient()
+    }
+    if (rawFile.status > -1) return
+    try {
+      // 指定上传到examplebucket的Object名称,例如exampleobject.txt。
+      rawFile.bucketName = this.generateFileName(rawFile)
+      // 分片上传
+      const res = await this.client.multipartUpload(
+        rawFile.bucketName,
+        rawFile.file,
+        {
+          // 获取分片上传进度、断点和返回值。
+          progress: (p, cpt, res) => {
+            this.onProgress(p, cpt, res, rawFile)
+          },
+          // 设置并发上传的分片数量。
+          parallel: 10,
+          // 设置分片大小。默认值为1 MB,最小值为100 KB。
+          partSize: 1024 * 1024,
+          // 文件类型
+          mime: rawFile.file.type,
+          headers: {
+            // 指定初始化分片上传时是否覆盖同名Object。此处设置为true,表示禁止覆盖同名Object。
+            'x-oss-forbid-overwrite': 'true',
+          },
+        }
+      )
+      this.onSuccess(res, rawFile)
+    } catch (err) {
+      this.onFaild(err, rawFile)
+      console.log(err)
+    }
+  }
+
+  // 上传文件
+  upload(file) {
+    const rawfile = this.generateRawfile(file)
+    this.fileList.push(rawfile)
+    this.multipartUpload(rawfile)
+  }
+
+  // 上传多个文件
+  uploadAll(fileList) {
+    fileList.forEach((file) => {
+      const rawFile = this.generateRawfile(file)
+      this.fileList.push(rawFile)
+      this.multipartUpload(rawFile)
+    })
+  }
+
+  // 监听上传进度
+  onProgress(p, cpt, res, rawFile) {
+    rawFile.status = 2
+    rawFile.percentage = Math.ceil(p * 100)
+    rawFile.checkpoint = cpt
+    rawFile.response = res
+    this.callback.progress && this.callback.progress(rawFile, this.fileList)
+  }
+
+  // 监听上传成功
+  onSuccess(response, rawFile) {
+    rawFile.status = 1
+    rawFile.response = response
+    rawFile.url = this.baseFileUrl + response.name
+    this.callback.success && this.callback.success(rawFile, this.fileList)
+  }
+
+  // 监听上传失败
+  onFaild(error, rawFile) {
+    rawFile.response = error
+    rawFile.status = 0
+    this.callback.faild && this.callback.faild(rawFile, this.fileList)
+  }
+
+  // 处理上传文件名称
+  generateFileName(rawfile) {
+    const upload_dir = process.env.UPLOAD_DIR
+    const name =
+      upload_dir +
+      rawfile.name.replace(/([^\\/]+)\.([^\\/]+)/i, (match, $1, $2) => {
+        const lastName = rawfile.uuid + '.' + $2
+        return lastName
+      })
+    return name
+  }
+
+  // 处理文件字段信息
+  generateRawfile(file) {
+    return {
+      uuid: uuidv4(),
+      name: file.name,
+      file: file,
+      percentage: 0,
+      status: -1, // 0 上次失败 1 上传成功 2 上传中 -1 待上传
+      url: '',
+      response: null,
+      checkpoint: null,
+    }
+  }
+}
+
+export default OssUploadUtils

+ 19 - 9
layouts/app-ross.vue

@@ -122,23 +122,33 @@ export default {
       drawer: false,
       isMounted: false,
       list: [
+        {
+          id: 4,
+          name: '挑战赛',
+          path: '/activity/challenge',
+          icon: 'icon-challenge',
+          auth: false,
+        },
         {
           id: 1,
           name: '授权申请',
           path: '/form/club-register',
           icon: 'icon-register',
+          auth: false,
         },
         {
           id: 2,
           name: '产品资料',
           path: '/docs/0',
           icon: 'icon-doc',
+          auth: false,
         },
         {
           id: 3,
           name: '意见反馈',
           path: '/feedback',
           icon: 'icon-feedback',
+          auth: true,
         },
       ],
     }
@@ -163,20 +173,14 @@ export default {
         'login_redicret',
         this.routePrefix + item.path
       )
-      if (item.id > 2 && !hasLogin) {
+      if (item.auth && !hasLogin) {
         this.$toast({ message: '请先登录', duration: 1000 })
         this.formType = 'login'
         this.$store.commit('app/SHOW_LOGIN')
         return
       }
-
-      if (item.id === 0) {
-        const url = this.routePrefix + item.path
-        this.$router.push(url)
-      } else {
-        const url = this.routePrefix + item.path
-        this.$router.push(url)
-      }
+      const url = this.routePrefix + item.path
+      this.$router.push(url)
     },
 
     // 点击登录
@@ -334,6 +338,9 @@ export default {
               &.icon-feedback {
                 background-image: url(~assets/theme-images/ross/pc-link-entry-feedback-active.png);
               }
+              // &.icon-challenge {
+              //   // background-image: url(~assets/theme-images/ross/pc-link-entry-challenge-active.png);
+              // }
             }
           }
         }
@@ -358,6 +365,9 @@ export default {
           &.icon-feedback {
             background-image: url(~assets/theme-images/ross/pc-link-entry-feedback.png);
           }
+          &.icon-challenge {
+            background-image: url(~assets/theme-images/ross/pc-link-entry-challenge.png);
+          }
         }
         .text {
           font-size: 16px;

File diff ditekan karena terlalu besar
+ 675 - 11
package-lock.json


+ 2 - 0
package.json

@@ -16,6 +16,7 @@
   "dependencies": {
     "@nuxtjs/axios": "^5.13.6",
     "@vant/touch-emulator": "^1.3.2",
+    "ali-oss": "^6.17.1",
     "animate.css": "^4.1.1",
     "clipboard": "^2.0.10",
     "core-js": "^3.19.3",
@@ -25,6 +26,7 @@
     "nuxt": "^2.15.8",
     "swiper": "^5.4.5",
     "three-dots": "^0.2.3",
+    "uuid": "^9.0.0",
     "vant": "^2.12.47",
     "vue": "^2.6.14",
     "vue-awesome-swiper": "^4.1.1",

+ 113 - 0
pages/_template/ross/activity/challenge/index.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="page">
+    <div class="page-top"></div>
+    <div class="page-content">
+      <div class="content"></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>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+export default {
+  layout: 'app-ross',
+  data() {
+    return {
+      status: 1,
+    }
+  },
+  computed: {
+    ...mapGetters(['routePrefix']),
+    activity() {
+      const result = {
+        0: { type: 'end', text: '已结束' },
+        1: { type: 'start', text: '进行中' },
+        2: { type: 'wait', text: '未开始' },
+      }
+      return result[this.status]
+    },
+  },
+  methods: {
+    toDetail() {
+      const url = `${this.routePrefix}/activity/challenge/list`
+      this.$router.push(url)
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@media screen and (min-width: 768px) {
+  .page {
+    padding-bottom: 269px;
+    .page-top {
+      width: 100%;
+      height: 530px;
+      background-position: center;
+      background: #ddd;
+    }
+
+    .page-content {
+      width: 1200px;
+      margin: 0 auto;
+      margin-top: 16px;
+
+      .content {
+        min-height: 1170px;
+        background: #fff5e8;
+      }
+
+      .entry {
+        .title {
+          font-size: 24px;
+          text-align: center;
+          font-weight: bold;
+          color: #282828;
+          margin: 85px 0 48px;
+        }
+
+        .cover {
+          position: relative;
+          width: 856px;
+          height: 340px;
+          margin: 0 auto;
+          background: pink;
+          cursor: pointer;
+
+          .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;
+
+            &.wait {
+              background: #f94b4b;
+            }
+            &.start {
+              background: #f3920d;
+            }
+            &.end {
+              background: rgba(0, 0, 0, 0.3);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 370 - 0
pages/_template/ross/activity/challenge/list.vue

@@ -0,0 +1,370 @@
+<template>
+  <div class="page">
+    <div class="page-top"></div>
+    <div class="page-content">
+      <div class="control">
+        <div class="search">
+          <el-input placeholder="搜索登录账户后四位或所属机构">
+            <i slot="prefix" class="el-input__icon el-icon-search"></i>
+          </el-input>
+        </div>
+        <div class="publish" @click="onPublish">发布视频</div>
+      </div>
+      <div class="video-content">
+        <div class="title">视频排名</div>
+        <div class="video-list">
+          <div class="video" v-for="(i, index) in 7" :key="index">
+            <div class="cover">
+              <img src="https://picsum.photos/288/198" alt="" />
+              <div class="name">医用透明质酸钠凝胶</div>
+              <div class="rank" :class="'rank-0' + (index + 1)">
+                {{ index + 1 }}
+              </div>
+              <div class="play"></div>
+            </div>
+            <div class="info">
+              <div class="club-name">上海禾雅堂科技有限公司</div>
+              <div class="mobile">137****6611</div>
+            </div>
+            <div class="foot">
+              <div class="date">2022-09-28</div>
+              <div class="praise">90</div>
+              <div class="pv">513</div>
+            </div>
+          </div>
+        </div>
+        <!-- 列表为空 -->
+        <SimpleEmpty
+          name="icon-empty-video.png"
+          description="敬请期待~"
+        ></SimpleEmpty>
+
+        <SimpleDialog
+          v-model="dialog"
+          :confirmText="dialogOption.confirmText"
+          :description="dialogOption.description"
+          :cancel="dialogOption.cancel"
+          @confirm="onConfirm"
+          @cancel="onCancel"
+        ></SimpleDialog>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+/**
+ * 抱歉,活动已结束,暂无法发布视频!
+ * 抱歉,由于您未认证机构,无法参与!
+ * 抱歉,平台规定每个用户只能发布一个视频,由于您已发布过视频,请勿再次发布!
+ *
+ * 确认证
+ */
+
+import { mapGetters } from 'vuex'
+export default {
+  layout: 'app-ross',
+  data() {
+    return {
+      dialog: false,
+      dialogOption: {
+        confirmText: '确定',
+        description: '抱歉,活动已结束,暂无法发布视频!',
+        cancel: false,
+      },
+    }
+  },
+  computed: {
+    ...mapGetters(['routePrefix']),
+  },
+  methods: {
+    onConfirm() {},
+    onCancel() {},
+    // 去发布视频
+    onPublish() {
+      const url = `${this.routePrefix}/activity/challenge/publish`
+      this.$router.push(url)
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@mixin ellipsis($line: 1) {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: $line;
+  -webkit-box-orient: vertical;
+}
+
+@media screen and (min-width: 768px) {
+  .page {
+    background: #fff;
+    min-height: calc(100vh - 80px - 80px);
+    .page-top {
+      width: 100%;
+      height: 530px;
+      background-position: center;
+      background: #ddd;
+    }
+
+    .page-content {
+      width: 1200px;
+      margin: 0 auto;
+      padding-bottom: 80px;
+
+      .control {
+        display: flex;
+        align-items: center;
+        padding: 32px 0;
+        .publish {
+          width: 120px;
+          height: 46px;
+          text-align: center;
+          line-height: 46px;
+          background: #f3920d;
+          color: #fff;
+          font-size: 16px;
+          border-radius: 4px;
+          cursor: pointer;
+          transition: all 0.2s;
+          margin-left: 48px;
+
+          &:hover {
+            background: #e98d0d;
+          }
+        }
+
+        .search {
+          flex: 1;
+          flex-shrink: 0;
+          .el-input {
+            height: 46px;
+            font-size: 16px;
+            .el-input__icon {
+              font-size: 24px;
+              line-height: 46px;
+              margin-left: 12px;
+            }
+
+            ::v-deep {
+              & > .el-input__inner {
+                height: 46px;
+                padding-left: 55px;
+              }
+            }
+          }
+        }
+      }
+
+      .video-content {
+        .title {
+          font-size: 16px;
+          color: #282828;
+          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;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 66 - 0
pages/_template/ross/activity/challenge/message.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="page">
+    <div class="page-top">
+      <div class="icon-submit-succsss"></div>
+      <div class="tip">视频发布成功&nbsp;!</div>
+    </div>
+    <div class="page-content">
+      <div class="title">温馨提示:</div>
+      <div class="content">
+        视频发布成功后,您可去首页查看您的视频排名,同时,平台会在1-2个工作日内将您的视频上传至抖音平台,您
+        可在1-2个工日后,通过抖音平台搜索“ROSS”抖音账号或“视频标题”找到该视频,再将该视频通过抖音分享
+        方式分享给更多的好友来帮您点赞获得排名。如1-2工日后,您在抖音平台未找到自己发布的视频,可电话咨询客
+        服,电话:<span>0755-85885625</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  layout: 'app-ross',
+}
+</script>
+
+<style lang="scss" scoped>
+@media screen and (min-width: 768px) {
+  .page-top {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    padding: 150px 0 80px;
+    .icon-submit-succsss {
+      width: 64px;
+      height: 64px;
+      background: url(~assets/theme-images/common/pc-icon-submit-success.png)
+        no-repeat center;
+      background-size: 64px;
+    }
+
+    .tip {
+      font-size: 24px;
+      font-weight: bold;
+      color: #1890ff;
+    }
+  }
+
+  .page-content {
+    width: 694px;
+    margin: 0 auto;
+    font-size: 14px;
+    line-height: 1.8;
+    .title {
+      color: #666666;
+      margin-bottom: 8px;
+    }
+    .content {
+      color: #999999;
+      text-align: justify;
+
+      span {
+        color: #f3920d;
+      }
+    }
+  }
+}
+</style>

+ 162 - 0
pages/_template/ross/activity/challenge/publish.vue

@@ -0,0 +1,162 @@
+<template>
+  <div class="page">
+    <div class="page-content">
+      <el-form label-position="top" :model="formData" :rules="rules">
+        <el-form-item label="发布抖音视:" prop="description">
+          <el-input
+            type="textarea"
+            rows="6"
+            v-model="formData.description"
+            placeholder="添加作品描述,能让更多的人看到..."
+          ></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"
+              @success="uploadCoverImageSuccess"
+              @remove="handleCoverImageRemove"
+            ></SimpleUploadImage>
+          </div>
+        </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>
+      </el-form>
+      <div class="tip">
+        <div class="title">温馨提示:</div>
+        <div class="content">
+          视频文件大小不超过8G,时长在30分钟以内;分辨率为720p(720x1280)及以上;
+          支持常用视频格式,推荐使用mp4、webm
+        </div>
+      </div>
+      <div class="control">
+        <div class="button cancel">取消</div>
+        <div class="button publish">发布</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  layout: 'app-ross',
+  data() {
+    return {
+      formData: {
+        coverImage: '',
+        fileUrl: '',
+        description: '',
+      },
+      rules: {
+        coverImage: [
+          {
+            required: true,
+            message: '请上传视频封面图片',
+            trigger: ['change'],
+          },
+        ],
+        fileUrl: [
+          {
+            required: true,
+            message: '请上传视频',
+            trigger: ['change'],
+          },
+        ],
+        description: [
+          {
+            required: true,
+            message: '请上传视频',
+            trigger: ['blur'],
+          },
+        ],
+      },
+      coverList: [],
+    }
+  },
+  methods: {
+    // 发票上传
+    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({ file, fileList }) {
+      this.coverList = []
+      this.formData.coverImage = ''
+    },
+    // 视频上传成功
+    onVideoUploadSuccess({ file, fileList }) {
+      this.formData.fileUrl = file.url
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@media screen and (min-width: 768px) {
+  ::v-deep {
+    .el-form-item__label {
+      font-size: 16px;
+    }
+  }
+
+  .page {
+    background: #fff;
+    padding: 40px 0;
+    .page-content {
+      width: 568px;
+      margin: 0 auto;
+
+      .tip {
+        margin: 60px 0;
+        font-size: 14px;
+        line-height: 1.8;
+        .title {
+          color: #666666;
+          margin-bottom: 8px;
+        }
+        .content {
+          color: #999999;
+        }
+      }
+
+      .control {
+        .button {
+          width: 295px;
+          height: 50px;
+          margin: 0 auto;
+          border-radius: 4px;
+          cursor: pointer;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          font-size: 16px;
+          &.cancel {
+            border: 1px solid #c2c2c2;
+            color: #666666;
+          }
+          &.publish {
+            background: #f3920d;
+            color: #fff;
+            margin-top: 16px;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

TEMPAT SAMPAH
static/icon-empty-video.png


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini