refactor: component/*.vue

This commit is contained in:
Emmm Monster
2021-05-24 22:19:37 +08:00
parent ca4ed149b2
commit c7e5dfb4c4
16 changed files with 345 additions and 311 deletions

View File

@@ -0,0 +1,77 @@
<template>
<el-upload
:auto-upload="false"
:on-change="addFile"
:show-file-list="false"
action=""
drag
multiple>
<i class="el-icon-upload"/>
<div class="el-upload__text">将文件拖到此处<em>点击选择</em></div>
<div slot="tip" class="el-upload__tip">本工具仅在浏览器内对文件进行解锁无需消耗流量</div>
<transition name="el-fade-in"><!--todo: add delay to animation-->
<el-progress
v-show="progress_show" :format="progress_string" :percentage="progress_value"
:stroke-width="16" :text-inside="true"
style="margin: 16px 6px 0 6px"
></el-progress>
</transition>
</el-upload>
</template>
<script>
import {spawn, Worker, Pool} from "threads"
import {CommonDecrypt} from "@/decrypt/common.ts";
import {DecryptQueue} from "@/component/utils";
export default {
name: "FileSelector",
data() {
return {
task_all: 0,
task_finished: 0,
queue: new DecryptQueue() // for http or file protocol
}
},
computed: {
progress_value() {
return this.task_all ? this.task_finished / this.task_all * 100 : 0
},
progress_show() {
return this.task_all !== this.task_finished
}
},
mounted() {
if (window.Worker) {
console.log("Using Worker Pool")
this.queue = Pool(
() => spawn(new Worker('@/component/worker.ts')),
navigator.hardwareConcurrency || 1
)
} else {
console.log("Using Queue in Main Thread")
}
},
methods: {
progress_string() {
return `${this.task_finished} / ${this.task_all}`
},
async addFile(file) {
this.task_all++
this.queue.queue(async (dec = CommonDecrypt) => {
console.log("start handling", file.name)
try {
this.$emit("success", await dec(file));
} catch (e) {
console.error(e)
this.$emit("error", file)
} finally {
this.task_finished++
}
})
},
}
}
</script>

View File

@@ -4,7 +4,7 @@
<el-table-column label="封面">
<template slot-scope="scope">
<el-image :src="scope.row.picture" style="width: 100px; height: 100px">
<div class="image-slot el-image__error" slot="error">
<div slot="error" class="image-slot el-image__error">
暂无封面
</div>
</el-image>
@@ -27,14 +27,14 @@
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button @click="handlePlay(scope.$index, scope.row)"
circle icon="el-icon-video-play" type="success">
<el-button circle
icon="el-icon-video-play" type="success" @click="handlePlay(scope.$index, scope.row)">
</el-button>
<el-button @click="handleDownload(scope.row)"
circle icon="el-icon-download">
<el-button circle
icon="el-icon-download" @click="handleDownload(scope.row)">
</el-button>
<el-button @click="handleDelete(scope.$index, scope.row)"
circle icon="el-icon-delete" type="danger">
<el-button circle
icon="el-icon-delete" type="danger" @click="handleDelete(scope.$index, scope.row)">
</el-button>
</template>
</el-table-column>
@@ -42,28 +42,28 @@
</template>
<script>
import {DownloadBlobMusic, RemoveBlobMusic} from './util'
import {DownloadBlobMusic, RemoveBlobMusic} from '@/component/utils'
export default {
name: "preview",
props: {
tableData: {type: Array, required: true},
download_format: {type: String, required: true}
export default {
name: "PreviewTable",
props: {
tableData: {type: Array, required: true},
filename_format: {type: String, required: true}
},
methods: {
handlePlay(index, row) {
this.$emit("music_changed", row.file);
},
handleDelete(index, row) {
RemoveBlobMusic(row);
this.tableData.splice(index, 1);
},
handleDownload(row) {
DownloadBlobMusic(row, this.download_format)
},
methods: {
handlePlay(index, row) {
this.$emit("music_changed", row.file);
},
handleDelete(index, row) {
RemoveBlobMusic(row);
this.tableData.splice(index, 1);
},
handleDownload(row) {
DownloadBlobMusic(row, this.download_format)
},
}
}
}
</script>
<style scoped>

View File

@@ -1,120 +0,0 @@
<template>
<el-upload
:auto-upload="false"
:on-change="handleFile"
:show-file-list="false"
action=""
drag
multiple>
<i class="el-icon-upload"/>
<div class="el-upload__text">将文件拖到此处<em>点击选择</em></div>
<div class="el-upload__tip" slot="tip">本工具仅在浏览器内对文件进行解锁无需消耗流量</div>
<transition name="el-fade-in">
<el-progress
:format="progressFormat" :percentage="progress_percent" :stroke-width="16"
:text-inside="true" style="margin: 16px 6px 0 6px"
v-show="progress_show"
></el-progress>
</transition>
</el-upload>
</template>
<script>
"use strict";// 严格模式 用于尾调用优化
export default {
name: "upload",
data() {
return {
cacheQueue: [],
workers: [],
idle_workers: [],
thread_num: 1,
progress_show: false,
progress_finished: 0,
progress_all: 0,
progress_percent: 0,
}
},
mounted() {
if (document.location.host !== "" && process.env.NODE_ENV === 'production') {
this.thread_num = navigator.hardwareConcurrency || 1;
const worker = require("workerize-loader!../decrypt/common");
// noinspection JSValidateTypes,JSUnresolvedVariable
this.workers.push(worker().CommonDecrypt);
this.idle_workers.push(0);
// delay to optimize for first loading
setTimeout(() => {
for (let i = 1; i < this.thread_num; i++) {
// noinspection JSValidateTypes,JSUnresolvedVariable
this.workers.push(worker().CommonDecrypt);
this.idle_workers.push(i);
}
}, 5000);
} else {
const dec = require('../decrypt/common');
this.workers.push(dec.CommonDecrypt);
this.idle_workers.push(0)
}
},
methods: {
progressFormat() {
return this.progress_finished + "/" + (this.progress_all)
},
progressChange(finish, all) {
this.progress_all += all;
this.progress_finished += finish;
this.progress_percent = Math.round(this.progress_finished / this.progress_all * 100);
if (this.progress_finished === this.progress_all) {
setTimeout(() => {
this.progress_show = false;
this.progress_finished = 0;
this.progress_all = 0;
}, 3000);
} else {
this.progress_show = true;
}
},
handleFile(file) {
this.progressChange(0, +1);
// 有空闲worker 立刻处理文件
if (this.idle_workers.length > 0) {
this.handleDoFile(file, this.idle_workers.shift());
}
// 无空闲worker 则放入缓存队列
else {
this.cacheQueue.push(file);
}
},
handleCacheQueue(worker_id) {
// 调用方法消费缓存队列中的数据
if (this.cacheQueue.length === 0) {
this.idle_workers.push(worker_id);
return
}
this.handleDoFile(this.cacheQueue.shift(), worker_id);
},
handleDoFile(file, worker_id) {
this.workers[worker_id](file).then(data => {
this.$emit("handle_finish", data);
// 完成之后 执行新任务 todo: 可能导致call stack过长
this.handleCacheQueue(worker_id);
this.progressChange(+1, 0);
}).catch(err => {
this.$emit("handle_error", err, file.name);
this.handleCacheQueue(worker_id);
this.progressChange(+1, 0);
})
},
}
}
</script>
<style scoped>
/*noinspection CssUnusedSymbol*/
.el-upload-dragger {
width: 80vw !important;
}
</style>

View File

@@ -1,4 +1,6 @@
export function DownloadBlobMusic(data, format) {
import {DecryptResult} from "@/decrypt/entity";
export function DownloadBlobMusic(data: DecryptResult, format: string) {//todo: use enum
const a = document.createElement('a');
a.href = data.file;
switch (format) {
@@ -21,10 +23,27 @@ export function DownloadBlobMusic(data, format) {
a.remove();
}
export function RemoveBlobMusic(data) {
export function RemoveBlobMusic(data: DecryptResult) {
URL.revokeObjectURL(data.file);
if (data.picture.startsWith("blob:")) {
if (data.picture?.startsWith("blob:")) {
URL.revokeObjectURL(data.picture);
}
}
export class DecryptQueue {
private readonly pending: (() => Promise<void>)[];
constructor() {
this.pending = []
}
queue(fn: () => Promise<void>) {
this.pending.push(fn)
this.consume()
}
private consume() {
const fn = this.pending.shift()
if (fn) fn().then(() => this.consume).catch(console.error)
}
}

4
src/component/worker.ts Normal file
View File

@@ -0,0 +1,4 @@
import {expose} from "threads/worker";
import {CommonDecrypt} from "@/decrypt/common";
expose(CommonDecrypt)