Преглед изворни кода

feat(kwlPage): 新增任务附件多媒体资源支持

添加了图片、视频等任务附件的展示与预览功能,支持点击图片放大、视频全屏播放,同时新增对应的数据字段和工具方法
lsc пре 4 часа
родитељ
комит
3d6d2495be
1 измењених фајлова са 181 додато и 3 уклоњено
  1. 181 3
      src/components/pages/workPage/components/kwlPage.vue

+ 181 - 3
src/components/pages/workPage/components/kwlPage.vue

@@ -25,15 +25,44 @@
     </div>
 
     <!-- 任务说明 -->
-    <div class="kwlCard kwlTaskDesc" v-show="work.taskDescription">
-       <!-- @click="showTaskDesc = !showTaskDesc" -->
+    <div class="kwlCard kwlTaskDesc" v-show="work.taskDescription || (work.taskUrl && work.taskUrl.length > 0)">
       <div class="kwlTaskHeader">
         <span class="kwlStar">✦</span>
         <span class="kwlTaskTitle">任务说明</span>
       </div>
-      <div class="kwlTaskContent" v-show="showTaskDesc">{{ work.taskDescription }}</div>
+      <div class="kwlTaskContent" v-show="showTaskDesc" v-if="work.taskDescription">{{ work.taskDescription }}</div>
+      <!-- 任务附件:图片/视频预览 -->
+      <div class="kwlTaskMedia" v-show="showTaskDesc" v-if="work.taskUrl && work.taskUrl.length > 0">
+        <div class="kwlTaskMediaItem" v-for="(url, idx) in work.taskUrl" :key="'taskUrl-' + idx">
+          <img v-if="isImage(url)" :src="url" @click="$hevueImgPreview(url)" />
+          <!-- 视频:缩略图 + 播放按钮,点击打开全屏弹窗 -->
+          <div class="kwlVideoThumb" v-else-if="isVideo(url)" @click="previewVideo(url)">
+            <video :src="url" muted></video>
+            <div class="kwlVideoPlayBtn">
+              <svg viewBox="0 0 24 24" width="32" height="32" fill="#fff">
+                <path d="M8 5v14l11-7z"></path>
+              </svg>
+            </div>
+          </div>
+          <a v-else :href="url" target="_blank" class="kwlTaskMediaLink">{{ url }}</a>
+        </div>
+      </div>
     </div>
 
+    <!-- 视频全屏播放弹窗 -->
+    <el-dialog
+      :visible.sync="videoDialogVisible"
+      :append-to-body="true"
+      :show-close="true"
+      custom-class="kwlVideoDialog"
+      width="80%"
+      @closed="onVideoDialogClosed"
+    >
+      <div class="kwlVideoDialogBody" v-if="currentVideoUrl">
+        <video :src="currentVideoUrl" controls autoplay class="kwlVideoFull"></video>
+      </div>
+    </el-dialog>
+
     <!-- KWL 三列 -->
     <div class="kwlColumns">
       <!-- K - 已知 -->
