Feature: optimize filename display

This commit is contained in:
萌萌哒赫萝 2023-08-15 00:00:27 -07:00
parent e8189b0837
commit 2336483927
6 changed files with 65 additions and 116 deletions

View File

@ -50,12 +50,12 @@ class LocalApi {
transBack (filePath: string | undefined) { transBack (filePath: string | undefined) {
if (!filePath) return '' if (!filePath) return ''
return this.isWindows return this.isWindows
? filePath.split(path.posix.sep).join(path.sep).replace(/^\\+/, '') ? filePath.split(path.posix.sep).join(path.sep).replace(/^\\+|\\+$/g, '')
: `/${filePath.replace(/^\/+/, '')}` : `/${filePath.replace(/^\/+|\/+$/g, '')}`
} }
formatFolder (item: fs.Stats, urlPrefix: string, fileName: string, filePath: string) { formatFolder (item: fs.Stats, urlPrefix: string, fileName: string, filePath: string) {
const key = this.transPathToUnix(filePath) const key = `${this.transPathToUnix(filePath)}/`.replace(/\/+$/, '/')
return { return {
...item, ...item,
key, key,
@ -71,8 +71,8 @@ class LocalApi {
} }
} }
formatFile (item: fs.Stats, urlPrefix: string, fileName: string, filePath: string) { formatFile (item: fs.Stats, urlPrefix: string, fileName: string, filePath: string, isDownload = false) {
const key = this.transPathToUnix(filePath) const key = isDownload ? filePath : this.transPathToUnix(filePath)
return { return {
...item, ...item,
key, key,
@ -106,7 +106,7 @@ class LocalApi {
finished: false finished: false
} }
try { try {
res = fsWalk.walkSync(prefix, { res = fsWalk.walkSync(this.transBack(prefix), {
followSymbolicLinks: true, followSymbolicLinks: true,
fs, fs,
stats: true, stats: true,
@ -114,9 +114,9 @@ class LocalApi {
}) })
if (res.length) { if (res.length) {
result.fullList.push( result.fullList.push(
...res.data ...res
.filter((item: fsWalk.Entry) => item.stats?.isFile()) .filter((item: fsWalk.Entry) => item.stats?.isFile())
.map((item: any) => this.formatFile(item, urlPrefix, item.name, item.path)) .map((item: any) => this.formatFile(item, urlPrefix, item.name, item.path, true))
) )
result.success = true result.success = true
} }
@ -133,7 +133,7 @@ class LocalApi {
const { customUrl = '', cancelToken, baseDir } = configMap const { customUrl = '', cancelToken, baseDir } = configMap
let prefix = configMap.prefix let prefix = configMap.prefix
prefix = this.transBack(prefix) prefix = this.transBack(prefix)
let urlPrefix = customUrl.replace(/\/+$/, '') const urlPrefix = customUrl.replace(/\/+$/, '')
let webPath = configMap.webPath || '' let webPath = configMap.webPath || ''
if (webPath && customUrl && webPath !== '/') { if (webPath && customUrl && webPath !== '/') {
webPath = webPath.replace(/^\/+|\/+$/, '') webPath = webPath.replace(/^\/+|\/+$/, '')
@ -156,20 +156,22 @@ class LocalApi {
withFileTypes: true withFileTypes: true
}) })
if (res.length) { if (res.length) {
let urlPrefixF
res.forEach((item: fs.Dirent) => { res.forEach((item: fs.Dirent) => {
const pathOfFile = path.join(prefix, item.name) const pathOfFile = path.join(prefix, item.name)
const relativePath = path.relative(baseDir, pathOfFile) let relative
const relative = webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/') if (customUrl) {
if (webPath && customUrl) { const relativePath = path.relative(this.transBack(baseDir), pathOfFile)
urlPrefix = relative relative = urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
urlPrefixF = this.isWindows ? relative.replace(/\/[a-zA-Z]:\//, '/') : relative
} else { } else {
urlPrefix = pathOfFile urlPrefixF = pathOfFile
} }
const stats = fs.statSync(pathOfFile) const stats = fs.statSync(pathOfFile)
if (item.isDirectory()) { if (item.isDirectory()) {
result.fullList.push(this.formatFolder(stats, urlPrefix, item.name, relativePath)) result.fullList.push(this.formatFolder(stats, urlPrefixF, item.name, pathOfFile))
} else { } else {
result.fullList.push(this.formatFile(stats, urlPrefix, item.name, relativePath)) result.fullList.push(this.formatFile(stats, urlPrefixF, item.name, pathOfFile))
} }
}) })
result.success = true result.success = true
@ -210,7 +212,7 @@ class LocalApi {
const { key } = configMap const { key } = configMap
let result = false let result = false
try { try {
await fs.rmdir(this.transBack(key), { await fs.rm(this.transBack(key), {
recursive: true recursive: true
}) })
result = true result = true
@ -241,7 +243,7 @@ class LocalApi {
noProgress: true noProgress: true
}) })
try { try {
fs.ensureFileSync(filePath) fs.ensureFileSync(this.transBack(key))
await fs.copyFile(filePath, this.transBack(key)) await fs.copyFile(filePath, this.transBack(key))
instance.updateUploadTask({ instance.updateUploadTask({
id, id,
@ -281,7 +283,7 @@ class LocalApi {
const instance = UpDownTaskQueue.getInstance() const instance = UpDownTaskQueue.getInstance()
for (const item of fileArray) { for (const item of fileArray) {
const { alias, bucketName, key, fileName } = item const { alias, bucketName, key, fileName } = item
const savedFilePath = path.join(downloadPath, fileName) const savedFilePath = path.join(downloadPath, fileName.replace(/[:*?"<>|]/g, ''))
const id = `${alias}-${bucketName}-local-${key}` const id = `${alias}-${bucketName}-local-${key}`
if (instance.getDownloadTask(id)) { if (instance.getDownloadTask(id)) {
continue continue

View File

@ -1,76 +0,0 @@
<template>
<el-image
:src="isShowThumbnail && item.isImage ?
base64Image
: require(`../manage/pages/assets/icons/${getFileIconPath(item.fileName ?? '')}`)"
fit="contain"
style="height: 100px;width: 100%;margin: 0 auto;"
>
<template #placeholder>
<el-icon>
<Loading />
</el-icon>
</template>
<template #error>
<el-image
:src="require(`../manage/pages/assets/icons/${getFileIconPath(item.fileName ?? '')}`)"
fit="contain"
style="height: 100px;width: 100%;margin: 0 auto;"
/>
</template>
</el-image>
</template>
<script lang="ts" setup>
import { ref, onBeforeMount, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { Loading } from '@element-plus/icons-vue'
import fs from 'fs-extra'
import mime from 'mime-types'
import path from 'path'
const base64Image = ref('')
const props = defineProps(
{
isShowThumbnail: {
type: Boolean,
required: true
},
item: {
type: Object,
required: true
},
localPath: {
type: String,
required: true
}
}
)
watch(
() => props.localPath,
async (newLocalPath, oldLocalPath) => {
if (newLocalPath !== oldLocalPath) {
try {
await createBase64Image()
} catch (e) {
console.log(e)
}
}
}
)
const createBase64Image = async () => {
const filePath = path.normalize(props.localPath)
const base64 = await fs.readFile(filePath, 'base64')
base64Image.value = `data:${mime.lookup(filePath) || 'image/png'};base64,${base64}`
}
onBeforeMount(async () => {
try {
await createBase64Image()
} catch (e) {
console.log(e)
}
})
</script>

View File

@ -59,7 +59,7 @@
class="icon" class="icon"
size="25px" size="25px"
> >
<DocumentAdd /> <Upload />
</el-icon> </el-icon>
</el-tooltip> </el-tooltip>
</el-button> </el-button>
@ -80,7 +80,7 @@
size="25px" size="25px"
style="margin-left: 5px;" style="margin-left: 5px;"
> >
<Upload /> <UploadFilled />
</el-icon> </el-icon>
</el-tooltip> </el-tooltip>
</el-button> </el-button>
@ -497,7 +497,7 @@ https://www.baidu.com/img/bd_logo1.png"
shadow="hover" shadow="hover"
> >
<el-image <el-image
v-if="!item.isDir && currentPicBedName !== 'webdavplist' && currentPicBedName !== 'local'" v-if="!item.isDir && currentPicBedName !== 'webdavplist'"
:src="isShowThumbnail && item.isImage ? :src="isShowThumbnail && item.isImage ?
item.url item.url
: require(`./assets/icons/${getFileIconPath(item.fileName ?? '')}`)" : require(`./assets/icons/${getFileIconPath(item.fileName ?? '')}`)"
@ -526,13 +526,6 @@ https://www.baidu.com/img/bd_logo1.png"
:url="item.url" :url="item.url"
@click="handleClickFile(item)" @click="handleClickFile(item)"
/> />
<ImageLocal
v-else-if="!item.isDir && currentPicBedName === 'local'"
:is-show-thumbnail="isShowThumbnail"
:item="item"
:local-path="item.key"
@click="handleClickFile(item)"
/>
<el-image <el-image
v-else v-else
:src="require('./assets/icons/folder.webp')" :src="require('./assets/icons/folder.webp')"
@ -554,7 +547,7 @@ https://www.baidu.com/img/bd_logo1.png"
:underline="false" :underline="false"
:type="item.checked ? 'primary' : 'info'" :type="item.checked ? 'primary' : 'info'"
> >
{{ formatFileName(item.fileName ?? '', 8) }} {{ formatFileName(item.fileName ?? '', 15) }}
</el-link> </el-link>
</el-tooltip> </el-tooltip>
</div> </div>
@ -1422,7 +1415,7 @@ import { ref, reactive, watch, onBeforeMount, computed, onBeforeUnmount } from '
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
// Element Plus // Element Plus
import { InfoFilled, Grid, Fold, 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 { InfoFilled, Grid, Fold, Close, Folder, FolderAdd, Upload, CircleClose, Loading, CopyDocument, Edit, UploadFilled, Link, Refresh, ArrowRight, HomeFilled, Document, Coin, Download, DeleteFilled, Sort, FolderOpened } from '@element-plus/icons-vue'
// //
import { useManageStore } from '../store/manageStore' import { useManageStore } from '../store/manageStore'
@ -1492,7 +1485,6 @@ import { videoExt } from '../utils/videofile'
// //
import ImageWebdav from '@/components/ImageWebdav.vue' import ImageWebdav from '@/components/ImageWebdav.vue'
import ImageLocal from '@/components/ImageLocal.vue'
// //
import { T as $T } from '@/i18n' import { T as $T } from '@/i18n'
@ -2113,6 +2105,16 @@ async function initCustomDomainList () {
currentCustomDomain.value = endpoint currentCustomDomain.value = endpoint
} }
handleChangeCustomUrl() handleChangeCustomUrl()
} else if (currentPicBedName.value === 'local') {
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) {
currentCustomDomain.value = currentTransformedConfig[configMap.bucketName].customUrl ?? ''
} else {
currentCustomDomain.value = ''
}
handleChangeCustomUrl()
} }
} }
@ -2535,7 +2537,7 @@ function handleCreateFolder () {
ElMessageBox.prompt($T('MANAGE_BUCKET_CREATE_FOLDER_BOX_TITLE'), $T('MANAGE_BUCKET_CREATE_FOLDER_BOX_TIP'), { ElMessageBox.prompt($T('MANAGE_BUCKET_CREATE_FOLDER_BOX_TITLE'), $T('MANAGE_BUCKET_CREATE_FOLDER_BOX_TIP'), {
confirmButtonText: $T('MANAGE_BUCKET_CREATE_FOLDER_BOX_CONFIRM'), confirmButtonText: $T('MANAGE_BUCKET_CREATE_FOLDER_BOX_CONFIRM'),
cancelButtonText: $T('MANAGE_BUCKET_CREATE_FOLDER_BOX_CANCEL'), cancelButtonText: $T('MANAGE_BUCKET_CREATE_FOLDER_BOX_CANCEL'),
inputPattern: /^[\u4e00-\u9fff_a-zA-Z0-9/]+$/, inputPattern: /^[\p{Unified_Ideograph}_a-zA-Z0-9-]+$/u,
inputErrorMessage: $T('MANAGE_BUCKET_CREATE_FOLDER_ERROR_MSG') inputErrorMessage: $T('MANAGE_BUCKET_CREATE_FOLDER_ERROR_MSG')
}).then(async ({ value }) => { }).then(async ({ value }) => {
let formatedPath = value let formatedPath = value
@ -2801,7 +2803,6 @@ async function getBucketFileListBackStage () {
param.baseDir = configMap.baseDir param.baseDir = configMap.baseDir
param.webPath = configMap.webPath param.webPath = configMap.webPath
} }
console.log(param)
ipcRenderer.send('getBucketListBackstage', configMap.alias, param) ipcRenderer.send('getBucketListBackstage', configMap.alias, param)
ipcRenderer.on('refreshFileTransferList', (evt: IpcRendererEvent, data) => { ipcRenderer.on('refreshFileTransferList', (evt: IpcRendererEvent, data) => {
fileTransferStore.refreshFileTransferList(data) fileTransferStore.refreshFileTransferList(data)
@ -3445,7 +3446,7 @@ const columns: Column<any>[] = [
<div <div
style="font-size: 14px;color: #303133;font-family: Arial, Helvetica, sans-serif;" style="font-size: 14px;color: #303133;font-family: Arial, Helvetica, sans-serif;"
> >
{formatFileName(item.fileName ?? '')} {formatFileName(item.fileName ?? '', 40)}
</div> </div>
</ElTooltip> </ElTooltip>
</div> </div>

View File

@ -74,9 +74,9 @@
effect="light" effect="light"
:content="item.alias" :content="item.alias"
placement="top" placement="top"
:disabled="item.alias.length <= 15" :disabled="isNeedToShorten(item.alias)"
> >
{{ item.alias.length > 15 ? item.alias.slice(0, 8) + '...' + item.alias.slice(-6) : item.alias }} {{ isNeedToShorten(item.alias) ? safeSliceF(item.alias, 17) + '...' : item.alias }}
</el-tooltip> </el-tooltip>
</el-button> </el-button>
</template> </template>
@ -290,6 +290,7 @@ import { getConfig as getPicBedsConfig } from '@/utils/dataSender'
// //
import { formatEndpoint } from '~/main/manage/utils/common' import { formatEndpoint } from '~/main/manage/utils/common'
import { isNeedToShorten, safeSliceF } from '#/utils/common'
// //
import { T as $T } from '@/i18n' import { T as $T } from '@/i18n'
@ -373,7 +374,7 @@ async function handleConfigChange (name: string) {
const aliasList = getAliasList() const aliasList = getAliasList()
const allKeys = Object.keys(supportedPicBedList[name].configOptions) const allKeys = Object.keys(supportedPicBedList[name].configOptions)
const resultMap:IStringKeyMap = {} const resultMap:IStringKeyMap = {}
const reg = /^[\u4e00-\u9fff_a-zA-Z0-9-]+$/ const reg = /^[\p{Unified_Ideograph}_a-zA-Z0-9-]+$/u
for (const key of allKeys) { for (const key of allKeys) {
const resultKey = name + '.' + key const resultKey = name + '.' + key
if (supportedPicBedList[name].configOptions[key].required) { if (supportedPicBedList[name].configOptions[key].required) {

View File

@ -14,7 +14,7 @@ import { availableIconList } from './icon'
import { getConfig } from './dataSender' import { getConfig } from './dataSender'
// 工具函数 // 工具函数
import { handleUrlEncode } from '~/universal/utils/common' import { handleUrlEncode, safeSliceF, isNeedToShorten } from '~/universal/utils/common'
export function randomStringGenerator (length: number): string { export function randomStringGenerator (length: number): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
@ -103,7 +103,7 @@ export function formatFileName (fileName: string, length: number = 20) {
let ext = path.extname(fileName) let ext = path.extname(fileName)
ext = ext.length > 5 ? ext.slice(ext.length - 5) : ext ext = ext.length > 5 ? ext.slice(ext.length - 5) : ext
const name = path.basename(fileName, ext) const name = path.basename(fileName, ext)
return name.length > length ? `${name.slice(0, length)}...${ext}` : fileName return isNeedToShorten(fileName, length) ? `${safeSliceF(name, length - 3 - ext.length)}...${ext}` : fileName
} }
export const getExtension = (fileName: string) => path.extname(fileName).slice(1) export const getExtension = (fileName: string) => path.extname(fileName).slice(1)

View File

@ -44,3 +44,24 @@ export const isDev = process.env.NODE_ENV === 'development'
export const trimValues = <T extends IStringKeyMap>(obj: T): {[K in keyof T]: T[K] extends string ? string : T[K]} => { export const trimValues = <T extends IStringKeyMap>(obj: T): {[K in keyof T]: T[K] extends string ? string : T[K]} => {
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, typeof value === 'string' ? value.trim() : value])) as {[K in keyof T]: T[K] extends string ? string : T[K]} return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, typeof value === 'string' ? value.trim() : value])) as {[K in keyof T]: T[K] extends string ? string : T[K]}
} }
export function isNeedToShorten (alias: string, cutOff: number = 20) {
let totalLen = 0
for (const s of alias) {
totalLen += s.charCodeAt(0) > 255 ? 2 : 1
}
return totalLen > cutOff
}
export function safeSliceF (str:string, total: number) {
let result = ''
let totalLen = 0
for (const s of str) {
if (totalLen >= total) {
break
}
result += s
totalLen += s.charCodeAt(0) > 255 ? 2 : 1
}
return result
}