Просмотр исходного кода

新增:文件库弹框支持视频文件

Yahoo 4 лет назад
Родитель
Сommit
2e636c0847

+ 14 - 1
src/api/upload/index.js

@@ -4,7 +4,8 @@ import { axios } from '@/utils/request'
  * api接口列表
  * api接口列表
  */
  */
 const api = {
 const api = {
-  image: '/upload/image'
+  image: '/upload/image',
+  video: '/upload/video'
 }
 }
 
 
 /**
 /**
@@ -18,3 +19,15 @@ export function image (data) {
     data
     data
   })
   })
 }
 }
+
+/**
+ * 上传视频
+ * @param {*} data
+ */
+export function video (data) {
+  return axios({
+    url: api.video,
+    method: 'post',
+    data
+  })
+}

+ 48 - 22
src/components/Modal/FilesModal/FilesModal.vue

@@ -38,10 +38,12 @@
             />
             />
             <!-- 上传按钮 -->
             <!-- 上传按钮 -->
             <div class="file-upload fl-r">
             <div class="file-upload fl-r">
-              <span class="upload-desc">大小不能超过2M</span>
+              <span
+                class="upload-desc"
+              >{{ fileType === FileTypeEnum.VIDEO.value ? '视频' : '图片' }}大小不能超过{{ uploadSizeLimit }}M</span>
               <a-upload
               <a-upload
                 name="iFile"
                 name="iFile"
-                accept="image/*"
+                :accept="accept"
                 :beforeUpload="beforeUpload"
                 :beforeUpload="beforeUpload"
                 :customRequest="onUpload"
                 :customRequest="onUpload"
                 :multiple="true"
                 :multiple="true"
@@ -61,7 +63,10 @@
                 :key="index"
                 :key="index"
                 @click="onSelectItem(index)"
                 @click="onSelectItem(index)"
               >
               >
-                <div class="img-cover" :style="{backgroundImage: `url('${item.preview_url}')`}"></div>
+                <div
+                  class="img-cover"
+                  :style="{ backgroundImage: `url('${item.preview_url}')`, width: fileType === FileTypeEnum.VIDEO.value ? '55px' : '95px' }"
+                ></div>
                 <p class="file-name oneline-hide">{{ item.file_name }}</p>
                 <p class="file-name oneline-hide">{{ item.file_name }}</p>
                 <div class="select-mask">
                 <div class="select-mask">
                   <a-icon class="selected-icon" type="check" />
                   <a-icon class="selected-icon" type="check" />
@@ -105,6 +110,7 @@
 
 
 <script>
 <script>
 import PropTypes from 'ant-design-vue/es/_util/vue-types'
 import PropTypes from 'ant-design-vue/es/_util/vue-types'
+import store from '@/store'
 import { debounce } from '@/utils/util'
 import { debounce } from '@/utils/util'
 import * as FileApi from '@/api/files'
 import * as FileApi from '@/api/files'
 import * as GroupApi from '@/api/files/group'
 import * as GroupApi from '@/api/files/group'