@@ -180,6 +209,7 @@ export default {
         learned: [],
         reflection: "",
         taskDescription: "",
+        taskUrl: [],
         showHowTo: false,
         kplaceholder:"",
         wplaceholder:"",
@@ -196,6 +226,8 @@ export default {
         loading: false,
         startTime: 0,
       },
+      videoDialogVisible: false,
+      currentVideoUrl: "",
       howToSteps: [
         "在K栏填写:我已经知道什么(Know)",
         "在W栏填写:我想知道什么(Want to know)",
@@ -234,6 +266,7 @@ export default {
         learned: [],
         reflection: "",
         taskDescription: "",
+        taskUrl: [],
         showHowTo: false,
         kplaceholder:"",
         wplaceholder:"",
@@ -262,6 +295,25 @@ export default {
     removeItem(type, idx) {
       this.work[type].splice(idx, 1);
     },
+    // 判断是否为图片
+    isImage(url) {
+      if (!url) return false;
+      return /\.(jpe?g|png|gif|bmp|webp|svg)(\?.*)?$/i.test(url);
+    },
+    // 判断是否为视频
+    isVideo(url) {
+      if (!url) return false;
+      return /\.(mp4|webm|ogg|mov|avi|wmv|flv|m4v)(\?.*)?$/i.test(url);
+    },
+    // 打开视频弹窗
+    previewVideo(url) {
+      this.currentVideoUrl = url;
+      this.videoDialogVisible = true;
+    },
+    // 视频弹窗关闭后清理
+    onVideoDialogClosed() {
+      this.currentVideoUrl = "";
+    },
     // 开始录音
     startRecord() {
       let iframe = this.$refs["iframeRef"];
@@ -512,6 +564,132 @@ export default {
   padding-left: 1.625rem;
 }
 
+/* 任务附件:图片/视频 */
+.kwlTaskMedia {
+  margin-top: .75rem;
+  padding-left: 1.625rem;
+  display: flex;
+  flex-wrap: wrap;
+  gap: .75rem;
+}
+
+.kwlTaskMediaItem {
+  width: 9.375rem;
+  max-width: 100%;
+}
+
+.kwlTaskMediaItem img {
+  width: 100%;
+  height: 6.25rem;
+  object-fit: cover;
+  border-radius: .5rem;
+  cursor: pointer;
+  border: .0625rem solid #e0e0e0;
+  transition: transform 0.2s;
+}
+
+.kwlTaskMediaItem img:hover {
+  transform: scale(1.02);
+}
+
+/* 视频缩略图 */
+.kwlVideoThumb {
+  position: relative;
+  width: 100%;
+  height: 6.25rem;
+  border-radius: .5rem;
+  overflow: hidden;
+  cursor: pointer;
+  background: #000;
+  border: .0625rem solid #e0e0e0;
+}
+
+.kwlVideoThumb video {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  pointer-events: none;
+}
+
+.kwlVideoPlayBtn {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  width: 3rem;
+  height: 3rem;
+  border-radius: 50%;
+  background: rgba(0, 0, 0, 0.55);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: background 0.2s;
+}
+
+.kwlVideoThumb:hover .kwlVideoPlayBtn {
+  background: rgba(54, 129, 252, 0.85);
+}
+
+.kwlVideoPlayBtn svg {
+  margin-left: .25rem;
+}
+
+.kwlTaskMediaLink {
+  display: inline-block;
+  font-size: .8125rem;
+  color: #3681FC;
+  word-break: break-all;
+}
+
+/* 视频全屏弹窗 */
+.kwlVideoDialog >>> .el-dialog {
+  background: transparent;
+  box-shadow: none;
+}
+
+.kwlVideoDialog >>> .el-dialog__header {
+  padding: 0;
+  margin: 0;
+}
+
+.kwlVideoDialog >>> .el-dialog__headerbtn {
+  top: -2.5rem;
+  right: -2.5rem;
+  width: 2.5rem;
+  height: 2.5rem;
+  background: rgba(0, 0, 0, 0.5);
+  border-radius: 50%;
+  z-index: 10;
+}
+
+.kwlVideoDialog >>> .el-dialog__close {
+  color: #fff;
+  font-size: 1.5rem;
+}
+
+.kwlVideoDialog >>> .el-dialog__body {
+  padding: 0;
+  background: transparent;
+}
+
+.kwlVideoDialog >>> .el-dialog__headerbtn:hover .el-dialog__close {
+  color: #fff;
+}
+
+.kwlVideoDialogBody {
+  width: 100%;
+  background: #000;
+  border-radius: .5rem;
+  overflow: hidden;
+}
+
+.kwlVideoFull {
+  width: 100%;
+  height: 70vh;
+  display: block;
+  background: #000;
+}
+
 /* 三列 */
 .kwlColumns {
   display: flex;