Card.js:修订间差异
来自卡厄思梦境WIKI
小无编辑摘要 |
无编辑摘要 |
||
| 第1行: | 第1行: | ||
(function() { | (function() { | ||
'use strict'; | 'use strict'; | ||
// 加载CSS | // 加载CSS | ||
mw.loader.load('/index.php?title= | mw.loader.load('/index.php?title=Mediawiki:Card.css&action=raw&ctype=text/css', 'text/css'); | ||
// | // 卡牌管理器类 | ||
class CardManager { | |||
currentFighter: '', | constructor() { | ||
this.currentFighter = ''; | |||
this.cards = {}; | |||
this.currentCard = null; | |||
init | this.fighters = []; | ||
this. | |||
this. | // 默认数据 | ||
this.defaultData = { | |||
order: [], | |||
ego: '' | |||
}; | |||
// 当前编辑的卡牌数据 | |||
this.cardData = { | |||
name: '', | |||
displayname: '', | |||
art: '', | |||
group: '', | |||
rarity: '', | |||
god: '', | |||
ap: '', | |||
type: '攻击', | |||
dict: '', | |||
desc_global: '', | |||
sub: '', | |||
isinspiration: false, | |||
isgod_inspiration: false, | |||
inspirations: [], | |||
god_inspirations: { | |||
circen: [], | |||
diallos: [], | |||
nihilum: [], | |||
secred: [], | |||
vitor: [] | |||
} | |||
}; | |||
} | |||
// 初始化 | |||
async init() { | |||
await this.loadFighters(); | |||
this.render(); | |||
this.bindEvents(); | this.bindEvents(); | ||
} | } | ||
// 加载战斗员列表 | |||
const | async loadFighters() { | ||
try { | |||
const api = new mw.Api(); | |||
const response = await api.get({ | |||
action: 'query', | |||
list: 'categorymembers', | |||
cmtitle: 'Category:战斗员', | |||
cmlimit: 500 | |||
}); | |||
this.fighters = response.query.categorymembers.map(page => page.title); | |||
} catch (error) { | |||
console.error('加载战斗员列表失败:', error); | |||
this.fighters = []; | |||
} | |||
} | |||
// 渲染主界面 | |||
render() { | |||
const container = this.createDiv('card-manager'); | |||
// | // 左侧面板 | ||
const leftPanel = this. | const leftPanel = this.createDiv('card-input-panel'); | ||
leftPanel.appendChild(this.renderFighterSelect()); | |||
leftPanel.appendChild(this.renderDefaultInfo()); | |||
leftPanel.appendChild(this.renderCardInput()); | |||
// 中间面板 | |||
const middlePanel = this.createDiv('card-list-panel'); | |||
middlePanel.appendChild(this.renderCardLists()); | |||
// | // 右侧面板 | ||
const | const rightPanel = this.createDiv('card-preview-panel'); | ||
rightPanel.appendChild(this.renderPreview()); | |||
container.appendChild(leftPanel); | |||
container.appendChild(middlePanel); | |||
container.appendChild(rightPanel); | |||
// 添加到页面 | |||
) | const content = document.getElementById('mw-content-text'); | ||
if (content) { | |||
content.innerHTML = ''; | |||
content.appendChild(container); | |||
} | |||
} | |||
// 创建div元素 | |||
createDiv(className, text = '') { | |||
const div = document.createElement('div'); | |||
if (className) div.className = className; | |||
if (text) div.textContent = text; | |||
return div; | |||
} | |||
// 渲染战斗员选择 | |||
renderFighterSelect() { | |||
const container = this.createDiv('form-group'); | |||
const label = this.createDiv('form-label', '当前战斗员'); | |||
container.appendChild(label); | |||
const select = this.createCustomSelect( | |||
this.fighters, | |||
this.currentFighter, | |||
(value) => { | |||
this.currentFighter = value; | |||
this.loadFighterData(); | |||
} | |||
); | ); | ||
// | container.appendChild(select); | ||
const | return container; | ||
} | |||
this. | // 渲染默认信息区 | ||
this.createInput(' | renderDefaultInfo() { | ||
); | const container = this.createDiv('default-info-section'); | ||
const title = this.createDiv('section-title', '默认信息'); | |||
container.appendChild(title); | |||
// 卡牌顺序 | |||
const orderGroup = this.createDiv('form-group'); | |||
const orderLabel = this.createDiv('form-label', '卡牌顺序(card.order)'); | |||
const orderInput = this.createInput('text', this.defaultData.order.join(','), (value) => { | |||
this.defaultData.order = value.split(',').map(s => s.trim()).filter(s => s); | |||
this.updatePreview(); | |||
}); | |||
orderGroup.appendChild(orderLabel); | |||
orderGroup.appendChild(orderInput); | |||
container.appendChild(orderGroup); | |||
// 属性 | |||
const egoGroup = this.createDiv('form-group'); | |||
const egoLabel = this.createDiv('form-label', '属性(ego)'); | |||
const egoInput = this.createInput('text', this.defaultData.ego, (value) => { | |||
this.defaultData.ego = value; | |||
this.updatePreview(); | |||
}); | |||
egoGroup.appendChild(egoLabel); | |||
egoGroup.appendChild(egoInput); | |||
container.appendChild(egoGroup); | |||
return container; | |||
} | |||
// 渲染卡牌输入区 | |||
renderCardInput() { | |||
const container = this.createDiv(); | |||
const title = this.createDiv('section-title', '卡牌数据'); | |||
container.appendChild(title); | |||
// 卡牌名称 | |||
container.appendChild(this.createFormGroup('卡牌名称', | |||
this.createInput('text', this.cardData.name, (value) => { | |||
this.cardData.name = value; | |||
this.updatePreview(); | |||
}) | |||
)); | |||
// 显示名称 | |||
container.appendChild(this.createFormGroup('显示名称(displayname)', | |||
this.createInput('text', this.cardData.displayname, (value) => { | |||
this.cardData.displayname = value; | |||
this.updatePreview(); | |||
}) | |||
)); | |||
// 图片 | |||
container.appendChild(this.createFormGroup('图片(art)', | |||
this.createInput('text', this.cardData.art, (value) => { | |||
this.cardData.art = value; | |||
this.updatePreview(); | |||
}) | |||
)); | |||
// 卡组 | |||
container.appendChild(this.createFormGroup('卡组(group)', | |||
this.createGroupSelect() | |||
)); | |||
// | // 稀有度 | ||
container.appendChild(this.createFormGroup('稀有度(rarity)', | |||
this.createRaritySelect() | |||
)); | |||
this. | |||
// 神明 | |||
container.appendChild(this.createFormGroup('神明', | |||
this.createGodSelect() | |||
)); | |||
// | // AP | ||
container.appendChild(this.createFormGroup('AP', | |||
this.createInput('text', this.cardData.ap, (value) => { | |||
this.cardData.ap = value; | |||
this.updatePreview(); | |||
); | }) | ||
)); | |||
// | // 卡牌类型 | ||
container.appendChild(this.createFormGroup('卡牌类型(type)', | |||
this.createTypeSelect() | |||
)); | |||
); | |||
// | // 机制 | ||
container.appendChild(this.createFormGroup('机制(dict)', | |||
this.createInput('text', this.cardData.dict, (value) => { | |||
this.cardData.dict = value; | |||
this.updatePreview(); | |||
); | }) | ||
)); | |||
// | // 描述 | ||
const | const descGroup = this.createDiv('form-group'); | ||
const descLabel = this.createDiv('form-label', '描述(desc_global)'); | |||
descGroup.appendChild(descLabel); | |||
descGroup.appendChild(this.createFormatButtons()); | |||
descGroup.appendChild(this.createTextarea(this.cardData.desc_global, (value) => { | |||
this.cardData.desc_global = value; | |||
); | this.updatePreview(); | ||
})); | |||
container.appendChild(descGroup); | |||
// 衍生卡牌 | |||
container.appendChild(this.createFormGroup('衍生卡牌(sub)', | |||
this.createInput('text', this.cardData.sub, (value) => { | |||
this.cardData.sub = value; | |||
this.updatePreview(); | |||
}) | |||
)); | |||
// 是否存在灵光一闪 | |||
container.appendChild(this.createFormGroup('是否存在灵光一闪', | |||
this.createCheckbox(this.cardData.isinspiration, (checked) => { | |||
this.cardData.isinspiration = checked; | |||
this.updatePreview(); | |||
) | }) | ||
); | )); | ||
// 是否存在神光一闪 | |||
container.appendChild(this.createFormGroup('是否存在神光一闪', | |||
this.createCheckbox(this.cardData.isgod_inspiration, (checked) => { | |||
this.cardData.isgod_inspiration = checked; | |||
this.updatePreview(); | |||
}) | |||
)); | |||
) | |||
); | |||
const | // 按钮组 | ||
const btnGroup = this.createDiv('btn-group'); | |||
const saveBtn = this.createDiv('btn btn-primary', '保存卡牌'); | |||
saveBtn.onclick = () => this.saveCard(); | |||
const newBtn = this.createDiv('btn btn-success', '新建卡牌'); | |||
newBtn.onclick = () => this.newCard(); | |||
btnGroup.appendChild(saveBtn); | |||
btnGroup.appendChild(newBtn); | |||
container.appendChild(btnGroup); | |||
return | return container; | ||
} | } | ||
// 创建表单组 | |||
createFormGroup(label, input) { | |||
const group = this.createDiv('form-group'); | |||
const labelDiv = this.createDiv('form-label', label); | |||
group.appendChild(labelDiv); | |||
group.appendChild(input); | |||
return group; | |||
} | |||
} | |||
// 创建自定义下拉框 | |||
createCustomSelect(options, selected, onChange) { | |||
const | const container = this.createDiv('custom-select'); | ||
const display = this.createDiv('select-display', selected || '请选择...'); | |||
const | const arrow = this.createDiv('select-arrow', '▼'); | ||
display.appendChild(arrow); | |||
const dropdown = this.createDiv('select-dropdown'); | |||
options.forEach(option => { | |||
const optionDiv = this.createDiv('select-option', option); | |||
if (option === selected) { | |||
optionDiv.classList.add('selected'); | |||
} | |||
optionDiv.onclick = () => { | |||
display.childNodes[0].textContent = option; | |||
dropdown.querySelectorAll('.select-option').forEach(o => o.classList.remove('selected')); | |||
optionDiv.classList.add('selected'); | |||
dropdown.classList.remove('active'); | |||
onChange(option); | |||
}; | |||
dropdown.appendChild(optionDiv); | |||
); | |||
}); | |||
}); | }); | ||
display.onclick = () => { | |||
dropdown.classList.toggle('active'); | |||
}; | |||
} | |||
// 点击外部关闭 | |||
document.addEventListener('click', (e) => { | |||
if (!container.contains(e.target)) { | |||
dropdown.classList.remove('active'); | |||
} | } | ||
}); | }); | ||
// | container.appendChild(display); | ||
container.appendChild(dropdown); | |||
return container; | |||
} | |||
// 创建输入框 | |||
createInput(type, value, onChange) { | |||
const input = this.createDiv('custom-input'); | |||
input.contentEditable = true; | |||
input.textContent = value; | |||
input.oninput = () => onChange(input.textContent); | |||
return input; | |||
} | |||
// 创建文本域 | |||
createTextarea(value, onChange) { | |||
const textarea = this.createDiv('custom-textarea'); | |||
textarea.contentEditable = true; | |||
textarea.innerHTML = value.replace(/<br>/g, '\n'); | |||
textarea.oninput = () => onChange(textarea.textContent); | |||
return textarea; | |||
} | |||
// 创建格式化按钮 | |||
createFormatButtons() { | |||
const container = this.createDiv('format-buttons'); | |||
const buttons = [ | |||
{ label: '蓝色文本', class: 'blue', template: '{{文本|蓝|%s}}' }, | |||
{ label: '绿色文本', class: 'green', template: '{{文本|绿|%s}}' }, | |||
{ label: '绿色描边', class: 'green', template: '{{描边|绿|%s}}' }, | |||
{ label: '词典', class: '', template: '{{词典|%s}}' }, | |||
{ label: '换行', class: '', template: '<br>' } | |||
]; | |||
buttons.forEach(btn => { | |||
const button = this.createDiv(`format-btn ${btn.class}`, btn.label); | |||
button.onclick = () => { | |||
const textarea = button.parentElement.nextElementSibling; | |||
this.insertTemplate(textarea, btn.template); | |||
}; | |||
container.appendChild(button); | |||
}); | }); | ||
// | return container; | ||
} | |||
const | |||
const textarea = | // 插入模板 | ||
insertTemplate(textarea, template) { | |||
const | const selection = window.getSelection(); | ||
const selectedText = selection.toString(); | |||
if (template === '<br>') { | |||
const text = textarea.textContent; | |||
const pos = this.getCaretPosition(textarea); | |||
textarea.textContent = text.slice(0, pos) + '\n' + text.slice(pos); | |||
} else { | |||
const result = template.replace('%s', selectedText || '选择文字'); | |||
if (selectedText) { | |||
document.execCommand('insertText', false, result); | |||
} else { | |||
const text = textarea.textContent; | |||
const pos = this.getCaretPosition(textarea); | |||
textarea.textContent = text.slice(0, pos) + result + text.slice(pos); | |||
} | |||
} | |||
textarea.dispatchEvent(new Event('input')); | |||
} | |||
// 获取光标位置 | |||
getCaretPosition(element) { | |||
const selection = window.getSelection(); | |||
if (selection.rangeCount > 0) { | |||
const range = selection.getRangeAt(0); | |||
const preCaretRange = range.cloneRange(); | |||
preCaretRange.selectNodeContents(element); | |||
preCaretRange.setEnd(range.endContainer, range.endOffset); | |||
return preCaretRange.toString().length; | |||
} | |||
return 0; | |||
} | |||
// 创建复选框 | |||
createCheckbox(checked, onChange) { | |||
const container = this.createDiv('checkbox-group'); | |||
const checkbox = this.createDiv('custom-checkbox'); | |||
if (checked) checkbox.classList.add('checked'); | |||
const label = this.createDiv('', '是'); | |||
checkbox.onclick = () => { | |||
checkbox.classList.toggle('checked'); | |||
const isChecked = checkbox.classList.contains('checked'); | |||
label.textContent = isChecked ? '是' : '否'; | |||
onChange(isChecked); | |||
}; | |||
container.appendChild(checkbox); | |||
container.appendChild(label); | |||
return container; | |||
} | |||
// 创建卡组选择 | |||
createGroupSelect() { | |||
const container = this.createDiv('group-options'); | |||
const groups = ['自我意识技能', '起始卡牌', '独特卡牌', '灵光一闪', '神光一闪']; | |||
groups.forEach(group => { | |||
const option = this.createDiv('group-option', group); | |||
if (this.cardData.group === group) { | |||
option.classList.add('selected'); | |||
} | } | ||
option.onclick = () => { | |||
container.querySelectorAll('.group-option').forEach(o => o.classList.remove('selected')); | |||
option.classList.add('selected'); | |||
this.cardData.group = group; | |||
this.updatePreview(); | |||
}; | |||
container.appendChild(option); | |||
}); | }); | ||
// | return container; | ||
} | |||
// 创建稀有度选择 | |||
createRaritySelect() { | |||
const container = this.createDiv('rarity-options'); | |||
const rarities = ['白', '蓝', '橙', '彩']; | |||
rarities.forEach(rarity => { | |||
const option = this.createDiv('rarity-option', rarity); | |||
if (this.cardData.rarity === rarity) { | |||
option.classList.add('selected'); | |||
} | |||
option.onclick = () => { | |||
container.querySelectorAll('.rarity-option').forEach(o => o.classList.remove('selected')); | |||
option.classList.add('selected'); | |||
this.cardData.rarity = rarity; | |||
this.updatePreview(); | |||
}; | |||
container.appendChild(option); | |||
}); | }); | ||
return container; | |||
} | |||
// 创建神明选择 | |||
createGodSelect() { | |||
const container = this.createDiv('god-options'); | |||
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor']; | |||
gods.forEach(god => { | |||
const option = this.createDiv('god-option', god); | |||
if (this.cardData.god === god) { | |||
option.classList.add('selected'); | |||
} | |||
option.onclick = () => { | |||
container.querySelectorAll('.god-option').forEach(o => o.classList.remove('selected')); | |||
option.classList.add('selected'); | |||
this.cardData.god = god; | |||
this.updatePreview(); | |||
}; | |||
container.appendChild(option); | |||
}); | }); | ||
return container; | |||
} | |||
// 创建类型选择 | |||
createTypeSelect() { | |||
const container = this.createDiv('type-options'); | |||
const types = ['攻击', '技能', '强化']; | |||
types.forEach(type => { | |||
const option = this.createDiv('type-option', type); | |||
if (this.cardData.type === type) { | |||
option.classList.add('selected'); | |||
} | |||
option.onclick = () => { | |||
container.querySelectorAll('.type-option').forEach(o => o.classList.remove('selected')); | |||
option.classList.add('selected'); | |||
this.cardData.type = type; | |||
this.updatePreview(); | |||
}; | |||
container.appendChild(option); | |||
}); | }); | ||
return container; | |||
} | |||
// 渲染卡牌列表 | |||
renderCardLists() { | |||
const container = this.createDiv(); | |||
// 卡牌列表 | |||
const cardListTitle = this.createDiv('panel-title', '卡牌列表'); | |||
container.appendChild(cardListTitle); | |||
const cardListContainer = this.createDiv('list-container'); | |||
cardListContainer.id = 'card-list'; | |||
this.updateCardList(cardListContainer); | |||
container.appendChild(cardListContainer); | |||
// | return container; | ||
} | |||
// 更新卡牌列表 | |||
updateCardList(container) { | |||
container.innerHTML = ''; | |||
if (Object.keys(this.cards).length === 0) { | |||
container.appendChild(this.createDiv('list-empty', '暂无卡牌')); | |||
return; | return; | ||
} | } | ||
const | Object.keys(this.cards).forEach(name => { | ||
const card = this.cards[name]; | |||
this. | const item = this.createDiv('card-list-item'); | ||
const info = this.createDiv(); | |||
const nameDiv = this.createDiv('card-list-item-name', name); | |||
const detailDiv = this.createDiv('card-list-item-info', | |||
`${card.type || '攻击'} | AP:${card.ap || 0} | ${card.rarity || ''}` | |||
); | |||
info.appendChild(nameDiv); | |||
info.appendChild(detailDiv); | |||
const deleteBtn = this.createDiv('delete-btn', '删除'); | |||
deleteBtn.onclick = (e) => { | |||
e.stopPropagation(); | |||
if (confirm(`确定要删除卡牌"${name}"吗?`)) { | |||
delete this.cards[name]; | |||
this.updateCardList(container); | |||
this.updatePreview(); | |||
// 如果删除的是当前编辑的卡牌,清空表单 | |||
if (this.currentCard === name) { | |||
this.newCard(); | |||
} | |||
} | |||
}; | |||
item.appendChild(info); | |||
item.appendChild(deleteBtn); | |||
item.onclick = () => { | |||
this.loadCard(name); | |||
container.querySelectorAll('.card-list-item').forEach(i => i.classList.remove('active')); | |||
item.classList.add('active'); | |||
}; | |||
container.appendChild(item); | |||
}); | |||
} | |||
// 渲染预览 | |||
renderPreview() { | |||
const container = this.createDiv(); | |||
const title = this.createDiv('panel-title', 'Lua代码预览'); | |||
container.appendChild(title); | |||
const preview = this.createDiv('preview-code'); | |||
preview.id = 'code-preview'; | |||
container.appendChild(preview); | |||
this.updatePreview(); | this.updatePreview(); | ||
const | // 添加复制按钮 | ||
const copyBtn = this.createDiv('btn btn-primary', '复制代码'); | |||
this. | copyBtn.style.marginTop = '10px'; | ||
copyBtn.onclick = () => this.copyCode(); | |||
container.appendChild(copyBtn); | |||
} | |||
return container; | |||
} | |||
const | |||
if (! | // 更新预览 | ||
updatePreview() { | |||
const preview = document.getElementById('code-preview'); | |||
if (!preview) return; | |||
preview.textContent = this.generateLuaCode(); | |||
} | |||
// 生成Lua代码 | |||
generateLuaCode() { | |||
let code = 'local card = {}\n\n'; | |||
// | // 生成order | ||
if ( | if (this.defaultData.order.length > 0) { | ||
code += `card.order = { "${this.defaultData.order.join(',')}" }\n\n`; | |||
} | } | ||
// | // 生成info | ||
if ( | code += 'card.info = {\n'; | ||
if (this.defaultData.ego) { | |||
code += ` ego = "${this.defaultData.ego}",\n`; | |||
} | } | ||
code += '}\n\n'; | |||
// 生成各个卡牌 | |||
Object.keys(this.cards).forEach(name => { | |||
const card = this.cards[name]; | |||
code += this.generateCardCode(name, card); | |||
Object.keys(this. | |||
const card = this. | |||
}); | }); | ||
code += 'return card'; | |||
return code; | |||
} | |||
$ | |||
// 生成单个卡牌代码 | |||
generateCardCode(name, card) { | |||
let code = `card["${name}"] = {\n`; | |||
code += ' base = {\n'; | |||
// 基础属性 | |||
if (card.displayname) { | |||
code += ` displayname = "${card.displayname}",\n`; | |||
} | |||
if (card.art) { | |||
code += ` art = "${card.art}",\n`; | |||
} | |||
if (card.group) { | |||
code += ` group = "${card.group}",\n`; | |||
} | |||
if (card.rarity) { | |||
code += ` rarity = "${card.rarity}",\n`; | |||
} | |||
if (card.ap) { | |||
code += ` ap = ${isNaN(card.ap) ? `"${card.ap}"` : card.ap},\n`; | |||
} | |||
if (card.type) { | |||
code += ` type = "${card.type}",\n`; | |||
} | |||
if (card.dict) { | |||
code += ` dict = "${card.dict}",\n`; | |||
} | |||
if (card.desc_global) { | |||
const desc = card.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"'); | |||
code += ` desc_global = "${desc}",\n`; | |||
} | |||
if (card.sub) { | |||
code += ` sub = "${card.sub}",\n`; | |||
} | |||
if (card.isinspiration) { | |||
code += ` isinspiration = 1,\n`; | |||
} | |||
if (card.isgod_inspiration) { | |||
code += ` isgod_inspiration = 1,\n`; | |||
} | |||
code += ' },\n'; | |||
// 变体数据 | |||
if (card.isinspiration || card.isgod_inspiration) { | |||
code += ' var = {\n'; | |||
// 灵光一闪 | |||
if (card.isinspiration && card.inspirations && card.inspirations.length > 0) { | |||
code += ' inspiration = {\n'; | |||
if ( | card.inspirations.forEach(insp => { | ||
code += ' {\n'; | |||
} | if (insp.ap !== undefined) { | ||
code += ` ap = ${isNaN(insp.ap) ? `"${insp.ap}"` : insp.ap},\n`; | |||
} | |||
if (insp.type) { | |||
code += ` type = "${insp.type}",\n`; | |||
} | |||
if (insp.desc_global) { | |||
const desc = insp.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"'); | |||
code += ` desc_global = "${desc}",\n`; | |||
} | } | ||
} | code += ' },\n'; | ||
} | }); | ||
code += ' },\n'; | |||
} | |||
// 神光一闪 | |||
if (card.isgod_inspiration && card.god_inspirations) { | |||
if (card. | code += ' god_inspiration = {\n'; | ||
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor']; | |||
gods.forEach(god => { | |||
if (card. | if (card.god_inspirations[god] && card.god_inspirations[god].length > 0) { | ||
code += ` ${god} = {\n`; | |||
card.god_inspirations[god].forEach(insp => { | |||
code += ' {\n'; | |||
if (insp.ap !== undefined) { | |||
if ( | code += ` ap = ${isNaN(insp.ap) ? `"${insp.ap}"` : insp.ap},\n`; | ||
} | |||
} | if (insp.type) { | ||
code += ` type = "${insp.type}",\n`; | |||
} | |||
if (insp.desc_global) { | |||
const desc = insp.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"'); | |||
code += ` desc_global = "${desc}",\n`; | |||
} | } | ||
code += ' },\n'; | |||
}); | }); | ||
code += ' },\n'; | |||
} | } | ||
}); | |||
} | code += ' },\n'; | ||
} | } | ||
code += ' },\n'; | |||
}); | } | ||
code += '}\n\n'; | |||
return code; | |||
} | |||
// 保存卡牌 | |||
saveCard() { | |||
if (!this.cardData.name) { | |||
alert('请输入卡牌名称!'); | |||
return; | |||
} | |||
// 克隆当前卡牌数据 | |||
const cardToSave = JSON.parse(JSON.stringify(this.cardData)); | |||
// 保存到cards对象 | |||
this.cards[cardToSave.name] = cardToSave; | |||
this.currentCard = cardToSave.name; | |||
// 更新order | |||
if (!this.defaultData.order.includes(cardToSave.name)) { | |||
this.defaultData.order.push(cardToSave.name); | |||
} | |||
const | // 更新列表和预览 | ||
const listContainer = document.getElementById('card-list'); | |||
if (listContainer) { | |||
this.updateCardList(listContainer); | |||
} | |||
this.updatePreview(); | |||
alert('卡牌保存成功!'); | |||
} | |||
// 新建卡牌 | |||
newCard() { | |||
this.currentCard = null; | |||
this.cardData = { | |||
name: '', | |||
displayname: '', | |||
art: '', | |||
group: '', | |||
rarity: '', | |||
god: '', | |||
ap: '', | |||
type: '攻击', | |||
dict: '', | |||
desc_global: '', | |||
sub: '', | |||
isinspiration: false, | |||
isgod_inspiration: false, | |||
inspirations: [], | |||
god_inspirations: { | |||
circen: [], | |||
diallos: [], | |||
nihilum: [], | |||
secred: [], | |||
vitor: [] | |||
} | } | ||
}); | }; | ||
} | |||
// 重新渲染输入区 | |||
const leftPanel = document.querySelector('.card-input-panel'); | |||
// | if (leftPanel) { | ||
leftPanel.innerHTML = ''; | |||
leftPanel.appendChild(this.renderFighterSelect()); | |||
leftPanel.appendChild(this.renderDefaultInfo()); | |||
leftPanel.appendChild(this.renderCardInput()); | |||
} | |||
// 清除列表选中状态 | |||
const listContainer = document.getElementById('card-list'); | |||
if (listContainer) { | |||
listContainer.querySelectorAll('.card-list-item').forEach(i => i.classList.remove('active')); | |||
} | |||
} | |||
// 加载卡牌 | |||
loadCard(name) { | |||
if (!this.cards[name]) return; | |||
this.currentCard = name; | |||
this.cardData = JSON.parse(JSON.stringify(this.cards[name])); | |||
// 重新渲染输入区 | |||
const leftPanel = document.querySelector('.card-input-panel'); | |||
if (leftPanel) { | |||
leftPanel.innerHTML = ''; | |||
leftPanel.appendChild(this.renderFighterSelect()); | |||
leftPanel.appendChild(this.renderDefaultInfo()); | |||
leftPanel.appendChild(this.renderCardInput()); | |||
} | |||
} | |||
// 加载战斗员数据 | |||
async loadFighterData() { | |||
if (!this.currentFighter) return; | |||
try { | try { | ||
const api = new mw.Api(); | |||
const | const moduleName = `模块:卡牌/${this.currentFighter}`; | ||
const response = await api.get({ | |||
action: 'query', | |||
const | prop: 'revisions', | ||
// | titles: moduleName, | ||
rvprop: 'content', | |||
rvslots: 'main' | |||
}); | |||
const pages = response.query.pages; | |||
const page = Object.values(pages)[0]; | |||
if (page.revisions) { | |||
const content = page.revisions[0].slots.main['*']; | |||
this.parseLuaCode(content); | |||
// 重新渲染 | |||
this.render(); | |||
} | } | ||
} catch (error) { | |||
console.error('加载战斗员数据失败:', error); | |||
} | |||
} | |||
// 解析Lua代码(简化版) | |||
parseLuaCode(code) { | |||
// 这里应该实现完整的Lua代码解析 | |||
// 由于复杂度较高,这里只做基本的解析示例 | |||
// 解析order | |||
const orderMatch = code.match(/card\.order\s*=\s*{\s*"([^"]*)"\s*}/); | |||
if (orderMatch) { | |||
this.defaultData.order = orderMatch[1].split(',').map(s => s.trim()); | |||
} | |||
// 解析ego | |||
const egoMatch = code.match(/ego\s*=\s*"([^"]*)"/); | |||
if (egoMatch) { | |||
this.defaultData.ego = egoMatch[1]; | |||
} | } | ||
} | |||
// 这里应该继续解析各个卡牌的数据 | |||
// 由于Lua语法复杂,建议使用专门的Lua解析器 | |||
} | |||
// 保存到Mediawiki | |||
async saveToMediawiki() { | |||
if (!this.currentFighter) { | if (!this.currentFighter) { | ||
alert('请先选择战斗员!'); | |||
return; | return; | ||
} | } | ||
const code = this.generateLuaCode(); | |||
const moduleName = `模块:卡牌/${this.currentFighter}`; | const moduleName = `模块:卡牌/${this.currentFighter}`; | ||
new mw.Api().postWithToken('csrf', { | try { | ||
const api = new mw.Api(); | |||
await api.postWithToken('csrf', { | |||
action: 'edit', | |||
title: moduleName, | |||
text: code, | |||
summary: '通过卡牌管理器更新', | |||
contentmodel: 'Scribunto' | |||
}). | }); | ||
alert('保存成功!'); | |||
} catch (error) { | |||
console.error('保存失败:', error); | |||
alert('保存失败:' + error); | |||
} | |||
} | |||
// 复制代码 | |||
copyCode() { | |||
const code = this.generateLuaCode(); | |||
// 创建临时文本域 | |||
const textarea = document.createElement('textarea'); | |||
textarea.value = code; | |||
textarea.style.position = 'fixed'; | |||
textarea.style.opacity = '0'; | |||
document.body.appendChild(textarea); | |||
textarea.select(); | |||
document.execCommand('copy'); | |||
document.body.removeChild(textarea); | |||
alert('代码已复制到剪贴板!'); | |||
} | } | ||
// 绑定事件 | |||
bindEvents() { | |||
// 可以在这里添加全局事件监听 | |||
// | |||
} | } | ||
} | |||
// 等待页面加载完成 | |||
if (mw.config.get('wgPageName') === 'Special:Card' || | |||
window.location.search.includes('cardmanager=1')) { | |||
mw.loader.using(['mediawiki.api']).then(() => { | |||
const manager = new CardManager(); | |||
manager.init(); | |||
}); | |||
} | |||
// 添加导航链接(可选) | |||
mw.loader.using(['mediawiki.util']).then(() => { | |||
mw.util.addPortletLink( | |||
'p-tb', | |||
'?cardmanager=1', | |||
'卡牌管理器', | |||
't-cardmanager', | |||
'打开卡牌管理器' | |||
); | |||
}); | }); | ||
})(); | })(); | ||
2025年10月23日 (四) 16:05的版本
(function() {
'use strict';
// 加载CSS
mw.loader.load('/index.php?title=Mediawiki:Card.css&action=raw&ctype=text/css', 'text/css');
// 卡牌管理器类
class CardManager {
constructor() {
this.currentFighter = '';
this.cards = {};
this.currentCard = null;
this.fighters = [];
// 默认数据
this.defaultData = {
order: [],
ego: ''
};
// 当前编辑的卡牌数据
this.cardData = {
name: '',
displayname: '',
art: '',
group: '',
rarity: '',
god: '',
ap: '',
type: '攻击',
dict: '',
desc_global: '',
sub: '',
isinspiration: false,
isgod_inspiration: false,
inspirations: [],
god_inspirations: {
circen: [],
diallos: [],
nihilum: [],
secred: [],
vitor: []
}
};
}
// 初始化
async init() {
await this.loadFighters();
this.render();
this.bindEvents();
}
// 加载战斗员列表
async loadFighters() {
try {
const api = new mw.Api();
const response = await api.get({
action: 'query',
list: 'categorymembers',
cmtitle: 'Category:战斗员',
cmlimit: 500
});
this.fighters = response.query.categorymembers.map(page => page.title);
} catch (error) {
console.error('加载战斗员列表失败:', error);
this.fighters = [];
}
}
// 渲染主界面
render() {
const container = this.createDiv('card-manager');
// 左侧面板
const leftPanel = this.createDiv('card-input-panel');
leftPanel.appendChild(this.renderFighterSelect());
leftPanel.appendChild(this.renderDefaultInfo());
leftPanel.appendChild(this.renderCardInput());
// 中间面板
const middlePanel = this.createDiv('card-list-panel');
middlePanel.appendChild(this.renderCardLists());
// 右侧面板
const rightPanel = this.createDiv('card-preview-panel');
rightPanel.appendChild(this.renderPreview());
container.appendChild(leftPanel);
container.appendChild(middlePanel);
container.appendChild(rightPanel);
// 添加到页面
const content = document.getElementById('mw-content-text');
if (content) {
content.innerHTML = '';
content.appendChild(container);
}
}
// 创建div元素
createDiv(className, text = '') {
const div = document.createElement('div');
if (className) div.className = className;
if (text) div.textContent = text;
return div;
}
// 渲染战斗员选择
renderFighterSelect() {
const container = this.createDiv('form-group');
const label = this.createDiv('form-label', '当前战斗员');
container.appendChild(label);
const select = this.createCustomSelect(
this.fighters,
this.currentFighter,
(value) => {
this.currentFighter = value;
this.loadFighterData();
}
);
container.appendChild(select);
return container;
}
// 渲染默认信息区
renderDefaultInfo() {
const container = this.createDiv('default-info-section');
const title = this.createDiv('section-title', '默认信息');
container.appendChild(title);
// 卡牌顺序
const orderGroup = this.createDiv('form-group');
const orderLabel = this.createDiv('form-label', '卡牌顺序(card.order)');
const orderInput = this.createInput('text', this.defaultData.order.join(','), (value) => {
this.defaultData.order = value.split(',').map(s => s.trim()).filter(s => s);
this.updatePreview();
});
orderGroup.appendChild(orderLabel);
orderGroup.appendChild(orderInput);
container.appendChild(orderGroup);
// 属性
const egoGroup = this.createDiv('form-group');
const egoLabel = this.createDiv('form-label', '属性(ego)');
const egoInput = this.createInput('text', this.defaultData.ego, (value) => {
this.defaultData.ego = value;
this.updatePreview();
});
egoGroup.appendChild(egoLabel);
egoGroup.appendChild(egoInput);
container.appendChild(egoGroup);
return container;
}
// 渲染卡牌输入区
renderCardInput() {
const container = this.createDiv();
const title = this.createDiv('section-title', '卡牌数据');
container.appendChild(title);
// 卡牌名称
container.appendChild(this.createFormGroup('卡牌名称',
this.createInput('text', this.cardData.name, (value) => {
this.cardData.name = value;
this.updatePreview();
})
));
// 显示名称
container.appendChild(this.createFormGroup('显示名称(displayname)',
this.createInput('text', this.cardData.displayname, (value) => {
this.cardData.displayname = value;
this.updatePreview();
})
));
// 图片
container.appendChild(this.createFormGroup('图片(art)',
this.createInput('text', this.cardData.art, (value) => {
this.cardData.art = value;
this.updatePreview();
})
));
// 卡组
container.appendChild(this.createFormGroup('卡组(group)',
this.createGroupSelect()
));
// 稀有度
container.appendChild(this.createFormGroup('稀有度(rarity)',
this.createRaritySelect()
));
// 神明
container.appendChild(this.createFormGroup('神明',
this.createGodSelect()
));
// AP
container.appendChild(this.createFormGroup('AP',
this.createInput('text', this.cardData.ap, (value) => {
this.cardData.ap = value;
this.updatePreview();
})
));
// 卡牌类型
container.appendChild(this.createFormGroup('卡牌类型(type)',
this.createTypeSelect()
));
// 机制
container.appendChild(this.createFormGroup('机制(dict)',
this.createInput('text', this.cardData.dict, (value) => {
this.cardData.dict = value;
this.updatePreview();
})
));
// 描述
const descGroup = this.createDiv('form-group');
const descLabel = this.createDiv('form-label', '描述(desc_global)');
descGroup.appendChild(descLabel);
descGroup.appendChild(this.createFormatButtons());
descGroup.appendChild(this.createTextarea(this.cardData.desc_global, (value) => {
this.cardData.desc_global = value;
this.updatePreview();
}));
container.appendChild(descGroup);
// 衍生卡牌
container.appendChild(this.createFormGroup('衍生卡牌(sub)',
this.createInput('text', this.cardData.sub, (value) => {
this.cardData.sub = value;
this.updatePreview();
})
));
// 是否存在灵光一闪
container.appendChild(this.createFormGroup('是否存在灵光一闪',
this.createCheckbox(this.cardData.isinspiration, (checked) => {
this.cardData.isinspiration = checked;
this.updatePreview();
})
));
// 是否存在神光一闪
container.appendChild(this.createFormGroup('是否存在神光一闪',
this.createCheckbox(this.cardData.isgod_inspiration, (checked) => {
this.cardData.isgod_inspiration = checked;
this.updatePreview();
})
));
// 按钮组
const btnGroup = this.createDiv('btn-group');
const saveBtn = this.createDiv('btn btn-primary', '保存卡牌');
saveBtn.onclick = () => this.saveCard();
const newBtn = this.createDiv('btn btn-success', '新建卡牌');
newBtn.onclick = () => this.newCard();
btnGroup.appendChild(saveBtn);
btnGroup.appendChild(newBtn);
container.appendChild(btnGroup);
return container;
}
// 创建表单组
createFormGroup(label, input) {
const group = this.createDiv('form-group');
const labelDiv = this.createDiv('form-label', label);
group.appendChild(labelDiv);
group.appendChild(input);
return group;
}
// 创建自定义下拉框
createCustomSelect(options, selected, onChange) {
const container = this.createDiv('custom-select');
const display = this.createDiv('select-display', selected || '请选择...');
const arrow = this.createDiv('select-arrow', '▼');
display.appendChild(arrow);
const dropdown = this.createDiv('select-dropdown');
options.forEach(option => {
const optionDiv = this.createDiv('select-option', option);
if (option === selected) {
optionDiv.classList.add('selected');
}
optionDiv.onclick = () => {
display.childNodes[0].textContent = option;
dropdown.querySelectorAll('.select-option').forEach(o => o.classList.remove('selected'));
optionDiv.classList.add('selected');
dropdown.classList.remove('active');
onChange(option);
};
dropdown.appendChild(optionDiv);
});
display.onclick = () => {
dropdown.classList.toggle('active');
};
// 点击外部关闭
document.addEventListener('click', (e) => {
if (!container.contains(e.target)) {
dropdown.classList.remove('active');
}
});
container.appendChild(display);
container.appendChild(dropdown);
return container;
}
// 创建输入框
createInput(type, value, onChange) {
const input = this.createDiv('custom-input');
input.contentEditable = true;
input.textContent = value;
input.oninput = () => onChange(input.textContent);
return input;
}
// 创建文本域
createTextarea(value, onChange) {
const textarea = this.createDiv('custom-textarea');
textarea.contentEditable = true;
textarea.innerHTML = value.replace(/<br>/g, '\n');
textarea.oninput = () => onChange(textarea.textContent);
return textarea;
}
// 创建格式化按钮
createFormatButtons() {
const container = this.createDiv('format-buttons');
const buttons = [
{ label: '蓝色文本', class: 'blue', template: '{{文本|蓝|%s}}' },
{ label: '绿色文本', class: 'green', template: '{{文本|绿|%s}}' },
{ label: '绿色描边', class: 'green', template: '{{描边|绿|%s}}' },
{ label: '词典', class: '', template: '{{词典|%s}}' },
{ label: '换行', class: '', template: '<br>' }
];
buttons.forEach(btn => {
const button = this.createDiv(`format-btn ${btn.class}`, btn.label);
button.onclick = () => {
const textarea = button.parentElement.nextElementSibling;
this.insertTemplate(textarea, btn.template);
};
container.appendChild(button);
});
return container;
}
// 插入模板
insertTemplate(textarea, template) {
const selection = window.getSelection();
const selectedText = selection.toString();
if (template === '<br>') {
const text = textarea.textContent;
const pos = this.getCaretPosition(textarea);
textarea.textContent = text.slice(0, pos) + '\n' + text.slice(pos);
} else {
const result = template.replace('%s', selectedText || '选择文字');
if (selectedText) {
document.execCommand('insertText', false, result);
} else {
const text = textarea.textContent;
const pos = this.getCaretPosition(textarea);
textarea.textContent = text.slice(0, pos) + result + text.slice(pos);
}
}
textarea.dispatchEvent(new Event('input'));
}
// 获取光标位置
getCaretPosition(element) {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
return preCaretRange.toString().length;
}
return 0;
}
// 创建复选框
createCheckbox(checked, onChange) {
const container = this.createDiv('checkbox-group');
const checkbox = this.createDiv('custom-checkbox');
if (checked) checkbox.classList.add('checked');
const label = this.createDiv('', '是');
checkbox.onclick = () => {
checkbox.classList.toggle('checked');
const isChecked = checkbox.classList.contains('checked');
label.textContent = isChecked ? '是' : '否';
onChange(isChecked);
};
container.appendChild(checkbox);
container.appendChild(label);
return container;
}
// 创建卡组选择
createGroupSelect() {
const container = this.createDiv('group-options');
const groups = ['自我意识技能', '起始卡牌', '独特卡牌', '灵光一闪', '神光一闪'];
groups.forEach(group => {
const option = this.createDiv('group-option', group);
if (this.cardData.group === group) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.group-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.group = group;
this.updatePreview();
};
container.appendChild(option);
});
return container;
}
// 创建稀有度选择
createRaritySelect() {
const container = this.createDiv('rarity-options');
const rarities = ['白', '蓝', '橙', '彩'];
rarities.forEach(rarity => {
const option = this.createDiv('rarity-option', rarity);
if (this.cardData.rarity === rarity) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.rarity-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.rarity = rarity;
this.updatePreview();
};
container.appendChild(option);
});
return container;
}
// 创建神明选择
createGodSelect() {
const container = this.createDiv('god-options');
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
gods.forEach(god => {
const option = this.createDiv('god-option', god);
if (this.cardData.god === god) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.god-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.god = god;
this.updatePreview();
};
container.appendChild(option);
});
return container;
}
// 创建类型选择
createTypeSelect() {
const container = this.createDiv('type-options');
const types = ['攻击', '技能', '强化'];
types.forEach(type => {
const option = this.createDiv('type-option', type);
if (this.cardData.type === type) {
option.classList.add('selected');
}
option.onclick = () => {
container.querySelectorAll('.type-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
this.cardData.type = type;
this.updatePreview();
};
container.appendChild(option);
});
return container;
}
// 渲染卡牌列表
renderCardLists() {
const container = this.createDiv();
// 卡牌列表
const cardListTitle = this.createDiv('panel-title', '卡牌列表');
container.appendChild(cardListTitle);
const cardListContainer = this.createDiv('list-container');
cardListContainer.id = 'card-list';
this.updateCardList(cardListContainer);
container.appendChild(cardListContainer);
return container;
}
// 更新卡牌列表
updateCardList(container) {
container.innerHTML = '';
if (Object.keys(this.cards).length === 0) {
container.appendChild(this.createDiv('list-empty', '暂无卡牌'));
return;
}
Object.keys(this.cards).forEach(name => {
const card = this.cards[name];
const item = this.createDiv('card-list-item');
const info = this.createDiv();
const nameDiv = this.createDiv('card-list-item-name', name);
const detailDiv = this.createDiv('card-list-item-info',
`${card.type || '攻击'} | AP:${card.ap || 0} | ${card.rarity || ''}`
);
info.appendChild(nameDiv);
info.appendChild(detailDiv);
const deleteBtn = this.createDiv('delete-btn', '删除');
deleteBtn.onclick = (e) => {
e.stopPropagation();
if (confirm(`确定要删除卡牌"${name}"吗?`)) {
delete this.cards[name];
this.updateCardList(container);
this.updatePreview();
// 如果删除的是当前编辑的卡牌,清空表单
if (this.currentCard === name) {
this.newCard();
}
}
};
item.appendChild(info);
item.appendChild(deleteBtn);
item.onclick = () => {
this.loadCard(name);
container.querySelectorAll('.card-list-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
};
container.appendChild(item);
});
}
// 渲染预览
renderPreview() {
const container = this.createDiv();
const title = this.createDiv('panel-title', 'Lua代码预览');
container.appendChild(title);
const preview = this.createDiv('preview-code');
preview.id = 'code-preview';
container.appendChild(preview);
this.updatePreview();
// 添加复制按钮
const copyBtn = this.createDiv('btn btn-primary', '复制代码');
copyBtn.style.marginTop = '10px';
copyBtn.onclick = () => this.copyCode();
container.appendChild(copyBtn);
return container;
}
// 更新预览
updatePreview() {
const preview = document.getElementById('code-preview');
if (!preview) return;
preview.textContent = this.generateLuaCode();
}
// 生成Lua代码
generateLuaCode() {
let code = 'local card = {}\n\n';
// 生成order
if (this.defaultData.order.length > 0) {
code += `card.order = { "${this.defaultData.order.join(',')}" }\n\n`;
}
// 生成info
code += 'card.info = {\n';
if (this.defaultData.ego) {
code += ` ego = "${this.defaultData.ego}",\n`;
}
code += '}\n\n';
// 生成各个卡牌
Object.keys(this.cards).forEach(name => {
const card = this.cards[name];
code += this.generateCardCode(name, card);
});
code += 'return card';
return code;
}
// 生成单个卡牌代码
generateCardCode(name, card) {
let code = `card["${name}"] = {\n`;
code += ' base = {\n';
// 基础属性
if (card.displayname) {
code += ` displayname = "${card.displayname}",\n`;
}
if (card.art) {
code += ` art = "${card.art}",\n`;
}
if (card.group) {
code += ` group = "${card.group}",\n`;
}
if (card.rarity) {
code += ` rarity = "${card.rarity}",\n`;
}
if (card.ap) {
code += ` ap = ${isNaN(card.ap) ? `"${card.ap}"` : card.ap},\n`;
}
if (card.type) {
code += ` type = "${card.type}",\n`;
}
if (card.dict) {
code += ` dict = "${card.dict}",\n`;
}
if (card.desc_global) {
const desc = card.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"');
code += ` desc_global = "${desc}",\n`;
}
if (card.sub) {
code += ` sub = "${card.sub}",\n`;
}
if (card.isinspiration) {
code += ` isinspiration = 1,\n`;
}
if (card.isgod_inspiration) {
code += ` isgod_inspiration = 1,\n`;
}
code += ' },\n';
// 变体数据
if (card.isinspiration || card.isgod_inspiration) {
code += ' var = {\n';
// 灵光一闪
if (card.isinspiration && card.inspirations && card.inspirations.length > 0) {
code += ' inspiration = {\n';
card.inspirations.forEach(insp => {
code += ' {\n';
if (insp.ap !== undefined) {
code += ` ap = ${isNaN(insp.ap) ? `"${insp.ap}"` : insp.ap},\n`;
}
if (insp.type) {
code += ` type = "${insp.type}",\n`;
}
if (insp.desc_global) {
const desc = insp.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"');
code += ` desc_global = "${desc}",\n`;
}
code += ' },\n';
});
code += ' },\n';
}
// 神光一闪
if (card.isgod_inspiration && card.god_inspirations) {
code += ' god_inspiration = {\n';
const gods = ['circen', 'diallos', 'nihilum', 'secred', 'vitor'];
gods.forEach(god => {
if (card.god_inspirations[god] && card.god_inspirations[god].length > 0) {
code += ` ${god} = {\n`;
card.god_inspirations[god].forEach(insp => {
code += ' {\n';
if (insp.ap !== undefined) {
code += ` ap = ${isNaN(insp.ap) ? `"${insp.ap}"` : insp.ap},\n`;
}
if (insp.type) {
code += ` type = "${insp.type}",\n`;
}
if (insp.desc_global) {
const desc = insp.desc_global.replace(/\n/g, '<br>').replace(/"/g, '\\"');
code += ` desc_global = "${desc}",\n`;
}
code += ' },\n';
});
code += ' },\n';
}
});
code += ' },\n';
}
code += ' },\n';
}
code += '}\n\n';
return code;
}
// 保存卡牌
saveCard() {
if (!this.cardData.name) {
alert('请输入卡牌名称!');
return;
}
// 克隆当前卡牌数据
const cardToSave = JSON.parse(JSON.stringify(this.cardData));
// 保存到cards对象
this.cards[cardToSave.name] = cardToSave;
this.currentCard = cardToSave.name;
// 更新order
if (!this.defaultData.order.includes(cardToSave.name)) {
this.defaultData.order.push(cardToSave.name);
}
// 更新列表和预览
const listContainer = document.getElementById('card-list');
if (listContainer) {
this.updateCardList(listContainer);
}
this.updatePreview();
alert('卡牌保存成功!');
}
// 新建卡牌
newCard() {
this.currentCard = null;
this.cardData = {
name: '',
displayname: '',
art: '',
group: '',
rarity: '',
god: '',
ap: '',
type: '攻击',
dict: '',
desc_global: '',
sub: '',
isinspiration: false,
isgod_inspiration: false,
inspirations: [],
god_inspirations: {
circen: [],
diallos: [],
nihilum: [],
secred: [],
vitor: []
}
};
// 重新渲染输入区
const leftPanel = document.querySelector('.card-input-panel');
if (leftPanel) {
leftPanel.innerHTML = '';
leftPanel.appendChild(this.renderFighterSelect());
leftPanel.appendChild(this.renderDefaultInfo());
leftPanel.appendChild(this.renderCardInput());
}
// 清除列表选中状态
const listContainer = document.getElementById('card-list');
if (listContainer) {
listContainer.querySelectorAll('.card-list-item').forEach(i => i.classList.remove('active'));
}
}
// 加载卡牌
loadCard(name) {
if (!this.cards[name]) return;
this.currentCard = name;
this.cardData = JSON.parse(JSON.stringify(this.cards[name]));
// 重新渲染输入区
const leftPanel = document.querySelector('.card-input-panel');
if (leftPanel) {
leftPanel.innerHTML = '';
leftPanel.appendChild(this.renderFighterSelect());
leftPanel.appendChild(this.renderDefaultInfo());
leftPanel.appendChild(this.renderCardInput());
}
}
// 加载战斗员数据
async loadFighterData() {
if (!this.currentFighter) return;
try {
const api = new mw.Api();
const moduleName = `模块:卡牌/${this.currentFighter}`;
const response = await api.get({
action: 'query',
prop: 'revisions',
titles: moduleName,
rvprop: 'content',
rvslots: 'main'
});
const pages = response.query.pages;
const page = Object.values(pages)[0];
if (page.revisions) {
const content = page.revisions[0].slots.main['*'];
this.parseLuaCode(content);
// 重新渲染
this.render();
}
} catch (error) {
console.error('加载战斗员数据失败:', error);
}
}
// 解析Lua代码(简化版)
parseLuaCode(code) {
// 这里应该实现完整的Lua代码解析
// 由于复杂度较高,这里只做基本的解析示例
// 解析order
const orderMatch = code.match(/card\.order\s*=\s*{\s*"([^"]*)"\s*}/);
if (orderMatch) {
this.defaultData.order = orderMatch[1].split(',').map(s => s.trim());
}
// 解析ego
const egoMatch = code.match(/ego\s*=\s*"([^"]*)"/);
if (egoMatch) {
this.defaultData.ego = egoMatch[1];
}
// 这里应该继续解析各个卡牌的数据
// 由于Lua语法复杂,建议使用专门的Lua解析器
}
// 保存到Mediawiki
async saveToMediawiki() {
if (!this.currentFighter) {
alert('请先选择战斗员!');
return;
}
const code = this.generateLuaCode();
const moduleName = `模块:卡牌/${this.currentFighter}`;
try {
const api = new mw.Api();
await api.postWithToken('csrf', {
action: 'edit',
title: moduleName,
text: code,
summary: '通过卡牌管理器更新',
contentmodel: 'Scribunto'
});
alert('保存成功!');
} catch (error) {
console.error('保存失败:', error);
alert('保存失败:' + error);
}
}
// 复制代码
copyCode() {
const code = this.generateLuaCode();
// 创建临时文本域
const textarea = document.createElement('textarea');
textarea.value = code;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
alert('代码已复制到剪贴板!');
}
// 绑定事件
bindEvents() {
// 可以在这里添加全局事件监听
}
}
// 等待页面加载完成
if (mw.config.get('wgPageName') === 'Special:Card' ||
window.location.search.includes('cardmanager=1')) {
mw.loader.using(['mediawiki.api']).then(() => {
const manager = new CardManager();
manager.init();
});
}
// 添加导航链接(可选)
mw.loader.using(['mediawiki.util']).then(() => {
mw.util.addPortletLink(
'p-tb',
'?cardmanager=1',
'卡牌管理器',
't-cardmanager',
'打开卡牌管理器'
);
});
})();