DiskFile.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <template>
  2. <div style="padding-right: 5px">
  3. <el-row style="padding: 5px">
  4. <el-button size="small" type="primary" icon="el-icon-upload" round @click="onClickUpload">上传</el-button>
  5. <el-button size="small" type="primary" icon="el-icon-folder-add" round @click="onCreateFolder">新建文件夹</el-button>
  6. <el-input
  7. v-model="inputData"
  8. placeholder="搜索我的文件"
  9. size="small"
  10. style="width: 30%; padding-left: 5px"
  11. clearable
  12. @keyup.enter.native="onSearchFile"
  13. >
  14. <el-button slot="append" icon="el-icon-search" @click="onSearchFile" />
  15. </el-input>
  16. <el-divider />
  17. <el-breadcrumb separator-class="el-icon-arrow-right">
  18. <el-breadcrumb-item
  19. v-for="(item, index) in pathList"
  20. :key="index"
  21. style="padding: 1px"
  22. :replace="true"
  23. :to="{ path: item.path }"
  24. >
  25. {{ item.name }}
  26. </el-breadcrumb-item>
  27. </el-breadcrumb>
  28. <el-divider />
  29. <el-table
  30. :data="dataList"
  31. :show-header="true"
  32. style="width: 100%"
  33. @selection-change="handleTableSectionChange"
  34. >
  35. <el-table-column
  36. align="center"
  37. type="selection"
  38. />
  39. <el-table-column
  40. prop="filename"
  41. label="文件名"
  42. >
  43. <template slot-scope="scope">
  44. <a
  45. style="text-decoration-line: none"
  46. href="javascript:void(0)"
  47. @click="onClickFilename(scope.row)"
  48. >
  49. <span>{{ scope.row.filename }}</span>
  50. </a>
  51. </template>
  52. </el-table-column>
  53. <el-table-column
  54. prop="size"
  55. label="大小"
  56. />
  57. <el-table-column
  58. prop="fileTypeStr"
  59. label="类型"
  60. />
  61. <el-table-column
  62. prop="updateTime"
  63. label="修改时间"
  64. />
  65. </el-table>
  66. <div style="margin-top: 20px">
  67. <el-button v-if="selectedTable.length !== 0" @click="addToAlbum">添加 {{ selectedTable.length }} 到相册</el-button>
  68. </div>
  69. <el-pagination
  70. background
  71. :small="screenWidth <= 768"
  72. layout="prev, pager, next"
  73. :page-size="pageSize"
  74. :current-page="currentPage"
  75. :total="totalSize"
  76. @current-change="handleCurrentChange"
  77. @prev-click="handleCurrentChange"
  78. @next-click="handleCurrentChange"
  79. />
  80. </el-row>
  81. <el-dialog
  82. :visible.sync="showPreviewDialog"
  83. :before-close="handlePreviewClose"
  84. width="100%"
  85. center
  86. >
  87. <el-card v-if="fileDetail !== null" class="box-card" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
  88. <div slot="header" class="clearfix">
  89. <span>{{ fileDetail.filename }}</span>
  90. </div>
  91. <div class="text item">
  92. <el-col v-if="fileType === 1001" :md="12">
  93. <div class="imgs">
  94. <el-image
  95. lazy
  96. fit="cover"
  97. class="coverImg"
  98. :src="fileDetail.url"
  99. />
  100. </div>
  101. </el-col>
  102. <el-col v-else-if="fileType === 1002" :md="12">
  103. <video
  104. :src="fileDetail.url"
  105. controls
  106. autoplay
  107. class="video"
  108. width="100%"
  109. />
  110. </el-col>
  111. <el-col v-else-if="fileType === 1003" :md="12">
  112. <audio
  113. :src="fileDetail.url"
  114. controls
  115. autoplay
  116. class="audio"
  117. />
  118. </el-col>
  119. <el-col v-else-if="fileType === 1004" :md="12">
  120. 文档
  121. </el-col>
  122. <el-col v-else-if="fileType === 1005" :md="12">
  123. 文件
  124. </el-col>
  125. </div>
  126. </el-card>
  127. </el-dialog>
  128. <el-dialog
  129. :visible.sync="showCreateFolderDialog"
  130. width="30%"
  131. center
  132. >
  133. <div>
  134. <el-form ref="createFolderForm" :model="createFolderForm">
  135. <el-form-item label="当前文件夹" label-width="120px" prop="title">
  136. <el-input
  137. v-model="createFolderForm.path"
  138. style="margin-left: 5px"
  139. readonly
  140. />
  141. </el-form-item>
  142. <el-form-item label="新文件夹名" label-width="120px" prop="title">
  143. <el-input
  144. v-model="createFolderForm.folderName"
  145. style="margin-left: 5px"
  146. clearable
  147. />
  148. </el-form-item>
  149. <el-button
  150. type="primary"
  151. plain
  152. size="small"
  153. icon="el-icon-plus"
  154. style="margin-left: 10px"
  155. @click="createFolder"
  156. >
  157. 创建文件夹
  158. </el-button>
  159. </el-form>
  160. </div>
  161. </el-dialog>
  162. <el-dialog
  163. :visible.sync="showUploadDialog"
  164. :before-close="handleUploadClose"
  165. :close-on-click-modal="false"
  166. width="50%"
  167. center
  168. >
  169. <div>
  170. <uploader
  171. v-if="options !== null"
  172. class="uploader-example"
  173. :options="options"
  174. :auto-start="true"
  175. @file-added="onFileAdded"
  176. @file-success="onFileSuccess"
  177. @file-progress="onFileProgress"
  178. @file-error="onFileError"
  179. >
  180. <uploader-unsupport />
  181. <uploader-drop>
  182. <p>拖动文件到此处或</p>
  183. <uploader-btn :attrs="attrs">选择文件</uploader-btn>
  184. </uploader-drop>
  185. <uploader-list />
  186. </uploader>
  187. </div>
  188. </el-dialog>
  189. <el-dialog
  190. :visible.sync="showAddToAlbumUploadDialog"
  191. width="50%"
  192. center
  193. >
  194. <el-table
  195. :data="albumKeyValues"
  196. :show-header="true"
  197. style="width: 100%"
  198. >
  199. <el-table-column
  200. prop="value"
  201. label="相册 ID"
  202. />
  203. <el-table-column
  204. prop="label"
  205. label="相册名"
  206. />
  207. <el-table-column label="操作">
  208. <template slot-scope="scope">
  209. <el-button
  210. size="mini"
  211. @click="onAddToAlbum(scope.row)"
  212. >选择</el-button>
  213. </template>
  214. </el-table-column>
  215. </el-table>
  216. </el-dialog>
  217. </div>
  218. </template>
  219. <script>
  220. import { addToAlbum, getAlbumKeyValues, getDiskChannelInfo, getDiskFile, getFileDetail } from '@/api/disk'
  221. import { hashFile } from '@/utils/functions'
  222. export default {
  223. name: 'DiskFile',
  224. data() {
  225. return {
  226. // 屏幕宽度, 为了控制分页条的大小
  227. screenWidth: document.body.clientWidth,
  228. currentPage: 1,
  229. pageSize: 10,
  230. totalSize: 0,
  231. dataList: [],
  232. // 对话框被选中的文件
  233. selectedTable: [],
  234. inputData: '',
  235. showPreviewDialog: false,
  236. fileDetail: null,
  237. fileType: 0,
  238. videoProp: null,
  239. pathList: [],
  240. queryForm: {
  241. pn: 1,
  242. path: '/',
  243. fileType: null
  244. },
  245. // ****************************************************************************************************************
  246. showCreateFolderDialog: false,
  247. createFolderForm: {
  248. path: '/',
  249. folderName: null
  250. },
  251. // ****************************************************************************************************************
  252. showAddToAlbumUploadDialog: false,
  253. albumKeyValues: [],
  254. // ****************************************************************************************************************
  255. showUploadDialog: false,
  256. options: {
  257. target: '',
  258. // 分块大小 10MB
  259. chunkSize: 1024 * 1024 * 10,
  260. // 失败自动重传次数
  261. maxChunkRetries: 3,
  262. fileParameterName: 'file',
  263. testChunks: true
  264. },
  265. attrs: {
  266. accept: '*'
  267. }
  268. }
  269. },
  270. created() {
  271. const pn = this.$route.query.pn
  272. if (pn !== undefined) {
  273. this.queryForm.pn = pn
  274. }
  275. const path = this.$route.query.path
  276. if (path !== undefined) {
  277. this.queryForm.path = path
  278. }
  279. document.title = '所有文件'
  280. this.getData()
  281. },
  282. methods: {
  283. // ****************************************************************************************************************
  284. onFileAdded(file) {
  285. if (file.file.size > 1024 * 1024 * 1024 * 20) {
  286. file.cancel()
  287. this.$notify({
  288. title: '提示',
  289. message: '文件应小于 20GB',
  290. type: 'warning',
  291. duration: 3000
  292. })
  293. return
  294. }
  295. this.setTitle(file.file.name)
  296. this.processVideo(file.file)
  297. file.pause()
  298. hashFile(file.file).then(result => {
  299. this.startUpload(result.sha256sum, file)
  300. })
  301. },
  302. startUpload(sha256sum, file) {
  303. file.uniqueIdentifier = sha256sum
  304. file.resume()
  305. },
  306. onFileProgress(rootFile, file, chunk) {
  307. },
  308. onFileSuccess(rootFile, file, response, chunk) {
  309. const res = JSON.parse(response)
  310. if (res.code === 0) {
  311. const resData = res.data
  312. this.form.videoFileId = resData.uploadId
  313. this.$notify({
  314. title: '提示',
  315. message: '视频已上传',
  316. type: 'warning',
  317. duration: 3000
  318. })
  319. } else {
  320. this.$notify({
  321. title: '提示',
  322. message: '视频文件上传失败',
  323. type: 'warning',
  324. duration: 3000
  325. })
  326. }
  327. },
  328. onFileError(rootFile, file, response, chunk) {
  329. this.$notify({
  330. title: '提示',
  331. message: '视频文件上传错误',
  332. type: 'warning',
  333. duration: 3000
  334. })
  335. },
  336. handleUploadClose() {
  337. this.showUploadDialog = false
  338. },
  339. onClickUpload() {
  340. this.showUploadDialog = true
  341. getDiskChannelInfo().then(resp => {
  342. if (resp.code === 0) {
  343. const respData = resp.data
  344. this.form.channelCode = respData.channelCode
  345. this.options = {
  346. target: respData.ossUrl,
  347. // 分块大小 10MB
  348. chunkSize: 1024 * 1024 * 10,
  349. // 失败自动重传次数
  350. maxChunkRetries: 3,
  351. fileParameterName: 'file',
  352. testChunks: true,
  353. // 服务器分片校验函数, 秒传及断点续传基础
  354. checkChunkUploadedByResponse: function(chunk, message) {
  355. const objMessage = JSON.parse(message)
  356. const respData = objMessage.data
  357. if (respData.skipUpload) {
  358. return true
  359. }
  360. return (respData.uploaded || []).indexOf(chunk.offset + 1) >= 0
  361. },
  362. query: (file, chunk) => {
  363. return {
  364. channelCode: respData.channelCode,
  365. multiparts: ''
  366. }
  367. },
  368. headers: {
  369. Authorization: 'Bearer ' + respData.token
  370. },
  371. withCredentials: false
  372. }
  373. this.showUploadDialog = true
  374. } else {
  375. this.$notify({
  376. title: '提示',
  377. message: '获取 OSS 服务器地址失败, 暂时无法上传视频文件',
  378. type: 'error',
  379. duration: 3000
  380. })
  381. }
  382. }).catch(error => {
  383. this.$notify({
  384. title: '获取 OSS 服务器地址失败, 暂时无法上传视频文件',
  385. message: error.message,
  386. type: 'warning',
  387. duration: 3000
  388. })
  389. })
  390. },
  391. // ****************************************************************************************************************
  392. handlePreviewClose() {
  393. this.showPreviewDialog = false
  394. this.fileDetail = null
  395. this.fileType = 0
  396. },
  397. onClickFilename(row) {
  398. this.fileType = row.fileType
  399. if (this.fileType === 1000) {
  400. const filename = row.filename
  401. const url = this.pathList[this.pathList.length - 1].path
  402. const arr = url.split('?')
  403. let path = ''
  404. if (arr.length === 1) {
  405. path = '/' + filename
  406. } else {
  407. path = arr[1].split('=')[1] + '/' + filename
  408. }
  409. this.$router.push({
  410. path: '/disk',
  411. query: {
  412. path: path
  413. }
  414. })
  415. this.$router.go(0)
  416. } else {
  417. getFileDetail(row.fileId).then(resp => {
  418. if (resp.code === 0) {
  419. this.showPreviewDialog = true
  420. this.fileDetail = resp.data
  421. if (this.fileType === 1002) {
  422. this.videoProp = {
  423. videoUrl: this.fileDetail.url
  424. }
  425. }
  426. }
  427. })
  428. }
  429. },
  430. // ****************************************************************************************************************
  431. onCreateFolder() {
  432. this.showCreateFolderDialog = true
  433. },
  434. createFolder() {
  435. console.log(this.createFolderForm)
  436. this.showCreateFolderDialog = false
  437. },
  438. // ****************************************************************************************************************
  439. handleCurrentChange(pageNumber) {
  440. this.currentPage = pageNumber
  441. this.queryForm.pn = this.currentPage
  442. this.getData()
  443. // 回到顶部
  444. scrollTo(0, 0)
  445. },
  446. getData() {
  447. getDiskFile(this.queryForm).then(resp => {
  448. if (resp.code === 0) {
  449. const respData = resp.data
  450. const namePathList = respData.namePathList
  451. const pageList = respData.pageList
  452. this.dataList = pageList.list
  453. this.totalSize = pageList.totalSize
  454. if (namePathList.length === 0) {
  455. this.pathList.push({ path: '/disk', name: '全部文件' })
  456. } else {
  457. for (const namePath of namePathList) {
  458. this.pathList.push({ path: '/disk?path=' + namePath.path, name: namePath.filename })
  459. }
  460. }
  461. }
  462. })
  463. },
  464. // 处理表格被选中
  465. handleTableSectionChange(val) {
  466. this.selectedTable = val
  467. },
  468. addToAlbum() {
  469. this.$message.info('addToAlbum')
  470. getAlbumKeyValues().then(resp => {
  471. if (resp.code === 0) {
  472. this.albumKeyValues = resp.data
  473. this.showAddToAlbumUploadDialog = true
  474. }
  475. })
  476. },
  477. onAddToAlbum(row) {
  478. const albumId = row.value
  479. const fileIds = []
  480. for (const item of this.selectedTable) {
  481. fileIds.push(item.fileId)
  482. }
  483. const jsonData = {}
  484. jsonData.albumId = albumId
  485. jsonData.fileIds = fileIds
  486. addToAlbum(jsonData).then(resp => {
  487. this.$message.info(resp.msg)
  488. }).finally(() => {
  489. this.showAddToAlbumUploadDialog = false
  490. this.selectedTable = []
  491. })
  492. },
  493. onSearchFile() {
  494. this.$message.info('search file')
  495. }
  496. }
  497. }
  498. </script>
  499. <style>
  500. /*处于手机屏幕时*/
  501. @media screen and (max-width: 768px){
  502. .coverImg {
  503. height: 120px !important;
  504. }
  505. }
  506. .coverImg {
  507. width: 100%;
  508. height: 320px;
  509. display: block;
  510. }
  511. </style>