Feature: add video,text file and markdown file preview

This commit is contained in:
萌萌哒赫萝
2023-02-21 18:42:50 +08:00
parent 893e33d591
commit b3ce9b9543
21 changed files with 967 additions and 23 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

@@ -1,4 +1,4 @@
/*
ea/*
*UI布局和部分样式代码参考了https://github.com/willnewii/qiniuClient
*感谢作者@willnewii
*/
@@ -29,7 +29,7 @@
/>
</el-select>
<el-input
v-else-if="['aliyun', 'qiniu', 'tcyun', 's3plist'].includes(currentPicBedName)"
v-else-if="showCustomUrlInput"
v-model="currentCustomUrl"
placeholder="请输入自定义域名"
style="width: 200px;"
@@ -764,18 +764,97 @@ https://www.baidu.com/img/bd_logo1.png"
</el-tab-pane>
</el-tabs>
</el-drawer>
<el-dialog
v-model="isShowMarkDownDialog"
title="预览MD"
center
align-center
draggable
fullscreen
close-on-press-escape
show-close
destroy-on-close
>
<div
style="-webkit-user-select: text"
v-html="markDownContent"
/>
<el-button
type="danger"
:icon="Close"
size="large"
style="position: fixed;bottom: 10px;right: 15px"
circle
@click="() => {isShowMarkDownDialog = false}"
/>
</el-dialog>
<el-dialog
v-model="isShowTextFileDialog"
title="预览"
center
align-center
draggable
fullscreen
close-on-press-escape
show-close
destroy-on-close
>
<highlightjs
style="-webkit-user-select: text;"
language="js"
:code="textfileContent"
/>
<el-button
type="danger"
:icon="Close"
size="large"
style="position: fixed;bottom: 10px;right: 15px"
circle
@click="() => {isShowTextFileDialog = false}"
/>
</el-dialog>
<el-dialog
v-model="isShowVideoFileDialog"
title="播放"
center
align-center
draggable
fullscreen
close-on-press-escape
show-close
destroy-on-close
>
<video-player
:src="videoFileUrl"
controls
:loop="true"
:volume="0.6"
:autoplay="true"
:width="1100"
:height="700"
/>
<el-button
type="danger"
:icon="Close"
size="large"
style="position: fixed;bottom: 10px;right: 15px"
circle
@click="() => {isShowVideoFileDialog = false}"
/>
</el-dialog>
</div>
</template>
<script lang="tsx" setup>
import { ref, reactive, watch, onBeforeMount, computed, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router'
import { Folder, FolderAdd, Upload, CircleClose, Loading, CopyDocument, Edit, DocumentAdd, Link, Refresh, ArrowRight, HomeFilled, Document, Coin, Download, DeleteFilled, Sort, FolderOpened } from '@element-plus/icons-vue'
import { Close, Folder, FolderAdd, Upload, CircleClose, Loading, CopyDocument, Edit, DocumentAdd, Link, Refresh, ArrowRight, HomeFilled, Document, Coin, Download, DeleteFilled, Sort, FolderOpened } from '@element-plus/icons-vue'
import { useManageStore } from '../store/manageStore'
import { renameFile, formatLink, formatFileName, getFileIconPath, formatFileSize, getExtension, isValidUrl } from '../utils/common'
import { ipcRenderer, clipboard, IpcRendererEvent } from 'electron'
import { fileCacheDbInstance } from '../store/bucketFileDb'
import { trimPath } from '~/main/manage/utils/common'
import axios from 'axios'
import {
ElMessage, ElMessageBox, ElNotification,
ElButton,
@@ -798,7 +877,9 @@ import path from 'path'
import { IUploadTask, IDownloadTask } from '~/main/manage/datastore/upDownTaskQueue'
import fs from 'fs-extra'
import { getConfig, saveConfig } from '../utils/dataSender'
import { marked } from 'marked'
import { textFileExt } from '../utils/textfile'
import { videoExt } from '../utils/videofile'
/*
configMap:{
prefix: string, -> baseDir
@@ -871,12 +952,20 @@ const isLoadingUploadPanelFiles = ref(false)
const tableData = reactive([] as any[])
const customUrlList = ref([] as any[])
const currentCustomUrl = ref('')
const isShowMarkDownDialog = ref(false)
const markDownContent = ref('')
const isShowTextFileDialog = ref(false)
const textfileContent = ref('')
const isShowVideoFileDialog = ref(false)
const videoFileUrl = ref('')
const showCustomUrlSelectList = computed(() => ['tcyun', 'aliyun', 'qiniu', 'github'].includes(currentPicBedName.value))
const showCreateNewFolder = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 'github', 's3plist'].includes(currentPicBedName.value))
const showCustomUrlInput = computed(() => ['aliyun', 'qiniu', 'tcyun', 's3plist', 'webdavplist'].includes(currentPicBedName.value))
const showRenameFileIcon = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 's3plist'].includes(currentPicBedName.value))
const showCreateNewFolder = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 'github', 's3plist', 'webdavplist'].includes(currentPicBedName.value))
const showRenameFileIcon = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 's3plist', 'webdavplist'].includes(currentPicBedName.value))
const showPresignedUrl = computed(() => ['tcyun', 'aliyun', 'qiniu', 'github', 's3plist'].includes(currentPicBedName.value))
@@ -1177,6 +1266,40 @@ async function handleClickFile (item: any) {
showLoadingPage.value = true
await resetParam(false)
showLoadingPage.value = false
} else if (item.fileName.endsWith('.md')) {
try {
ElMessage({
message: '开始获取文件',
duration: 300,
type: 'success'
})
const fileUrl = item.url
const res = await axios.get(fileUrl)
const content = res.data
markDownContent.value = marked(content)
isShowMarkDownDialog.value = true
} catch (error) {
ElMessage.error('获取文件内容失败')
}
} else if (textFileExt.includes(path.extname(item.fileName).toLowerCase()) ||
textFileExt.includes(item.fileName.toLowerCase())
) {
try {
ElMessage({
message: '开始获取文件',
duration: 300,
type: 'success'
})
const fileUrl = item.url
const res = await axios.get(fileUrl)
textfileContent.value = res.data
isShowTextFileDialog.value = true
} catch (error) {
ElMessage.error('获取文件内容失败')
}
} else if (videoExt.includes(path.extname(item.fileName).toLowerCase())) {
videoFileUrl.value = item.url
isShowVideoFileDialog.value = true
}
}
@@ -1206,7 +1329,7 @@ async function handelChangeCustomUrl () {
showLoadingPage.value = true
await resetParam(true)
showLoadingPage.value = false
} else if (['aliyun', 'tcyun', 'qiniu', 's3plist'].includes(currentPicBedName.value)) {
} else if (['aliyun', 'tcyun', 'qiniu', 's3plist', 'webdavplist'].includes(currentPicBedName.value)) {
const currentConfigs = await getConfig<any>('picBed')
const currentConfig = currentConfigs[configMap.alias]
const currentTransformedConfig = JSON.parse(currentConfig.transformedConfig ?? '{}')
@@ -1297,6 +1420,20 @@ async function initCustomUrlList () {
}
}
handelChangeCustomUrl()
} else if (currentPicBedName.value === 'webdavplist') {
const currentConfigs = await getConfig<any>('picBed')
const currentConfig = currentConfigs[configMap.alias]
const currentTransformedConfig = JSON.parse(currentConfig.transformedConfig ?? '{}')
if (currentTransformedConfig[configMap.bucketName] && currentTransformedConfig[configMap.bucketName]?.customUrl) {
currentCustomUrl.value = currentTransformedConfig[configMap.bucketName].customUrl
} else {
let endpoint = manageStore.config.picBed[configMap.alias].endpoint
if (!/^https?:\/\//.test(endpoint)) {
endpoint = 'http://' + endpoint
}
currentCustomUrl.value = endpoint
}
handelChangeCustomUrl()
}
}

View File

@@ -153,6 +153,7 @@
v-if="supportedPicBedList[item.icon].configOptions[option].type === 'string'"
v-model.trim="configResult[item.icon + '.' + option]"
:placeholder="supportedPicBedList[item.icon].configOptions[option].placeholder"
:disabled="!!supportedPicBedList[item.icon].configOptions[option].disabled"
/>
<el-switch
v-else-if="supportedPicBedList[item.icon].configOptions[option].type === 'boolean'"

View File

@@ -316,7 +316,8 @@ const urlMap : IStringKeyMap = {
qiniu: 'https://portal.qiniu.com',
tcyun: 'https://console.cloud.tencent.com/cos',
upyun: 'https://console.upyun.com',
s3plist: 'https://aws.amazon.com/cn/s3/'
s3plist: 'https://aws.amazon.com/cn/s3/',
webdavplist: 'https://baike.baidu.com/item/WebDAV/4610909'
}
const openPicBedUrl = () => shell.openExternal(urlMap[currentPagePicBedConfig.picBedName])
@@ -442,7 +443,8 @@ const menuTitleMap:IStringKeyMap = {
s3plist: '存储桶',
smms: '相册',
imgur: '相册',
github: '仓库'
github: '仓库',
webdavplist: ''
}
const showNewIconList = ['aliyun', 'qiniu', 'tcyun']

View File

@@ -27,15 +27,16 @@ export class FileCacheDb extends Dexie {
upyun: Table<IFileCache, string>
imgur: Table<IFileCache, string>
s3plist: Table<IFileCache, string>
webdavplist: Table<IFileCache, string>
constructor () {
super('bucketFileDb')
const tableNames = ['tcyun', 'aliyun', 'qiniu', 'github', 'smms', 'upyun', 'imgur', 's3plist']
const tableNames = ['tcyun', 'aliyun', 'qiniu', 'github', 'smms', 'upyun', 'imgur', 's3plist', 'webdavplist']
const tableNamesMap = tableNames.reduce((acc, cur) => {
acc[cur] = '&key, value'
return acc
}, {} as IStringKeyMap)
this.version(1).stores(tableNamesMap)
this.version(2).stores(tableNamesMap)
this.tcyun = this.table('tcyun')
this.aliyun = this.table('aliyun')
this.qiniu = this.table('qiniu')
@@ -44,6 +45,7 @@ export class FileCacheDb extends Dexie {
this.upyun = this.table('upyun')
this.imgur = this.table('imgur')
this.s3plist = this.table('s3plist')
this.webdavplist = this.table('webdavplist')
}
}

View File

@@ -661,5 +661,100 @@ export const supportedPicBedList: IStringKeyMap = {
options: ['alias', 'accessKeyId', 'secretAccessKey', 'endpoint', 'sslEnabled', 's3ForcePathStyle', 'proxy', 'aclForUpload', 'bucketName', 'baseDir', 'paging', 'itemsPerPage'],
refLink: 'https://github.com/wayjam/picgo-plugin-s3',
referenceText: '配置教程请参考:'
},
webdavplist: {
name: 'WebDAV',
icon: 'webdavplist',
configOptions: {
alias: {
required: true,
description: '配置别名-必需',
placeholder: '该配置的唯一标识',
type: 'string',
rule: aliasRule,
default: 'webdavplist-A',
tooltip: aliasTooltip
},
endpoint: {
required: true,
description: '地址-必需',
placeholder: '例如https://example.com/dav',
type: 'string',
rule: defaultBaseRule('rootDomain'),
tooltip: '请填写完整的WebDAV地址例如https://example.com/dav'
},
username: {
required: true,
description: '用户名-必需',
placeholder: '请输入用户名',
type: 'string',
rule: defaultBaseRule('username')
},
bucketName: {
required: true,
description: '特殊配置',
placeholder: '例如bucket1',
type: 'string',
default: 'webdav',
disabled: true,
tooltip: '此处不可修改,仅为软件兼容性考虑'
},
password: {
required: true,
description: '密码-必需',
placeholder: '请输入密码',
type: 'string',
rule: defaultBaseRule('password')
},
baseDir: {
required: false,
description: '起始目录-可选',
placeholder: '例如:/test1',
type: 'string',
default: '/',
tooltip: baseDirTooltip
},
customUrl: {
required: false,
description: '自定义域名-可选',
placeholder: '例如https://example.com',
type: 'string',
tooltip: '如果您的WebDAV服务器支持自定义域名请填写完整的自定义域名例如https://example.com',
rule: [
{
validator: (rule: any, value: any, callback: any) => {
if (value) {
if (!/^https?:\/\/.+/.test(value)) {
callback(new Error('自定义域名请以http://或https://开头'))
} else {
callback()
}
} else {
callback()
}
},
trigger: 'change'
}
]
},
proxy: {
required: false,
description: '代理-可选',
placeholder: '例如http://127.0.0.1:1080',
type: 'string',
tooltip: '如果需要特殊网络环境才能访问,请使用代理'
},
sslEnabled: {
required: true,
description: '使用HTTPS连接',
default: true,
type: 'boolean',
tooltip: '根据WebDAV服务器的配置如果您的服务器不支持HTTPS请关闭该选项'
}
},
explain: 'WebDAV配置',
options: ['alias', 'endpoint', 'username', 'password', 'bucketName', 'baseDir', 'customUrl', 'proxy', 'sslEnabled'],
refLink: 'https://pichoro.horosama.com/#/PicHoroDocs/configure?id=webdav',
referenceText: '配置教程请参考:'
}
}

View File

@@ -0,0 +1,41 @@
export const textFileExt = [
'.bat',
'.c',
'.cmd',
'.conf',
'.config',
'.cpp',
'.css',
'.csv',
'.dart',
'.gitattributes',
'.gitconfig',
'.gitignore',
'.gitkeep',
'.gitmodules',
'.go',
'.h',
'.hpp',
'.htm',
'.html',
'.ini',
'.java',
'.js',
'.json',
'.log',
'.php',
'.prop',
'.properties',
'.py',
'.rc',
'.sh',
'.tsv',
'.txt',
'.xml',
'.yaml',
'.yml',
'.ts',
'.yarnrc',
'.condarc',
'license'
]

View File

@@ -0,0 +1,29 @@
export const videoExt = [
'.aac',
'.amv',
'.avi',
'.flac',
'.flv',
'.m2ts',
'.m4a',
'.m4v',
'.mp3',
'.mpeg',
'.mpg',
'.mts',
'.ogg',
'.ogv',
'.vob',
'.wav',
'.webm',
'.mp4',
'.3g2',
'.3gp',
'.asf',
'.mov',
'.mxf',
'.rm',
'.rmvb',
'.wmv',
'.mkv'
]

View File

@@ -906,7 +906,7 @@ function handleLanguageChange (val: string) {
}
function goConfigPage () {
sendToMain(OPEN_URL, 'https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
sendToMain(OPEN_URL, 'https://piclist.cn/configure.html')
}
function goShortCutPage () {