User:Wecury/js/cat-tool.js
来自Vocawiki
更多操作
< User:Wecury | js
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl + F5或Ctrl + R(Mac为⌘ R)
- Google Chrome:按Ctrl + Shift + R(Mac为⌘ Shift R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl + F5。
/*!
* 分类页面小工具
* ------------------------------------------------------------------
* 功能:在分类页面标题旁添加一个“分类工具”按钮,
* 点击后弹出弹窗,用于快速完成分类页的编辑工作:
*
* 标签页一“建立作者分类”:选择/填写作者国籍,自动生成并写入 Catnav 模板;可勾选“存在对应条目”并填写条目名(勾选后自动填入标题中“作者:”之后的字段),以追加 Catmore 行。国籍留空(无法确认)时,直接归入“按作者分类的图片”。
*
* 标签页二“建立分类重定向”:填写重定向目标,自动生成并写入 分类重定向 模板。若当前分类名以“作者:”开头输入框会自动预填“作者:”。
*
* 使用:
* 在个人 JS(User:你的用户名/common.js)中加入一行引用:
* importScript('User:Wecury/js/cat-tool.js');
*/
"use strict";
// 防止这个页面被错误加分类
var OPEN_LINK = "[" + "[";
var CLOSE_LINK = "]" + "]";
var OPEN_TPL = "{" + "{";
var CLOSE_TPL = "}" + "}";
var TOOL_LINK = OPEN_LINK + "User:Wecury/js#分类页小工具|分类工具" + CLOSE_LINK;
$(function () {
// 防止脚本在同页被重复执行时重复添加按钮/弹窗
if (window.CATEGORY_TOOL_LOADED) {
return;
}
window.CATEGORY_TOOL_LOADED = true;
if (mw.config.get("wgNamespaceNumber") !== 14) {
return;
}
const currentPage = mw.config.get("wgPageName") || "";
if (!currentPage) {
return;
}
const citizenHeadingContainer = document.querySelector(".firstHeading-container");
const heading = document.getElementById("firstHeading");
if (!citizenHeadingContainer && !heading) {
return;
}
// ---- 样式 ----
if (!document.getElementById("category-tool-styles")) {
const style = document.createElement("style");
style.id = "category-tool-styles";
style.textContent = [
".category-tool-trigger { margin-left: 12px; }",
"#firstHeading > .category-tool-trigger { vertical-align: 4px; }",
".category-tool-dialog { border-radius: var(--border-radius-medium, 2px); }",
".category-tool-dialog .cdx-tabs > .cdx-tabs__header { margin-bottom: 16px; }",
".category-tool-dialog .cdx-tabs__prev-scroller, .category-tool-dialog .cdx-tabs__next-scroller, .category-tool-dialog .cdx-tabs__scroll-button { display: none !important; }",
".category-tool-dialog .cdx-field { margin-bottom: 8px; }",
".category-tool-article { display: flex; align-items: center; gap: 8px; margin: 12px 0 4px; }",
".category-tool-article .cdx-checkbox { margin-bottom: 0; }",
".category-tool-article .cdx-text-input { flex: 1; min-width: 0; }",
".category-tool-preview-label { margin: 16px 0 4px; font-size: 0.875rem; color: var(--color-subtle, #54595d); }",
".category-tool-preview { margin: 0; padding: 8px 12px; background-color: var(--background-color-neutral-subtle, #f8f9fa); border: 1px solid var(--border-color-subtle, #c8ccd1); border-radius: var(--border-radius-medium, 2px); font-size: 0.8125rem; white-space: pre-wrap; word-break: break-all; }",
].join("\n");
document.head.appendChild(style);
}
// ---- 添加「分类工具」入口按钮 ----
const trigger = document.createElement("button");
trigger.type = "button";
trigger.className =
"cdx-button cdx-button--action-default cdx-button--weight-normal cdx-button--size-medium cdx-button--framed cdx-dialog__footer__default-action category-tool-trigger";
trigger.textContent = "分类工具";
trigger.title = "打开分类页小工具";
if (citizenHeadingContainer) {
citizenHeadingContainer.insertBefore(
trigger,
citizenHeadingContainer.querySelector(".mw-indicators") || null
);
} else if (heading) {
heading.appendChild(trigger);
}
// ---- 常用作者国籍 ----
const nationalities = [
"日本", "中国", "美国", "韩国", "英国", "法国", "德国",
"俄罗斯", "意大利", "西班牙", "荷兰", "瑞典", "芬兰", "丹麦",
"挪威", "波兰", "加拿大", "澳大利亚", "新西兰", "巴西", "墨西哥",
"阿根廷", "泰国", "越南", "新加坡", "马来西亚", "印度尼西亚",
"菲律宾", "印度", "土耳其", "乌克兰", "比利时", "奥地利", "瑞士",
"葡萄牙", "希腊",
];
// ---- 重定向目标预填 ----
const canonicalNs = mw.config.get("wgCanonicalNamespace") || "";
const titleNoNs = canonicalNs && currentPage.startsWith(canonicalNs + ":")
? currentPage.slice(canonicalNs.length + 1)
: currentPage;
const defaultTarget = titleNoNs.indexOf("作者:") === 0 ? "作者:" : "";
// 条目名自动填充:标题含“作者:”时取其后字段(如 Category:作者:Iyowa → Iyowa),否则取整个标题
const articleDefault = titleNoNs.indexOf("作者:") === 0 ? titleNoNs.slice("作者:".length) : titleNoNs;
// ---- 加载Codex并挂载弹窗 ----
mw.loader.using(["@wikimedia/codex", "mediawiki.api", "mediawiki.notification"]).then(function (require) {
const Vue = require("vue");
const Codex = require("@wikimedia/codex");
const mountPoint = document.body.appendChild(document.createElement("div"));
Vue.createMwApp({
data() {
return {
open: false,
activeTab: "author",
nationality: "",
articleExists: false,
articleName: "",
redirectTarget: defaultTarget,
busy: false,
};
},
computed: {
nationalityMenu() {
return nationalities.map(function (n) {
return { label: n, value: n };
});
},
authorWikitext() {
const n = (this.nationality || "").trim();
const article = this.articleExists ? (this.articleName || "").trim() : "";
const catnav =
OPEN_TPL + "Catnav|图片|按作者分类的图片" +
(n ? "|" + n + "作者作品" : "") + CLOSE_TPL;
const catmore = article
? "\n" + OPEN_TPL + "Catmore|" + article + CLOSE_TPL
: "";
const catlink =
OPEN_LINK + "Category:" +
(n ? n + "作者作品" : "按作者分类的图片") + CLOSE_LINK;
return catnav + catmore + "\n" + catlink;
},
redirectWikitext() {
const t = (this.redirectTarget || "").trim();
return (
"#REDIRECT " + OPEN_LINK + "Category:" + t + CLOSE_LINK + "\n" +
OPEN_TPL + "分类重定向|" + t + CLOSE_TPL
);
},
canSubmit() {
if (this.activeTab === "redirect") {
return !!(this.redirectTarget || "").trim();
}
if (this.articleExists && !(this.articleName || "").trim()) {
return false;
}
return true;
},
},
watch: {
articleExists(checked) {
// 勾选“存在对应条目”后,若条目名未填,自动填充“作者:”后面的字段
if (checked && !(this.articleName || "").trim() && articleDefault) {
this.articleName = articleDefault;
}
},
},
methods: {
openDialog() {
this.open = true;
const harden = () => {
document
.querySelectorAll(".category-tool-dialog .cdx-tabs__scroll-button")
.forEach((btn) => btn.setAttribute("inert", ""));
document
.querySelectorAll(".category-tool-dialog .cdx-combobox__expand-button")
.forEach((btn) => {
if (!btn.dataset.hardened) {
btn.dataset.hardened = "1";
btn.addEventListener("mousedown", (e) => e.preventDefault());
btn.addEventListener("focus", () => btn.blur());
}
});
};
setTimeout(harden, 0);
setTimeout(harden, 200);
},
closeDialog() {
if (!this.busy) {
this.open = false;
}
},
onPrimary() {
if (this.activeTab === "redirect") {
this.submitRedirect();
} else {
this.submitAuthor();
}
},
notify(message, type) {
mw.notify(message, { type: type || "notice" });
},
submitAuthor() {
if (this.articleExists && !(this.articleName || "").trim()) {
this.notify("请填写对应条目名称。", "error");
return;
}
this.doEdit(this.authorWikitext, TOOL_LINK + ":快速建立作者分类");
},
submitRedirect() {
if (!(this.redirectTarget || "").trim()) {
this.notify("请填写重定向目标。", "error");
return;
}
this.doEdit(this.redirectWikitext, TOOL_LINK + ":快速建立分类重定向");
},
doEdit(text, summary) {
this.busy = true;
const api = new mw.Api();
const that = this;
api.postWithToken("csrf", {
action: "edit",
title: currentPage,
text: text,
summary: summary,
tags: "Automation tool",
})
.done(function (data) {
that.busy = false;
if (data && data.edit && data.edit.result === "Success") {
that.open = false;
that.notify("编辑成功", "success");
setTimeout(function () {
location.reload();
}, 800);
} else {
that.notify("编辑失败:" + JSON.stringify(data), "error");
}
})
.fail(function (code, data) {
that.busy = false;
that.notify(
"编辑失败:" + code +
(data && data.error && data.error.info
? ":" + data.error.info
: ""),
"error"
);
});
},
},
mounted() {
// 入口按钮点击打开弹窗
trigger.addEventListener("click", this.openDialog);
},
template: `
<cdx-dialog v-model:open="open"
class="category-tool-dialog"
title="分类页小工具"
:use-close-button="true"
close-button-label="关闭"
>
<template #footer>
<div class="cdx-dialog__footer__actions">
<cdx-button
action="progressive"
weight="primary"
:disabled="busy || !canSubmit"
@click="onPrimary"
>
{{ busy ? '处理中…' : '确定' }}
</cdx-button>
<cdx-button action="destructive" weight="quiet" :disabled="busy" @click="closeDialog">取消</cdx-button>
</div>
</template>
<cdx-tabs v-model:active="activeTab">
<cdx-tab name="author" label="建立作者分类">
<cdx-field
label="作者国籍"
description="选择或填写作者国籍;留空(无法确认)时,直接归入“按作者分类的图片”。"
>
<cdx-combobox
v-model:input-value="nationality"
v-model:selected="nationality"
:menu-items="nationalityMenu"
placeholder="未选择国籍"
/>
</cdx-field>
<div class="category-tool-article">
<cdx-checkbox v-model="articleExists">
存在对应条目
</cdx-checkbox>
<cdx-text-input
v-model="articleName"
:disabled="!articleExists"
placeholder="条目名"
/>
</div>
<p class="category-tool-preview-label">将写入的文本:</p>
<pre class="category-tool-preview">{{ authorWikitext }}</pre>
</cdx-tab>
<cdx-tab name="redirect" label="建立分类重定向">
<cdx-field
label="重定向目标"
description="将当前分类页重定向到目标页面(无需带“Category:”前缀)。"
>
<cdx-text-input
v-model="redirectTarget"
placeholder="目标分类名称"
/>
</cdx-field>
<p class="category-tool-preview-label">将写入的文本:</p>
<pre class="category-tool-preview">{{ redirectWikitext }}</pre>
</cdx-tab>
</cdx-tabs>
</cdx-dialog>
`,
})
.component("cdx-dialog", Codex.CdxDialog)
.component("cdx-tabs", Codex.CdxTabs)
.component("cdx-tab", Codex.CdxTab)
.component("cdx-button", Codex.CdxButton)
.component("cdx-checkbox", Codex.CdxCheckbox)
.component("cdx-text-input", Codex.CdxTextInput)
.component("cdx-combobox", Codex.CdxCombobox)
.component("cdx-field", Codex.CdxField)
.mount(mountPoint);
});
});