From c81987263589f3ee16dd9dc9fbbf97ddaabce6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=8C=E8=90=8C=E5=93=92=E8=B5=AB=E8=90=9D?= Date: Thu, 2 Mar 2023 23:07:26 +0800 Subject: [PATCH] :hammer: Refactor: modify some code --- src/main/manage/utils/threadPool.ts | 75 ++++++++++++++++++++++++ src/renderer/components/ImageWebdav.vue | 2 + src/renderer/manage/pages/logIn.vue | 4 +- src/renderer/manage/pages/manageMain.vue | 6 +- src/renderer/manage/utils/common.ts | 23 ++++---- src/renderer/manage/utils/constants.ts | 58 ------------------ 6 files changed, 91 insertions(+), 77 deletions(-) create mode 100644 src/main/manage/utils/threadPool.ts diff --git a/src/main/manage/utils/threadPool.ts b/src/main/manage/utils/threadPool.ts new file mode 100644 index 0000000..70bef7e --- /dev/null +++ b/src/main/manage/utils/threadPool.ts @@ -0,0 +1,75 @@ +import { Worker, WorkerOptions } from 'worker_threads' + +interface Task { + data: any + workerOptions: WorkerOptions | undefined + resolve: (result: any) => void + reject: (error: any) => void +} + +class ThreadPool { + private size: number + private workerPath: string + private availablePool: Worker[] + private taskQueue: Task[] + private busyPool: Worker[] + private callBackList: any[] + + constructor (size: number, workerPath: string) { + this.size = size + this.workerPath = workerPath + this.availablePool = [] + this.busyPool = [] + for (let i = 0; i < this.size; i++) { + this.availablePool.push(new Worker(this.workerPath)) + } + this.taskQueue = [] + this.callBackList = [] + this.init() + } + + private init () { + for (const worker of this.availablePool) { + worker.on('message', (result) => { + const { data } = result + this.callBackList.shift()(data) + this.busyPool = this.busyPool.filter((w) => w.threadId !== worker.threadId) + this.availablePool.push(worker) + this.processQueue() + }) + } + } + + private processQueue () { + if (this.taskQueue.length === 0) return + if (this.availablePool.length === 0) return + const task = this.taskQueue.shift() + const worker = this.availablePool.pop() + if (worker && task) { + this.callBackList.push(task.resolve) + this.busyPool.push(worker) + worker.postMessage(task.data) + } + } + + public async addTask (data: any, workerOptions?: WorkerOptions): Promise { + return new Promise((resolve, reject) => { + this.taskQueue.push({ data, workerOptions, resolve, reject }) + this.processQueue() + }) + } + + public async destroy (): Promise { + const terminatePromises = this.availablePool.map((worker) => new Promise((resolve) => { + worker.terminate() + worker.on('exit', () => { + resolve(true) + }) + })) + await Promise.all(terminatePromises) + this.availablePool = [] + this.taskQueue = [] + } +} + +export default ThreadPool diff --git a/src/renderer/components/ImageWebdav.vue b/src/renderer/components/ImageWebdav.vue index 598e47d..6a9a9ce 100644 --- a/src/renderer/components/ImageWebdav.vue +++ b/src/renderer/components/ImageWebdav.vue @@ -54,6 +54,8 @@ const urlCreateObjectURL = async () => { headers: props.headers }).then(res => res.blob()).then(blob => { base64Url.value = URL.createObjectURL(blob) + }).catch(err => { + console.log(err) }) } diff --git a/src/renderer/manage/pages/logIn.vue b/src/renderer/manage/pages/logIn.vue index 5cf27ec..fce4532 100644 --- a/src/renderer/manage/pages/logIn.vue +++ b/src/renderer/manage/pages/logIn.vue @@ -14,7 +14,7 @@ style="width: 100%;overflow-y: scroll;height: calc(100vh - 50px);" > shell.openExternal(urlMap[currentPagePicBedConfig.picBedName]) @@ -445,8 +444,7 @@ const menuTitleMap:IStringKeyMap = { smms: '相册', imgur: '相册', github: '仓库', - webdavplist: '', - localplist: '本地' + webdavplist: '' } const showNewIconList = ['aliyun', 'qiniu', 'tcyun'] diff --git a/src/renderer/manage/utils/common.ts b/src/renderer/manage/utils/common.ts index a5469cb..0c14efa 100644 --- a/src/renderer/manage/utils/common.ts +++ b/src/renderer/manage/utils/common.ts @@ -76,15 +76,17 @@ export function getFileIconPath (fileName: string) { return availableIconList.includes(ext) ? `${ext}.webp` : 'unknown.webp' } +const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + export function formatFileSize (size: number) { if (size === 0) return '' - const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] const index = Math.floor(Math.log2(size) / 10) return `${(size / Math.pow(2, index * 10)).toFixed(2)} ${units[index]}` } export function formatFileName (fileName: string, length: number = 20) { - const ext = path.extname(fileName) + let ext = path.extname(fileName) + ext = ext.length > 5 ? ext.slice(ext.length - 5) : ext const name = path.basename(fileName, ext) return name.length > length ? `${name.slice(0, length)}...${ext}` : fileName } @@ -92,7 +94,7 @@ export function formatFileName (fileName: string, length: number = 20) { export const getExtension = (fileName: string) => path.extname(fileName).slice(1) export const isImage = (fileName: string) => - ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico'].includes(getExtension(fileName)) + ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico', 'svg'].includes(getExtension(fileName)) export function formObjToTableData (obj: any) { const exclude = [undefined, null, '', 'transformedConfig'] @@ -103,16 +105,11 @@ export function formObjToTableData (obj: any) { } export function isValidUrl (str: string) { - const pattern = new RegExp( - '^([a-zA-Z]+:\\/\\/)?' + - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + - '((\\d{1,3}\\.){3}\\d{1,3}))' + - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + - '(\\?[;&a-z\\d%_.~+=-]*)?' + - '(\\#[-a-z\\d_]*)?$', - 'i' - ) - return pattern.test(str) + try { + return !!new URL(str) + } catch (e) { + return false + } } export interface IHTTPProxy { diff --git a/src/renderer/manage/utils/constants.ts b/src/renderer/manage/utils/constants.ts index 255cac1..f38eea6 100644 --- a/src/renderer/manage/utils/constants.ts +++ b/src/renderer/manage/utils/constants.ts @@ -756,63 +756,5 @@ export const supportedPicBedList: IStringKeyMap = { options: ['alias', 'endpoint', 'username', 'password', 'bucketName', 'baseDir', 'customUrl', 'proxy', 'sslEnabled'], refLink: 'https://pichoro.horosama.com/#/PicHoroDocs/configure?id=webdav', referenceText: '配置教程请参考:' - }, - localplist: { - name: '本地', - icon: 'localplist', - configOptions: { - alias: { - required: true, - description: '配置别名-必需', - placeholder: '该配置的唯一标识', - type: 'string', - rule: aliasRule, - default: 'localplist-A', - tooltip: aliasTooltip - }, - baseDir: { - required: false, - description: '起始目录-可选', - placeholder: '例如:/test1', - type: 'string', - default: '/', - tooltip: '请填写完整的本地路径,例如:/test1' - }, - customUrl: { - required: false, - description: '自定义域名-可选', - placeholder: '例如:https://example.com', - type: 'string', - tooltip: '如果您的本地服务器支持自定义域名,请填写完整的自定义域名,例如: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' - } - ] - }, - webDir: { - required: false, - description: 'Web目录-可选', - placeholder: '例如:/test1', - type: 'string', - default: '/', - tooltip: '请填写您的Web服务器的根目录,例如:/test1' - } - }, - explain: '本地配置', - options: ['alias', 'baseDir', 'customUrl', 'webDir'], - refLink: '', - referenceText: '' } }