Переглянути джерело

新增文件单个和批量分享功能,支持提取码和保存到本地;新增登录权限校验和弹窗提示

liyating 3 роки тому
батько
коміт
128ffefc3d

+ 1 - 0
src/App.vue

@@ -39,6 +39,7 @@ export default {
     isFooterShow() {
       let routerNameList = [
         'File',
+        'Share',
         'Error_401',
         'Error_404',
         'Error_500'

+ 2 - 2
src/views/file/components/FileList/components/BreadCrumb.vue → src/components/BreadCrumb.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="breadcrumb-wrapper">
     <div class="title">当前位置:</div>
-    <el-breadcrumb v-if="fileType" separator="/">
+    <el-breadcrumb v-if="fileType && $route.name !== 'Share'" separator="/">
       <el-breadcrumb-item>{{ fileTypeMap[fileType] }}</el-breadcrumb-item>
     </el-breadcrumb>
     <el-breadcrumb v-else separator="/">
@@ -60,7 +60,7 @@ export default {
             _path.push(filePathList[i])
             res.push({
               path: '/',
-              name: '全部文件'
+              name: this.$route.name === 'Share' ? '分享文件' : '全部文件'
             })
           }
         }

+ 100 - 41
src/views/file/components/FileList/components/FileTable.vue → src/components/FileTable.vue

@@ -3,7 +3,7 @@
     <!-- 文件表格 -->
     <el-table
       class="file-table"
-      :class="'file-type-' + fileType"
+      :class="['file-type-' + fileType, routeName === 'Share' ? 'share' : '']"
       ref="multipleTable"
       fit
       v-loading="loading"
@@ -18,7 +18,7 @@
         <template slot-scope="scope">
           <img
             :src="setFileImg(scope.row)"
-            style="width: 30px; max-height: 30px; cursor: pointer;"
+            style="width: 30px; max-height: 30px; cursor: pointer"
             @click="handleFileNameClick(scope.row)"
           />
         </template>
@@ -108,28 +108,33 @@
         </template>
         <template slot-scope="scope">
           <div v-if="operaColumnExpand">
-            <el-button type="text" size="mini" @click.native="handleDeleteFileBtnClick(scope.row)">删除</el-button>
+            <el-button type="text" size="mini" @click.native="handleDeleteFileBtnClick(scope.row)" v-if="deleteBtnShow"
+              >删除</el-button
+            >
             <el-button
               type="text"
               size="mini"
               @click.native="handleRestoreFileBtnClick(scope.row)"
-              v-if="fileType === 6"
+              v-if="restoreBtnShow"
               >还原</el-button
             >
-            <el-button type="text" size="mini" @click.native="handleMoveFileBtnClick(scope.row)" v-if="fileType !== 6"
+            <el-button type="text" size="mini" @click.native="handleMoveFileBtnClick(scope.row)" v-if="moveBtnShow"
               >移动</el-button
             >
-            <el-button type="text" size="mini" @click.native="handleRenameFileBtnClick(scope.row)" v-if="fileType !== 6"
+            <el-button type="text" size="mini" @click.native="handleRenameFileBtnClick(scope.row)" v-if="renameBtnShow"
               >重命名</el-button
             >
-            <el-button type="text" size="mini" v-if="scope.row.isDir === 0 && fileType !== 6">
+            <el-button type="text" size="mini" @click.native="handleShareFileBtnClick(scope.row)" v-if="shareBtnShow"
+              >分享</el-button
+            >
+            <el-button type="text" size="mini" v-if="downloadBtnShow && scope.row.isDir === 0">
               <a target="_blank" style="display: block; color: inherit" :href="getDownloadFilePath(scope.row)">下载</a>
             </el-button>
             <el-button
               type="text"
               size="mini"
               @click.native="handleUnzipFileBtnClick(scope.row)"
-              v-if="fileType !== 6 && (scope.row.extendName == 'zip' || scope.row.extendName == 'rar')"
+              v-if="unzipBtnShow && ['zip', 'rar'].includes(scope.row.extendName)"
               >解压缩</el-button
             >
           </div>
@@ -139,26 +144,31 @@
               <i class="el-icon-arrow-down el-icon--right"></i>
             </el-button>
             <el-dropdown-menu slot="dropdown">
-              <el-dropdown-item @click.native="handleDeleteFileBtnClick(scope.row)">删除</el-dropdown-item>
-              <el-dropdown-item @click.native="handleRestoreFileBtnClick(scope.row)" v-if="fileType === 6"
+              <el-dropdown-item @click.native="handleDeleteFileBtnClick(scope.row)" v-if="deleteBtnShow"
+                >删除</el-dropdown-item
+              >
+              <el-dropdown-item @click.native="handleRestoreFileBtnClick(scope.row)" v-if="restoreBtnShow"
                 >还原</el-dropdown-item
               >
-              <el-dropdown-item @click.native="handleMoveFileBtnClick(scope.row)" v-if="fileType !== 6"
+              <el-dropdown-item @click.native="handleMoveFileBtnClick(scope.row)" v-if="moveBtnShow"
                 >移动</el-dropdown-item
               >
-              <el-dropdown-item @click.native="handleRenameFileBtnClick(scope.row)" v-if="fileType !== 6"
+              <el-dropdown-item @click.native="handleRenameFileBtnClick(scope.row)" v-if="renameBtnShow"
                 >重命名</el-dropdown-item
               >
-              <el-dropdown-item
-                v-if="scope.row.extendName === 'zip' && fileType !== 6"
-                @click.native="handleUnzipFileBtnClick(scope.row)"
-                >解压缩</el-dropdown-item
+              <el-dropdown-item v-if="shareBtnShow" @click.native="handleShareFileBtnClick(scope.row)"
+                >分享</el-dropdown-item
               >
-              <el-dropdown-item v-if="scope.row.isDir === 0 && fileType !== 6">
+              <el-dropdown-item v-if="downloadBtnShow && scope.row.isDir === 0">
                 <a target="_blank" style="display: block; color: inherit" :href="getDownloadFilePath(scope.row)"
                   >下载</a
                 >
               </el-dropdown-item>
+              <el-dropdown-item
+                v-if="unzipBtnShow && ['zip', 'rar'].includes(scope.row.extendName)"
+                @click.native="handleUnzipFileBtnClick(scope.row)"
+                >解压缩</el-dropdown-item
+              >
             </el-dropdown-menu>
           </el-dropdown>
         </template>
@@ -198,16 +208,6 @@ export default {
   data() {
     return {
       operaColumnExpand: this.getCookies('operaColumnExpand') || false, //  表格操作列-是否收缩
-      //  移动文件模态框数据
-      dialogMoveFile: {
-        isBatchMove: false,
-        visible: false, //  是否可见
-        fileTree: [], //  目录树
-        defaultProps: {
-          children: 'children',
-          label: 'label'
-        }
-      },
       //  可以识别的文件类型
       fileImgTypeList: [
         'png',
@@ -300,15 +300,49 @@ export default {
     },
     // 操作列宽度
     operaColumnWidth() {
-      return this.fileType === 6
+      return this.routeName === 'Share'
+        ? 100
+        : this.fileType === 6
         ? 120
         : this.operaColumnExpand
         ? this.isIncludeNormalFile
           ? this.isIncludeZipRarFile
-            ? 230
-            : 190
-          : 160
-        : 150
+            ? 270
+            : 230
+          : 200
+        : 100
+    },
+    // 路由名称
+    routeName() {
+      return this.$route.name
+    },
+    // 删除按钮是否显示
+    deleteBtnShow() {
+      return this.routeName !== 'Share'
+    },
+    // 还原按钮是否显示
+    restoreBtnShow() {
+      return this.fileType === 6 && this.routeName !== 'Share'
+    },
+    // 移动按钮是否显示
+    moveBtnShow() {
+      return this.fileType !== 6 && this.routeName !== 'Share'
+    },
+    // 重命名按钮是否显示
+    renameBtnShow() {
+      return this.fileType !== 6 && this.routeName !== 'Share'
+    },
+    // 删除按钮是否显示
+    shareBtnShow() {
+      return this.fileType !== 6 && this.routeName !== 'Share'
+    },
+    // 下载按钮是否显示
+    downloadBtnShow() {
+      return this.fileType !== 6
+    },
+    // 下载按钮是否显示
+    unzipBtnShow() {
+      return this.fileType !== 6 && this.routeName !== 'Share'
     }
   },
   watch: {
@@ -395,12 +429,20 @@ export default {
     handleFileNameClick(row) {
       //  若是目录则进入目录
       if (row.isDir) {
-        this.$router.push({
-          query: {
-            filePath: row.filePath + row.fileName + '/',
-            fileType: 0
-          }
-        })
+        if (this.routeName === 'Share') {
+          this.$router.push({
+            query: {
+              filePath: row.filePath + row.fileName + '/'
+            }
+          })
+        } else {
+          this.$router.push({
+            query: {
+              filePath: row.filePath + row.fileName + '/',
+              fileType: 0
+            }
+          })
+        }
       }
       //  若是文件,则进行相应的预览
       else {
@@ -456,10 +498,10 @@ export default {
     /**
      * 移动按钮点击事件
      * @description 向父组件传递当前操作的文件信息,并打开“移动文件对话框”
-     * @param {object} file 文件信息
+     * @param {object} fileInfo 文件信息
      */
-    handleMoveFileBtnClick(file) {
-      this.$emit('setOperationFile', file)
+    handleMoveFileBtnClick(fileInfo) {
+      this.$emit('setOperationFile', fileInfo)
       //  第一个参数: 是否批量移动;第二个参数:打开/关闭移动文件对话框
       this.$emit('setMoveFileDialogData', false, true)
     },
@@ -622,6 +664,15 @@ export default {
           this.$message.error(res.message)
         }
       })
+    },
+    /**
+     * 文件分享按钮点击事件
+     * @description 打开对话框让用户选择过期时间和提取码
+     * @param {object} fileInfo 文件信息
+     */
+    handleShareFileBtnClick(fileInfo) {
+      this.$emit('setSelectionFile', [fileInfo])
+      this.$emit('setShareFileDialogData')
     }
   }
 }
@@ -650,6 +701,14 @@ export default {
     }
   }
 
+  .file-table.share {
+    height: calc(100vh - 109px) !important;
+
+    >>> .el-table__body-wrapper {
+      height: calc(100vh - 161px) !important;
+    }
+  }
+
   .file-table {
     width: 100% !important;
     height: calc(100vh - 203px);

+ 1 - 3
src/components/Header.vue

@@ -46,13 +46,11 @@ export default {
   methods: {
     /**
      * 退出登录
-     * @description 清除 cookie 存放的 token、downloadDomain 和 viewDomain
-     *              并跳转到登录页面
+     * @description 清除 cookie 存放的 token 和 viewDomain 并跳转到登录页面
      */
     exitButton() {
       this.$message.success('退出登录成功!')
       this.$store.dispatch('getUserInfo').then(() => {
-        this.removeCookies('downloadDomain')
         this.removeCookies('viewDomain')
         this.removeCookies('token')
         this.$router.push({ path: '/login' })

+ 27 - 6
src/views/file/components/FileList/components/MoveFileDialog.vue → src/components/MoveFileDialog.vue

@@ -1,38 +1,59 @@
 <template>
   <div class="move-dialog-wrapper">
     <!-- 移动文件-选择目标路径 -->
-    <el-dialog title="选择目标路径" :visible.sync="dialogMoveFile.visible">
+    <el-dialog title="选择目标路径" :visible.sync="dialogData.visible" @open="handleDialogOpen">
       <div class="el-dialog-div">
         <el-tree
-          :data="dialogMoveFile.fileTree"
+          :data="fileTree"
           :props="defaultProps"
           :highlight-current="true"
           @node-click="handleNodeClick"
         ></el-tree>
       </div>
       <div slot="footer" class="dialog-footer">
-        <el-button @click="$emit('setMoveFileDialogData', null, false)">取 消</el-button>
-        <el-button type="primary" @click="$emit('confirmMoveFile')">确 定</el-button>
+        <el-button @click="$emit('setDialogData', null, false)">取 消</el-button>
+        <el-button type="primary" @click="$emit('confirmDialog')">确 定</el-button>
       </div>
     </el-dialog>
   </div>
 </template>
 
 <script>
+import { getFoldTree } from '@/request/file.js'
+
 export default {
   name: 'MoveFileDialog',
   props: {
-    dialogMoveFile: Object
+    dialogData: Object
   },
   data() {
     return {
       defaultProps: {
         children: 'children',
         label: 'label'
-      }
+      },
+      fileTree: []
     }
   },
   methods: {
+    /**
+     * 对话框打开的回调
+     */
+    handleDialogOpen() {
+      this.initFileTree()
+    },
+    /**
+     * 初始化文件目录树
+     */
+    initFileTree() {
+      getFoldTree().then((res) => {
+        if (res.success) {
+          this.fileTree = [res.data]
+        } else {
+          this.$message.error(res.message)
+        }
+      })
+    },
     /**
      * 目录树节点点击回调函数
      * @description 将当前节点中的文件夹路径传递给父组件

+ 3 - 1
src/element.js

@@ -51,7 +51,8 @@ import {
   Checkbox,
   Popover,
   Carousel,
-  CarouselItem
+  CarouselItem,
+  DatePicker
 } from "element-ui";
 
 const element = {
@@ -105,6 +106,7 @@ const element = {
     Vue.use(Popover);
     Vue.use(Carousel);
     Vue.use(CarouselItem);
+    Vue.use(DatePicker);
     Vue.prototype.$loading = Loading.service;
     Vue.prototype.$msgbox = MessageBox;
     Vue.prototype.$alert = MessageBox.alert;

+ 8 - 5
src/globalFunction.js

@@ -11,13 +11,13 @@ const globalFunction = {
   getImgMinPath: function (row) {
     let fileUrl = ''
     if (row.fileUrl) {
-      if (row.isOSS == 1) { 
+      if (row.isOSS == 1) {
         // 阿里云OSS对象存储
         fileUrl = `https://${Cookies.get('viewDomain')}${row.fileUrl}?x-oss-process=image/resize,m_fill,h_150,w_150/rotate,0`
-      } else {  
+      } else {
         // 本地磁盘存储
         let index = row.fileUrl.lastIndexOf('.')
-        fileUrl = 'api' + row.fileUrl.substr(0, index) + '_min' + row.fileUrl.substr(index)
+        fileUrl = '/api' + row.fileUrl.substr(0, index) + '_min' + row.fileUrl.substr(index)
       }
     }
     return fileUrl
@@ -27,7 +27,7 @@ const globalFunction = {
    * @param {object} row 文件信息
    * @returns {string} 文件路径
    */
-   getViewFilePath: function (row) {
+  getViewFilePath: function (row) {
     let fileUrl = ''
     if (Number(row.isOSS) === 1) {
       fileUrl = `https://${Cookies.get('viewDomain')}${row.fileUrl}`  // 阿里云OSS对象存储
@@ -83,7 +83,10 @@ const globalFunction = {
    * @param {object} others 域名、路径等,封装到对象中
    */
   removeCookies: function (name, others = null) {
-    Cookies.remove(name, { domain: config.domain, ...others})
+    Cookies.remove(name, { domain: config.domain, ...others })
+  },
+  getShareLink: function (shareBatchNum) {
+    return `${location.protocol}//${location.host}/share/${shareBatchNum}`
   }
 }
 

+ 12 - 0
src/request/file.js

@@ -48,6 +48,18 @@ export const renameFile = p => post('/file/renamefile', p);
 export const unzipFile = p => post('/file/unzipfile', p);
 // 全局搜索文件
 export const searchFile = p => get('/file/search', p);
+// 分享文件
+export const shareFile = p => post('/share/sharefile', p);
+// 校验分享链接过期时间
+export const checkShareLinkEndtime = p => get('/share/checkendtime', p);
+// 校验分享链接是否需要提取码
+export const checkShareLinkType = p => get('/share/sharetype', p);
+// 校验分享链接提取码是否正确
+export const checkShareLinkCode = p => get('/share/checkextractioncode', p);
+// 获取分享文件列表
+export const getShareFileList = p => get('/share/sharefileList', p);
+// 保存分享文件
+export const saveShareFile = p => post('/share/savesharefile', p);
 
 /**
  * 文件批量操作相关接口

+ 24 - 1
src/request/http.js

@@ -1,5 +1,21 @@
 import axios from 'axios'
 import globalFunction from '@/globalFunction.js'
+import router from '@/router/router'
+import { MessageBox } from 'element-ui';
+
+// 登录提醒
+const loginTip = function() {
+  MessageBox.alert('您尚未登录,请先登录', '操作提示', {
+    confirmButtonText: '确定',
+    callback: () => {
+      console.log(router)
+      router.push({
+        path: '/login',
+        query: { Rurl: router.currentRoute.fullPath }  //  将当前页面的url传递给login页面进行操作
+      })
+    }
+  });
+}
 
 // 请求超时时间
 axios.defaults.timeout = 10000 * 5
@@ -36,10 +52,17 @@ axios.interceptors.response.use(
   (error) => {
     if (error.response.status) {
       console.log(error.response)
-      return Promise.reject(error.response)
+      switch(error.response.status) {
+        case 401:
+          loginTip()
+          break
+        default:
+          return Promise.reject(error.response)
+      }
     }
   }
 )
+
 /**
  * 封装 get方法 对应 GET 请求
  * @param {string} url 请求url

+ 6 - 0
src/router/router.js

@@ -33,6 +33,12 @@ export default new Router({
           description: '基于Spring Boot + Vue CLI@3 框架开发的Web文件系统,旨在为用户提供一个简单、方便的文件存储方案'
         }
       }
+    },{
+      path: '/share/:shareBatchNum',
+      name: 'Share',
+      component: () => import(/* webpackChunkName: "share" */ '@/views/Share/index.vue'),
+      meta: { title: '分享 - 奇文网盘' },
+      props: true
     }, {
       path: '*',
       name: 'Error_404',

+ 0 - 2
src/store/module/user.js

@@ -31,8 +31,6 @@ export default {
     getUserInfo(context) {
       return checkUserLoginInfo().then((res) => {
         if (res.success) {
-          // 存储文件下载域名
-          globalFunction.setCookies("downloadDomain", res.data.downloadDomain)
           // 存储文件预览域名
           globalFunction.setCookies("viewDomain", res.data.viewDomain)
           // 改变登录状态

+ 304 - 0
src/views/Share/index.vue

@@ -0,0 +1,304 @@
+<template>
+  <div class="share-wrapper">
+    <div class="share-file-wrapper" v-if="shareStep === 3">
+      <div class="operation-wrapper">
+        <!-- 面包屑导航栏 -->
+        <BreadCrumb class="breadcrumb" :fileType="7"></BreadCrumb>
+        <el-button type="primary" size="mini" icon="el-icon-takeaway-box" @click="handleSaveBtnClick"
+          >保存到网盘</el-button
+        >
+      </div>
+      <!-- 文件列表-表格模式 -->
+      <FileTable
+        :fileType="7"
+        :filePath="filePath"
+        :fileList="fileList"
+        :loading="loading"
+        @setSelectionFile="setSelectionFile"
+      ></FileTable>
+    </div>
+    <!-- 文件分享对话框 -->
+    <el-dialog
+      title="文件分享"
+      :visible.sync="dialogShareFile.visible"
+      :show-close="false"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="500px"
+    >
+      <div class="end-time" v-if="shareStep === 1">此分享链接已过期</div>
+      <el-form
+        class="extraction-code-form"
+        v-if="shareStep === 2"
+        ref="codeForm"
+        :model="dialogShareFile.codeForm"
+        :rules="dialogShareFile.codeFormRules"
+        label-width="80px"
+      >
+        <el-form-item label="提取码" prop="extractionCode">
+          <el-input v-model="dialogShareFile.codeForm.extractionCode"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button v-if="shareStep === 1" @click="handleCloseBtnClick()">关 闭</el-button>
+        <el-button v-else type="primary" @click="handleSubmitBtnClick('codeForm')">提 交</el-button>
+      </span>
+    </el-dialog>
+    <!-- 保存到网盘 路径选择对话框 -->
+    <MoveFileDialog
+      :dialogData="dialogSelectPath"
+      @setSelectFilePath="setSelectFilePath"
+      @confirmDialog="confirmSelectPathDialog"
+      @setDialogData="setSelectPathDialogData"
+    ></MoveFileDialog>
+  </div>
+</template>
+
+<script>
+import BreadCrumb from '@/components/BreadCrumb'
+import FileTable from '@/components/FileTable'
+import MoveFileDialog from '@/components/MoveFileDialog'
+import {
+  checkShareLinkEndtime,
+  checkShareLinkType,
+  checkShareLinkCode,
+  getShareFileList,
+  saveShareFile
+} from '@/request/file.js'
+
+export default {
+  name: 'Share',
+  components: {
+    BreadCrumb,
+    FileTable,
+    MoveFileDialog
+  },
+  data() {
+    return {
+      // 文件分享对话框数据
+      dialogShareFile: {
+        visible: false,
+        codeForm: {
+          extractionCode: ''
+        },
+        codeFormRules: {
+          extractionCode: [
+            {
+              required: true,
+              message: '请输入提取码',
+              trigger: 'blur'
+            }
+          ]
+        }
+      },
+      shareStep: 0,
+      fileList: [],
+      loading: false,
+      // 保存到网盘对话框数据
+      dialogSelectPath: {
+        visible: false //  是否可见
+      },
+      selectFilePath: '', //  保存到网盘的目标路径
+      selectionFile: [] //  表格勾选的文件列表
+    }
+  },
+  computed: {
+    shareBatchNum() {
+      return this.$route.params.shareBatchNum
+    },
+    filePath() {
+      return this.$route.query.filePath
+    },
+    shareFilePath() {
+      return this.$route.query.filePath
+    }
+  },
+  watch: {
+    filePath() {
+      this.getShareList()
+    }
+  },
+  created() {
+    if (!this.filePath) {
+      this.$router.replace({
+        query: {
+          filePath: '/'
+        }
+      })
+    }
+  },
+  mounted() {
+    this.checkEndTime()
+  },
+  methods: {
+    /**
+     * 表格勾选框事件 | 保存被勾选的文件
+     * @param {object[]} selection 被勾选的文件数组
+     */
+    setSelectionFile(selection) {
+      this.selectionFile = selection
+    },
+    /**
+     * 校验分享链接过期时间
+     */
+    checkEndTime() {
+      checkShareLinkEndtime({
+        shareBatchNum: this.shareBatchNum
+      }).then((res) => {
+        if (res.success) {
+          if (this.getCookies(`share_${this.shareBatchNum}`) === 'true') {
+            this.checkShareComplete()
+          } else {
+            this.dialogShareFile.visible = true
+            this.checkShareType()
+          }
+        } else {
+          this.dialogShareFile.visible = true
+          this.shareStep = 1 //  链接已过期
+        }
+      })
+    },
+    /**
+     * 校验分享类型
+     * @description 校验分享链接是公共还是私密
+     */
+    checkShareType() {
+      checkShareLinkType({
+        shareBatchNum: this.shareBatchNum
+      }).then((res) => {
+        if (res.success) {
+          // 0 公共 1 私密
+          if (res.data.shareType === 0) {
+            this.shareStep = 3 //  不是私密链接,直接展示文件列表
+            this.getShareList()
+            this.dialogShareFile.visible = false
+          }
+          if (res.data.shareType === 1) {
+            this.shareStep = 2 //  是私密链接时,让用户输入提取码
+          }
+        } else {
+          this.$message.error(res.message)
+        }
+      })
+    },
+    /**
+     * 分享文件验证对话框 取消按钮
+     */
+    handleCloseBtnClick() {
+      this.dialogShareFile.visible = false
+      this.$router.push({ name: 'File', query: { fileType: 0, filePath: '/' } })
+    },
+    /**
+     * 提交按钮点击事件
+     */
+    handleSubmitBtnClick(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          checkShareLinkCode({
+            extractionCode: this.dialogShareFile.codeForm.extractionCode,
+            shareBatchNum: this.shareBatchNum
+          }).then((res) => {
+            if (res.success) {
+              this.setCookies(`share_${this.shareBatchNum}`, true)
+              this.$refs[formName].resetFields() //  清空表单
+              this.checkShareComplete()
+            } else {
+              this.$message.error(res.message)
+            }
+          })
+        } else {
+          return false
+        }
+      })
+    },
+    /**
+     * 校验全部通过
+     */
+    checkShareComplete() {
+      this.shareStep = 3 //  展示文件列表
+      this.dialogShareFile.visible = false
+      this.getShareList()
+    },
+    /**
+     * 获取分享文件列表
+     */
+    getShareList() {
+      this.loading = true
+      getShareFileList({
+        shareFilePath: this.shareFilePath,
+        shareBatchNum: this.shareBatchNum
+      }).then((res) => {
+        if (res.success) {
+          this.fileList = res.data
+          this.loading = false
+        } else {
+          this.$message.error(res.message)
+        }
+      })
+    },
+    /**
+     * 保存到网盘按钮点击事件
+     */
+    handleSaveBtnClick() {
+      if (this.selectionFile.length) {
+        this.dialogSelectPath.visible = true
+      } else {
+        this.$message.warning('请先勾选要保存的文件')
+      }
+    },
+    /**
+     * 移动文件模态框 | 设置移动后的文件路径
+     * @param {string} selectFilePath 目标文件夹路径
+     */
+    setSelectFilePath(selectFilePath) {
+      this.selectFilePath = selectFilePath
+    },
+    /**
+     * 移动文件模态框 | 确定按钮事件
+     */
+    confirmSelectPathDialog() {
+      saveShareFile({
+        filePath: this.selectFilePath,
+        files: JSON.stringify(
+          this.selectionFile.map((item) => {
+            return {
+              userFileId: item.userFileId
+            }
+          })
+        )
+      }).then((res) => {
+        if (res.success) {
+          this.$message.success('保存成功')
+          // this.getTableDataByType()
+          this.dialogSelectPath.visible = false
+          this.selectionFile = []
+        } else {
+          this.$message.error(res.message)
+        }
+      })
+    },
+    /**
+     * 设置移动文件模态框相关数据
+     * @param {boolean} isBatchMove 是否批量移动,为 null时是确认移动,值由之前的值而定,在此业务中此致无用
+     * @param {boolean} visible 移动文件对话框状态
+     */
+    setSelectPathDialogData(isBatchMove, visible) {
+      this.dialogSelectPath.visible = visible
+    }
+  }
+}
+</script>
+
+<style lang="stylus" scoped>
+.share-wrapper {
+  .share-file-wrapper {
+    width: 100%;
+
+    .operation-wrapper {
+      display: flex;
+      justify-content: space-between;
+      padding: 8px 0;
+    }
+  }
+}
+</style>

+ 0 - 10
src/views/file/components/FileList/components/FileGrid.vue

@@ -107,16 +107,6 @@ export default {
   data() {
     return {
       fileListSorted: [],
-      //  移动文件模态框数据
-      dialogMoveFile: {
-        isBatchMove: false,
-        visible: false, //  是否可见
-        fileTree: [], //  目录树
-        defaultProps: {
-          children: 'children',
-          label: 'label'
-        }
-      },
       //  可以识别的文件类型
       fileImgTypeList: [
         'png',

+ 39 - 21
src/views/file/components/FileList/components/OperationMenu.vue

@@ -44,6 +44,15 @@
         v-if="fileType !== 6"
         >批量下载</el-button
       >
+      <el-button
+        size="mini"
+        type="primary"
+        :disabled="!selectionFile.length"
+        icon="el-icon-share"
+        @click="handleBatchShareBtnClick()"
+        v-if="fileType !== 6 && $route.name !== 'Share'"
+        >分享</el-button
+      >
     </el-button-group>
 
     <!-- 全局搜素文件 -->
@@ -63,14 +72,17 @@
     </el-input>
 
     <!-- 批量操作 -->
-    <i class="batch-icon el-icon-finished" :class="batchOperate ? 'active' : ''" :title="batchOperate ? '取消批量操作' : '批量操作'" v-if="fileModel === 1" @click="handleBatchOperationChange()"></i>
-    <el-divider direction="vertical" v-if="fileModel === 1" ></el-divider>
+    <i
+      class="batch-icon el-icon-finished"
+      :class="batchOperate ? 'active' : ''"
+      :title="batchOperate ? '取消批量操作' : '批量操作'"
+      v-if="fileModel === 1"
+      @click="handleBatchOperationChange()"
+    ></i>
+    <el-divider direction="vertical" v-if="fileModel === 1"></el-divider>
 
     <!-- 操作栏收纳 -->
-    <el-popover
-      placement="bottom"
-      trigger="hover"
-    >
+    <el-popover placement="bottom" trigger="hover">
       <i slot="reference" class="setting-icon el-icon-setting"></i>
       <!-- 选择表格列 -->
       <SelectColumn></SelectColumn>
@@ -180,7 +192,6 @@ export default {
         },
         loading: false
       },
-      fileTree: [],
       batchDeleteFileDialog: false,
       fileGroupLable: 0 //  文件展示模式
     }
@@ -212,7 +223,7 @@ export default {
         return this.$store.getters.gridSize
       },
       set(val) {
-        this.$store.commit("changeGridSize", val)
+        this.$store.commit('changeGridSize', val)
       }
     }
   },
@@ -370,6 +381,12 @@ export default {
        */
       this.$emit('setMoveFileDialogData', true, true)
     },
+    /**
+     * 分享按钮点击事件
+     */
+    handleBatchShareBtnClick() {
+      this.$emit("setShareFileDialogData")
+    },
     /**
      * 批量下载按钮点击事件
      */
@@ -414,7 +431,7 @@ export default {
      * @param {number} val 改变后的数值
      */
     formatTooltip(val) {
-      return `${val}px`;
+      return `${val}px`
     }
   }
 }
@@ -465,29 +482,30 @@ export default {
     margin-right: 8px;
   }
 
-  .batch-icon,
-  .setting-icon {
-    font-size 20px
-    cursor pointer
-    color $SecondaryText
+  .batch-icon, .setting-icon {
+    font-size: 20px;
+    cursor: pointer;
+    color: $SecondaryText;
+
     &:hover {
-      color $Primary
+      color: $Primary;
     }
   }
 
   .batch-icon.active {
-    color $Primary
+    color: $Primary;
   }
-
 }
+
 .split-line {
-  margin 8px 0  
+  margin: 8px 0;
 }
+
 .change-file-model, .change-grid-size {
   .title {
-    margin 8px 0
-    color $SecondaryText
-    font-size 14px
+    margin: 8px 0;
+    color: $SecondaryText;
+    font-size: 14px;
   }
 }
 </style>

+ 197 - 0
src/views/file/components/FileList/components/ShareFileDialog.vue

@@ -0,0 +1,197 @@
+<template>
+  <div>
+    <!-- 分享对话框 -->
+    <el-dialog
+      title="分享文件"
+      :visible.sync="dialogShareFile.visible"
+      :close-on-click-modal="false"
+      width="550px"
+      @close="handleShareFileDialogCancel('shareFileForm')"
+    >
+      <el-form
+        v-show="!dialogShareFile.success"
+        class="share-file-form"
+        :model="form"
+        ref="shareFileForm"
+        label-suffix=":"
+        label-width="130px"
+        :rules="rules"
+      >
+        <el-form-item label="链接有效期至" prop="endTime">
+          <el-date-picker
+            v-model="form.endTime"
+            type="datetime"
+            placeholder="选择日期时间"
+            align="right"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            :editable="false"
+            :clearable="false"
+            :picker-options="pickerOptions"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="是否需要提取码" prop="shareType">
+          <el-radio-group v-model="form.shareType">
+            <el-radio :label="1">是</el-radio>
+            <el-radio :label="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <el-form
+        v-if="dialogShareFile.success"
+        class="share-success-form"
+        :model="dialogShareFile.shareData"
+        ref="shareSuccessForm"
+        label-suffix=":"
+        label-width="90px"
+      >
+        <div class="success-tip">
+          <i class="el-icon-success"></i>
+          <span class="text">成功创建分享链接</span>
+        </div>
+        <el-form-item label="分享链接" prop="shareBatchNum">
+          <el-input
+            :value="getShareLink(dialogShareFile.shareData.shareBatchNum)"
+            :readonly="true"
+            type="textarea"
+            autosize
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="提取码" prop="extractionCode" v-if="dialogShareFile.shareData.extractionCode">
+          <el-input v-model="dialogShareFile.shareData.extractionCode" :readonly="true"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button v-if="dialogShareFile.success" type="primary" @click="handleCopyBtnClick()"
+          >复制链接及提取码</el-button
+        >
+        <template v-else>
+          <el-button @click="handleShareFileDialogCancel('shareFileForm')">取 消</el-button>
+          <el-button type="primary" :loading="dialogShareFile.loading" @click="handleShareFileDialogOk('shareFileForm')"
+            >确 定</el-button
+          >
+        </template>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ShareFileDialog',
+  props: {
+    dialogShareFile: {
+      required: true,
+      type: Object
+    }
+  },
+  data() {
+    return {
+      // 分享文件对话框数据
+      form: {
+        // endTime: (new Date()).getTime() + 3600 * 1000 * 24,
+        endTime: '',
+        shareType: 0
+      },
+      rules: {
+        endTime: [{ required: true, message: '请选择链接有效期', trigger: 'blur' }]
+      },
+      loading: false,
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: '今天',
+            onClick(picker) {
+              picker.$emit('pick', new Date())
+            }
+          },
+          {
+            text: '1天',
+            onClick(picker) {
+              const date = new Date()
+              date.setTime(date.getTime() + 3600 * 1000 * 24)
+              picker.$emit('pick', date)
+            }
+          },
+          {
+            text: '7天',
+            onClick(picker) {
+              const date = new Date()
+              date.setTime(date.getTime() + 3600 * 1000 * 24 * 7)
+              picker.$emit('pick', date)
+            }
+          },
+          {
+            text: '30天',
+            onClick(picker) {
+              const date = new Date()
+              date.setTime(date.getTime() + 3600 * 1000 * 24 * 30)
+              picker.$emit('pick', date)
+            }
+          }
+        ]
+      }
+    }
+  },
+  methods: {
+    /**
+     * 分享文件对话框 | 取消按钮点击事件
+     * @description 关闭对话框,重置表单
+     * @param {string} formName 表单ref值
+     */
+    handleShareFileDialogCancel(formName) {
+      this.$emit('setDialogShareFileData', false)
+      this.$refs[formName].resetFields()
+    },
+    /**
+     * 分享文件对话框 | 确定按钮点击事件
+     * @description 校验表单,校验通过后调用分享文件接口
+     * @param {string} formName 表单ref值
+     */
+    handleShareFileDialogOk(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.$emit('setDialogShareFileData', true, this.form)
+        } else {
+          return false
+        }
+      })
+    },
+    /**
+     * 复制链接及提取码按钮点击事件
+     */
+    handleCopyBtnClick() {
+      let input = document.createElement('textarea') // 直接构建textarea以保持换行
+      input.value =
+        this.dialogShareFile.shareData.extractionCode === null
+          ? `分享链接:${this.getShareLink(
+              this.dialogShareFile.shareData.shareBatchNum
+            )}\n复制链接到浏览器中并输入提取码即可查看文件`
+          : `分享链接:${this.getShareLink(this.dialogShareFile.shareData.shareBatchNum)}\n提取码:${
+              this.dialogShareFile.shareData.extractionCode
+            }\n复制链接到浏览器中并输入提取码即可查看文件` // 设置内容
+      document.body.appendChild(input) // 添加临时实例
+      input.select() // 选择实例内容
+      document.execCommand('Copy') // 执行复制
+      document.body.removeChild(input) // 删除临时实例
+      this.$message.success('复制成功')
+    }
+  }
+}
+</script>
+
+<style lang="stylus" scoped>
+@import '~@/assets/styles/varibles.styl';
+
+.success-tip {
+  margin-bottom: 16px;
+  text-align: center;
+  color: $Success;
+
+  .el-icon-success {
+    margin-right: 8px;
+    height: 20px;
+    line-height: 20px;
+  }
+}
+</style>

+ 76 - 29
src/views/file/components/FileList/index.vue

@@ -11,6 +11,7 @@
         @getSearchFileList="getSearchFileList"
         @getTableDataByType="getTableDataByType"
         @setMoveFileDialogData="setMoveFileDialogData"
+        @setShareFileDialogData="setShareFileDialogData"
       ></OperationMenu>
     </el-header>
     <div class="middle-wrapper">
@@ -25,6 +26,7 @@
       :loading="loading"
       v-if="fileModel === 0"
       @setMoveFileDialogData="setMoveFileDialogData"
+      @setShareFileDialogData="setShareFileDialogData"
       @setOperationFile="setOperationFile"
       @setSelectionFile="setSelectionFile"
       @getTableDataByType="getTableDataByType"
@@ -59,30 +61,36 @@
     </div>
     <!-- 移动文件模态框 -->
     <MoveFileDialog
-      :dialogMoveFile="dialogMoveFile"
+      :dialogData="dialogMoveFile"
       @setSelectFilePath="setSelectFilePath"
-      @confirmMoveFile="confirmMoveFile"
-      @setMoveFileDialogData="setMoveFileDialogData"
+      @confirmDialog="confirmMoveFile"
+      @setDialogData="setMoveFileDialogData"
     ></MoveFileDialog>
+    <!-- 分享文件模态框 -->
+    <ShareFileDialog
+      :dialogShareFile="dialogShareFile"
+      @setDialogShareFileData="setDialogShareFileData"
+    ></ShareFileDialog>
   </div>
 </template>
 
 <script>
 import OperationMenu from './components/OperationMenu'
-import BreadCrumb from './components/BreadCrumb'
-import FileTable from './components/FileTable'
+import BreadCrumb from '@/components/BreadCrumb'
+import FileTable from '@/components/FileTable'
 import FileGrid from './components/FileGrid'
 import FileTimeLine from './components/FileTimeLine'
-import MoveFileDialog from './components/MoveFileDialog'
+import MoveFileDialog from '@/components/MoveFileDialog'
+import ShareFileDialog from './components/ShareFileDialog'
 
 import {
   getFileListByPath,
   getFileListByType,
   getRecoveryFile,
-  getFoldTree,
   moveFile,
   batchMoveFile,
-  searchFile
+  searchFile,
+  shareFile
 } from '@/request/file.js'
 
 export default {
@@ -93,7 +101,8 @@ export default {
     FileTable,
     FileGrid,
     FileTimeLine,
-    MoveFileDialog
+    MoveFileDialog,
+    ShareFileDialog
   },
   data() {
     return {
@@ -108,12 +117,18 @@ export default {
       //  移动文件模态框数据
       dialogMoveFile: {
         isBatchMove: false,
-        visible: false, //  是否可见
-        fileTree: [] //  目录树
+        visible: false //  是否可见
       },
       selectFilePath: '', //  移动文件路径
       operationFile: {}, // 当前操作行
       selectionFile: [], // 勾选的文件
+      // 分享文件对话框数据
+      dialogShareFile: {
+        visible: false,
+        loading: false,
+        success: false,
+        shareData: {}
+      },
       //  可以识别的文件类型
       fileImgTypeList: [
         'png',
@@ -223,7 +238,7 @@ export default {
       this.getTableDataByType()
     },
     batchOperate(value) {
-      if(!value) {
+      if (!value) {
         this.selectionFile = []
       }
     }
@@ -352,23 +367,9 @@ export default {
      * @param {boolean} visible 移动文件对话框状态
      */
     setMoveFileDialogData(isBatchMove, visible) {
-      this.initFileTree()
       this.dialogMoveFile.isBatchMove = isBatchMove ? isBatchMove : this.dialogMoveFile.isBatchMove
       this.dialogMoveFile.visible = visible
     },
-
-    /**
-     * 移动文件模态框 | 初始化文件目录树
-     */
-    initFileTree() {
-      getFoldTree().then((res) => {
-        if (res.success) {
-          this.dialogMoveFile.fileTree = [res.data]
-        } else {
-          this.$message.error(res.message)
-        }
-      })
-    },
     /**
      * 移动文件模态框 | 设置移动后的文件路径
      * @param {string} selectFilePath 目标文件夹路径
@@ -425,15 +426,61 @@ export default {
         currentPage: this.pageData.currentPage,
         pageCount: this.pageData.pageCount,
         fileName: fileName
-      }).then(res => {
+      }).then((res) => {
         this.loading = false
-        if(res.success) {
-          this.fileList = res.data.searchHits.map(item => item.content)
+        if (res.success) {
+          this.fileList = res.data.searchHits.map((item) => item.content)
           this.pageData.total = res.data.totalHits
         } else {
           this.$message.error(res.message)
         }
       })
+    },
+    /**
+     * 设置分享文件对话框状态
+     */
+    setShareFileDialogData() {
+      this.dialogShareFile.visible = true
+    },
+    /**
+     * 分享文件对话框确定|取消按钮点击事件
+     * @param {boolean} status 对话框状态
+     * @param {object} data 分享文件数据
+     */
+    setDialogShareFileData(status, data) {
+      if (status) {
+        this.dialogShareFile.loading = true
+        shareFile({
+          ...data,
+          remarks: '',
+          files: JSON.stringify(
+            this.selectionFile.map((item) => {
+              return {
+                userFileId: item.userFileId
+              }
+            })
+          )
+        }).then(
+          (res) => {
+            this.dialogShareFile.loading = false
+            if (res.success) {
+              this.dialogShareFile.success = true
+              this.dialogShareFile.shareData = res.data
+            } else {
+              this.$message.error(res.message)
+            }
+          },
+          (error) => {
+            console.log(error)
+            this.$message.error(error.message)
+            this.dialogShareFile.loading = false
+          }
+        )
+      } else {
+        this.dialogShareFile.visible = false
+        this.dialogShareFile.loading = false
+        this.dialogShareFile.success = false
+      }
     }
   }
 }