Card.js:修订间差异
来自卡厄思梦境WIKI
无编辑摘要 |
小无编辑摘要 |
||
| 第1行: | 第1行: | ||
// MediaWiki:Card.js | |||
(function() { | (function() { | ||
'use strict'; | |||
// 加载CSS | // 加载CSS | ||
mw.loader.load('/index.php?title=Template:Card | mw.loader.load('/index.php?title=Template:Card.css&action=raw&ctype=text/css', 'text/css'); | ||
// | |||
mw. | // 主管理器对象 | ||
$( | const CardManager = { | ||
currentFighter: '', | |||
cardData: {}, | |||
cardList: [], | |||
init: function() { | |||
this.createInterface(); | |||
this.loadFighters(); | |||
this.bindEvents(); | |||
}, | |||
// | |||
$('#mw- | 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 { | } 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`; | |||
var | |||
} | |||
} | } | ||
}); | |||
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; | |||
})(); | })(); | ||
2025年10月23日 (四) 15:58的版本
// 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;
})();