Parcourir la source

feat(PromotionTable): 新增下载功能;新增删除功能

fxs il y a 2 mois
Parent
commit
0b1512730f

+ 1 - 0
components.d.ts

@@ -62,6 +62,7 @@ declare module 'vue' {
     TableQueryForm: typeof import('./src/components/table/TableQueryForm.vue')['default']
     TagSelect: typeof import('./src/components/Material/TagSelect.vue')['default']
     TimeLineChart: typeof import('./src/components/echarts/TimeLineChart.vue')['default']
+    VideoPreviewDialog: typeof import('./src/components/dialog/VideoPreviewDialog.vue')['default']
   }
   export interface ComponentCustomProperties {
     vLoading: typeof import('element-plus/es')['ElLoadingDirective']

BIN
src/assets/icon/dialog/dialog-close.png


BIN
src/assets/icon/dialog/dialog-close2.png


BIN
src/assets/icon/dialog/dialog-close3.png


+ 108 - 0
src/components/dialog/VideoPreviewDialog.vue

@@ -0,0 +1,108 @@
+<script setup lang="ts">
+import { getAssetsImageUrl } from '@/utils/common'
+
+// withDefaults(
+//   defineProps<{
+//     videoUrl: string
+//   }>(),
+//   {
+//     videoUrl: '',
+//   },
+// )
+
+// 视频弹窗可见性
+const value = defineModel()
+const videoUrl = defineModel('videoUrl', { default: '' })
+
+const handleClose = () => {
+  value.value = false
+  videoUrl.value = ''
+}
+</script>
+
+<template>
+  <div class="videoPreviewDialog">
+    <el-dialog
+      v-model="value"
+      class="naked-dialog"
+      :show-close="false"
+      align-center
+      :close-on-click-modal="false"
+      @close="handleClose"
+    >
+      <template #header="{ close }">
+        <div class="dialogHeader">
+          <img
+            :src="getAssetsImageUrl('icon' + '/dialog/dialog-close3.png')"
+            @click="close"
+            alt=""
+            class="dialogClose"
+          />
+          <!--          <el-button class="dialogClose" plain @click="close" circle>-->
+          <!--            X-->
+          <!--          </el-button>-->
+        </div>
+      </template>
+      <div class="video-wrapper">
+        <video controls :src="videoUrl" class="fullscreen-video"></video>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style scoped>
+:deep(.naked-dialog) {
+  background: transparent !important;
+  box-shadow: none !important;
+
+  /* 隐藏默认边框 */
+  .el-dialog__header {
+    /*display: none;*/
+    padding-bottom: 5px;
+  }
+
+  .el-dialog__body {
+    padding: 0 !important;
+    background: transparent;
+  }
+}
+
+@keyframes rotate360 {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+.dialogHeader {
+  position: relative;
+  width: 100%;
+  height: 32px;
+}
+
+.dialogClose {
+  width: 32px;
+  height: 32px;
+  position: absolute;
+  right: 0;
+  top: 0;
+  cursor: pointer;
+}
+
+.dialogClose:hover {
+  /*transform: rotate(360deg);*/
+  animation: rotate360 0.6s ease-in-out;
+}
+
+.video-wrapper {
+  position: relative;
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  /*background: rgba(0, 0, 0, 0.4);*/
+}
+</style>

+ 41 - 2
src/components/table/PromotionTable.vue

@@ -26,6 +26,7 @@ import {
 
 import customIndicatorDialog from '../dialog/customIndicatorDialog.vue'
 import TableQueryForm from './TableQueryForm.vue'
+import VideoPreviewDialog from '@/components/dialog/VideoPreviewDialog.vue'
 
 type TableQueryForm = InstanceType<typeof TableQueryForm>
 type CustomIndicatorDialog = InstanceType<typeof customIndicatorDialog>
@@ -57,6 +58,14 @@ const customIndicatorDialogRef = ref<CustomIndicatorDialog>()
 // 表格上方查询表单
 const tableQueryFormRef = ref<TableQueryForm>()
 
+const videoDialogInfo = ref<{
+  visible: boolean
+  url: string
+}>({
+  visible: false,
+  url: '',
+})
+
 // 当前滚动条是否固定状态,这个主要用于在给sizeOb使用
 // 如果是固定状态,那么在表格大小改变的时候,left需要重置到表格的最左侧的left位置
 // 如果不是,那么设为0即可
@@ -547,6 +556,22 @@ const batchDel = () => {
   console.log('批量删除')
 }
 
+const getPreviewSrcList = (type: TableFieldType, src: string) => {
+  if (type === TableFieldType.Video) return []
+  return [src]
+}
+
+const handlePreview = (type: TableFieldType, row: any) => {
+  if (type === TableFieldType.Img) {
+    return
+  }
+  videoDialogInfo.value.visible = true
+  videoDialogInfo.value = {
+    visible: true,
+    url: row['download'],
+  }
+}
+
 onUnmounted(() => {
   cleanup()
 })
@@ -575,7 +600,7 @@ defineExpose({
               >添加账户
             </el-button>
           </slot>
-          <el-button class="w120">批量操作</el-button>
+          <!--          <el-button class="w120">批量操作</el-button>-->
         </div>
         <div class="tableOperationRight">
           <slot name="exportData" v-if="props.needExport">
@@ -650,6 +675,12 @@ defineExpose({
             width="55"
             :fixed="true"
           ></el-table-column>
+          <el-table-column
+            label="序号"
+            type="index"
+            width="55"
+            :fixed="true"
+          ></el-table-column>
           <template v-for="item in tableFieldsInfo" :key="item.name">
             <el-table-column
               v-if="item.name === 'action'"
@@ -690,11 +721,15 @@ defineExpose({
                     item.type === TableFieldType.Video
                   "
                 >
+                  <!--                  :preview-src-list="[scope.row[item.name]]"-->
                   <el-image
                     class="tableFieldImg"
-                    :preview-src-list="[scope.row[item.name]]"
                     :src="scope.row[item.name]"
                     :preview-teleported="true"
+                    :preview-src-list="
+                      getPreviewSrcList(item.type, scope.row[item.name])
+                    "
+                    @click="handlePreview(item.type, scope.row)"
                   >
                     <template #error>
                       <el-image
@@ -764,6 +799,10 @@ defineExpose({
         </template>
       </el-dialog>
     </div>
+    <video-preview-dialog
+      v-model:video-url="videoDialogInfo.url"
+      v-model="videoDialogInfo.visible"
+    ></video-preview-dialog>
   </div>
 </template>
 

+ 3 - 1
src/config/API.ts

@@ -16,7 +16,9 @@ const MaterialAPI = {
   imgFilterInfo: `${baseURL}/property/getImageListHead`, // 图片列表筛选信息
   videoFilterInfo: `${baseURL}/property/getVideoListHead`, // 视频列表筛选信息
   updateImgTags: `${baseURL}/property/updateImageTags`, // 更新图片标签
-  updateVideoTags: `${baseURL}/property/updateVideoTags`,
+  updateVideoTags: `${baseURL}/property/updateVideoTags`, // 更新视频标签
+  deleteImg: `${baseURL}/property/imageDelete`, // 删除图片
+  deleteVideo: `${baseURL}/property/videoDelete`, // 删除视频
 }
 
 export { MaterialAPI, LoginAPI }

+ 8 - 5
src/hooks/useTableScroll.ts

@@ -12,8 +12,7 @@ export function useTableScroll(
    * 这里调整的是滚动条的包裹容器,他相对于表格容器来定位,
    * 而内部的实际的滚动条相对于这个容器使用tranlate来调整位置
    * 所以在调回的时候,直接把left设置为0,仍然能保持滚动条的相对位置
-   * @param {*} state true:固定在可视区域,false:固定在表格容器内
-   * @return {*}
+   * @param state true:固定在可视区域,false:固定在表格容器内
    */
   const setScrollPos = (state: boolean) => {
     if (state) {
@@ -31,8 +30,8 @@ export function useTableScroll(
 
   /**
    * @description: 观察表格是否在可视区域,设置滚动条的对应位置
-   * @param {*} entries 观察实例,包含当前观察的元素的状态
-   * @return {*}
+   * @param  entries 观察实例,包含当前观察的元素的状态
+   * @return
    */
   const obScroll = (entries: IntersectionObserverEntry[]) => {
     setScrollPos(entries[0].intersectionRatio > 0)
@@ -94,6 +93,10 @@ export function useTableScroll(
     }
   }
 
+  /**
+   * 寻找最近的拥有滚动条的父元素
+   * @param el 当前元素
+   */
   const findScrollParent = (el: HTMLElement): HTMLElement | null => {
     let parent = el.parentElement
 
@@ -118,7 +121,7 @@ export function useTableScroll(
     }
     return null
 
-    // // 未找到则返回window
+    // // 未找到则返回window,
     // return window
   }
 

+ 13 - 0
src/utils/file/downloadFile.ts

@@ -0,0 +1,13 @@
+export function downloadFile(url: string) {
+  // 创建隐藏的 <a> 标签
+  const link = document.createElement('a')
+  link.href = url
+  link.style.display = 'none'
+
+  // 设置 download 属性(即使跨域,部分浏览器仍会尝试下载)
+  link.download = url.split('/').pop() || 'file' // 自动提取文件名
+
+  document.body.appendChild(link)
+  link.click()
+  document.body.removeChild(link)
+}

+ 29 - 4
src/views/Material/FilmLibrary.vue

@@ -25,6 +25,8 @@ import type TableCustomIndicatorController from '@/utils/localStorage/tableCusto
 import TagSelect from '@/components/Material/TagSelect.vue'
 import router from '@/router'
 import type { TagItem } from '@/views/Material/types/uploadFileType.ts'
+import { downloadFile } from '@/utils/file/downloadFile.ts'
+import { ElMessageBox } from 'element-plus'
 
 const { getGameInfo, getAllTags } = useMaterial()
 
@@ -206,9 +208,27 @@ const viewDetails = (row: any) => {
   viewRowInfo.value = row
 }
 
-const delRow = (selectInfo: any[]) => {
-  console.log(selectInfo)
-  console.log('批量删除')
+const delRow = async (selectInfo: any[]) => {
+  const idList = selectInfo.map(item => item.id)
+  const confirm = await ElMessageBox.confirm(
+    `确认删除${idList.length}个信息?`,
+    {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+    },
+  )
+  if (!confirm) return
+  const url = isVideo.value ? MaterialAPI.deleteVideo : MaterialAPI.deleteImg
+  const res = (await axiosInstance.post(url, {
+    idList,
+  })) as ResponseInfo
+  if (res.code !== 0) {
+    ElMessage.error('删除失败')
+    return
+  }
+  ElMessage.success('删除成功')
+  await updateTableData()
 }
 
 const editTag = (selectInfo: any[]) => {
@@ -323,7 +343,12 @@ onMounted(async () => {
             <el-button text type="primary" @click="viewDetails(row)"
               >详情
             </el-button>
-            <el-button text type="success">下载</el-button>
+            <el-button
+              text
+              type="success"
+              @click="() => downloadFile(row['download'])"
+              >下载</el-button
+            >
           </div>
         </template>
         <template #operationBtnContainer="{ selectedInfo }">