feat: 新增空格键快速预览docx和excel文件

This commit is contained in:
jmal 2024-08-01 16:04:22 +08:00
parent 48881a860f
commit 06cad76ce0
8 changed files with 164 additions and 45 deletions

View File

@ -15,6 +15,9 @@
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
},
"dependencies": {
"@vue-office/docx": "^1.6.2",
"@vue-office/excel": "^1.7.11",
"@vue/composition-api": "^1.7.2",
"ant-design-vue": "^1.6.5",
"clipboard": "^2.0.6",
"cropperjs": "^1.5.6",
@ -52,6 +55,7 @@
"vditor": "3.10.4",
"vue-aplayer": "^1.6.1",
"vue-color": "^2.8.1",
"vue-demi": "^0.14.6",
"vue-runtime-helpers": "^1.1.2",
"vue-simple-uploader": "0.7.6",
"vuedraggable": "^2.23.2",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
import { Icon } from 'ant-design-vue'
const IconFont = Icon.createFromIconfontCN({
scriptUrl: document.location.protocol + '/resource/font_2024484_jfax4gppvg.js'
scriptUrl: document.location.protocol + '/resource/font_2024484_yd6mpe5qfy.js'
})
// 把此段代码贴到iconfont项目页面的控制台中, 以获取图标列表

View File

@ -93,7 +93,7 @@
</div>
<div v-else-if="(item.contentType && item.contentType.startsWith('image')) || item.showCover">
<el-image v-if="grid || grid === 'details'" :style="{'height':details?'110px':(gridWidth-35) + 'px'}"
fit="contain" :src="item.fileId ? (imageUrl+item.fileId) : (imageUrl+item.id)">
fit="contain" :src="item.fileId ? (imageUrl+item.fileId) : (imageUrl+item.id)" :class="item.showCover ? 'cover' : ''">
<div slot="error" class="image-slot-error">
<svg-icon icon-class="image"/>
</div>
@ -309,5 +309,8 @@ export default {
padding: 0 2px;
line-height: 12px;
}
>>> .el-image.cover {
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
}
</style>

View File

@ -603,7 +603,7 @@
:status.sync="iframePreviewVisible"
></iframe-preview>
<file-details :audio-cover-url="audioCoverUrl" :image-url="imageUrl" :row-context-data="rowContextData" :visible.sync="drawer"></file-details>
<file-details :audio-cover-url="audioCoverUrl" :image-url="imageUrl" :file="rowContextData" :visible.sync="drawer" @openFile="fileClick"></file-details>
<el-dialog
class="open-file-dialog"
@ -1159,7 +1159,7 @@ export default {
if (item.isFolder || !this.grid) {
return filename;
}
const singleLine = item.contentType && item.contentType.startsWith('image')
const singleLine = (item.contentType && item.contentType.startsWith('image')) || item.showCover
const gridFilenameLength = singleLine ? 14 : 32
// 分离文件名和后缀
let parts = filename.split('.');
@ -1376,15 +1376,19 @@ export default {
},
// 获取字符的长度。中文字符长度为2,其他字符长度为1
getCharLength(char) {
return this.isChineseChar(char) ? 2 : 1
//return this.isChineseChar(char) ? 2 : 1
// 简单判断字符长度:中文字符和全角字符长度为2,其他为1
if (this.isChineseChar(char)) {
return 2;
} else if (char === ' ') {
return 2; // 空格作为一个字符处理
} else {
return 1;
}
},
// 获取有效长度。如果有后缀,则包括后缀和点的长度;否则是基础名称和后7位的长度
getEffectiveLength(base, suffix) {
let chineseLength = Array.from(base).reduce(
(count, char) => count + this.getCharLength(char),
0
)
let chineseLength = Array.from(base).reduce((count, char) => count + this.getCharLength(char), 0)
return suffix ? chineseLength + suffix.length + 1 : chineseLength
},
checkCmdKey(event) {
@ -1397,9 +1401,13 @@ export default {
const isCmd = this.checkCmdKey(event)
const checkPreviewVisible = this.checkPreviewVisible()
// space
if (keyCode === 32 && this.selectRowData.length > 0 && !this.drawer && !checkPreviewVisible) {
this.drawer = true
this.drawerShowTime = Date.now()
if (keyCode === 32 && this.selectRowData.length > 0 && !checkPreviewVisible) {
if (!this.drawer) {
this.drawer = true
this.drawerShowTime = Date.now()
} else {
this.drawer = false
}
event.preventDefault()
event.stopPropagation()
}
@ -4206,6 +4214,7 @@ export default {
},
// 点击文件或文件夹
fileClick(row, event) {
this.drawer = false
if (this.queryFileType === 'trash') {
return
}
@ -4582,5 +4591,4 @@ export default {
.mt-5 {
margin-top: 5px;
}
</style>

View File

@ -1,16 +1,31 @@
<template>
<div>
<!--文件详细信息-->
<!-- <el-drawer :title="rowContextData.name" :visible.sync="visible">-->
<!-- -->
<!-- </el-drawer>-->
<el-dialog :visible.sync="visible" width="420px">
<div class="drawer-icon">
<el-dialog :visible.sync="visible" class="details-content" :custom-class="officePreview ? 'dialog-office':''">
<div v-if="officePreview" class="office-preview">
<vue-office-docx
v-if="file.suffix === 'docx' && visible"
:src="fleUrl"
@rendered="rendered"
/>
<vue-office-excel
v-if="file.suffix === 'xlsx' && visible"
:options="excelOptions"
:src="fleUrl"
@rendered="rendered"
/>
</div>
<div v-else class="drawer-icon">
<el-image v-if="file && file.showCover" fit="contain" :src="imageUrl + file.id + '&showCover=true'" class="cover">
<div slot="error" class="image-slot-error">
<svg-icon icon-class="image"/>
</div>
</el-image>
<icon-file
v-else
class="drawer-icon-font"
:grid="true"
:details="true"
:item="rowContextData"
:item="file"
:image-url="imageUrl"
:audio-cover-url="audioCoverUrl"
></icon-file>
@ -26,7 +41,7 @@
</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="cancelScan()">取消</el-button>
<el-button type="primary" size="mini" @click="scanDirectory(rowContextData)">确定</el-button>
<el-button type="primary" size="mini" @click="scanDirectory(file)">确定</el-button>
</div>
<el-button slot="reference" id="scanDirectoryBtn-file" type="primary" size="small" :loading="syncLoading"><i class="el-icon-refresh"></i></el-button>
</el-popover>
@ -36,57 +51,68 @@
<el-scrollbar wrap-class="scrollbar-wrapper" class="details-form-list">
<el-form-item label="名称:">
<span>{{ rowContextData.name }}</span>
<span>{{ file.name }}</span>
</el-form-item>
<el-form-item label="类型:">
<span>{{
rowContextData.isFolder ? '文件夹' : rowContextData.contentType
file.isFolder ? '文件夹' : file.contentType
}}</span>
</el-form-item>
<div v-if="rowContextData.music">
<div v-if="file.music">
<el-form-item label="🎵 歌手:">
<span>{{ rowContextData.music.singer }}</span>
<span>{{ file.music.singer }}</span>
</el-form-item>
<el-form-item label="🎵 专辑:">
<span>{{ '《' + rowContextData.music.album + '》' }}</span>
<span>{{ '《' + file.music.album + '》' }}</span>
</el-form-item>
<el-form-item label="🎵 歌名:">
<span>{{ '《' + rowContextData.music.songName + '》' }}</span>
<span>{{ '《' + file.music.songName + '》' }}</span>
</el-form-item>
</div>
<el-form-item
v-show="rowContextData.w && rowContextData.h"
v-show="file.w && file.h"
label="尺寸:"
class="details-resolution"
>
<span>{{ rowContextData.w + ' x ' + rowContextData.h }}</span>
<span>{{ file.w + ' x ' + file.h }}</span>
</el-form-item>
<el-form-item label="大小:">
<span> {{ formatSize(rowContextData.size) }}</span>
<span> {{ formatSize(file.size) }}</span>
</el-form-item>
<el-form-item label="位置:" class="details-position">
<a :href="'/?path=' + rowContextData.path + '&highlight=' + rowContextData.name">{{ rowContextData.path }}</a>
<a :href="'/?path=' + file.path + '&highlight=' + file.name">{{ file.path }}</a>
</el-form-item>
<el-form-item label="创建时间:">
<span>{{ rowContextData.uploadDate }}</span>
<span>{{ file.uploadDate }}</span>
</el-form-item>
<el-form-item label="修改时间:">
<span>{{ rowContextData.updateDate }}</span>
<span>{{ file.updateDate }}</span>
</el-form-item>
<el-form-item v-if="rowContextData.exif" label="">
<span style="white-space: break-spaces;">{{ formatExif(rowContextData.exif) }}</span>
<el-form-item v-if="file.exif" label="">
<span style="white-space: break-spaces;">{{ formatExif(file.exif) }}</span>
</el-form-item>
<el-form-item v-if="rowContextData.video" label="">
<span style="white-space: break-spaces;">{{ formatVideo(rowContextData.video) }}</span>
<el-form-item v-if="file.video" label="">
<span style="white-space: break-spaces;">{{ formatVideo(file.video) }}</span>
</el-form-item>
</el-scrollbar>
<el-button v-if="allowOpenFile" type="primary" size="small" @click="openFile" class="open-file">打开文件</el-button>
</el-form>
</el-dialog>
</div>
</template>
<script>
// 引入VueOfficeDocx 组件
import VueOfficeDocx from '@vue-office/docx'
// 引入VueOfficeDocx 相关样式
import '@vue-office/docx/lib/index.css'
// 引入VueOfficeExcel 组件
import VueOfficeExcel from '@vue-office/excel'
// 引入VueOfficeExcel 相关样式
import '@vue-office/excel/lib/index.css'
import fileConfig from '@/utils/file-config'
import { formatSize } from '@/utils/number'
import IconFile from '@/components/Icon/IconFile'
import {mapState} from "vuex";
@ -96,14 +122,16 @@ import settingApi from "@/api/setting-api";
export default {
name: 'FileDetails',
components: {
IconFile
IconFile,
VueOfficeDocx,
VueOfficeExcel
},
props: {
visible: {
type: Boolean,
required: true,
},
rowContextData: {
file: {
type: Object,
required: true,
},
@ -122,12 +150,28 @@ export default {
clickSync: false,
indexingPercent: 100,
syncPercent: 100,
excelOptions: {
minColLength: 10,
minRowLength: 10,
widthOffset: 10,
heightOffset: 10,
excel: window.location.origin + fileConfig.previewUrl(this.$store.state.user.name, this.file, this.$store.getters.token)
}
}
},
mounted() {
},
computed: {
...mapState(['message']),
fleUrl() {
return window.location.origin + fileConfig.previewUrl(this.$store.state.user.name, this.file, this.$store.getters.token)
},
officePreview() {
return ['docx', 'xlsx'].includes(this.file.suffix) && this.file.size > 0 && this.file.size < 1024 * 1024 * 5
},
allowOpenFile() {
return (this.file.contentType.indexOf('office') > -1 || ['pdf', 'epub'].includes(this.file.suffix))
}
},
watch: {
message(msg) {
@ -143,6 +187,12 @@ export default {
}
},
methods: {
openFile() {
this.$emit('openFile', this.file)
},
rendered() {
console.log('rendered')
},
formatSize(size) {
return formatSize(size)
},
@ -204,7 +254,8 @@ export default {
}
.details-form {
padding: 20px 0;
padding-bottom: 20px;
width: 280px;
>>> .el-scrollbar__view {
max-height: 50vh;
@ -254,8 +305,14 @@ export default {
}
.drawer-icon {
margin-right: 20px;
text-align: center;
position: relative;
flex: 1;
display: flex;
align-content: center;
flex-wrap: wrap;
justify-content: center;
>>> .icon-favorite {
display: none;
}
@ -267,7 +324,52 @@ export default {
}
}
.drawer-icon-font >>> .svg-icon {
font-size: 8rem;
.drawer-icon-font {
margin-bottom: 40px;
>>> .svg-icon {
font-size: 8rem;
}
}
.details-content {
display: flex;
align-items: center;
justify-content: center;
>>> .el-dialog__body {
display: flex;
}
}
>>> .el-image {
height: 200px !important;
}
>>> .el-image.cover {
height: unset !important;
margin-bottom: 20px;
}
>>> .el-dialog {
margin-top: 0 !important;
width: fit-content;
min-width: 500px;
max-width: 800px;
}
>>> .dialog-office {
max-width: 1400px;
min-width: 1225px;
height: 90vh;
}
.office-preview {
flex: 20;
height: 80vh;
margin-right: 20px;
>>> .docx-wrapper {
padding: 10px;
}
}
.open-file {
margin-top: 10px;
}
</style>

View File

@ -91,6 +91,8 @@ iconClass.set('plist','file-plist')
iconClass.set('iso','file-iso')
iconClass.set('glb','file-glb')
iconClass.set('gltf','file-glb')
iconClass.set('epub','epub')
iconClass.set('dwg','dwg')
export function findIconClass(file){
if(!file){