@@ -126,7 +132,9 @@ export default {
     // 最大选择的数量限制, multiple模式下有效
     // 最大选择的数量限制, multiple模式下有效
     maxNum: PropTypes.integer.def(100),
     maxNum: PropTypes.integer.def(100),
     // 已选择的数量
     // 已选择的数量
-    selectedNum: PropTypes.integer.def(0)
+    selectedNum: PropTypes.integer.def(0),
+    // 文件类型 (10图片 30视频)
+    fileType: PropTypes.integer.def(FileTypeEnum.IMAGE.value),
   },
   },
   data () {
   data () {
     return {
     return {
@@ -134,8 +142,14 @@ export default {
       title: '图片库',
       title: '图片库',
       // modal(对话框)是否可见
       // modal(对话框)是否可见
       visible: false,
       visible: false,
+      // 枚举类
+      FileTypeEnum,
       // 后端上传api
       // 后端上传api
       uploadUrl: UploadApi.image,
       uploadUrl: UploadApi.image,
+      // 上传文件大小限制
+      uploadSizeLimit: 2,
+      // 文件上传的格式限制
+      accept: '',
       // 查询参数
       // 查询参数
       queryParam: {
       queryParam: {
         // 文件类型: 图片
         // 文件类型: 图片
@@ -169,18 +183,36 @@ export default {
   },
   },
   methods: {
   methods: {
 
 
-    /**
-     * 显示对话框
-     */
+    // 显示对话框
     show () {
     show () {
       // 显示窗口
       // 显示窗口
       this.visible = true
       this.visible = true
+      // 初始化文件类型
+      this.initFileType()
       // 获取分组列表
       // 获取分组列表
       this.getGroupList()
       this.getGroupList()
       // 获取文件列表
       // 获取文件列表
       this.getFileList()
       this.getFileList()
     },
     },
 
 
+    // 初始化文件类型
+    initFileType () {
+      const publicConfig = store.getters.publicConfig
+      if (this.fileType === FileTypeEnum.IMAGE.value) {
+        this.title = '图片库'
+        this.accept = 'image/jpeg,image/png,image/gif,image/webp'
+        this.uploadUrl = UploadApi.image
+        this.uploadSizeLimit = publicConfig.uploadImageSize || 2
+      }
+      if (this.fileType === FileTypeEnum.VIDEO.value) {
+        this.title = '视频库'
+        this.accept = '.mp4'
+        this.uploadUrl = UploadApi.video
+        this.uploadSizeLimit = publicConfig.uploadVideoSize || 10
+      }
+      this.queryParam.fileType = this.fileType
+    },
+
     // 获取文件分组列表
     // 获取文件分组列表
     getGroupList () {
     getGroupList () {
       // this.isLoading = true
       // this.isLoading = true
@@ -279,9 +311,9 @@ export default {
       // 显示错误提示(防抖处理)
       // 显示错误提示(防抖处理)
       const showErrorMsg = debounce(this.$message.error, 20)
       const showErrorMsg = debounce(this.$message.error, 20)
       // 验证文件大小
       // 验证文件大小
-      const isLt1M = file.size / 1024 / 1024 < 2
-      if (!isLt1M) {
-        showErrorMsg('文件大小不能超出2MB')
+      const fileSizeMb = file.size / 1024 / 1024
+      if (fileSizeMb > this.uploadSizeLimit) {
+        showErrorMsg(`文件大小不能超出${this.uploadFileSizeLimit}MB`)
         return false
         return false
       }
       }
       // 验证文件上传数量
       // 验证文件上传数量
@@ -292,9 +324,7 @@ export default {
       return true
       return true
     },
     },
 
 
-    /**
-     * 事件: 自定义上传事件
-     */
+    // 事件: 自定义上传
     onUpload (info) {
     onUpload (info) {
       this.isLoading = true
       this.isLoading = true
       // 记录上传状态
       // 记录上传状态
@@ -304,7 +334,7 @@ export default {
       formData.append('iFile', info.file)
       formData.append('iFile', info.file)
       formData.append('groupId', this.queryParam.groupId)
       formData.append('groupId', this.queryParam.groupId)
       // 开始上传
       // 开始上传
-      UploadApi.image(formData)
+      this.uploadUrl(formData)
         .finally(() => {
         .finally(() => {
           this.uploading.pop()
           this.uploading.pop()
           if (this.uploading.length === 0) {
           if (this.uploading.length === 0) {
@@ -320,9 +350,7 @@ export default {
       this.handleRefresh()
       this.handleRefresh()
     },
     },
 
 
-    /**
-     * 关闭对话框事件
-     */
+    // 关闭对话框事件
     handleCancel () {
     handleCancel () {
       this.visible = false
       this.visible = false
       this.selectedIndexs = []
       this.selectedIndexs = []
@@ -340,9 +368,7 @@ export default {
       this.getFileList()
       this.getFileList()
     },
     },
 
 
-    /**
-     * 删除文件
-     */
+    // 删除文件
     handleDelete (item) {
     handleDelete (item) {
       const that = this
       const that = this
       const fileIds = this.getSelectedItemIds()
       const fileIds = this.getSelectedItemIds()
@@ -441,7 +467,7 @@ export default {
     .group-add {
     .group-add {
       display: block;
       display: block;
       margin-top: 20px;
       margin-top: 20px;
-      font-size: 13px;
+      font-size: @font-size-base;
       padding: 0 30px;
       padding: 0 30px;
     }
     }
   }
   }
@@ -534,7 +560,7 @@ export default {
           margin-right: 10px;
           margin-right: 10px;
         }
         }
         .btn-mini {
         .btn-mini {
-          font-size: 13px;
+          font-size: @font-size-base;
           padding: 0 15px;
           padding: 0 15px;
           height: 28px;
           height: 28px;
         }
         }

+ 2 - 3
src/components/SelectImage/SelectImage.vue

@@ -9,7 +9,7 @@
       @end="drag=false"
       @end="drag=false"
       @update="onUpdate"
       @update="onUpdate"
     >
     >
-      <transition-group type="transition" :name="'flip-list'">
+      <transition-group class="draggable-item" type="transition" :name="'flip-list'">
         <div
         <div
           v-for="(item, index) in selectedItems"
           v-for="(item, index) in selectedItems"
           :key="item.file_id"
           :key="item.file_id"
@@ -18,7 +18,7 @@
         >
         >
           <!-- 预览图 -->
           <!-- 预览图 -->
           <a :href="item.preview_url" target="_blank">
           <a :href="item.preview_url" target="_blank">
-            <div class="img-cover" :style="{backgroundImage: `url('${item.preview_url}')`}"></div>
+            <div class="img-cover" :style="{ backgroundImage: `url('${item.preview_url}')` }"></div>
           </a>
           </a>
           <!-- 删除文件 -->
           <!-- 删除文件 -->
           <a-icon
           <a-icon
@@ -160,7 +160,6 @@ export default {
 }
 }
 
 
 .image-list {
 .image-list {
-  margin-bottom: -10px;
   // 多选模式下margin
   // 多选模式下margin
   &.multiple {
   &.multiple {
     .file-item,
     .file-item,

+ 237 - 0
src/components/SelectVideo/SelectVideo.vue

@@ -0,0 +1,237 @@
+<template>
+  <div class="video-list clearfix" :class="{ multiple }">
+    <!-- 文件列表 -->
+    <!-- draggable是拖拽组件 -->
+    <draggable
+      v-if="selectedItems.length"
+      v-model="selectedItems"
+      @start="drag=true"
+      @end="drag=false"
+      @update="onUpdate"
+    >
+      <transition-group type="transition" :name="'flip-list'">
+        <div
+          v-for="(item, index) in selectedItems"
+          :key="item.file_id"
+          class="file-item"
+          :style="{ width: `${width}px`, height: `${width}px` }"
+        >
+          <!-- 预览图 -->
+          <a :href="item.external_url" target="_blank">
+            <div class="img-cover" :style="{ backgroundImage: `url('${item.preview_url}')` }"></div>
+          </a>
+          <!-- 删除文件 -->
+          <a-icon
+            class="icon-close"
+            theme="filled"
+            type="close-circle"
+            @click="handleDeleteFileItem(index)"
+          />
+        </div>
+      </transition-group>
+    </draggable>
+    <!-- 视频选择器 -->
+    <!-- 如果单选, selectedItems无内容时 显示 -->
+    <!-- 如果多选, selectedItems数量小于 maxNum 显示 -->
+    <div
+      v-show="(!multiple && selectedItems.length <= 0) || (multiple && selectedItems.length < maxNum)"
+      class="selector"
+      :style="{width: `${width}px`, height: `${width}px`}"
+      title="点击选择视频"
+      @click="handleSelectVideo"
+    >
+      <a-icon class="icon-plus" :style="{ fontSize: `${width * 0.4}px` }" type="plus" />
+    </div>
+    <!-- 文件选择器 -->
+    <FilesModal
+      ref="FilesModal"
+      :multiple="multiple"
+      :maxNum="maxNum"
+      :selectedNum="selectedItems.length"
+      :fileType="FileTypeEnum.VIDEO.value"
+      @handleSubmit="handleSelectVideoSubmit"
+    />
+  </div>
+</template>
+
+<script>
+import PropTypes from 'ant-design-vue/es/_util/vue-types'
+import draggable from 'vuedraggable'
+import cloneDeep from 'lodash.clonedeep'
+import { FilesModal } from '@/components/Modal'
+import FileTypeEnum from '@/common/enum/file/FileType'
+
+// 视频选择器组件
+export default {
+  name: 'SelectVideo',
+  components: {
+    FilesModal,
+    draggable
+  },
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
+  props: {
+    // 多选模式, 如果false为单选
+    multiple: PropTypes.bool.def(false),
+    // 最大选择的数量限制, multiple模式下有效
+    maxNum: PropTypes.integer.def(100),
+    // 默认选中的文件
+    defaultList: PropTypes.array.def([]),
+    // 元素的尺寸(宽)
+    width: PropTypes.integer.def(80)
+  },
+  data () {
+    return {
+      // 枚举类
+      FileTypeEnum,
+      // 选择的视频列表
+      selectedItems: [],
+      // 禁止传参 (防止selectedItems为空时defaultList重新赋值)
+      allowProps: true
+    }
+  },
+  watch: {
+    // 设置默认数据
+    defaultList: {
+      // 首次加载的时候执行函数
+      immediate: true,
+      handler (val) {
+        const { selectedItems, allowProps } = this
+        if (val.length && !selectedItems.length && allowProps) {
+          this.selectedItems = cloneDeep(val)
+          this.onChange()
+        }
+      }
+    }
+  },
+  created () {
+  },
+  methods: {
+
+    // 拖动排序后的回调事件
+    onUpdate () {
+      this.onChange()
+    },
+
+    // 打开文件选择器
+    handleSelectVideo () {
+      this.$refs.FilesModal.show()
+    },
+
+    // 文件库modal确认回调
+    handleSelectVideoSubmit (result) {
+      if (result.length > 0) {
+        // 记录选中的视频列表
+        const { multiple, selectedItems } = this
+        this.selectedItems = multiple ? selectedItems.concat(result) : result
+        this.onChange()
+      }
+    },
+
+    // 删除文件
+    handleDeleteFileItem (index) {
+      this.selectedItems.splice(index, 1)
+      if (this.selectedItems.length === 0) {
+        this.allowProps = false
+      }
+      this.onChange()
+    },
+
+    // 触发change事件
+    onChange () {
+      const { multiple, selectedItems } = this
+      if (selectedItems.length <= 0) {
+        return this.$emit('change', multiple ? [] : 0)
+      }
+      // 生成fileId
+      const fileId = multiple ? selectedItems.map(item => item.file_id) : selectedItems[0].file_id
+      // 触发change事件
+      return this.$emit('change', fileId, selectedItems)
+    }
+
+  }
+}
+</script>
+
+<style lang="less" scoped>
+/deep/.flip-list-move {
+  transition: transform 0.3s !important;
+}
+/deep/.no-move {
+  transition: transform 0s;
+}
+
+.video-list {
+  margin-bottom: 6px;
+  // 多选模式下margin
+  &.multiple {
+    .file-item,
+    .selector {
+      margin-right: 10px;
+      margin-bottom: 10px;
+    }
+  }
+}
+
+// 文件元素
+.file-item {
+  position: relative;
+  float: left;
+  width: 80px;
+  height: 80px;
+  position: relative;
+  padding: 2px;
+  border: 1px solid #ddd;
+  background: #fff;
+  .img-cover {
+    display: block;
+    margin: 0 auto;
+    width: 45px;
+    height: 100%;
+    background: no-repeat center center / 100%;
+  }
+  &:hover {
+    .icon-close {
+      display: block;
+    }
+  }
+  .icon-close {
+    display: none;
+    position: absolute;
+    top: -8px;
+    right: -8px;
+    cursor: pointer;
+    font-size: 16px;
+    color: #c5c5c5;
+    &:hover {
+      color: #7d7d7d;
+    }
+  }
+  &:hover {
+    border: 1px solid #a7c3de;
+  }
+}
+
+// 选择器
+.selector {
+  width: 80px;
+  height: 80px;
+  float: left;
+  border: 1px dashed #e2e2e2;
+  text-align: center;
+  color: #dad9d9;
+  cursor: pointer;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  &:hover {
+    border: 1px dashed #40a9ff;
+    color: #40a9ff;
+  }
+  .icon-plus {
+    font-size: 32px;
+  }
+}
+</style>

+ 2 - 0
src/components/SelectVideo/index.js

@@ -0,0 +1,2 @@
+import SelectVideo from './SelectVideo'
+export default SelectVideo

+ 2 - 0
src/components/index.js

@@ -4,6 +4,7 @@ import ContentHeader from '@/components/ContentHeader'
 import { STable } from '@/components/Table'
 import { STable } from '@/components/Table'
 
 
 import SelectImage from '@/components/SelectImage'
 import SelectImage from '@/components/SelectImage'
+import SelectVideo from '@/components/SelectVideo'
 import SelectGoods from '@/components/SelectGoods'
 import SelectGoods from '@/components/SelectGoods'
 import SelectRegion from '@/components/SelectRegion'
 import SelectRegion from '@/components/SelectRegion'
 import Getpoint from '@/components/Getpoint'
 import Getpoint from '@/components/Getpoint'
@@ -16,6 +17,7 @@ export {
   Dialog,
   Dialog,
 
 
   SelectImage,
   SelectImage,
+  SelectVideo,
   SelectGoods,
   SelectGoods,
   SelectRegion,
   SelectRegion,
   Getpoint,
   Getpoint,