MediaWiki:Card.js
来自卡厄思梦境WIKI
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
// MediaWiki:Card.js
(function() {
'use strict';
// 加载CSS
mw.loader.load('/index.php?title=Template:Card.css&action=raw&ctype=text/css', 'text/css');
// 主管理器对象
const CardManager = {
currentFighter: '',
cardData: {},
cardList: [],
init: function() {
this.createInterface();
this.loadFighters();
this.bindEvents();
},
createInterface: function() {
const container = $('<div>').attr('id', 'card-manager').addClass('card-manager-container');
// 创建三列布局
const leftPanel = this.createLeftPanel();
const centerPanel = this.createCenterPanel();
const rightPanel = this.createRightPanel();
container.append(leftPanel, centerPanel, rightPanel);
$('#mw-content-text').prepend(container);
},
createLeftPanel: function() {
const panel = $('<div>').addClass('card-panel card-panel-left');
// 战斗员选择
const fighterSection = $('<div>').addClass('card-section');
fighterSection.append(
$('<div>').addClass('section-title').text('战斗员选择'),
$('<div>').addClass('form-group').append(
$('<div>').addClass('form-label').text('当前战斗员:'),
$('<div>').attr('id', 'fighter-select').addClass('custom-select').append(
$('<div>').addClass('select-display').text('请选择战斗员'),
$('<div>').addClass('select-options').hide()
)
)
);
// 默认信息区
const defaultInfo = $('<div>').addClass('card-section');
defaultInfo.append(
$('<div>').addClass('section-title').text('默认信息'),
this.createInput('card-order', '卡牌顺序', '灭,灭,救,虚无残影,消灭烙印,黑洞,虚妄的誓约,无忧的回声'),
this.createInput('ego', '属性', '虚无')
);
// 卡牌数据输入区
const cardDataSection = $('<div>').addClass('card-section');
cardDataSection.append(
$('<div>').addClass('section-title').text('卡牌数据'),
this.createInput('card-name', '卡牌名称', '黑洞'),
this.createInput('displayname', '显示名称', ''),
this.createInput('art', '图片', 'unique_1064_02.png'),
this.createDropdown('group', '卡组', ['自我意识技能', '起始卡牌', '独特卡牌', '灵光一闪', '神光一闪']),
this.createDropdown('rarity', '稀有度', ['白', '蓝', '橙', '彩']),
this.createDropdown('god', '神明', ['circen', 'diallos', 'nihilum', 'secred', 'vitor']),
this.createInput('ap', 'AP', '2'),
this.createDropdown('type', '卡牌类型', ['攻击', '技能', '强化']),
this.createInput('dict', '机制', ''),
this.createDescriptionInput(),
this.createInput('sub', '衍生卡牌', ''),
this.createCheckbox('isinspiration', '是否存在灵光一闪'),
this.createCheckbox('god_inspiration', '是否存在神光一闪')
);
panel.append(fighterSection, defaultInfo, cardDataSection);
return panel;
},
createCenterPanel: function() {
const panel = $('<div>').addClass('card-panel card-panel-center');
// 卡牌列表
const cardListSection = $('<div>').addClass('card-section');
cardListSection.append(
$('<div>').addClass('section-title').text('卡牌列表'),
$('<div>').attr('id', 'card-list').addClass('card-list')
);
// 灵光一闪列表
const inspirationSection = $('<div>').addClass('card-section');
inspirationSection.append(
$('<div>').addClass('section-title').text('灵光一闪列表'),
$('<div>').attr('id', 'inspiration-list').addClass('card-list')
);
// 神光一闪列表
const godInspirationSection = $('<div>').addClass('card-section');
godInspirationSection.append(
$('<div>').addClass('section-title').text('神光一闪列表'),
$('<div>').attr('id', 'god-inspiration-list').addClass('card-list')
);
// 操作按钮
const buttonSection = $('<div>').addClass('card-section button-group');
buttonSection.append(
$('<div>').addClass('card-button button-add').text('添加卡牌'),
$('<div>').addClass('card-button button-update').text('更新卡牌'),
$('<div>').addClass('card-button button-delete').text('删除卡牌'),
$('<div>').addClass('card-button button-save').text('保存到模块')
);
panel.append(cardListSection, inspirationSection, godInspirationSection, buttonSection);
return panel;
},
createRightPanel: function() {
const panel = $('<div>').addClass('card-panel card-panel-right');
const previewSection = $('<div>').addClass('card-section');
previewSection.append(
$('<div>').addClass('section-title').text('Lua代码预览'),
$('<div>').addClass('code-preview-wrapper').append(
$('<pre>').attr('id', 'lua-preview').addClass('lua-preview')
)
);
panel.append(previewSection);
return panel;
},
createInput: function(id, label, placeholder) {
return $('<div>').addClass('form-group').append(
$('<div>').addClass('form-label').text(label + ':'),
$('<div>').attr('id', id).addClass('custom-input').attr('contenteditable', 'true')
.attr('data-placeholder', placeholder || '')
);
},
createDropdown: function(id, label, options) {
const dropdown = $('<div>').addClass('form-group').append(
$('<div>').addClass('form-label').text(label + ':'),
$('<div>').attr('id', id).addClass('custom-select').append(
$('<div>').addClass('select-display').text(options[0]),
$('<div>').addClass('select-options').hide()
)
);
const optionsContainer = dropdown.find('.select-options');
options.forEach(opt => {
optionsContainer.append(
$('<div>').addClass('select-option').text(opt)
);
});
return dropdown;
},
createCheckbox: function(id, label) {
return $('<div>').addClass('form-group').append(
$('<div>').addClass('form-label').text(label + ':'),
$('<div>').attr('id', id).addClass('custom-checkbox').append(
$('<div>').addClass('checkbox-box'),
$('<div>').addClass('checkbox-label').text('否')
).data('checked', false)
);
},
createDescriptionInput: function() {
const descGroup = $('<div>').addClass('form-group');
// 工具栏
const toolbar = $('<div>').addClass('desc-toolbar').append(
$('<div>').addClass('toolbar-btn').text('蓝色文本').data('format', 'blue'),
$('<div>').addClass('toolbar-btn').text('绿色文本').data('format', 'green'),
$('<div>').addClass('toolbar-btn').text('绿色描边').data('format', 'outline'),
$('<div>').addClass('toolbar-btn').text('词典').data('format', 'dict'),
$('<div>').addClass('toolbar-btn').text('换行').data('format', 'br')
);
descGroup.append(
$('<div>').addClass('form-label').text('描述:'),
toolbar,
$('<div>').attr('id', 'desc_global').addClass('custom-textarea')
.attr('contenteditable', 'true')
.html('伤害{{文本|蓝|240}}%<br>按照消灭卡牌的数量,伤害量+{{文本|蓝|40}}%<br>(最多{{文本|蓝|10}}次)')
);
return descGroup;
},
loadFighters: function() {
// 通过API加载战斗员分类的页面
new mw.Api().get({
action: 'query',
list: 'categorymembers',
cmtitle: 'Category:战斗员',
cmlimit: 500,
format: 'json'
}).done(function(data) {
const fighterSelect = $('#fighter-select .select-options');
fighterSelect.empty();
data.query.categorymembers.forEach(function(member) {
fighterSelect.append(
$('<div>').addClass('select-option').text(member.title).data('fighter', member.title)
);
});
});
},
bindEvents: function() {
const self = this;
// 下拉框事件
$(document).on('click', '.custom-select', function(e) {
e.stopPropagation();
const options = $(this).find('.select-options');
$('.select-options').not(options).hide();
options.toggle();
});
$(document).on('click', '.select-option', function(e) {
e.stopPropagation();
const select = $(this).closest('.custom-select');
const display = select.find('.select-display');
display.text($(this).text());
$(this).parent().hide();
if (select.attr('id') === 'fighter-select') {
self.currentFighter = $(this).data('fighter');
self.loadFighterCards();
}
});
// 复选框事件
$(document).on('click', '.custom-checkbox', function() {
const isChecked = !$(this).data('checked');
$(this).data('checked', isChecked);
$(this).find('.checkbox-box').toggleClass('checked');
$(this).find('.checkbox-label').text(isChecked ? '是' : '否');
});
// 描述工具栏事件
$(document).on('click', '.toolbar-btn', function() {
const format = $(this).data('format');
const textarea = $('#desc_global')[0];
const selection = window.getSelection();
const selectedText = selection.toString();
let insertText = '';
switch(format) {
case 'blue':
insertText = selectedText ? `{{文本|蓝|${selectedText}}}` : '{{文本|蓝|}}';
break;
case 'green':
insertText = selectedText ? `{{文本|绿|${selectedText}}}` : '{{文本|绿|}}';
break;
case 'outline':
insertText = selectedText ? `{{描边|绿|${selectedText}}}` : '{{描边|绿|}}';
break;
case 'dict':
insertText = selectedText ? `{{词典|${selectedText}}}` : '{{词典|}}';
break;
case 'br':
insertText = '<br>';
break;
}
document.execCommand('insertHTML', false, insertText);
});
// 按钮事件
$('.button-add').on('click', function() {
self.addCard();
});
$('.button-update').on('click', function() {
self.updateCard();
});
$('.button-delete').on('click', function() {
self.deleteCard();
});
$('.button-save').on('click', function() {
self.saveToModule();
});
// 输入变化时更新预览
$(document).on('input', '.custom-input, .custom-textarea', function() {
self.updatePreview();
});
// 点击其他地方关闭下拉框
$(document).on('click', function() {
$('.select-options').hide();
});
},
addCard: function() {
const cardName = $('#card-name').text();
if (!cardName) {
mw.notify('请输入卡牌名称', {type: 'error'});
return;
}
const cardData = this.getFormData();
this.cardData[cardName] = cardData;
this.updateCardList();
this.updatePreview();
mw.notify('卡牌添加成功', {type: 'success'});
},
updateCard: function() {
const cardName = $('#card-name').text();
if (!cardName || !this.cardData[cardName]) {
mw.notify('请选择要更新的卡牌', {type: 'error'});
return;
}
const cardData = this.getFormData();
this.cardData[cardName] = cardData;
this.updateCardList();
this.updatePreview();
mw.notify('卡牌更新成功', {type: 'success'});
},
deleteCard: function() {
const cardName = $('#card-name').text();
if (!cardName || !this.cardData[cardName]) {
mw.notify('请选择要删除的卡牌', {type: 'error'});
return;
}
delete this.cardData[cardName];
this.updateCardList();
this.updatePreview();
this.clearForm();
mw.notify('卡牌删除成功', {type: 'success'});
},
getFormData: function() {
const data = {
base: {
displayname: $('#displayname').text(),
art: $('#art').text(),
group: $('#group .select-display').text(),
rarity: $('#rarity .select-display').text(),
ap: $('#ap').text(),
type: $('#type .select-display').text(),
dict: $('#dict').text(),
desc_global: $('#desc_global').html(),
sub: $('#sub').text(),
isinspiration: $('#isinspiration').data('checked') ? 1 : 0,
god_inspiration: $('#god_inspiration').data('checked') ? 1 : 0
},
var: {}
};
// 如果有灵光一闪,添加默认数据
if (data.base.isinspiration) {
data.var.inspiration = [
{ ap: 1 },
{ desc_global: data.base.desc_global },
{ desc_global: data.base.desc_global },
{ ap: 3, desc_global: data.base.desc_global },
{ ap: 1, type: '强化', desc_global: data.base.desc_global }
];
}
// 如果有神光一闪,添加默认数据
if (data.base.god_inspiration) {
data.var.god_inspiration = {
circen: [{ ap: 1 }],
diallos: [{ ap: 1 }],
nihilum: [{ ap: 1 }],
secred: [{ ap: 1 }],
vitor: [{ ap: 1 }]
};
}
return data;
},
updateCardList: function() {
const cardList = $('#card-list');
cardList.empty();
Object.keys(this.cardData).forEach(cardName => {
const card = this.cardData[cardName];
const cardItem = $('<div>').addClass('card-item').text(cardName);
cardItem.on('click', () => this.loadCard(cardName));
cardList.append(cardItem);
// 更新灵光一闪列表
if (card.base.isinspiration) {
const inspirationItem = $('<div>').addClass('card-item inspiration-item')
.text(cardName + ' - 灵光一闪');
$('#inspiration-list').append(inspirationItem);
}
// 更新神光一闪列表
if (card.base.god_inspiration) {
const godItem = $('<div>').addClass('card-item god-item')
.text(cardName + ' - 神光一闪');
$('#god-inspiration-list').append(godItem);
}
});
},
loadCard: function(cardName) {
const card = this.cardData[cardName];
if (!card) return;
$('#card-name').text(cardName);
$('#displayname').text(card.base.displayname || '');
$('#art').text(card.base.art || '');
$('#group .select-display').text(card.base.group || '');
$('#rarity .select-display').text(card.base.rarity || '');
$('#ap').text(card.base.ap || '');
$('#type .select-display').text(card.base.type || '');
$('#dict').text(card.base.dict || '');
$('#desc_global').html(card.base.desc_global || '');
$('#sub').text(card.base.sub || '');
const inspirationChecked = card.base.isinspiration === 1;
$('#isinspiration').data('checked', inspirationChecked);
$('#isinspiration .checkbox-box').toggleClass('checked', inspirationChecked);
$('#isinspiration .checkbox-label').text(inspirationChecked ? '是' : '否');
const godChecked = card.base.god_inspiration === 1;
$('#god_inspiration').data('checked', godChecked);
$('#god_inspiration .checkbox-box').toggleClass('checked', godChecked);
$('#god_inspiration .checkbox-label').text(godChecked ? '是' : '否');
},
clearForm: function() {
$('.custom-input').text('');
$('.custom-textarea').html('');
$('.custom-checkbox').data('checked', false);
$('.checkbox-box').removeClass('checked');
$('.checkbox-label').text('否');
},
updatePreview: function() {
const cardOrder = $('#card-order').text();
const ego = $('#ego').text();
let luaCode = 'local card = {}\n\n';
luaCode += `card.order = { "${cardOrder}" }\n\n`;
luaCode += 'card.info = {\n';
luaCode += ` ego = "${ego}",\n`;
luaCode += '}\n\n';
Object.keys(this.cardData).forEach(cardName => {
const card = this.cardData[cardName];
luaCode += `card["${cardName}"] = {\n`;
luaCode += ' base = {\n';
Object.keys(card.base).forEach(key => {
const value = card.base[key];
if (value !== '' && value !== 0) {
if (typeof value === 'number') {
luaCode += ` ${key} = ${value},\n`;
} else {
luaCode += ` ${key} = "${value}",\n`;
}
}
});
luaCode += ' },\n';
if (card.var && Object.keys(card.var).length > 0) {
luaCode += ' var = {\n';
if (card.var.inspiration) {
luaCode += ' inspiration = {\n';
card.var.inspiration.forEach(insp => {
luaCode += ' {\n';
Object.keys(insp).forEach(key => {
if (typeof insp[key] === 'number') {
luaCode += ` ${key} = ${insp[key]},\n`;
} else {
luaCode += ` ${key} = "${insp[key]}",\n`;
}
});
luaCode += ' },\n';
});
luaCode += ' },\n';
}
if (card.var.god_inspiration) {
luaCode += ' god_inspiration = {\n';
Object.keys(card.var.god_inspiration).forEach(god => {
luaCode += ` ${god} = {\n`;
card.var.god_inspiration[god].forEach(godInsp => {
luaCode += ' {\n';
Object.keys(godInsp).forEach(key => {
if (typeof godInsp[key] === 'number') {
luaCode += ` ${key} = ${godInsp[key]},\n`;
} else {
luaCode += ` ${key} = "${godInsp[key]}",\n`;
}
});
luaCode += ' },\n';
});
luaCode += ' },\n';
});
luaCode += ' },\n';
}
luaCode += ' },\n';
}
luaCode += '}\n\n';
});
luaCode += 'return card';
$('#lua-preview').text(luaCode);
},
loadFighterCards: function() {
if (!this.currentFighter) return;
const moduleName = `模块:卡牌/${this.currentFighter}`;
new mw.Api().get({
action: 'query',
prop: 'revisions',
titles: moduleName,
rvprop: 'content',
format: 'json'
}).done((data) => {
const pages = data.query.pages;
const pageId = Object.keys(pages)[0];
if (pageId !== '-1') {
const content = pages[pageId].revisions[0]['*'];
// 这里需要解析Lua内容并加载到界面
this.parseLuaContent(content);
}
});
},
parseLuaContent: function(content) {
// 简化的Lua解析,实际使用可能需要更复杂的解析器
try {
// 提取卡牌数据
const cardMatches = content.matchAll(/card\["([^"]+)"\]\s*=\s*{([^}]+})}/gs);
this.cardData = {};
for (const match of cardMatches) {
const cardName = match[1];
// 这里需要更复杂的解析逻辑
// 暂时使用简化版本
}
this.updateCardList();
this.updatePreview();
} catch (e) {
console.error('解析Lua内容失败:', e);
}
},
saveToModule: function() {
if (!this.currentFighter) {
mw.notify('请先选择战斗员', {type: 'error'});
return;
}
const moduleName = `模块:卡牌/${this.currentFighter}`;
const luaContent = $('#lua-preview').text();
new mw.Api().postWithToken('csrf', {
action: 'edit',
title: moduleName,
text: luaContent,
summary: '更新卡牌数据',
format: 'json'
}).done(function() {
mw.notify('保存成功!', {type: 'success'});
}).fail(function(error) {
mw.notify('保存失败:' + error, {type: 'error'});
});
}
};
// 页面加载完成后初始化
$(document).ready(function() {
// 只在特定页面加载
if (mw.config.get('wgPageName') === 'Special:CardManager' ||
mw.config.get('wgPageName').indexOf('卡牌管理') !== -1) {
CardManager.init();
}
});
// 添加到全局对象以便调试
window.CardManager = CardManager;
})();