卡厄思
梦
境
菜单
首页
回到首页
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', // 'base', 'inspiration', 'god_inspiration' currentGod: '凯尔肯', currentInspirationIndex: null, currentGodInspirationIndex: null }; // 卡牌数据结构 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: { 凯尔肯: [], 戴奥斯: [], 尼希隆: [], 赛克瑞德: [], 维托: [] } } }; } // 创建空变体结构 function createEmptyVariant() { return { ap: '', type: '', desc_global: '', dict: '', sub: '' // 新增:变体卡牌的衍生卡牌字段 }; } // 创建元素辅助函数 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 extractStringValue(text, key) { const pattern = new RegExp(key + '\\s*=\\s*"([^"]*)"', 'm'); const match = text.match(pattern); return match ? match[1] : ''; } // 辅助函数:提取数值 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; for (let i = 0; i < content.length; i++) { const char = content[i]; const prevChar = i > 0 ? content[i - 1] : ''; if (char === '"' && prevChar !== '\\') { inString = !inString; } if (!inString) { if (char === '{') { if (depth > 0) { currentBlock += char; } depth++; } else if (char === '}') { depth--; if (depth === 0 && currentBlock) { const inspiration = {}; const ap = extractValue(currentBlock, 'ap'); if (ap) inspiration.ap = ap; const type = extractStringValue(currentBlock, 'type'); if (type) inspiration.type = type; const desc = extractStringValue(currentBlock, 'desc_global'); if (desc) inspiration.desc_global = desc; const dict = extractStringValue(currentBlock, 'dict'); if (dict) inspiration.dict = dict; // 新增:解析变体的 sub 字段 const sub = extractStringValue(currentBlock, 'sub'); if (sub) inspiration.sub = sub; if (Object.keys(inspiration).length > 0) { inspirations.push(inspiration); } currentBlock = ''; } else if (depth > 0) { currentBlock += char; } } else if (depth > 0) { currentBlock += char; } } else if (depth > 0) { currentBlock += char; } } return inspirations; } // 辅助函数:使用括号计数提取块内容 function extractBlock(content, startPattern) { const match = content.match(startPattern); if (!match) return null; let startIdx = match.index + match[0].length; let depth = 1; let endIdx = startIdx; let inString = false; for (let i = startIdx; i < content.length && depth > 0; i++) { const char = content[i]; const prevChar = i > 0 ? content[i - 1] : ''; if (char === '"' && prevChar !== '\\') { inString = !inString; } if (!inString) { if (char === '{') { depth++; } else if (char === '}') { depth--; if (depth === 0) { endIdx = i; break; } } } } return content.substring(startIdx, endIdx); } // 解析Lua代码 function parseLuaCode(luaText) { const cards = []; const defaultInfo = { order: '', ego: '' }; try { const orderMatch = luaText.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/); if (orderMatch) { defaultInfo.order = orderMatch[1]; } const egoMatch = luaText.match(/card\.info\s*=\s*\{[^}]*ego\s*=\s*"([^"]+)"/); if (egoMatch) { defaultInfo.ego = egoMatch[1]; } const cardPattern = /card\["([^"]+)"\]\s*=\s*\{/g; let cardMatch; while ((cardMatch = cardPattern.exec(luaText)) !== null) { const cardName = cardMatch[1]; const cardStartIdx = cardMatch.index + cardMatch[0].length; let depth = 1; let cardEndIdx = cardStartIdx; let inString = false; for (let i = cardStartIdx; i < luaText.length && depth > 0; i++) { const char = luaText[i]; const prevChar = i > 0 ? luaText[i - 1] : ''; if (char === '"' && prevChar !== '\\') { inString = !inString; } if (!inString) { if (char === '{') { depth++; } else if (char === '}') { depth--; if (depth === 0) { cardEndIdx = i; break; } } } } const cardContent = luaText.substring(cardStartIdx, cardEndIdx); const card = createEmptyCard(); card.name = cardName; const baseContent = extractBlock(cardContent, /base\s*=\s*\{/); if (baseContent) { card.base.displayname = extractStringValue(baseContent, 'displayname') || ''; card.base.art = extractStringValue(baseContent, 'art') || ''; card.base.group = extractStringValue(baseContent, 'group') || ''; card.base.rarity = extractStringValue(baseContent, 'rarity') || ''; card.base.god = extractStringValue(baseContent, 'god') || ''; card.base.ap = extractValue(baseContent, 'ap') || ''; card.base.type = extractStringValue(baseContent, 'type') || ''; card.base.dict = extractStringValue(baseContent, 'dict') || ''; card.base.desc_global = extractStringValue(baseContent, 'desc_global') || ''; card.base.sub = extractStringValue(baseContent, 'sub') || ''; card.base.isinspiration = extractNumberValue(baseContent, 'isinspiration') || 0; card.base.isgod_inspiration = extractNumberValue(baseContent, 'isgod_inspiration') || 0; } const varContent = extractBlock(cardContent, /var\s*=\s*\{/); if (varContent) { const inspirationContent = extractBlock(varContent, /inspiration\s*=\s*\{/); if (inspirationContent) { card.var.inspiration = parseInspirationList(inspirationContent); } const godInspirationContent = extractBlock(varContent, /god_inspiration\s*=\s*\{/); if (godInspirationContent) { // 修改:使用中文键名解析神光一闪 ['凯尔肯', '戴奥斯', '尼希隆', '赛克瑞德', '维托'].forEach(god => { const godPattern = new RegExp('\\["?' + god + '"?\\]\\s*=\\s*\\{'); const godContent = extractBlock(godInspirationContent, godPattern); if (godContent) { card.var.god_inspiration[god] = parseInspirationList(godContent); } }); } } cards.push(card); } } catch (error) { console.error('解析Lua代码失败:', error); } return { cards, defaultInfo }; } // 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'; if (state.defaultInfo.order) { code += `card.order = { "${escapeLuaString(state.defaultInfo.order)}" }\n\n`; } 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'; 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`; // 新增:输出变体的 sub 字段 if (insp.sub) code += ` sub = "${escapeLuaString(insp.sub)}",\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`; // 新增:输出变体的 sub 字段 if (insp.sub) code += ` sub = "${escapeLuaString(insp.sub)}",\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, 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; const parsed = parseLuaCode(content); state.defaultInfo = parsed.defaultInfo; return parsed.cards; } catch (error) { console.error('加载卡牌数据失败:', error); return []; } } // 更新代码预览(不触发滚动) function updateCodePreview() { const codePreview = document.querySelector('.code-preview'); if (codePreview) { codePreview.textContent = generateLuaCode(); } } // 渲染变体卡牌编辑表单 function renderVariantEditor(container) { if (!state.currentCard) return; if (state.editMode === 'inspiration' && state.currentInspirationIndex !== null) { const insp = state.currentCard.var.inspiration[state.currentInspirationIndex]; if (!insp) return; const variantSection = createElement('div', 'variant-edit-section'); const variantTitle = createElement('div', 'section-title'); variantTitle.textContent = `灵光一闪 #${state.currentInspirationIndex + 1}`; variantTitle.style.borderColor = '#9c27b0'; variantSection.appendChild(variantTitle); // 返回按钮 const backBtn = createButton('← 返回基础信息', 'btn', () => { state.editMode = 'base'; state.currentInspirationIndex = null; renderWithoutScroll(); }); backBtn.style.marginBottom = '15px'; backBtn.style.width = '100%'; variantSection.appendChild(backBtn); // AP输入 const apInput = createInput('text', insp.ap, (value) => { insp.ap = value; updateCodePreview(); }, 'AP (数字、X、Ø)'); variantSection.appendChild(createFormGroup('AP:', apInput)); // 类型选择 const typeSelect = createSelect( ['', '攻击', '技能', '强化'], insp.type || '', (value) => { insp.type = value; updateCodePreview(); } ); variantSection.appendChild(createFormGroup('类型:', typeSelect.wrapper)); // 机制输入 const dictInput = createInput('text', insp.dict, (value) => { insp.dict = value; updateCodePreview(); }, '多个机制使用、隔开'); variantSection.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, '{{文本|蓝|选择文字}}', true); }); toolbar.appendChild(blueTextBtn); const greenTextBtn = createButton('绿色文本', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{文本|绿|选择文字}}', true); }); toolbar.appendChild(greenTextBtn); const blueUnderlineBtn = createButton('蓝色下划线', 'btn btn-blue', () => { insertTextAtCursor(descInput, '{{文本|蓝|下划线|选择文字}}', true); }); toolbar.appendChild(blueUnderlineBtn); const greenUnderlineBtn = createButton('绿色下划线', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{文本|绿|下划线|选择文字}}', true); }); toolbar.appendChild(greenUnderlineBtn); const strokeTextBtn = createButton('绿色描边', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{描边|绿|选择文字}}', true); }); toolbar.appendChild(strokeTextBtn); const dictBtn = createButton('词典', 'btn', () => { insertTextAtCursor(descInput, '{{词典|选择文字}}', true); }); toolbar.appendChild(dictBtn); const wrapBtn = createButton('换行', 'btn', () => { insertTextAtCursor(descInput, '<br>'); }); toolbar.appendChild(wrapBtn); descSection.appendChild(toolbar); const descInput = createInput('textarea', insp.desc_global, (value) => { insp.desc_global = value; updateCodePreview(); }, '变体描述'); descInput.style.minHeight = '150px'; descSection.appendChild(descInput); variantSection.appendChild(descSection); // 新增:衍生卡牌输入框 const subInput = createInput('textarea', insp.sub || '', (value) => { insp.sub = value; updateCodePreview(); }, '衍生卡牌'); subInput.style.minHeight = '35px'; variantSection.appendChild(createFormGroup('衍生卡牌:', subInput)); // 删除按钮 const deleteBtn = createButton('删除此变体', 'btn-danger', () => { if (confirm('确定要删除这个灵光一闪吗?')) { state.currentCard.var.inspiration.splice(state.currentInspirationIndex, 1); state.editMode = 'base'; state.currentInspirationIndex = null; renderWithoutScroll(); } }); deleteBtn.style.marginTop = '20px'; deleteBtn.style.width = '100%'; variantSection.appendChild(deleteBtn); container.appendChild(variantSection); } else if (state.editMode === 'god_inspiration' && state.currentGodInspirationIndex !== null) { const insp = state.currentCard.var.god_inspiration[state.currentGod][state.currentGodInspirationIndex]; if (!insp) return; const variantSection = createElement('div', 'variant-edit-section'); const variantTitle = createElement('div', 'section-title'); variantTitle.textContent = `${state.currentGod} 神光 #${state.currentGodInspirationIndex + 1}`; variantTitle.style.borderColor = '#673ab7'; variantSection.appendChild(variantTitle); // 返回按钮 const backBtn = createButton('← 返回基础信息', 'btn', () => { state.editMode = 'base'; state.currentGodInspirationIndex = null; renderWithoutScroll(); }); backBtn.style.marginBottom = '15px'; backBtn.style.width = '100%'; variantSection.appendChild(backBtn); // AP输入 const apInput = createInput('text', insp.ap, (value) => { insp.ap = value; updateCodePreview(); }, 'AP (数字、X、Ø)'); variantSection.appendChild(createFormGroup('AP:', apInput)); // 类型选择 const typeSelect = createSelect( ['', '攻击', '技能', '强化'], insp.type || '', (value) => { insp.type = value; updateCodePreview(); } ); variantSection.appendChild(createFormGroup('类型:', typeSelect.wrapper)); // 机制输入 const dictInput = createInput('text', insp.dict, (value) => { insp.dict = value; updateCodePreview(); }, '多个机制使用、隔开'); variantSection.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, '{{文本|蓝|选择文字}}', true); }); toolbar.appendChild(blueTextBtn); const greenTextBtn = createButton('绿色文本', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{文本|绿|选择文字}}', true); }); toolbar.appendChild(greenTextBtn); const blueUnderlineBtn = createButton('蓝色下划线', 'btn btn-blue', () => { insertTextAtCursor(descInput, '{{文本|蓝|下划线|选择文字}}', true); }); toolbar.appendChild(blueUnderlineBtn); const greenUnderlineBtn = createButton('绿色下划线', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{文本|绿|下划线|选择文字}}', true); }); toolbar.appendChild(greenUnderlineBtn); const strokeTextBtn = createButton('绿色描边', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{描边|绿|选择文字}}', true); }); toolbar.appendChild(strokeTextBtn); const dictBtn = createButton('词典', 'btn', () => { insertTextAtCursor(descInput, '{{词典|选择文字}}', true); }); toolbar.appendChild(dictBtn); const wrapBtn = createButton('换行', 'btn', () => { insertTextAtCursor(descInput, '<br>'); }); toolbar.appendChild(wrapBtn); const circenBtn = createButton('凯尔肯', 'btn', () => { insertTextAtCursor(descInput, '{{图标|凯尔肯}}'); }); toolbar.appendChild(circenBtn); const diallosnBtn = createButton('戴奥斯', 'btn', () => { insertTextAtCursor(descInput, '{{图标|戴奥斯}}'); }); toolbar.appendChild(diallosBtn); const nihiluBtn = createButton('尼希隆', 'btn', () => { insertTextAtCursor(descInput, '{{图标|尼希隆}}'); }); toolbar.appendChild(nihiluBtn); const secredBtn = createButton('赛克瑞德', 'btn', () => { insertTextAtCursor(descInput, '{{图标|赛克瑞德}}'); }); toolbar.appendChild(secredBtn); const vitorBtn = createButton('维托', 'btn', () => { insertTextAtCursor(descInput, '{{图标|维托}}'); }); toolbar.appendChild(vitorBtn); descSection.appendChild(toolbar); const descInput = createInput('textarea', insp.desc_global, (value) => { insp.desc_global = value; updateCodePreview(); }, '变体描述'); descInput.style.minHeight = '150px'; descSection.appendChild(descInput); variantSection.appendChild(descSection); // 新增:衍生卡牌输入框 const subInput = createInput('textarea', insp.sub || '', (value) => { insp.sub = value; updateCodePreview(); }, '衍生卡牌'); subInput.style.minHeight = '35px'; variantSection.appendChild(createFormGroup('衍生卡牌:', subInput)); // 删除按钮 const deleteBtn = createButton('删除此变体', 'btn-danger', () => { if (confirm('确定要删除这个神光一闪吗?')) { state.currentCard.var.god_inspiration[state.currentGod].splice(state.currentGodInspirationIndex, 1); state.editMode = 'base'; state.currentGodInspirationIndex = null; renderWithoutScroll(); } }); deleteBtn.style.marginTop = '20px'; deleteBtn.style.width = '100%'; variantSection.appendChild(deleteBtn); container.appendChild(variantSection); } } // 不触发滚动的渲染函数 function renderWithoutScroll() { const container = document.getElementById('card-manager-container'); if (!container) return; // 保存当前滚动位置 const scrollPositions = { input: 0, list: 0, preview: 0 }; const inputSection = container.querySelector('.card-input-section'); const listSection = container.querySelector('.card-list-section'); const previewSection = container.querySelector('.card-preview-section'); if (inputSection) scrollPositions.input = inputSection.scrollTop; if (listSection) scrollPositions.list = listSection.scrollTop; if (previewSection) scrollPositions.preview = previewSection.scrollTop; // 执行渲染 render(); // 恢复滚动位置 requestAnimationFrame(() => { const newInputSection = container.querySelector('.card-input-section'); const newListSection = container.querySelector('.card-list-section'); const newPreviewSection = container.querySelector('.card-preview-section'); if (newInputSection) newInputSection.scrollTop = scrollPositions.input; if (newListSection) newListSection.scrollTop = scrollPositions.list; if (newPreviewSection) newPreviewSection.scrollTop = scrollPositions.preview; }); } // 渲染函数 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; state.editMode = 'base'; state.currentInspirationIndex = null; state.currentGodInspirationIndex = 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; updateCodePreview(); }, 'card.order'); defaultSection.appendChild(createFormGroup('卡牌顺序:', orderInput)); const egoInput = createInput('text', state.defaultInfo.ego, (value) => { state.defaultInfo.ego = value; updateCodePreview(); }, 'card.info.ego'); defaultSection.appendChild(createFormGroup('属性:', egoInput)); inputSection.appendChild(defaultSection); } // 卡牌编辑区 - 根据editMode显示不同内容 if (state.currentCard) { if (state.editMode === 'base') { // 显示基础信息编辑 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; updateCodePreview(); // 更新列表显示 const cardItems = document.querySelectorAll('.card-item'); cardItems.forEach((item, idx) => { if (state.cards[idx] === state.currentCard) { const nameEl = item.querySelector('.card-item-name'); if (nameEl) nameEl.textContent = value || '未命名卡牌'; } }); }, '卡牌名称'); cardSection.appendChild(createFormGroup('卡牌名称:', nameInput)); // 显示名称 const displaynameInput = createInput('text', state.currentCard.base.displayname, (value) => { state.currentCard.base.displayname = value; updateCodePreview(); }, '显示名称'); cardSection.appendChild(createFormGroup('显示名称:', displaynameInput)); // 图片 const artInput = createInput('text', state.currentCard.base.art, (value) => { state.currentCard.base.art = value; updateCodePreview(); }, '图片文件名'); cardSection.appendChild(createFormGroup('图片:', artInput)); // 分组 const groupInput = createInput('text', state.currentCard.base.group, (value) => { state.currentCard.base.group = value; updateCodePreview(); }, '分组'); cardSection.appendChild(createFormGroup('分组:', groupInput)); // 稀有度 const raritySelect = createSelect( ['', '白', '蓝', '橙', '彩'], state.currentCard.base.rarity || '', (value) => { state.currentCard.base.rarity = value; updateCodePreview(); } ); cardSection.appendChild(createFormGroup('稀有度:', raritySelect.wrapper)); // 神明 const godSelect = createSelect( ['', '凯尔肯', '戴奥斯', '尼希隆', '赛克瑞德', '维托'], state.currentCard.base.god || '', (value) => { state.currentCard.base.god = value; updateCodePreview(); } ); cardSection.appendChild(createFormGroup('神明:', godSelect.wrapper)); // AP const apInput = createInput('text', state.currentCard.base.ap, (value) => { state.currentCard.base.ap = value; updateCodePreview(); }, 'AP (可以是数字、X、Ø等)'); cardSection.appendChild(createFormGroup('AP:', apInput)); // 类型 const typeSelect = createSelect( ['', '攻击', '技能', '强化'], state.currentCard.base.type || '', (value) => { state.currentCard.base.type = value; updateCodePreview(); } ); cardSection.appendChild(createFormGroup('类型:', typeSelect.wrapper)); // 机制 const dictInput = createInput('text', state.currentCard.base.dict, (value) => { state.currentCard.base.dict = value; updateCodePreview(); }, '多个机制使用、隔开'); 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, '{{文本|蓝|选择文字}}', true); }); toolbar.appendChild(blueTextBtn); const greenTextBtn = createButton('绿色文本', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{文本|绿|选择文字}}', true); }); toolbar.appendChild(greenTextBtn); const blueUnderlineBtn = createButton('蓝色下划线', 'btn btn-blue', () => { insertTextAtCursor(descInput, '{{文本|蓝|下划线|选择文字}}', true); }); toolbar.appendChild(blueUnderlineBtn); const greenUnderlineBtn = createButton('绿色下划线', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{文本|绿|下划线|选择文字}}', true); }); toolbar.appendChild(greenUnderlineBtn); const strokeTextBtn = createButton('绿色描边', 'btn btn-green', () => { insertTextAtCursor(descInput, '{{描边|绿|选择文字}}', true); }); toolbar.appendChild(strokeTextBtn); const dictBtn = createButton('词典', 'btn', () => { insertTextAtCursor(descInput, '{{词典|选择文字}}', true); }); toolbar.appendChild(dictBtn); const wrapBtn = createButton('换行', 'btn', () => { insertTextAtCursor(descInput, '<br>'); }); toolbar.appendChild(wrapBtn); descSection.appendChild(toolbar); const descInput = createInput('textarea', state.currentCard.base.desc_global, (value) => { state.currentCard.base.desc_global = value; updateCodePreview(); }, '卡牌描述'); descInput.style.minHeight = '150px'; descSection.appendChild(descInput); cardSection.appendChild(descSection); // 衍生卡牌 const subInput = createInput('textarea', state.currentCard.base.sub, (value) => { state.currentCard.base.sub = value; updateCodePreview(); }, '衍生卡牌'); subInput.style.minHeight = '35px'; cardSection.appendChild(createFormGroup('衍生卡牌:', subInput)); // 是否存在灵光一闪 const inspirationCheckbox = createCheckbox(state.currentCard.base.isinspiration, (value) => { state.currentCard.base.isinspiration = value; if (!value) { state.currentCard.var.inspiration = []; } updateCodePreview(); renderWithoutScroll(); }); 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 = { 凯尔肯: [], 戴奥斯: [], 尼希隆: [], 赛克瑞德: [], 维托: [] }; } updateCodePreview(); renderWithoutScroll(); }); cardSection.appendChild(createFormGroup('是否存在神光一闪:', godInspirationCheckbox)); inputSection.appendChild(cardSection); // 保存卡牌按钮 const saveCardBtn = createButton('保存卡牌', 'btn-primary', () => { updateCodePreview(); alert('卡牌已保存到代码预览!'); }); saveCardBtn.style.marginTop = '20px'; saveCardBtn.style.width = '100%'; inputSection.appendChild(saveCardBtn); } else { // 显示变体编辑 renderVariantEditor(inputSection); } } // 添加新卡牌按钮 if (state.currentFighter && state.editMode === 'base') { const addCardBtn = createButton('+ 新增卡牌', 'btn-success', () => { const newCard = createEmptyCard(); newCard.name = '新卡牌' + (state.cards.length + 1); state.cards.push(newCard); state.currentCard = newCard; state.editMode = 'base'; state.currentInspirationIndex = null; state.currentGodInspirationIndex = null; renderWithoutScroll(); }); 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'); cardListContainer.style.minHeight = '250px'; 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 && state.editMode === 'base' ? ' 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; state.editMode = 'base'; state.currentInspirationIndex = null; state.currentGodInspirationIndex = null; renderWithoutScroll(); }); // 删除按钮 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; state.editMode = 'base'; state.currentInspirationIndex = null; state.currentGodInspirationIndex = null; } renderWithoutScroll(); } }); 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'); inspirationSection.style.minHeight = '250px'; const inspirationHeader = createElement('div', 'list-header'); const inspirationTitle = createElement('div', 'list-title'); inspirationTitle.textContent = '灵光一闪列表'; inspirationHeader.appendChild(inspirationTitle); const addInspirationBtn = createButton('+ 添加', 'btn-primary', () => { const newIndex = state.currentCard.var.inspiration.length; state.currentCard.var.inspiration.push(createEmptyVariant()); state.editMode = 'inspiration'; state.currentInspirationIndex = newIndex; state.currentGodInspirationIndex = null; 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-simple' + (state.editMode === 'inspiration' && state.currentInspirationIndex === idx ? ' active' : '') ); const inspName = createElement('div', 'inspiration-item-name'); inspName.textContent = `灵光 #${idx + 1}`; const inspInfo = createElement('div', 'inspiration-item-info'); const infoText = []; if (insp.type) infoText.push(insp.type); if (insp.ap !== '') infoText.push('AP:' + insp.ap); inspInfo.textContent = infoText.join(' | ') || '点击编辑'; inspItem.appendChild(inspName); inspItem.appendChild(inspInfo); inspItem.addEventListener('click', () => { state.editMode = 'inspiration'; state.currentInspirationIndex = idx; state.currentGodInspirationIndex = null; renderWithoutScroll(); }); inspirationSection.appendChild(inspItem); }); } listSection.appendChild(inspirationSection); } // 神光一闪列表 if (state.currentCard && state.currentCard.base.isgod_inspiration) { const godInspirationSection = createElement('div', 'inspiration-section'); godInspirationSection.style.minHeight = '250px'; 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'); ['凯尔肯', '戴奥斯', '尼希隆', '赛克瑞德', '维托'].forEach(god => { const godTab = createElement('div', 'god-tab' + (state.currentGod === god ? ' active' : '')); godTab.textContent = god; godTab.addEventListener('click', () => { state.currentGod = god; renderWithoutScroll(); }); godSelectGroup.appendChild(godTab); }); godInspirationSection.appendChild(godSelectGroup); // 当前神明的神光一闪列表 const currentGodInspirations = state.currentCard.var.god_inspiration[state.currentGod]; const addGodInspirationBtn = createButton('+ 添加 ' + state.currentGod + ' 神光', 'btn-primary', () => { const newIndex = currentGodInspirations.length; currentGodInspirations.push(createEmptyVariant()); state.editMode = 'god_inspiration'; state.currentGodInspirationIndex = newIndex; state.currentInspirationIndex = null; 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-simple' + (state.editMode === 'god_inspiration' && state.currentGodInspirationIndex === idx ? ' active' : '') ); const inspName = createElement('div', 'inspiration-item-name'); inspName.textContent = `${state.currentGod} 神光 #${idx + 1}`; const inspInfo = createElement('div', 'inspiration-item-info'); const infoText = []; if (insp.type) infoText.push(insp.type); if (insp.ap !== '') infoText.push('AP:' + insp.ap); inspInfo.textContent = infoText.join(' | ') || '点击编辑'; inspItem.appendChild(inspName); inspItem.appendChild(inspInfo); inspItem.addEventListener('click', () => { state.editMode = 'god_inspiration'; state.currentGodInspirationIndex = idx; state.currentInspirationIndex = null; renderWithoutScroll(); }); 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); } } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } window.CardManager = { init: init, render: render, state: state, createCard: createEmptyCard, generateCode: generateLuaCode }; })();
该页面使用的模板:
模板:图标
(
查看源代码
)
模板:描边
(
查看源代码
)
模板:描边/颜色
(
查看源代码
)
模板:文本
(
查看源代码
)
模板:词典
(
查看源代码
)
模块:文本
(
查看源代码
)
模块:词典
(
查看源代码
)
模块:词典/data
(
查看源代码
)
返回
MediaWiki:Card.js
。