卡厄思
梦
境
菜单
首页
回到首页
WIKI工具
全站样式
全站JS
修改导航栏
测试
沙盒
可视化管理器
战斗员管理器
卡牌管理器
伙伴管理器
装备管理器
词典管理器
图鉴
战斗员
伙伴
装备
怪物卡牌
中立卡牌
词典
小工具
配队模拟器
节奏榜生成器
搜索
链入页面
相关更改
特殊页面
页面信息
最近更改
登录
MediaWiki
查看“︁Card.js”︁的源代码
←
MediaWiki:Card.js
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
此页面为本wiki上的软件提供界面文本,并受到保护以防止滥用。 如欲修改所有wiki的翻译,请访问
translatewiki.net
上的MediaWiki本地化项目。
您无权编辑此JavaScript页面,因为编辑此页面可能会影响所有访问者。
您可以查看和复制此页面的源代码。
(function() { 'use strict'; // 加载CSS mw.loader.load('/index.php?title=Mediawiki:Card.css&action=raw&ctype=text/css', 'text/css'); // 状态管理 const state = { currentFighter: '', fighters: [], cards: [], currentCard: null, defaultInfo: { order: '', ego: '' }, editMode: 'base', currentGod: 'circen' }; // 卡牌数据结构 function createEmptyCard() { return { name: '', base: { displayname: '', art: '', group: '', rarity: '', god: '', ap: '', type: '', dict: '', desc_global: '', sub: '', isinspiration: 0, isgod_inspiration: 0 }, var: { inspiration: [], god_inspiration: { circen: [], diallos: [], nihilum: [], secred: [], vitor: [] } } }; } // 创建元素辅助函数 function createElement(tag, className, attributes = {}) { const el = document.createElement(tag); if (className) el.className = className; Object.entries(attributes).forEach(([key, value]) => { if (key === 'textContent') { el.textContent = value; } else if (key.startsWith('on')) { el.addEventListener(key.substring(2).toLowerCase(), value); } else { el.setAttribute(key, value); } }); return el; } // 创建自定义下拉选择器 function createSelect(options, selectedValue, onChange) { const select = createElement('div', 'form-select'); select.textContent = selectedValue || options[0] || '请选择'; select.setAttribute('tabindex', '0'); const dropdown = createElement('div', 'dropdown-menu'); options.forEach(option => { const item = createElement('div', 'dropdown-item'); item.textContent = option; item.addEventListener('click', () => { select.textContent = option; dropdown.style.display = 'none'; if (onChange) onChange(option); }); dropdown.appendChild(item); }); select.addEventListener('click', (e) => { e.stopPropagation(); dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none'; const rect = select.getBoundingClientRect(); dropdown.style.top = rect.bottom + 'px'; dropdown.style.left = rect.left + 'px'; dropdown.style.width = rect.width + 'px'; }); document.addEventListener('click', () => { dropdown.style.display = 'none'; }); const wrapper = createElement('div'); wrapper.style.position = 'relative'; wrapper.appendChild(select); document.body.appendChild(dropdown); return { wrapper, select, dropdown }; } // 创建自定义输入框 function createInput(type, value, onChange, placeholder = '') { const input = createElement('div', 'form-input'); input.setAttribute('contenteditable', 'true'); input.textContent = value || ''; input.setAttribute('data-placeholder', placeholder); input.style.minHeight = type === 'textarea' ? '100px' : 'auto'; if (!value) { input.style.color = '#999'; input.textContent = placeholder; } input.addEventListener('focus', () => { if (input.textContent === placeholder) { input.textContent = ''; input.style.color = '#333'; } }); input.addEventListener('blur', () => { if (!input.textContent.trim()) { input.textContent = placeholder; input.style.color = '#999'; } if (onChange) onChange(input.textContent === placeholder ? '' : input.textContent); }); input.addEventListener('input', () => { if (onChange && input.textContent !== placeholder) { onChange(input.textContent); } }); return input; } // 创建复选框 function createCheckbox(checked, onChange) { const wrapper = createElement('div', 'checkbox-wrapper'); const checkbox = createElement('div', checked ? 'checkbox checked' : 'checkbox'); const label = createElement('div', ''); label.textContent = '是'; wrapper.addEventListener('click', () => { const newChecked = !checkbox.classList.contains('checked'); if (newChecked) { checkbox.classList.add('checked'); } else { checkbox.classList.remove('checked'); } if (onChange) onChange(newChecked ? 1 : 0); }); wrapper.appendChild(checkbox); wrapper.appendChild(label); return wrapper; } // 创建表单组 function createFormGroup(label, control) { const group = createElement('div', 'form-group'); const labelEl = createElement('div', 'form-label'); labelEl.textContent = label; group.appendChild(labelEl); group.appendChild(control); return group; } // 创建按钮 function createButton(text, className, onClick) { const btn = createElement('div', 'btn ' + className); btn.textContent = text; btn.addEventListener('click', onClick); return btn; } // 文本插入辅助函数 function insertTextAtCursor(element, text, wrap = false) { element.focus(); const selection = window.getSelection(); if (wrap && selection.rangeCount > 0) { const range = selection.getRangeAt(0); const selectedText = range.toString(); if (selectedText) { range.deleteContents(); const textNode = document.createTextNode(text.replace('选择文字', selectedText)); range.insertNode(textNode); return; } } document.execCommand('insertText', false, text); } // 辅助函数:提取数值 function extractNumberValue(text, key) { const pattern = new RegExp(key + '\\s*=\\s*(\\d+)', 'm'); const match = text.match(pattern); return match ? parseInt(match[1]) : 0; } // 辅助函数:提取任意值(包括X、Ø等) function extractValue(text, key) { const pattern = new RegExp(key + '\\s*=\\s*([^,\\n]+)', 'm'); const match = text.match(pattern); if (match) { let value = match[1].trim(); // 移除引号 value = value.replace(/^["']|["']$/g, ''); return value; } return ''; } // 解析灵光一闪/神光一闪列表(修复版) function parseInspirationList(content) { const inspirations = []; // 使用更精确的解析方法 let depth = 0; let currentBlock = ''; let inString = false; let escapeNext = false; let startNewBlock = false; for (let i = 0; i < content.length; i++) { const char = content[i]; const prevChar = i > 0 ? content[i - 1] : ''; // 处理转义字符 if (escapeNext) { if (depth > 0) currentBlock += char; escapeNext = false; continue; } if (char === '\\') { escapeNext = true; if (depth > 0) currentBlock += char; continue; } // 处理字符串 if (char === '"' && !escapeNext) { inString = !inString; if (depth > 0) currentBlock += char; continue; } if (inString) { if (depth > 0) currentBlock += char; continue; } // 处理括号 if (char === '{') { if (depth === 0) { startNewBlock = true; } else { currentBlock += char; } depth++; } else if (char === '}') { depth--; if (depth === 0 && startNewBlock) { // 解析完整的块 if (currentBlock.trim()) { const inspiration = parseInspirationBlock(currentBlock); if (Object.keys(inspiration).length > 0) { inspirations.push(inspiration); } } currentBlock = ''; startNewBlock = false; } else if (depth > 0) { currentBlock += char; } } else if (depth > 0) { currentBlock += char; } } return inspirations; } // 解析单个灵光块 function parseInspirationBlock(blockContent) { const inspiration = {}; // 解析 ap const apPattern = /ap\s*=\s*(?:"([^"]*)"|'([^']*)'|(\d+)|([^,\n}\s]+))/; const apMatch = blockContent.match(apPattern); if (apMatch) { const apValue = apMatch[1] || apMatch[2] || apMatch[3] || apMatch[4]; if (apValue !== undefined && apValue !== '') { inspiration.ap = apValue.trim(); } } // 解析 type const typePattern = /type\s*=\s*"([^"]*)"/; const typeMatch = blockContent.match(typePattern); if (typeMatch && typeMatch[1]) { inspiration.type = typeMatch[1]; } // 解析 desc_global const descPattern = /desc_global\s*=\s*"((?:[^"\\]|\\.)*)"/; const descMatch = blockContent.match(descPattern); if (descMatch && descMatch[1]) { inspiration.desc_global = descMatch[1].replace(/\\"/g, '"').replace(/\\n/g, '\n'); } // 解析 dict const dictPattern = /dict\s*=\s*"((?:[^"\\]|\\.)*)"/; const dictMatch = blockContent.match(dictPattern); if (dictMatch && dictMatch[1]) { inspiration.dict = dictMatch[1].replace(/\\"/g, '"'); } return inspiration; } // 解析Lua代码(修复版) function parseLuaCode(luaText) { const cards = []; const defaultInfo = { order: '', ego: '' }; try { // 解析 card.order const orderMatch = luaText.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/); if (orderMatch) { defaultInfo.order = orderMatch[1]; } // 解析 card.info.ego const egoMatch = luaText.match(/card\.info\s*=\s*\{[^}]*ego\s*=\s*"([^"]+)"/); if (egoMatch) { defaultInfo.ego = egoMatch[1]; } // 使用更精确的方法解析每张卡牌 const lines = luaText.split('\n'); let currentCard = null; let inCard = false; let inBase = false; let inVar = false; let inInspiration = false; let inGodInspiration = false; let currentGod = null; let braceDepth = 0; let baseContent = ''; let inspirationContent = ''; let godInspirationContent = ''; let godContent = ''; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const trimmed = line.trim(); // 检测新卡牌开始 const cardStartMatch = line.match(/^card\["([^"]+)"\]\s*=\s*\{/); if (cardStartMatch) { inCard = true; currentCard = createEmptyCard(); currentCard.name = cardStartMatch[1]; braceDepth = 1; continue; } if (!inCard) continue; // 计算括号深度 for (let char of line) { if (char === '{') braceDepth++; if (char === '}') braceDepth--; } // 检测 base 开始 if (trimmed.startsWith('base = {')) { inBase = true; baseContent = ''; continue; } // 收集 base 内容 if (inBase) { if (trimmed === '},') { inBase = false; // 解析 base 内容 parseBaseContent(currentCard, baseContent); } else { baseContent += line + '\n'; } continue; } // 检测 var 开始 if (trimmed.startsWith('var = {')) { inVar = true; continue; } // 在 var 内部检测 inspiration if (inVar && trimmed.startsWith('inspiration = {')) { inInspiration = true; inspirationContent = ''; continue; } // 收集 inspiration 内容 if (inInspiration) { if (trimmed === '},') { inInspiration = false; currentCard.var.inspiration = parseInspirationList(inspirationContent); } else { inspirationContent += line + '\n'; } continue; } // 检测 god_inspiration 开始 if (inVar && trimmed.startsWith('god_inspiration = {')) { inGodInspiration = true; godInspirationContent = ''; continue; } // 在 god_inspiration 内部 if (inGodInspiration) { if (trimmed === '},') { inGodInspiration = false; inVar = false; } else { // 检测单个神明 const godMatch = trimmed.match(/^(circen|diallos|nihilum|secred|vitor)\s*=\s*\{/); if (godMatch) { if (currentGod && godContent) { // 保存前一个神明的数据 currentCard.var.god_inspiration[currentGod] = parseInspirationList(godContent); } currentGod = godMatch[1]; godContent = ''; } else if (trimmed === '},') { // 当前神明结束 if (currentGod && godContent) { currentCard.var.god_inspiration[currentGod] = parseInspirationList(godContent); currentGod = null; godContent = ''; } } else if (currentGod) { godContent += line + '\n'; } } continue; } // 检测卡牌结束 if (braceDepth === 0 && inCard) { cards.push(currentCard); currentCard = null; inCard = false; inBase = false; inVar = false; inInspiration = false; inGodInspiration = false; baseContent = ''; inspirationContent = ''; godContent = ''; currentGod = null; } } // 处理最后一张卡牌 if (currentCard) { cards.push(currentCard); } } catch (error) { console.error('解析Lua代码失败:', error); console.error('错误堆栈:', error.stack); } return { cards, defaultInfo }; } // 解析 base 内容 function parseBaseContent(card, content) { card.base.displayname = extractStringValue(content, 'displayname') || ''; card.base.art = extractStringValue(content, 'art') || ''; card.base.group = extractStringValue(content, 'group') || ''; card.base.rarity = extractStringValue(content, 'rarity') || ''; card.base.god = extractStringValue(content, 'god') || ''; card.base.type = extractStringValue(content, 'type') || ''; card.base.dict = extractStringValue(content, 'dict') || ''; card.base.desc_global = extractStringValue(content, 'desc_global') || ''; card.base.sub = extractStringValue(content, 'sub') || ''; // 解析 ap(可能是数字、字符串或特殊值) const apMatch = content.match(/ap\s*=\s*(?:"([^"]*)"|(\d+)|([^,\n]+))/); if (apMatch) { card.base.ap = apMatch[1] || apMatch[2] || (apMatch[3] ? apMatch[3].trim() : ''); } card.base.isinspiration = extractNumberValue(content, 'isinspiration') || 0; card.base.isgod_inspiration = extractNumberValue(content, 'isgod_inspiration') || 0; } // 辅助函数:提取字符串值(支持转义) function extractStringValue(text, key) { const pattern = new RegExp(key + '\\s*=\\s*"((?:[^"\\\\]|\\\\.)*)"', 'm'); const match = text.match(pattern); if (match && match[1]) { // 处理转义字符 return match[1] .replace(/\\"/g, '"') .replace(/\\n/g, '\n') .replace(/\\t/g, '\t') .replace(/\\\\/g, '\\'); } return ''; } // Lua字符串转义 function escapeLuaString(str) { if (!str) return ''; return str.replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\t/g, '\\t'); } // 生成Lua代码 function generateLuaCode() { let code = 'local card = {}\n\n'; // 生成 card.order if (state.defaultInfo.order) { code += `card.order = { "${escapeLuaString(state.defaultInfo.order)}" }\n\n`; } // 生成 card.info if (state.defaultInfo.ego) { code += 'card.info = {\n'; code += ` ego = "${escapeLuaString(state.defaultInfo.ego)}",\n`; code += '}\n\n'; } // 生成每张卡牌 state.cards.forEach(card => { code += `card["${escapeLuaString(card.name)}"] = {\n`; code += ' base = {\n'; const base = card.base; if (base.displayname) code += ` displayname = "${escapeLuaString(base.displayname)}",\n`; if (base.art) code += ` art = "${escapeLuaString(base.art)}",\n`; if (base.group) code += ` group = "${escapeLuaString(base.group)}",\n`; if (base.rarity) code += ` rarity = "${escapeLuaString(base.rarity)}",\n`; if (base.god) code += ` god = "${escapeLuaString(base.god)}",\n`; if (base.ap !== '') { if (isNaN(base.ap)) { code += ` ap = "${escapeLuaString(base.ap)}",\n`; } else { code += ` ap = ${base.ap},\n`; } } if (base.type) code += ` type = "${escapeLuaString(base.type)}",\n`; if (base.dict) code += ` dict = "${escapeLuaString(base.dict)}",\n`; if (base.desc_global) code += ` desc_global = "${escapeLuaString(base.desc_global)}",\n`; if (base.sub) code += ` sub = "${escapeLuaString(base.sub)}",\n`; if (base.isinspiration) code += ` isinspiration = ${base.isinspiration},\n`; if (base.isgod_inspiration) code += ` isgod_inspiration = ${base.isgod_inspiration},\n`; code += ' },\n'; // 生成 var 部分 const hasInspiration = card.var.inspiration.length > 0; const hasGodInspiration = Object.values(card.var.god_inspiration).some(arr => arr.length > 0); if (hasInspiration || hasGodInspiration) { code += ' var = {\n'; // 灵光一闪 if (hasInspiration) { code += ' inspiration = {\n'; card.var.inspiration.forEach(insp => { code += ' {\n'; if (insp.ap !== '' && insp.ap !== undefined) { if (isNaN(insp.ap)) { code += ` ap = "${escapeLuaString(insp.ap)}",\n`; } else { code += ` ap = ${insp.ap},\n`; } } if (insp.type) code += ` type = "${escapeLuaString(insp.type)}",\n`; if (insp.desc_global) code += ` desc_global = "${escapeLuaString(insp.desc_global)}",\n`; if (insp.dict) code += ` dict = "${escapeLuaString(insp.dict)}",\n`; code += ' },\n'; }); code += ' },\n'; } // 神光一闪 if (hasGodInspiration) { code += ' god_inspiration = {\n'; Object.entries(card.var.god_inspiration).forEach(([god, inspList]) => { if (inspList.length > 0) { code += ` ${god} = {\n`; inspList.forEach(insp => { code += ' {\n'; if (insp.ap !== '' && insp.ap !== undefined) { if (isNaN(insp.ap)) { code += ` ap = "${escapeLuaString(insp.ap)}",\n`; } else { code += ` ap = ${insp.ap},\n`; } } if (insp.type) code += ` type = "${escapeLuaString(insp.type)}",\n`; if (insp.desc_global) code += ` desc_global = "${escapeLuaString(insp.desc_global)}",\n`; if (insp.dict) code += ` dict = "${escapeLuaString(insp.dict)}",\n`; code += ' },\n'; }); code += ' },\n'; } }); code += ' },\n'; } code += ' },\n'; } code += '}\n\n'; }); code += 'return card'; return code; } // 加载战斗员列表 async function loadFighters() { try { const api = new mw.Api(); const result = await api.get({ action: 'query', list: 'allpages', apprefix: '卡牌/', apnamespace: 828, // Module namespace aplimit: 500 }); if (result.query && result.query.allpages) { state.fighters = result.query.allpages.map(page => { return page.title.replace('模块:卡牌/', ''); }); } } catch (error) { console.error('加载战斗员列表失败:', error); } } // 加载战斗员卡牌数据 async function loadFighterCards(fighter) { if (!fighter) return []; try { const api = new mw.Api(); // 获取页面内容 const result = await api.get({ action: 'query', prop: 'revisions', titles: '模块:卡牌/' + fighter, rvprop: 'content', rvslots: 'main', formatversion: 2 }); if (!result.query || !result.query.pages || result.query.pages.length === 0) { console.error('未找到页面'); return []; } const page = result.query.pages[0]; if (page.missing) { console.error('页面不存在'); return []; } const content = page.revisions[0].slots.main.content; // 解析Lua代码 const parsed = parseLuaCode(content); // 更新默认信息 state.defaultInfo = parsed.defaultInfo; return parsed.cards; } catch (error) { console.error('加载卡牌数据失败:', error); return []; } } // 渲染函数 function render() { const container = document.getElementById('card-manager-container'); if (!container) return; container.innerHTML = ''; const manager = createElement('div', 'card-manager'); // 左侧输入区 const inputSection = createElement('div', 'card-input-section'); // 战斗员选择 const title = createElement('div', 'section-title'); title.textContent = '卡牌管理器'; inputSection.appendChild(title); const fighterSelect = createSelect(state.fighters, state.currentFighter, async (value) => { state.currentFighter = value; // 显示加载提示 const loading = createElement('div', 'loading-indicator'); loading.textContent = '正在加载卡牌数据...'; document.body.appendChild(loading); try { const cards = await loadFighterCards(value); state.cards = cards; state.currentCard = cards.length > 0 ? cards[0] : null; render(); } catch (error) { alert('加载失败:' + error.message); } finally { if (loading.parentNode) { document.body.removeChild(loading); } } }); inputSection.appendChild(createFormGroup('选择战斗员:', fighterSelect.wrapper)); // 默认信息区 if (state.currentFighter) { const defaultSection = createElement('div', 'default-info-section'); const defaultTitle = createElement('div', 'section-title'); defaultTitle.textContent = '默认信息'; defaultTitle.style.borderColor = '#ff9800'; defaultSection.appendChild(defaultTitle); const orderInput = createInput('text', state.defaultInfo.order, (value) => { state.defaultInfo.order = value; render(); }, 'card.order'); defaultSection.appendChild(createFormGroup('卡牌顺序:', orderInput)); const egoInput = createInput('text', state.defaultInfo.ego, (value) => { state.defaultInfo.ego = value; render(); }, 'card.info.ego'); defaultSection.appendChild(createFormGroup('EGO:', egoInput)); inputSection.appendChild(defaultSection); } // 卡牌编辑区 if (state.currentCard) { const cardSection = createElement('div', ''); cardSection.style.marginTop = '20px'; const cardTitle = createElement('div', 'section-title'); cardTitle.textContent = '卡牌信息'; cardSection.appendChild(cardTitle); // 卡牌名称 const nameInput = createInput('text', state.currentCard.name, (value) => { state.currentCard.name = value; render(); }, '卡牌名称'); cardSection.appendChild(createFormGroup('卡牌名称:', nameInput)); // 显示名称 const displaynameInput = createInput('text', state.currentCard.base.displayname, (value) => { state.currentCard.base.displayname = value; render(); }, '显示名称'); cardSection.appendChild(createFormGroup('显示名称:', displaynameInput)); // 图片 const artInput = createInput('text', state.currentCard.base.art, (value) => { state.currentCard.base.art = value; render(); }, '图片文件名'); cardSection.appendChild(createFormGroup('图片:', artInput)); // 分组 const groupInput = createInput('text', state.currentCard.base.group, (value) => { state.currentCard.base.group = value; render(); }, '分组'); cardSection.appendChild(createFormGroup('分组:', groupInput)); // 稀有度 const raritySelect = createSelect( ['', 'ZAYIN', 'TETH', 'HE', 'WAW', 'ALEPH'], state.currentCard.base.rarity || '', (value) => { state.currentCard.base.rarity = value; render(); } ); cardSection.appendChild(createFormGroup('稀有度:', raritySelect.wrapper)); // 神明 const godSelect = createSelect( ['', 'circen', 'diallos', 'nihilum', 'secred', 'vitor'], state.currentCard.base.god || '', (value) => { state.currentCard.base.god = value; render(); } ); cardSection.appendChild(createFormGroup('神明:', godSelect.wrapper)); // AP const apInput = createInput('text', state.currentCard.base.ap, (value) => { state.currentCard.base.ap = value; render(); }, 'AP (可以是数字、X、Ø等)'); cardSection.appendChild(createFormGroup('AP:', apInput)); // 类型 const typeSelect = createSelect( ['', '攻击', '技能', '强化'], state.currentCard.base.type || '', (value) => { state.currentCard.base.type = value; render(); } ); cardSection.appendChild(createFormGroup('类型:', typeSelect.wrapper)); // 词典 const dictInput = createInput('text', state.currentCard.base.dict, (value) => { state.currentCard.base.dict = value; render(); }, '词典关键词'); cardSection.appendChild(createFormGroup('词典:', dictInput)); // 描述 const descSection = createElement('div', 'form-group'); const descLabel = createElement('div', 'form-label'); descLabel.textContent = '描述:'; descSection.appendChild(descLabel); // 描述工具栏 const toolbar = createElement('div', 'button-group'); const blueTextBtn = createButton('蓝色文本', 'btn btn-blue', () => { insertTextAtCursor(descInput, '{blue|选择文字|}', true); }); toolbar.appendChild(blueTextBtn); const greenTextBtn = createButton('绿色文本', 'btn btn-green', () => { insertTextAtCursor(descInput, '{green|选择文字|}', true); }); toolbar.appendChild(greenTextBtn); const strokeTextBtn = createButton('绿色描边', 'btn btn-green', () => { insertTextAtCursor(descInput, '{stroke|选择文字|}', true); }); toolbar.appendChild(strokeTextBtn); const dictBtn = createButton('词典', 'btn', () => { const keyword = prompt('请输入词典关键词:'); if (keyword) { insertTextAtCursor(descInput, `{dict|${keyword}|}`); } }); toolbar.appendChild(dictBtn); const wrapBtn = createButton('换行', 'btn', () => { insertTextAtCursor(descInput, '{wrap|}'); }); toolbar.appendChild(wrapBtn); descSection.appendChild(toolbar); const descInput = createInput('textarea', state.currentCard.base.desc_global, (value) => { state.currentCard.base.desc_global = value; render(); }, '卡牌描述'); descInput.style.minHeight = '150px'; descSection.appendChild(descInput); cardSection.appendChild(descSection); // 副描述 const subInput = createInput('textarea', state.currentCard.base.sub, (value) => { state.currentCard.base.sub = value; render(); }, '副描述'); subInput.style.minHeight = '80px'; cardSection.appendChild(createFormGroup('副描述:', subInput)); // 是否存在灵光一闪 const inspirationCheckbox = createCheckbox(state.currentCard.base.isinspiration, (value) => { state.currentCard.base.isinspiration = value; if (!value) { state.currentCard.var.inspiration = []; } render(); }); cardSection.appendChild(createFormGroup('是否存在灵光一闪:', inspirationCheckbox)); // 是否存在神光一闪 const godInspirationCheckbox = createCheckbox(state.currentCard.base.isgod_inspiration, (value) => { state.currentCard.base.isgod_inspiration = value; if (!value) { state.currentCard.var.god_inspiration = { circen: [], diallos: [], nihilum: [], secred: [], vitor: [] }; } render(); }); cardSection.appendChild(createFormGroup('是否存在神光一闪:', godInspirationCheckbox)); inputSection.appendChild(cardSection); } // 添加新卡牌按钮 const addCardBtn = createButton('+ 新增卡牌', 'btn-success', () => { const newCard = createEmptyCard(); newCard.name = '新卡牌' + (state.cards.length + 1); state.cards.push(newCard); state.currentCard = newCard; render(); }); addCardBtn.style.marginTop = '20px'; addCardBtn.style.width = '100%'; inputSection.appendChild(addCardBtn); manager.appendChild(inputSection); // 中间列表区 const listSection = createElement('div', 'card-list-section'); // 卡牌列表 const cardListContainer = createElement('div', 'list-container'); const cardListHeader = createElement('div', 'list-header'); const cardListTitle = createElement('div', 'list-title'); cardListTitle.textContent = '卡牌列表'; cardListHeader.appendChild(cardListTitle); cardListContainer.appendChild(cardListHeader); if (state.cards.length === 0) { const emptyState = createElement('div', 'empty-state'); const emptyIcon = createElement('div', 'empty-state-icon'); emptyIcon.textContent = '📋'; const emptyText = createElement('div', 'empty-state-text'); emptyText.textContent = '暂无卡牌,点击"新增卡牌"开始创建'; emptyState.appendChild(emptyIcon); emptyState.appendChild(emptyText); cardListContainer.appendChild(emptyState); } else { state.cards.forEach((card, index) => { const cardItem = createElement('div', 'card-item' + (state.currentCard === card ? ' active' : '')); const cardName = createElement('div', 'card-item-name'); cardName.textContent = card.name || '未命名卡牌'; const cardInfo = createElement('div', 'card-item-info'); const infoText = []; if (card.base.type) infoText.push(card.base.type); if (card.base.ap !== '') infoText.push('AP:' + card.base.ap); if (card.base.rarity) infoText.push(card.base.rarity); cardInfo.textContent = infoText.join(' | ') || '暂无信息'; cardItem.appendChild(cardName); cardItem.appendChild(cardInfo); cardItem.addEventListener('click', () => { state.currentCard = card; render(); }); // 删除按钮 const deleteBtn = createButton('删除', 'btn-danger', (e) => { e.stopPropagation(); if (confirm('确定要删除卡牌"' + card.name + '"吗?')) { state.cards.splice(index, 1); if (state.currentCard === card) { state.currentCard = state.cards[0] || null; } render(); } }); deleteBtn.style.cssText = 'margin-top:8px;width:100%;'; cardItem.appendChild(deleteBtn); cardListContainer.appendChild(cardItem); }); } listSection.appendChild(cardListContainer); // 灵光一闪列表 if (state.currentCard && state.currentCard.base.isinspiration) { const inspirationSection = createElement('div', 'inspiration-section'); const inspirationHeader = createElement('div', 'list-header'); const inspirationTitle = createElement('div', 'list-title'); inspirationTitle.textContent = '灵光一闪列表'; inspirationHeader.appendChild(inspirationTitle); const addInspirationBtn = createButton('+ 添加', 'btn-primary', () => { state.currentCard.var.inspiration.push({ ap: '', type: '', desc_global: '' }); render(); }); inspirationHeader.appendChild(addInspirationBtn); inspirationSection.appendChild(inspirationHeader); if (state.currentCard.var.inspiration.length === 0) { const emptyState = createElement('div', 'empty-state'); emptyState.style.padding = '20px'; const emptyText = createElement('div', 'empty-state-text'); emptyText.textContent = '暂无灵光一闪,点击"添加"创建'; emptyState.appendChild(emptyText); inspirationSection.appendChild(emptyState); } else { state.currentCard.var.inspiration.forEach((insp, idx) => { const inspItem = createElement('div', 'inspiration-item'); const inspHeader = createElement('div', 'inspiration-item-header'); const inspTitle = createElement('div', 'inspiration-item-title'); inspTitle.textContent = `灵光 #${idx + 1}`; inspHeader.appendChild(inspTitle); const deleteInspBtn = createButton('删除', 'btn-danger', () => { if (confirm('确定要删除这个灵光一闪吗?')) { state.currentCard.var.inspiration.splice(idx, 1); render(); } }); deleteInspBtn.style.padding = '4px 8px'; deleteInspBtn.style.fontSize = '12px'; inspHeader.appendChild(deleteInspBtn); inspItem.appendChild(inspHeader); // AP输入 const inspApInput = createInput('text', insp.ap, (value) => { insp.ap = value; }, 'AP'); inspApInput.style.marginBottom = '8px'; inspApInput.style.fontSize = '13px'; inspItem.appendChild(inspApInput); // 类型选择 const inspTypeSelect = createSelect( ['', '攻击', '技能', '强化'], insp.type || '', (value) => { insp.type = value; } ); inspTypeSelect.wrapper.style.marginBottom = '8px'; inspItem.appendChild(inspTypeSelect.wrapper); // 描述输入 const inspDescInput = createInput('textarea', insp.desc_global, (value) => { insp.desc_global = value; }, '描述'); inspDescInput.style.minHeight = '60px'; inspDescInput.style.fontSize = '13px'; inspItem.appendChild(inspDescInput); inspirationSection.appendChild(inspItem); }); } listSection.appendChild(inspirationSection); } // 神光一闪列表 if (state.currentCard && state.currentCard.base.isgod_inspiration) { const godInspirationSection = createElement('div', 'inspiration-section'); const godInspirationHeader = createElement('div', 'list-header'); const godInspirationTitle = createElement('div', 'list-title'); godInspirationTitle.textContent = '神光一闪列表'; godInspirationHeader.appendChild(godInspirationTitle); godInspirationSection.appendChild(godInspirationHeader); // 神明选择标签 const godSelectGroup = createElement('div', 'god-select-group'); ['circen', 'diallos', 'nihilum', 'secred', 'vitor'].forEach(god => { const godTab = createElement('div', 'god-tab' + (state.currentGod === god ? ' active' : '')); godTab.textContent = god; godTab.addEventListener('click', () => { state.currentGod = god; render(); }); godSelectGroup.appendChild(godTab); }); godInspirationSection.appendChild(godSelectGroup); // 当前神明的神光一闪列表 const currentGodInspirations = state.currentCard.var.god_inspiration[state.currentGod]; const addGodInspirationBtn = createButton('+ 添加 ' + state.currentGod + ' 神光', 'btn-primary', () => { currentGodInspirations.push({ ap: '', type: '', desc_global: '' }); render(); }); addGodInspirationBtn.style.marginBottom = '10px'; addGodInspirationBtn.style.width = '100%'; godInspirationSection.appendChild(addGodInspirationBtn); if (currentGodInspirations.length === 0) { const emptyState = createElement('div', 'empty-state'); emptyState.style.padding = '20px'; const emptyText = createElement('div', 'empty-state-text'); emptyText.textContent = `暂无 ${state.currentGod} 神光一闪`; emptyState.appendChild(emptyText); godInspirationSection.appendChild(emptyState); } else { currentGodInspirations.forEach((insp, idx) => { const inspItem = createElement('div', 'inspiration-item'); const inspHeader = createElement('div', 'inspiration-item-header'); const inspTitle = createElement('div', 'inspiration-item-title'); inspTitle.textContent = `${state.currentGod} 神光 #${idx + 1}`; inspHeader.appendChild(inspTitle); const deleteInspBtn = createButton('删除', 'btn-danger', () => { if (confirm('确定要删除这个神光一闪吗?')) { currentGodInspirations.splice(idx, 1); render(); } }); deleteInspBtn.style.padding = '4px 8px'; deleteInspBtn.style.fontSize = '12px'; inspHeader.appendChild(deleteInspBtn); inspItem.appendChild(inspHeader); // AP输入 const inspApInput = createInput('text', insp.ap, (value) => { insp.ap = value; }, 'AP'); inspApInput.style.marginBottom = '8px'; inspApInput.style.fontSize = '13px'; inspItem.appendChild(inspApInput); // 类型选择 const inspTypeSelect = createSelect( ['', '攻击', '技能', '强化'], insp.type || '', (value) => { insp.type = value; } ); inspTypeSelect.wrapper.style.marginBottom = '8px'; inspItem.appendChild(inspTypeSelect.wrapper); // 描述输入 const inspDescInput = createInput('textarea', insp.desc_global, (value) => { insp.desc_global = value; }, '描述'); inspDescInput.style.minHeight = '60px'; inspDescInput.style.fontSize = '13px'; inspItem.appendChild(inspDescInput); godInspirationSection.appendChild(inspItem); }); } listSection.appendChild(godInspirationSection); } manager.appendChild(listSection); // 右侧预览区 const previewSection = createElement('div', 'card-preview-section'); const previewTitle = createElement('div', 'section-title'); previewTitle.textContent = 'Lua 代码预览'; previewSection.appendChild(previewTitle); // 复制按钮 const copyBtn = createButton('复制代码', 'btn-primary', () => { const code = generateLuaCode(); navigator.clipboard.writeText(code).then(() => { alert('代码已复制到剪贴板!'); }).catch(err => { console.error('复制失败:', err); // 降级方案 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('代码已复制到剪贴板!'); }); }); copyBtn.style.marginBottom = '10px'; copyBtn.style.width = '100%'; previewSection.appendChild(copyBtn); // 保存按钮 const saveBtn = createButton('保存到Wiki', 'btn-success', async () => { if (!state.currentFighter) { alert('请先选择战斗员!'); return; } const code = generateLuaCode(); const pageName = '模块:卡牌/' + state.currentFighter; const loading = createElement('div', 'loading-indicator'); loading.textContent = '正在保存...'; document.body.appendChild(loading); try { const api = new mw.Api(); await api.postWithToken('csrf', { action: 'edit', title: pageName, text: code, summary: '通过卡牌管理器更新卡牌数据', contentmodel: 'Scribunto' }); alert('保存成功!'); } catch (error) { console.error('保存失败:', error); alert('保存失败:' + error); } finally { if (loading.parentNode) { document.body.removeChild(loading); } } }); saveBtn.style.marginBottom = '10px'; saveBtn.style.width = '100%'; previewSection.appendChild(saveBtn); // 代码显示 const codePreview = createElement('div', 'code-preview'); codePreview.textContent = generateLuaCode(); previewSection.appendChild(codePreview); manager.appendChild(previewSection); container.appendChild(manager); } // 初始化 async function init() { // 创建容器 let container = document.getElementById('card-manager-container'); if (!container) { container = createElement('div', ''); container.id = 'card-manager-container'; // 查找插入位置 const content = document.getElementById('mw-content-text'); if (content) { content.insertBefore(container, content.firstChild); } else { document.body.appendChild(container); } } // 显示加载提示 const loading = createElement('div', 'loading-indicator'); loading.textContent = '正在初始化...'; document.body.appendChild(loading); try { // 加载战斗员列表 await loadFighters(); // 如果有战斗员,默认选择第一个 if (state.fighters.length > 0) { state.currentFighter = state.fighters[0]; state.cards = await loadFighterCards(state.currentFighter); if (state.cards.length > 0) { state.currentCard = state.cards[0]; } } // 渲染界面 render(); } catch (error) { console.error('初始化失败:', error); alert('初始化失败:' + error.message); } finally { if (loading.parentNode) { document.body.removeChild(loading); } } } // 等待DOM加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // 导出API供外部使用 window.CardManager = { init: init, render: render, state: state, createCard: createEmptyCard, generateCode: generateLuaCode }; })();
该页面使用的模板:
模板:图标
(
查看源代码
)
模板:描边
(
查看源代码
)
模板:描边/颜色
(
查看源代码
)
模板:文本
(
查看源代码
)
模板:词典
(
查看源代码
)
模块:文本
(
查看源代码
)
模块:词典
(
查看源代码
)
模块:词典/data
(
查看源代码
)
返回
MediaWiki:Card.js
。