MediaWiki:Common.js/uploader.js:修订间差异
跳到导航
跳到搜索
imported>=海豚= 无编辑摘要 |
小无编辑摘要 |
||
第2行: | 第2行: | ||
"use strict"; | "use strict"; | ||
$(() => (async () => { | $(() => (async () => { | ||
await mw.loader.using([ | await mw.loader.using(["mediawiki.api"]); | ||
const commonsApi = new mw.Api({ | const commonsApi = new mw.Api({ | ||
timeout: 7000, | timeout: 7000, |
2022年12月17日 (六) 23:56的最新版本
// <pre>
"use strict";
$(() => (async () => {
await mw.loader.using(["mediawiki.api"]);
const commonsApi = new mw.Api({
timeout: 7000,
});
const cdnUrl = {
requireJs: "https://cdn.jsdelivr.net/npm/requirejs@2.3.6/require.min.js",
jss: "https://cdn.jsdelivr.net/npm/jss@10.4.0/dist/jss.min.js",
jssPreset: "https://cdn.jsdelivr.net/npm/jss-preset-default@10.4.0/dist/jss-preset-default.min.js",
vue: "https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js",
};
/**
* @param {string} sourceUrl
* @return {Promise<never>}
*/
const loadScript = (sourceUrl) => new Promise((resolve, reject) => {
const scriptTag = document.createElement("script");
scriptTag.src = sourceUrl;
document.body.appendChild(scriptTag);
scriptTag.addEventListener("load", resolve);
scriptTag.addEventListener("error", reject);
});
/**
* @param {{ [moduleName: string]: string }[]} modulePaths
* @return {Promise<any>}
*/
const loadModules = (modulePaths) => new Promise((resolve, reject) => {
const paths = Object.fromEntries(modulePaths.map((m) => Object.entries(m).map(([k, v]) => [k, v.replace(/\.js$/, "")])[0]));
const loadModuleNames = modulePaths.map(item => Object.keys(item)[0]);
window.require.config({ paths });
window.require(loadModuleNames, (...modules) => resolve(modules), reject);
});
/**
* @typedef {object} MwApiError
* @property {string} code
* @property {string} info
*/
/**
* @typedef {object} JQueryXHRError
* @property {number} status
* @property {string} statusText
*/
/**
* @param {string} word
* @return {Promise<object, Error>}
*/
const getHints = (word) => commonsApi.post({
action: "query",
format: "json",
list: "search",
srsearch: word,
srnamespace: "14",
srlimit: "20",
});
/**
* @param {object} options
* @param {File} options.body
* @param {string} options.fileName
* @param {string} options.comment
* @param {string} options.pageContent
* @return {Promise<object, (Error | MwApiError | JQueryXHRError)>}
*/
async function upload({ body, fileName, comment, pageContent }) {
const data = {
filename: fileName,
comment: comment,
text: pageContent,
action: "upload",
format: "json",
ignorewarnings: true,
token: await commonsApi.getToken("csrf"),
};
if (typeof body === "string") {
data.url = body;
} else {
data.file = body;
}
const formData = new FormData();
Object.entries(data).forEach(([key, value]) => {
formData.append(key, value);
});
// 点名批评 mediawiki.api 没考虑过跨域传文件!
return $.ajax({
url: `${mw.config.get("wgServer") + mw.config.get("wgScriptPath")}/api.php`,
type: "post",
timeout: 30000,
xhrFields: { withCredentials: true },
contentType: false,
processData: false,
data: formData,
}).promise().then((data) => {
if ("error" in data) {
throw data.error;
}
return data;
});
}
/**
* @param {Error | MwApiError | JQueryXHRError} errorObject
* @return {string}
*/
function errorInfo(errorObject) {
if (errorObject instanceof Error) {
return `${errorObject} ${errorObject.stack.split("\n")[1].trim()}`;
} else if (typeof errorObject.status === "number") {
return `[${errorObject.status}] ${errorObject.statusText}`;
}
return `${errorObject.code} - ${errorObject.info}`;
}
/**
* @param {string[]} fileNames
* @return {Promise<object, Error>}
*/
async function checkFileNames(fileNames) {
const data = await commonsApi.post({
action: "query",
format: "json",
titles: fileNames.map(item => `File:${item}`).join("|"),
prop: "",
});
return Object.fromEntries(Object.values(data.query.pages).map((item) => [item.title.replace("File:", ""), !("missing" in item)]));
}
await loadScript(cdnUrl.requireJs);
const [
{ "default": jss },
{ "default": jssPreset },
Vue,
] = await loadModules([
{ jss: cdnUrl.jss },
{ jssPreset: cdnUrl.jssPreset },
{ Vue: cdnUrl.vue },
]);
jss.setup(jssPreset());
$(document.body).append('<div id="widget-fileUploader"></div>');
const template = `
<div id="widget-fileUploader" :class="s.container">
<input
ref="fileInput"
style="display:none"
type="file"
multiple="multiple"
:accept="allowedFileTypes.map(item => '.' + item).join(',')"
@change="addFileByFileSelector"
@click="clearHint"
/>
<div :class="s.closeBtn" @click="hideWidget">×</div>
<div :class="s.body">
<div
:class="s.fileList"
@dragenter.prevent="() => {}"
@dragover.prevent="() => {}"
@drop.prevent="addFileByDropping"
>
<div
v-if="files.length === 0"
key="hintMask"
class="hintMask"
@click="$refs.fileInput.click()"
>
<div class="hintText">点此添加文件,或将文件拖放至此</div>
</div>
<div
v-for="(item, index) in files"
:key="item.body.lastModified"
class="item"
:data-name="item.fileName"
:data-selected="index === focusedFileIndex"
title="单击选中文件,双击复制文件名"
@click="focusFile(index)"
>
<img
v-if="isImageFile(item.body)"
:src="item.objectUrl"
/>
<div v-else class="unablePreviewHint">
<div>不支持预览的文件类型</div>
<div
v-if="typeof item.body !== 'string'"
class="type"
>Mimetype: {{ item.body.type }}</div>
</div>
<div class="removeBtn" @click.stop="files.splice(index, 1)">×</div>
</div>
<div
v-if="files.length !== 0"
class="item addFileBox"
@click="$refs.fileInput.click()"
/>
</div>
<div :class="s.panel">
<div class="block">
<div class="input-container" title="上传后使用文件时的名字,要求不能和现有文件重复">
<span>文件名:</span>
<input v-model.trim="form.fileName" @click="clearHint" />
</div>
<div class="input-container categoryInput" title="所有文件共享分类">
<span>分<span style="visibility: hidden;">一</span>类:</span>
<input
ref="categoryInput"
v-model.trim="form.categoryInput"
@input="loadCategoryHint"
@keydown.enter="addCategory(form.categoryInput)"
@keydown.up.prevent="handlerFor_categoryInput_wasKeyDowned"
/>
<div class="inputHint">按下回车添加分类</div>
<div
ref="categoryHints"
v-if="categoryHints.length !== 0"
class="categoryHints"
tabindex="0"
@keydown.enter="addCategory(categoryHints[categoryHintFocusedIndex])"
@keydown.prevent="handlerFor_categoryHints_wasKeyDowned"
>
<div
v-for="(item, index) in categoryHints"
class="item"
:data-selected="index === categoryHintFocusedIndex"
@click="addCategory(item)"
>{{ item }}</div>
</div>
</div>
<div class="categories">
<div
v-for="(item, index) in form.categories"
class="item"
title="点击删除分类"
@click="form.categories.splice(index, 1)"
>{{ item }}</div>
</div>
</div>
<div class="block">
<div class="input-container">
<span>角色名:</span>
<input v-model.trim="form.charaName" @click="clearHint" />
</div>
<div class="input-container">
<span>作<span style="visibility: hidden;">一</span>者:</span>
<input v-model.trim="form.author" @click="clearHint" />
</div>
<div class="input-container">
<span>源地址:</span>
<input v-model.trim="form.source" @click="clearHint" />
</div>
</div>
<div
class="block"
style="flex-direction:column; justify-content:space-around; align-items:flex-start;"
>
<div class="input-container" title="所有文件共享前缀">
<span>添加前缀:</span>
<input v-model.trim="form.prefix" style="width:calc(100% - 6em)" @click="clearHint" />
</div>
<div
class="input-container"
style="justify-content:flex-start;"
>
</div>
<div class="buttons" @click="clearHint">
<button @click="addSourceUrlFile">添加源地址文件</button>
<button :disabled="status === 2" title="执行上传文件" @click="submit(false)">上传</button>
<button
:disabled="status === 2"
title="在发生文件名已存在的情况时,自动滤掉已存在的文件。通常用于在上一次批量上传中一部分失败后,再次尝试将之前没传上去的文件重新上传"
@click="submit(true)"
>差分上传</button>
<button title="将当前文件除文件名的信息同步到全部文件" @click="asyncCurrentFileInfo">同步已输入信息至全部</button>
<button @click="showManual">-使用说明-</button>
</div>
</div>
</div>
</div>
</div>
`;
const createStyles = () => jss.createStyleSheet({
container: {
width: "100%",
height: "100%",
position: "fixed",
top: 0,
left: 0,
backgroundColor: "rgba(0, 0, 0, 0.3)",
zIndex: 100,
},
closeBtn: {
fontSize: 30,
fontWeight: "bold",
color: "white",
fontFamily: "Simsun",
position: "fixed",
top: 10,
right: 20,
transition: "transform 0.3s",
zIndex: 10001,
cursor: "pointer",
"&:hover": {
transform: "rotate(90deg)",
},
},
body: {
minWidth: 800,
maxWidth: 930,
height: 500,
backgroundColor: "white",
borderRadius: 10,
border: "5px #eee solid",
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
margin: "auto",
},
fileList: {
height: "70%",
backgroundColor: "white",
borderRadius: "10px 10px 0 0",
position: "relative",
borderBottom: "3px #ccc solid",
boxSizing: "border-box",
overflow: "auto",
cursor: "pointer",
paddingBottom: 10,
"& .hintMask": {
position: "absolute",
width: "100%",
height: "100%",
top: 0,
left: 0,
"&::before, &::after": {
content: '""',
width: 40,
height: 150,
backgroundColor: "#ddd",
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
margin: "auto",
},
"&::after": {
width: 150,
height: 40,
},
"& > .hintText": {
fontSize: 22,
color: "#ddd",
position: "absolute",
left: "50%",
transform: "translateX(-50%)",
bottom: 30,
whiteSpace: "nowrap",
},
},
"& > .item": {
width: 200,
height: 150,
boxSizing: "border-box",
backgroundColor: "white",
marginLeft: 10,
marginTop: 10,
border: "1px #ccc solid",
display: "inline-block",
position: "relative",
cursor: "pointer",
verticalAlign: "middle",
"&.addFileBox": {
"&::before, &::after": {
content: '""',
width: 15,
height: 60,
backgroundColor: "#ddd",
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
margin: "auto",
},
"&::after": {
width: 60,
height: 15,
},
},
'&[data-selected="true"]': {
borderColor: "#4EBE8C",
"&::after": {
content: '""',
display: "block",
position: "absolute",
width: "100%",
height: "100%",
top: 0,
left: 0,
boxSizing: "border-box",
border: "3px #4EBE8C solid",
pointerEvents: "none",
},
},
"&::before": {
content: "attr(data-name)",
display: "block",
width: "100%",
position: "absolute",
bottom: 0,
left: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)",
color: "white",
fontSize: 13,
textAlign: "center",
lineHeight: "25px",
overflow: "hidden",
height: 25,
textOverflow: "ellipsis",
whiteSpace: "nowrap",
boxSizing: "border-box",
padding: "0 10px",
},
"& > img": {
width: "100%",
height: "100%",
padding: 5,
boxSizing: "border-box",
objectFit: "scale-down",
},
"& > .unablePreviewHint": {
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
color: "#666",
fontSize: 13,
"& > .type": {
width: "80%",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
},
"& > .removeBtn": {
width: 20,
height: 20,
borderRadius: "50%",
textAlign: "center",
lineHeight: "20px",
fontWeight: "bold",
fontFamily: "黑体",
position: "absolute",
top: 5,
right: 5,
"&:hover": {
backgroundColor: "#666",
color: "white",
},
},
},
},
panel: {
height: "30%",
padding: 10,
boxSizing: "border-box",
display: "flex",
"& > .block": {
display: "flex",
flex: 1,
flexWrap: "wrap",
alignItems: "center",
height: "100%",
padding: "0 10px",
"& .input-container": {
minWidth: 240,
position: "relative",
"& > *": {
verticalAlign: "middle",
fontSize: 14,
},
"& > input": {
boxSizing: "border-box",
width: "calc(100% - 5em)",
minWidth: 150,
},
},
},
"& .categoryInput": {
position: "relative",
"& .inputHint": {
opacity: 0,
transition: "opacity 0.2s",
backgroundColor: "#fffeee",
border: "1px #ccc solid",
padding: "2px 10px",
position: "absolute",
bottom: "calc(100% - 7px)",
left: "calc(100% - 7px)",
zIndex: 1,
borderRadius: 5,
whiteSpace: "nowrap",
},
"& > input:focus + .inputHint": {
opacity: 1,
},
},
"& .categoryHints": {
minWidth: 170,
maxHeight: 140,
backgroundColor: "white",
whiteSpace: "nowrap",
overflow: "auto",
position: "absolute",
right: 9,
bottom: "100%",
border: "1px #666 solid",
boxSizing: "border-box",
borderBottom: "none",
display: "flex",
flexDirection: "column-reverse",
"& > .item": {
minHeight: 20,
lineHeight: "20px",
boxSizing: "border-box",
padding: "0 5px",
width: "100%",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
cursor: "pointer",
'&[data-selected="true"]': {
backgroundColor: "#ccc",
},
},
},
"& .categories": {
width: "100%",
height: 25,
border: "1px #ccc solid",
borderRadius: 5,
overflow: "auto",
marginRight: 5,
boxSizing: "border-box",
"& > .item": {
display: "inline-block",
lineHeight: "15px",
textAlign: "center",
border: "1px #666 solid",
backgroundColor: "#eee",
margin: "2px 3px",
padding: "0 5px",
fontSize: 14,
cursor: "pointer",
},
},
"& .buttons": {
width: "100%",
"& > button": {
marginTop: 5,
},
},
},
}).attach().classes;
new Vue({
el: "#widget-fileUploader",
template,
data() {
return {
s: createStyles(), // 样式
allowedFileTypes: ["ogg", "ogv", "oga", "flac", "opus", "wav", "webm", "mp3", "png", "gif", "jpg", "jpeg", "webp", "svg", "pdf", "ppt", "jp2", "doc", "docx", "xls", "xlsx", "psd", "sai", "swf", "mp4"],
files: [], // 待上传的文件
focusedFileIndex: 0,
categoryHints: [],
categoryInputDebounceTimeoutKey: 0,
categoryHintFocusedIndex: -1,
categoryFocused: false,
status: 1, // 0:失败,1:初始化,2:提交中,3:成功
form: {
fileName: "",
categoryInput: "", // 分类输入栏
categories: [], // 实际要提交的分类
charaName: "",
author: "",
source: "",
prefix: "",
license: "Copyright",
},
doubleClickTimeoutKey: 0, // 用于双击复制文件名
};
},
mounted() {
$("#loading").html('<a href="javascript:void(0);" id="show">点此重新调出上传界面</a>');
$("#show").on("click", () => {
$("#widget-fileUploader").fadeIn(200);
return false;
});
},
watch: {
files() {
this.focusedFileIndex === 0 && this.focusFile(0);
},
form: {
deep: true,
handler() {
if (!this.files[this.focusedFileIndex]) { return; }
this.files[this.focusedFileIndex] = {
...this.files[this.focusedFileIndex],
fileName: this.form.fileName,
author: this.form.author,
charaName: this.form.charaName,
source: this.form.source,
license: this.form.license,
};
},
},
license(val) {
if (val === "none:gotoCommons") {
alert("该协议需要手动填写授权证明,请到共享站进行上传");
window.open(`${mw.config.get("wgServer").replace("zh.moegirl", "commons.moegirl") + mw.config.get("wgScriptPath")}/Special:上传文件`, "_blank");
}
},
},
computed: {
license() {
return this.form.license;
},
},
methods: {
createFileItem(fileBody) {
return {
body: fileBody,
objectUrl: typeof fileBody === "string" ? fileBody : URL.createObjectURL(fileBody),
fileName: typeof fileBody === "string" ? fileBody.replace(/.+\/(.+?)$/, "$1") : fileBody.name,
author: "",
charaName: "",
source: "",
};
},
isImageFile(fileBody) {
const imageType = ["jpg", "png", "jpeg", "gif", "webp"];
return imageType.includes((typeof fileBody === "string" ? fileBody : fileBody.name).replace(/.+\.(.+?)$/, "$1"));
},
hideWidget() {
$("#widget-fileUploader").fadeOut(200);
$("#content").css("position", "relative");
},
loadCategoryHint() {
clearTimeout(this.categoryInputDebounceTimeoutKey);
this.categoryInputDebounceTimeoutKey = setTimeout(() => {
if (this.form.categoryInput === "") { return; }
getHints(this.form.categoryInput)
.then(data => {
const hints = data.query.search.map(item => item.title.split("Category:")[1]);
this.categoryHints = hints;
});
}, 500);
},
clearHint() {
this.categoryHints = [];
},
resetCategory() {
this.form.categoryInput = "";
this.categoryHints = [];
this.categoryHintFocusedIndex = -1;
},
addCategory(categoryName) {
if (!this.form.categories.includes(categoryName)) {
this.form.categories.push(categoryName);
}
this.resetCategory();
},
// 实现上下键切换分类提示
handlerFor_categoryHints_wasKeyDowned(e) {
if (e.code === "ArrowUp") {
this.categoryHintFocusedIndex++;
if (this.categoryHintFocusedIndex > this.categoryHints.length - 1) {
this.categoryHintFocusedIndex = 0;
}
}
if (e.code === "ArrowDown") {
this.categoryHintFocusedIndex--;
if (this.categoryHintFocusedIndex < 0) {
this.$refs.categoryInput.focus();
}
}
this.categoryHintFocusedIndex >= 0 && this.$refs.categoryHints.querySelectorAll("div")[this.categoryHintFocusedIndex].scrollIntoView();
},
handlerFor_categoryInput_wasKeyDowned() {
if (this.categoryHints.length === 0 || !this.$refs.categoryHints) { return; }
this.$refs.categoryHints.focus();
this.categoryHintFocusedIndex = 0;
},
addFileByFileSelector(e) {
Array.from(e.target.files).forEach(file => {
if (this.files.length === 50) { return; }
if (file.size / 1024 / 1024 > 20) { return alert(`文件【${file.name}】大小超过20m,无法上传!`); }
this.files.push(this.createFileItem(file));
});
e.target.value = "";
if (this.files.length === 50) { mw.notify("一次最多上传50个文件", { type: "wran" }); }
},
addFileByDropping(e) {
Array.from(e.dataTransfer.files).forEach(file => {
if (this.files.length === 50) { return; }
if (!this.allowedFileTypes.includes(file.name.replace(/.+\.(.+?)$/, "$1"))) { return alert(`【${file.name}】不支持上传这种格式的文件!`); }
if (file.size / 1024 / 1024 > 8) { return alert(`【${file.name}】的大小超过8m,无法上传!`); }
this.files.push(this.createFileItem(file));
});
if (this.files.length === 50) { mw.notify("一次最多上传50个文件", { type: "wran" }); }
},
focusFile(index) {
this.focusedFileIndex = index;
const file = this.files[index];
this.form = {
...this.form,
fileName: file.fileName,
author: file.author,
charaName: file.charaName,
source: file.source,
license: file.license,
};
// 实现双击复制文件名
if (this.doubleClickTimeoutKey === 0) {
this.doubleClickTimeoutKey = setTimeout(() => {
this.doubleClickTimeoutKey = 0;
}, 300);
} else {
mw.notify("已复制文件名");
this.copyFileName(this.form.prefix + file.fileName);
clearTimeout(this.doubleClickTimeoutKey);
this.doubleClickTimeoutKey = 0;
}
},
addSourceUrlFile() {
const url = (prompt("请输入文件地址:(萌娘百科、哔哩哔哩等网站图片加入了防盗链,你必须把图片下载下来后使用本地上传)") || "").trim();
if (!url) { return; }
this.files.push(this.createFileItem(url));
},
copyFileName(fileName) {
const inputTag = document.createElement("input");
inputTag.value = fileName;
inputTag.style.cssText = "position: fixed; left: -9999px;";
document.body.appendChild(inputTag);
inputTag.focus();
document.execCommand("selectAll");
document.execCommand("copy");
setTimeout(() => document.body.removeChild(inputTag), 1000);
},
asyncCurrentFileInfo() {
if (!confirm("确定要将当前选中的文件信息(不含文件名)同步到所有文件中?")) { return; }
const currentFile = this.files[this.focusedFileIndex];
if (!currentFile) { return mw.notify("当前未选中文件"); }
this.files.forEach(item => {
item.author = currentFile.author;
item.charaName = currentFile.charaName;
item.source = currentFile.source;
item.license = currentFile.license;
});
mw.notify("已同步");
},
showManual() {
alert([
"使用说明",
"1. 该插件是一个批量上传图片工具,同时支持拖拽上传,批量上传。",
"2. 若文件上传时发生异常,请以本站的最近更改为准。",
"3. 每个文件拥有独立的信息,但“分类”和“添加前缀”是共享的。在需要同步每个文件的角色名、作者等信息时可以使用“同步文件信息”的功能。",
"4. 差分上传是指在发生文件名已存在的情况时,自动滤掉已存在的文件。通常用于在上一次批量上传中一部分失败后,再次尝试将之前没传上去的文件重新上传。",
"5. 双击文件可以自动复制“前缀 + 文件名”。",
].join("\n"));
},
async submit(diffMode) {
if (this.files.length === 0) { return mw.notify("您还没有上传任何文件", { type: "warn" }); }
if (this.files.some(item => item.fileName === "")) { return mw.notify("存在文件名为空的文件", { type: "warn" }); }
const duplicateFilesName = new Set();
const filesName = this.files.map(({ fileName }) => fileName);
for (const n of filesName) {
if (filesName.indexOf(n) !== filesName.lastIndexOf(n)) {
duplicateFilesName.add(n);
}
}
if (duplicateFilesName.size > 0) {
return alert([
"这些文件名发生了重复,请不要给要上传的文件设置相同的名称:",
...Array.from(duplicateFilesName.values()),
].join("\n"));
}
const authorizedForMoegirlFiles = this.files.filter(item => item.license === "none:gotoCommons");
if (authorizedForMoegirlFiles.length > 0) {
return alert([
"这些文件的授权协议不允许使用上传工具,请在本次上传中删除,并前往共享站填写授权信息后上传:",
...authorizedForMoegirlFiles.map(item => item.fileName),
].join("\n"));
}
if (!confirm("确定要开始上传吗?")) { return; }
let postData = this.files.map(item => {
const metaCategories = `${item.charaName ? `[[分类:${item.charaName}]]` : ""}${item.author ? `[[分类:作者:${item.author}]]` : ""}`;
const source = item.source ? `源地址:${item.source}` : "";
const comment = [];
if (item.charaName) {
comment.push(`人物:[[分类:${item.charaName}|${item.charaName}]]`);
}
if (item.author) {
comment.push(`作者:[[分类:${item.author}|${item.author}]]`);
}
if (item.source) {
comment.push(`源地址:${item.source}`);
}
if (this.form.categories.length > 0) {
comment.push(`其他分类:[[分类:${this.form.categories.join("]]、[[分类:")}]]`);
}
const pageContent = [
"== 文件说明 ==",
source,
"基于批量上传工具上传的文件",
metaCategories + this.form.categories.map(item => `[[分类:${item}]]`).join(""),
].join("\n");
return {
body: item.body,
fileName: this.form.prefix + item.fileName,
comment: comment.join(","),
pageContent,
};
});
mw.notify(`开始${diffMode ? "差分" : ""}上传,共${postData.length}个文件...`);
console.log(`---- Moegirl:fileUploader 开始${diffMode ? "差分" : ""}上传,共${postData.length}个文件 ----`);
this.status = 2;
const printLogFn = (type = "info") => msg => { mw.notify(msg, { type }); console.log(msg); };
const printLog = printLogFn();
printLog.warn = printLogFn("warn");
printLog.error = printLogFn("error");
try {
const checkedResult = await checkFileNames(postData.map(item => item.fileName));
const existedFiles = postData.filter(item => checkedResult[item.fileName.replace(/^./, s => s.toUpperCase())]); // 首字母转大写,因为checkedResult返回的文件名首字母是大写
if (existedFiles.length > 0 && !diffMode) {
alert([
"这些文件名已被使用,请为对应的文件更换其他名称:",
...existedFiles.map(item => item.fileName),
].join("\n"));
this.status = 1;
return;
}
if (diffMode) { postData = postData.filter(item => !checkedResult[item.fileName.replace(/^./, s => s.toUpperCase())]); }
if (diffMode && postData.length === 0) {
alert("差分模式下没有可以上传的文件");
this.status = 1;
return;
}
printLog.warn(`差分上传共需要上传${postData.length}个文件`);
let uploadResults = [];
if (postData.length <= 3) {
uploadResults = await Promise.all(
postData.map(item => new Promise(resolve => {
upload(item)
.then(() => {
printLog(`【${item.fileName}】上传成功`);
resolve({ fileName: item.fileName, result: true });
})
.catch((e) => {
printLog.error(`【${item.fileName}】上传失败:${errorInfo(e)}`);
console.error(e);
resolve({ fileName: item.fileName, result: false });
});
})),
);
} else {
printLog.warn("上传文件超过3个,执行分段上传");
// 分段上传
const segmentedPostData = [[]];
for (const item of postData) {
if (segmentedPostData[segmentedPostData.length - 1].length >= 3) {
segmentedPostData.push([]);
}
segmentedPostData[segmentedPostData.length - 1].push(item);
}
console.log(segmentedPostData);
for (let i = 0, len = segmentedPostData.length; i < len; i++) {
printLog(`共${len}个分段,现在开始第${i + 1}个`);
const segment = segmentedPostData[i];
const segmentedUploadResult = await Promise.all(
segment.map(item => new Promise(resolve => {
upload(item)
.then(() => {
printLog(`【${item.fileName}】上传成功`);
resolve({ fileName: item.fileName, result: true });
})
.catch((e) => {
printLog.error(`【${item.fileName}】上传失败:${errorInfo(e)}`);
resolve({ fileName: item.fileName, result: false });
});
})),
);
uploadResults.push(...segmentedUploadResult);
printLog(`第${i + 1}个分段完成,其中${segmentedUploadResult.filter(item => item.result).length}个成功,${segmentedUploadResult.filter(item => !item.result).length}个失败`);
}
}
const report = [
`全部上传结果:共计${uploadResults.length}个文件,其中${uploadResults.filter(item => item.result).length}个成功,${uploadResults.filter(item => !item.result).length}个失败`,
...uploadResults.map((item, index) => `${index + 1}. 【${item.fileName}】${item.result ? "成功" : "失败"}`),
].join("\n");
console.log(report);
alert(report);
this.status = 3;
} catch (e) {
console.log("上传流程出现错误", e);
mw.notify("网络错误,请重试", { type: "error" });
this.status = 0;
}
},
},
});
})());