MediaWiki

Card.js:修订间差异

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献
无编辑摘要
律Rhyme留言 | 贡献
无编辑摘要
第16行: 第16行:
         init: function() {
         init: function() {
             this.characterName = this.getCharacterName();
             this.characterName = this.getCharacterName();
            this.createUI();
             this.bindEvents();
             this.bindEvents();
             this.loadCardModule();
             this.loadCardModule();
        },
       
        // 创建UI
        createUI: function() {
            const container = document.getElementById('card-manager-root');
            if (!container) return;
           
            container.innerHTML = `
                <h2>卡牌数据管理器</h2>
                <div class="cm-container">
                    <div class="cm-section cm-input-section">
                        <div class="cm-section-title">卡牌数据</div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌名称:</div>
                            <input type="text" id="card-name" class="cm-input" placeholder="请输入卡牌名称...">
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">卡组类型:</div>
                            <input type="text" id="deck-type" class="cm-input" list="deck-list" placeholder="请选择或输入...">
                            <datalist id="deck-list">
                                <option value="起始卡牌">
                                <option value="独特卡牌">
                                <option value="灵光一闪">
                                <option value="衍生卡牌">
                            </datalist>
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">图片文件:</div>
                            <input type="text" id="card-art" class="cm-input" placeholder="如: unique_1003_01.png">
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">属性:</div>
                            <input type="text" id="card-attr" class="cm-input" list="attr-list" placeholder="请选择或输入...">
                            <datalist id="attr-list">
                                <option value="热情">
                                <option value="秩序">
                                <option value="正义">
                                <option value="本能">
                                <option value="虚无">
                            </datalist>
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">稀有度:</div>
                            <input type="text" id="card-rarity" class="cm-input" list="rarity-list" placeholder="请选择或输入...">
                            <datalist id="rarity-list">
                                <option value="白">
                                <option value="蓝">
                                <option value="橙">
                                <option value="彩">
                            </datalist>
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">AP (行动点):</div>
                            <input type="text" id="card-ap" class="cm-input" placeholder="输入数字或X">
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌类型:</div>
                            <input type="text" id="card-type" class="cm-input" list="type-list" placeholder="请选择或输入...">
                            <datalist id="type-list">
                                <option value="攻击">
                                <option value="技能">
                                <option value="强化">
                                <option value="状态异常">
                            </datalist>
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌机制:</div>
                            <input type="text" id="card-mechanism" class="cm-input" placeholder="如:消灭">
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌描述:</div>
                            <div class="cm-format-buttons">
                                <span class="cm-btn cm-btn-small cm-btn-blue" data-action="blue-text">蓝色文本</span>
                                <span class="cm-btn cm-btn-small cm-btn-green" data-action="green-text">绿色文本</span>
                                <span class="cm-btn cm-btn-small cm-btn-green" data-action="green-stroke">绿色描边</span>
                                <span class="cm-btn cm-btn-small cm-btn-orange" data-action="insert-br">插入&lt;br&gt;</span>
                            </div>
                            <textarea id="card-desc" class="cm-textarea" placeholder="请输入卡牌描述..."></textarea>
                        </div>
                       
                        <div class="cm-form-group">
                            <div class="cm-label">衍生卡牌:</div>
                            <input type="text" id="card-derived" class="cm-input" placeholder="请输入衍生卡牌名称">
                        </div>
                       
                        <div class="cm-button-group">
                            <span class="cm-btn cm-btn-success" data-action="add-card">添加卡牌</span>
                            <span class="cm-btn cm-btn-success" data-action="add-variant">添加变体</span>
                        </div>
                       
                        <div class="cm-button-group">
                            <span class="cm-btn" data-action="save-card">保存数据</span>
                            <span class="cm-btn" data-action="clear-form">清空表单</span>
                        </div>
                    </div>
                   
                    <div class="cm-section cm-list-section">
                        <div class="cm-section-title">卡牌列表</div>
                        <div id="card-list" class="cm-list"></div>
                       
                        <div class="cm-section-title" style="margin-top: 20px;">变体列表</div>
                        <div id="variant-list" class="cm-list"></div>
                       
                        <div class="cm-button-group">
                            <span class="cm-btn cm-btn-danger" data-action="delete-card">删除卡牌</span>
                            <span class="cm-btn cm-btn-danger" data-action="delete-variant">删除变体</span>
                        </div>
                    </div>
                   
                    <div class="cm-section cm-preview-section">
                        <div class="cm-section-title">Lua代码预览</div>
                        <pre id="code-preview" class="cm-code-preview">-- 暂无数据</pre>
                       
                        <div class="cm-button-group">
                            <span class="cm-btn" data-action="export">导出到模块</span>
                            <span class="cm-btn" data-action="import">从模块导入</span>
                        </div>
                    </div>
                </div>
            `;
         },
         },
          
          
第28行: 第158行:
         // 绑定事件
         // 绑定事件
         bindEvents: function() {
         bindEvents: function() {
             $('#add-card-btn').on('click', () => this.addCard());
             const container = document.getElementById('card-manager-root');
            $('#add-variant-btn').on('click', () => this.addVariant());
             if (!container) return;
            $('#save-card-btn').on('click', () => this.saveCard());
            $('#clear-form-btn').on('click', () => this.clearForm());
            $('#delete-card-btn').on('click', () => this.deleteCard());
            $('#delete-variant-btn').on('click', () => this.deleteVariant());
            $('#export-btn').on('click', () => this.exportToModule());
             $('#import-btn').on('click', () => this.importFromModule());
              
              
             // 格式化按钮
             // 按钮点击事件
             $('#blue-text-btn').on('click', () => this.insertFormat('文本', ''));
             container.addEventListener('click', (e) => {
            $('#green-text-btn').on('click', () => this.insertFormat('文本', '绿'));
                const target = e.target;
            $('#green-stroke-btn').on('click', () => this.insertFormat('描边', '绿'));
                const action = target.getAttribute('data-action');
            $('#br-btn').on('click', () => this.insertBr());
               
           
                if (!action) return;
            // 卡牌选择
               
            $('#card-list').on('click', '.card-item', (e) => {
                switch(action) {
                const index = $(e.currentTarget).data('index');
                    case 'add-card':
                this.selectCard(index);
                        this.addCard();
            });
                        break;
           
                    case 'add-variant':
            // 变体选择
                        this.addVariant();
            $('#variant-list').on('click', '.variant-item', (e) => {
                        break;
                const index = $(e.currentTarget).data('index');
                    case 'save-card':
                this.selectVariant(index);
                        this.saveCard();
                        break;
                    case 'clear-form':
                        this.clearForm();
                        break;
                    case 'delete-card':
                        this.deleteCard();
                        break;
                    case 'delete-variant':
                        this.deleteVariant();
                        break;
                    case 'export':
                        this.exportToModule();
                        break;
                    case 'import':
                        this.importFromModule();
                        break;
                    case 'blue-text':
                        this.insertFormat('文本', '');
                        break;
                    case 'green-text':
                        this.insertFormat('文本', '绿');
                        break;
                    case 'green-stroke':
                        this.insertFormat('描边', '绿');
                        break;
                    case 'insert-br':
                        this.insertBr();
                        break;
                }
               
                // 处理卡牌和变体选择
                if (target.classList.contains('cm-card-item')) {
                    const index = parseInt(target.getAttribute('data-index'));
                    this.selectCard(index);
                } else if (target.classList.contains('cm-variant-item')) {
                    const index = parseInt(target.getAttribute('data-index'));
                    this.selectVariant(index);
                }
             });
             });
              
              
             // 卡组类型变化
             // 卡组类型变化
             $('#deck-type').on('change', (e) => {
             const deckType = document.getElementById('deck-type');
                const isVariant = $(e.target).val() === '灵光一闪';
            if (deckType) {
                this.toggleVariantFields(isVariant);
                deckType.addEventListener('change', (e) => {
            });
                    const isVariant = e.target.value === '灵光一闪';
                    this.toggleVariantFields(isVariant);
                });
            }
         },
         },
          
          
         // 切换变体字段状态
         // 切换变体字段状态
         toggleVariantFields: function(isVariant) {
         toggleVariantFields: function(isVariant) {
             $('#card-name').prop('disabled', isVariant);
             const fields = ['card-name', 'card-art', 'card-attr', 'card-rarity', 'card-derived'];
            $('#card-art').prop('disabled', isVariant);
             fields.forEach(id => {
            $('#card-attr').prop('disabled', isVariant);
                const el = document.getElementById(id);
             $('#card-rarity').prop('disabled', isVariant);
                if (el) el.disabled = isVariant;
            $('#card-derived').prop('disabled', isVariant);
            });
         },
         },
          
          
第74行: 第239行:
         insertFormat: function(type, color) {
         insertFormat: function(type, color) {
             const textarea = document.getElementById('card-desc');
             const textarea = document.getElementById('card-desc');
            if (!textarea) return;
           
             const start = textarea.selectionStart;
             const start = textarea.selectionStart;
             const end = textarea.selectionEnd;
             const end = textarea.selectionEnd;
第88行: 第255行:
         insertBr: function() {
         insertBr: function() {
             const textarea = document.getElementById('card-desc');
             const textarea = document.getElementById('card-desc');
            if (!textarea) return;
           
             const start = textarea.selectionStart;
             const start = textarea.selectionStart;
             const text = textarea.value;
             const text = textarea.value;
第99行: 第268行:
             const variant = {};
             const variant = {};
              
              
             const art = $('#card-art').val().trim();
             const art = this.getInputValue('card-art');
             if (art) variant.art = art;
             if (art) variant.art = art;
              
              
             const deck = $('#deck-type').val().trim();
             const deck = this.getInputValue('deck-type');
             if (deck) variant['卡组'] = deck;
             if (deck) variant['卡组'] = deck;
              
              
             const attr = $('#card-attr').val().trim();
             const attr = this.getInputValue('card-attr');
             if (attr) variant['属性'] = attr;
             if (attr) variant['属性'] = attr;
              
              
             const rarity = $('#card-rarity').val().trim();
             const rarity = this.getInputValue('card-rarity');
             if (rarity) variant['稀有度'] = rarity;
             if (rarity) variant['稀有度'] = rarity;
              
              
             const ap = $('#card-ap').val().trim();
             const ap = this.getInputValue('card-ap');
             if (ap) {
             if (ap) {
                 variant['AP'] = ap.toUpperCase() === 'X' ? 'X' : parseInt(ap);
                 variant['AP'] = ap.toUpperCase() === 'X' ? 'X' : parseInt(ap);
             }
             }
              
              
             const mechanism = $('#card-mechanism').val().trim();
             const mechanism = this.getInputValue('card-mechanism');
             if (mechanism) variant['机制'] = mechanism;
             if (mechanism) variant['机制'] = mechanism;
              
              
             const cardType = $('#card-type').val().trim();
             const cardType = this.getInputValue('card-type');
             if (cardType) variant['类型'] = cardType;
             if (cardType) variant['类型'] = cardType;
              
              
             const desc = $('#card-desc').val().trim();
             const desc = this.getInputValue('card-desc');
             if (desc) variant['描述'] = desc;
             if (desc) variant['描述'] = desc;
              
              
             const derived = $('#card-derived').val().trim();
             const derived = this.getInputValue('card-derived');
             if (derived) variant['衍生卡牌'] = derived;
             if (derived) variant['衍生卡牌'] = derived;
              
              
             return {
             return {
                 name: $('#card-name').val().trim(),
                 name: this.getInputValue('card-name'),
                 variants: [variant]
                 variants: [variant]
             };
             };
        },
       
        // 获取输入值
        getInputValue: function(id) {
            const el = document.getElementById(id);
            return el ? el.value.trim() : '';
        },
       
        // 设置输入值
        setInputValue: function(id, value) {
            const el = document.getElementById(id);
            if (el) el.value = value || '';
         },
         },
          
          
         // 设置表单数据
         // 设置表单数据
         setFormData: function(card, variantIndex = 0) {
         setFormData: function(card, variantIndex = 0) {
             $('#card-name').val(card.name);
             this.setInputValue('card-name', card.name);
              
              
             if (variantIndex < card.variants.length) {
             if (variantIndex < card.variants.length) {
第144行: 第325行:
                 this.toggleVariantFields(isVariant);
                 this.toggleVariantFields(isVariant);
                  
                  
                 $('#card-art').val(variant.art || '');
                 this.setInputValue('card-art', variant.art);
                 $('#deck-type').val(variant['卡组'] || '');
                 this.setInputValue('deck-type', variant['卡组']);
                 $('#card-attr').val(variant['属性'] || '');
                 this.setInputValue('card-attr', variant['属性']);
                 $('#card-rarity').val(variant['稀有度'] || '');
                 this.setInputValue('card-rarity', variant['稀有度']);
                 $('#card-ap').val(variant['AP'] !== undefined ? variant['AP'] : '');
                 this.setInputValue('card-ap', variant['AP'] !== undefined ? variant['AP'] : '');
                 $('#card-mechanism').val(variant['机制'] || '');
                 this.setInputValue('card-mechanism', variant['机制']);
                 $('#card-type').val(variant['类型'] || '');
                 this.setInputValue('card-type', variant['类型']);
                 $('#card-desc').val(variant['描述'] || '');
                 this.setInputValue('card-desc', variant['描述']);
                 $('#card-derived').val(variant['衍生卡牌'] || '');
                 this.setInputValue('card-derived', variant['衍生卡牌']);
             }
             }
         },
         },
第158行: 第339行:
         // 清空表单
         // 清空表单
         clearForm: function() {
         clearForm: function() {
             $('#card-name').val('');
             const fields = ['card-name', 'card-art', 'deck-type', 'card-attr', 'card-rarity',
            $('#card-art').val('');
                          'card-ap', 'card-mechanism', 'card-type', 'card-desc', 'card-derived'];
            $('#deck-type').val('');
             fields.forEach(id => this.setInputValue(id, ''));
            $('#card-attr').val('');
            $('#card-rarity').val('');
            $('#card-ap').val('');
            $('#card-mechanism').val('');
            $('#card-type').val('');
            $('#card-desc').val('');
             $('#card-derived').val('');
              
              
             this.toggleVariantFields(false);
             this.toggleVariantFields(false);
第308行: 第482行:
         // 更新卡牌列表
         // 更新卡牌列表
         updateCardList: function() {
         updateCardList: function() {
             const $list = $('#card-list');
             const list = document.getElementById('card-list');
             $list.empty();
             if (!list) return;
           
            list.innerHTML = '';
              
              
             this.cards.forEach((card, index) => {
             this.cards.forEach((card, index) => {
                 const $item = $('<div>')
                 const item = document.createElement('div');
                    .addClass('card-item')
                item.className = 'cm-card-item';
                    .attr('data-index', index)
                item.setAttribute('data-index', index);
                    .text(card.name);
                item.textContent = card.name;
                  
                  
                 if (this.currentCard === card) {
                 if (this.currentCard === card) {
                     $item.addClass('active');
                     item.classList.add('cm-active');
                 }
                 }
                  
                  
                 $list.append($item);
                 list.appendChild(item);
             });
             });
         },
         },
第327行: 第503行:
         // 更新变体列表
         // 更新变体列表
         updateVariantList: function() {
         updateVariantList: function() {
             const $list = $('#variant-list');
             const list = document.getElementById('variant-list');
             $list.empty();
             if (!list) return;
           
            list.innerHTML = '';
              
              
             if (this.currentCard) {
             if (this.currentCard) {
第335行: 第513行:
                     const label = index === 0 ? `主卡牌 (${deck})` : `变体 ${index} (灵光一闪)`;
                     const label = index === 0 ? `主卡牌 (${deck})` : `变体 ${index} (灵光一闪)`;
                      
                      
                     const $item = $('<div>')
                     const item = document.createElement('div');
                        .addClass('variant-item')
                    item.className = 'cm-variant-item';
                        .attr('data-index', index)
                    item.setAttribute('data-index', index);
                        .text(label);
                    item.textContent = label;
                      
                      
                     if (this.currentVariantIndex === index) {
                     if (this.currentVariantIndex === index) {
                         $item.addClass('active');
                         item.classList.add('cm-active');
                     }
                     }
                      
                      
                     $list.append($item);
                     list.appendChild(item);
                 });
                 });
             }
             }
第412行: 第590行:
         updatePreview: function() {
         updatePreview: function() {
             const code = this.generateLuaCode();
             const code = this.generateLuaCode();
             $('#code-preview').text(code);
             const preview = document.getElementById('code-preview');
            if (preview) preview.textContent = code;
         },
         },
          
          
第425行: 第604行:
             const moduleName = `模块:卡牌/${this.characterName}`;
             const moduleName = `模块:卡牌/${this.characterName}`;
              
              
            // 使用MediaWiki API保存
             new mw.Api().postWithToken('csrf', {
             new mw.Api().postWithToken('csrf', {
                 action: 'edit',
                 action: 'edit',
第481行: 第659行:
                 this.cards = [];
                 this.cards = [];
                  
                  
                // 提取cardOrder
                 const orderMatch = content.match(/local\s+cardOrder\s*=\s*\{([^}]+)\}/s);
                 const orderMatch = content.match(/local\s+cardOrder\s*=\s*\{([^}]+)\}/s);
                 const cardOrder = [];
                 const cardOrder = [];
第493行: 第670行:
                 }
                 }
                  
                  
                // 提取card表
                 const cardMatch = content.match(/local\s+card\s*=\s*\{(.+)\}\s*p\.card/s);
                 const cardMatch = content.match(/local\s+card\s*=\s*\{(.+)\}\s*p\.card/s);
                 if (!cardMatch) {
                 if (!cardMatch) {
第501行: 第677行:
                  
                  
                 const cardContent = cardMatch[1];
                 const cardContent = cardMatch[1];
               
                // 解析每个卡牌
                 const cardPattern = /\["([^"]+)"\]\s*=\s*\{/g;
                 const cardPattern = /\["([^"]+)"\]\s*=\s*\{/g;
                 let match;
                 let match;
第514行: 第688行:
                 }
                 }
                  
                  
                // 按cardOrder排序
                 const sortedNames = [];
                 const sortedNames = [];
                 cardOrder.forEach(name => {
                 cardOrder.forEach(name => {
第524行: 第697行:
                 sortedNames.push(...allCardNames);
                 sortedNames.push(...allCardNames);
                  
                  
                // 解析每个卡牌的变体
                 sortedNames.forEach(cardName => {
                 sortedNames.forEach(cardName => {
                     const variants = this.extractCardVariants(cardContent, cardName);
                     const variants = this.extractCardVariants(cardContent, cardName);
第546行: 第718行:
         },
         },
          
          
        // 提取卡牌变体
         extractCardVariants: function(content, cardName) {
         extractCardVariants: function(content, cardName) {
             const variants = [];
             const variants = [];
第603行: 第774行:
         },
         },
          
          
        // 解析变体
         parseVariant: function(content) {
         parseVariant: function(content) {
             const variant = {};
             const variant = {};
第614行: 第784行:
                  
                  
                 if (match[2].startsWith('"')) {
                 if (match[2].startsWith('"')) {
                    // 字符串值
                     value = match[3]
                     value = match[3]
                         .replace(/\\"/g, '"')
                         .replace(/\\"/g, '"')
第620行: 第789行:
                         .replace(/\\n/g, '\n');
                         .replace(/\\n/g, '\n');
                 } else {
                 } else {
                    // 数字或其他值
                     const val = match[5];
                     const val = match[5];
                     if (/^-?\d+$/.test(val)) {
                     if (/^-?\d+$/.test(val)) {
第636行: 第804行:
     };
     };
      
      
     // 页面加载完成后初始化
     mw.loader.using(['mediawiki.api', 'mediawiki.notify']).then(() => {
    $(document).ready(() => {
         if (mw.config.get('wgPageName') === 'MediaWiki:Card') {
         if (mw.config.get('wgPageName') === 'MediaWiki:Card') {
             CardManager.init();
             CardManager.init();

2025年10月2日 (四) 10:19的版本

/**
 * MediaWiki 卡牌管理器
 * 用于管理模块:卡牌/角色名的数据
 */

(function() {
    'use strict';

    const CardManager = {
        cards: [],
        currentCard: null,
        currentVariantIndex: null,
        characterName: '',
        
        // 初始化
        init: function() {
            this.characterName = this.getCharacterName();
            this.createUI();
            this.bindEvents();
            this.loadCardModule();
        },
        
        // 创建UI
        createUI: function() {
            const container = document.getElementById('card-manager-root');
            if (!container) return;
            
            container.innerHTML = `
                <h2>卡牌数据管理器</h2>
                <div class="cm-container">
                    <div class="cm-section cm-input-section">
                        <div class="cm-section-title">卡牌数据</div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌名称:</div>
                            <input type="text" id="card-name" class="cm-input" placeholder="请输入卡牌名称...">
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">卡组类型:</div>
                            <input type="text" id="deck-type" class="cm-input" list="deck-list" placeholder="请选择或输入...">
                            <datalist id="deck-list">
                                <option value="起始卡牌">
                                <option value="独特卡牌">
                                <option value="灵光一闪">
                                <option value="衍生卡牌">
                            </datalist>
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">图片文件:</div>
                            <input type="text" id="card-art" class="cm-input" placeholder="如: unique_1003_01.png">
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">属性:</div>
                            <input type="text" id="card-attr" class="cm-input" list="attr-list" placeholder="请选择或输入...">
                            <datalist id="attr-list">
                                <option value="热情">
                                <option value="秩序">
                                <option value="正义">
                                <option value="本能">
                                <option value="虚无">
                            </datalist>
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">稀有度:</div>
                            <input type="text" id="card-rarity" class="cm-input" list="rarity-list" placeholder="请选择或输入...">
                            <datalist id="rarity-list">
                                <option value="白">
                                <option value="蓝">
                                <option value="橙">
                                <option value="彩">
                            </datalist>
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">AP (行动点):</div>
                            <input type="text" id="card-ap" class="cm-input" placeholder="输入数字或X">
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌类型:</div>
                            <input type="text" id="card-type" class="cm-input" list="type-list" placeholder="请选择或输入...">
                            <datalist id="type-list">
                                <option value="攻击">
                                <option value="技能">
                                <option value="强化">
                                <option value="状态异常">
                            </datalist>
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌机制:</div>
                            <input type="text" id="card-mechanism" class="cm-input" placeholder="如:消灭">
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">卡牌描述:</div>
                            <div class="cm-format-buttons">
                                <span class="cm-btn cm-btn-small cm-btn-blue" data-action="blue-text">蓝色文本</span>
                                <span class="cm-btn cm-btn-small cm-btn-green" data-action="green-text">绿色文本</span>
                                <span class="cm-btn cm-btn-small cm-btn-green" data-action="green-stroke">绿色描边</span>
                                <span class="cm-btn cm-btn-small cm-btn-orange" data-action="insert-br">插入&lt;br&gt;</span>
                            </div>
                            <textarea id="card-desc" class="cm-textarea" placeholder="请输入卡牌描述..."></textarea>
                        </div>
                        
                        <div class="cm-form-group">
                            <div class="cm-label">衍生卡牌:</div>
                            <input type="text" id="card-derived" class="cm-input" placeholder="请输入衍生卡牌名称">
                        </div>
                        
                        <div class="cm-button-group">
                            <span class="cm-btn cm-btn-success" data-action="add-card">添加卡牌</span>
                            <span class="cm-btn cm-btn-success" data-action="add-variant">添加变体</span>
                        </div>
                        
                        <div class="cm-button-group">
                            <span class="cm-btn" data-action="save-card">保存数据</span>
                            <span class="cm-btn" data-action="clear-form">清空表单</span>
                        </div>
                    </div>
                    
                    <div class="cm-section cm-list-section">
                        <div class="cm-section-title">卡牌列表</div>
                        <div id="card-list" class="cm-list"></div>
                        
                        <div class="cm-section-title" style="margin-top: 20px;">变体列表</div>
                        <div id="variant-list" class="cm-list"></div>
                        
                        <div class="cm-button-group">
                            <span class="cm-btn cm-btn-danger" data-action="delete-card">删除卡牌</span>
                            <span class="cm-btn cm-btn-danger" data-action="delete-variant">删除变体</span>
                        </div>
                    </div>
                    
                    <div class="cm-section cm-preview-section">
                        <div class="cm-section-title">Lua代码预览</div>
                        <pre id="code-preview" class="cm-code-preview">-- 暂无数据</pre>
                        
                        <div class="cm-button-group">
                            <span class="cm-btn" data-action="export">导出到模块</span>
                            <span class="cm-btn" data-action="import">从模块导入</span>
                        </div>
                    </div>
                </div>
            `;
        },
        
        // 从页面标题获取角色名
        getCharacterName: function() {
            const match = mw.config.get('wgPageName').match(/模块:卡牌\/(.+)/);
            return match ? match[1] : '';
        },
        
        // 绑定事件
        bindEvents: function() {
            const container = document.getElementById('card-manager-root');
            if (!container) return;
            
            // 按钮点击事件
            container.addEventListener('click', (e) => {
                const target = e.target;
                const action = target.getAttribute('data-action');
                
                if (!action) return;
                
                switch(action) {
                    case 'add-card':
                        this.addCard();
                        break;
                    case 'add-variant':
                        this.addVariant();
                        break;
                    case 'save-card':
                        this.saveCard();
                        break;
                    case 'clear-form':
                        this.clearForm();
                        break;
                    case 'delete-card':
                        this.deleteCard();
                        break;
                    case 'delete-variant':
                        this.deleteVariant();
                        break;
                    case 'export':
                        this.exportToModule();
                        break;
                    case 'import':
                        this.importFromModule();
                        break;
                    case 'blue-text':
                        this.insertFormat('文本', '蓝');
                        break;
                    case 'green-text':
                        this.insertFormat('文本', '绿');
                        break;
                    case 'green-stroke':
                        this.insertFormat('描边', '绿');
                        break;
                    case 'insert-br':
                        this.insertBr();
                        break;
                }
                
                // 处理卡牌和变体选择
                if (target.classList.contains('cm-card-item')) {
                    const index = parseInt(target.getAttribute('data-index'));
                    this.selectCard(index);
                } else if (target.classList.contains('cm-variant-item')) {
                    const index = parseInt(target.getAttribute('data-index'));
                    this.selectVariant(index);
                }
            });
            
            // 卡组类型变化
            const deckType = document.getElementById('deck-type');
            if (deckType) {
                deckType.addEventListener('change', (e) => {
                    const isVariant = e.target.value === '灵光一闪';
                    this.toggleVariantFields(isVariant);
                });
            }
        },
        
        // 切换变体字段状态
        toggleVariantFields: function(isVariant) {
            const fields = ['card-name', 'card-art', 'card-attr', 'card-rarity', 'card-derived'];
            fields.forEach(id => {
                const el = document.getElementById(id);
                if (el) el.disabled = isVariant;
            });
        },
        
        // 插入格式
        insertFormat: function(type, color) {
            const textarea = document.getElementById('card-desc');
            if (!textarea) return;
            
            const start = textarea.selectionStart;
            const end = textarea.selectionEnd;
            const text = textarea.value;
            const selectedText = text.substring(start, end);
            
            const formatted = `{{${type}|${color}|${selectedText}}}`;
            textarea.value = text.substring(0, start) + formatted + text.substring(end);
            textarea.focus();
            textarea.setSelectionRange(start + formatted.length - selectedText.length - 2, start + formatted.length - 2);
        },
        
        // 插入换行
        insertBr: function() {
            const textarea = document.getElementById('card-desc');
            if (!textarea) return;
            
            const start = textarea.selectionStart;
            const text = textarea.value;
            textarea.value = text.substring(0, start) + '<br>' + text.substring(start);
            textarea.focus();
            textarea.setSelectionRange(start + 4, start + 4);
        },
        
        // 获取表单数据
        getFormData: function() {
            const variant = {};
            
            const art = this.getInputValue('card-art');
            if (art) variant.art = art;
            
            const deck = this.getInputValue('deck-type');
            if (deck) variant['卡组'] = deck;
            
            const attr = this.getInputValue('card-attr');
            if (attr) variant['属性'] = attr;
            
            const rarity = this.getInputValue('card-rarity');
            if (rarity) variant['稀有度'] = rarity;
            
            const ap = this.getInputValue('card-ap');
            if (ap) {
                variant['AP'] = ap.toUpperCase() === 'X' ? 'X' : parseInt(ap);
            }
            
            const mechanism = this.getInputValue('card-mechanism');
            if (mechanism) variant['机制'] = mechanism;
            
            const cardType = this.getInputValue('card-type');
            if (cardType) variant['类型'] = cardType;
            
            const desc = this.getInputValue('card-desc');
            if (desc) variant['描述'] = desc;
            
            const derived = this.getInputValue('card-derived');
            if (derived) variant['衍生卡牌'] = derived;
            
            return {
                name: this.getInputValue('card-name'),
                variants: [variant]
            };
        },
        
        // 获取输入值
        getInputValue: function(id) {
            const el = document.getElementById(id);
            return el ? el.value.trim() : '';
        },
        
        // 设置输入值
        setInputValue: function(id, value) {
            const el = document.getElementById(id);
            if (el) el.value = value || '';
        },
        
        // 设置表单数据
        setFormData: function(card, variantIndex = 0) {
            this.setInputValue('card-name', card.name);
            
            if (variantIndex < card.variants.length) {
                const variant = card.variants[variantIndex];
                const isVariant = variant['卡组'] === '灵光一闪';
                
                this.toggleVariantFields(isVariant);
                
                this.setInputValue('card-art', variant.art);
                this.setInputValue('deck-type', variant['卡组']);
                this.setInputValue('card-attr', variant['属性']);
                this.setInputValue('card-rarity', variant['稀有度']);
                this.setInputValue('card-ap', variant['AP'] !== undefined ? variant['AP'] : '');
                this.setInputValue('card-mechanism', variant['机制']);
                this.setInputValue('card-type', variant['类型']);
                this.setInputValue('card-desc', variant['描述']);
                this.setInputValue('card-derived', variant['衍生卡牌']);
            }
        },
        
        // 清空表单
        clearForm: function() {
            const fields = ['card-name', 'card-art', 'deck-type', 'card-attr', 'card-rarity', 
                           'card-ap', 'card-mechanism', 'card-type', 'card-desc', 'card-derived'];
            fields.forEach(id => this.setInputValue(id, ''));
            
            this.toggleVariantFields(false);
            this.currentCard = null;
            this.currentVariantIndex = null;
        },
        
        // 添加卡牌
        addCard: function() {
            const cardData = this.getFormData();
            if (!cardData.name) {
                mw.notify('卡牌名称不能为空!', {type: 'error'});
                return;
            }
            
            this.cards.push(cardData);
            this.currentCard = cardData;
            this.currentVariantIndex = 0;
            
            this.updateCardList();
            this.updateVariantList();
            this.updatePreview();
            this.clearForm();
            
            mw.notify(`卡牌 "${cardData.name}" 添加成功!`, {type: 'success'});
        },
        
        // 添加变体
        addVariant: function() {
            if (!this.currentCard) {
                mw.notify('请先选择一个卡牌!', {type: 'error'});
                return;
            }
            
            const variantData = this.getFormData();
            const variant = variantData.variants[0];
            variant['卡组'] = '灵光一闪';
            
            this.currentCard.variants.push(variant);
            this.currentVariantIndex = this.currentCard.variants.length - 1;
            
            this.updateVariantList();
            this.updatePreview();
            
            mw.notify(`为 "${this.currentCard.name}" 添加变体成功!`, {type: 'success'});
        },
        
        // 保存卡牌
        saveCard: function() {
            if (!this.currentCard || this.currentVariantIndex === null) {
                mw.notify('请先选择要保存的卡牌或变体!', {type: 'error'});
                return;
            }
            
            const cardData = this.getFormData();
            const isVariant = this.currentVariantIndex > 0;
            
            if (isVariant) {
                const variant = cardData.variants[0];
                variant['卡组'] = '灵光一闪';
                this.currentCard.variants[this.currentVariantIndex] = variant;
            } else {
                this.currentCard.name = cardData.name;
                this.currentCard.variants[0] = cardData.variants[0];
            }
            
            this.updateCardList();
            this.updateVariantList();
            this.updatePreview();
            
            mw.notify('数据保存成功!', {type: 'success'});
        },
        
        // 删除卡牌
        deleteCard: function() {
            if (!this.currentCard) {
                mw.notify('请先选择要删除的卡牌!', {type: 'error'});
                return;
            }
            
            if (!confirm(`确定要删除卡牌 "${this.currentCard.name}" 吗?`)) {
                return;
            }
            
            const index = this.cards.indexOf(this.currentCard);
            this.cards.splice(index, 1);
            
            this.currentCard = null;
            this.currentVariantIndex = null;
            
            this.updateCardList();
            this.updateVariantList();
            this.updatePreview();
            this.clearForm();
            
            mw.notify('卡牌删除成功!', {type: 'success'});
        },
        
        // 删除变体
        deleteVariant: function() {
            if (!this.currentCard || this.currentVariantIndex === null) {
                mw.notify('请先选择要删除的变体!', {type: 'error'});
                return;
            }
            
            if (this.currentVariantIndex === 0) {
                mw.notify('不能删除主卡牌变体!', {type: 'error'});
                return;
            }
            
            if (!confirm('确定要删除这个变体吗?')) {
                return;
            }
            
            this.currentCard.variants.splice(this.currentVariantIndex, 1);
            this.currentVariantIndex = 0;
            
            this.updateVariantList();
            this.updatePreview();
            this.setFormData(this.currentCard, 0);
            
            mw.notify('变体删除成功!', {type: 'success'});
        },
        
        // 选择卡牌
        selectCard: function(index) {
            this.currentCard = this.cards[index];
            this.currentVariantIndex = 0;
            this.updateVariantList();
            this.setFormData(this.currentCard, 0);
        },
        
        // 选择变体
        selectVariant: function(index) {
            if (!this.currentCard) return;
            this.currentVariantIndex = index;
            this.setFormData(this.currentCard, index);
        },
        
        // 更新卡牌列表
        updateCardList: function() {
            const list = document.getElementById('card-list');
            if (!list) return;
            
            list.innerHTML = '';
            
            this.cards.forEach((card, index) => {
                const item = document.createElement('div');
                item.className = 'cm-card-item';
                item.setAttribute('data-index', index);
                item.textContent = card.name;
                
                if (this.currentCard === card) {
                    item.classList.add('cm-active');
                }
                
                list.appendChild(item);
            });
        },
        
        // 更新变体列表
        updateVariantList: function() {
            const list = document.getElementById('variant-list');
            if (!list) return;
            
            list.innerHTML = '';
            
            if (this.currentCard) {
                this.currentCard.variants.forEach((variant, index) => {
                    const deck = variant['卡组'] || '未知';
                    const label = index === 0 ? `主卡牌 (${deck})` : `变体 ${index} (灵光一闪)`;
                    
                    const item = document.createElement('div');
                    item.className = 'cm-variant-item';
                    item.setAttribute('data-index', index);
                    item.textContent = label;
                    
                    if (this.currentVariantIndex === index) {
                        item.classList.add('cm-active');
                    }
                    
                    list.appendChild(item);
                });
            }
        },
        
        // 生成Lua代码
        generateLuaCode: function() {
            if (this.cards.length === 0) {
                return '-- 暂无数据';
            }
            
            let lua = 'local p = {}\n\n';
            
            // cardOrder
            lua += 'local cardOrder = {\n';
            this.cards.forEach(card => {
                if (card.variants[0] && card.variants[0]['卡组'] !== '衍生卡牌') {
                    lua += `    "${this.escapeLua(card.name)}",\n`;
                }
            });
            lua += '}\n\n';
            
            // card data
            lua += 'local card = {\n';
            this.cards.forEach(card => {
                lua += `    ["${this.escapeLua(card.name)}"] = {\n`;
                
                card.variants.forEach(variant => {
                    lua += '        {\n';
                    
                    const fieldOrder = ['art', '卡组', '属性', '稀有度', 'AP', '机制', '类型', '描述', '衍生卡牌'];
                    
                    fieldOrder.forEach(field => {
                        if (variant[field] !== undefined && variant[field] !== '') {
                            const value = variant[field];
                            if (typeof value === 'string') {
                                lua += `            ["${field}"] = "${this.escapeLua(value)}",\n`;
                            } else {
                                lua += `            ["${field}"] = ${value},\n`;
                            }
                        }
                    });
                    
                    lua += '        },\n';
                });
                
                lua += '    },\n';
            });
            lua += '}\n\n';
            
            lua += 'p.card = card\n';
            lua += 'p.cardOrder = cardOrder\n\n';
            lua += 'return p\n';
            
            return lua;
        },
        
        // 转义Lua字符串
        escapeLua: function(str) {
            if (typeof str !== 'string') return str;
            return str.replace(/\\/g, '\\\\')
                      .replace(/"/g, '\\"')
                      .replace(/\n/g, '\\n');
        },
        
        // 更新预览
        updatePreview: function() {
            const code = this.generateLuaCode();
            const preview = document.getElementById('code-preview');
            if (preview) preview.textContent = code;
        },
        
        // 导出到模块
        exportToModule: function() {
            if (this.cards.length === 0) {
                mw.notify('没有数据可导出!', {type: 'error'});
                return;
            }
            
            const code = this.generateLuaCode();
            const moduleName = `模块:卡牌/${this.characterName}`;
            
            new mw.Api().postWithToken('csrf', {
                action: 'edit',
                title: moduleName,
                text: code,
                summary: '更新卡牌数据',
                contentmodel: 'Scribunto'
            }).done(() => {
                mw.notify(`成功保存到 ${moduleName}`, {type: 'success'});
            }).fail((err) => {
                mw.notify('保存失败: ' + err, {type: 'error'});
            });
        },
        
        // 从模块导入
        importFromModule: function() {
            const moduleName = prompt('请输入要导入的模块名称(如:妮雅):');
            if (!moduleName) return;
            
            this.loadCardModule(moduleName);
        },
        
        // 加载卡牌模块
        loadCardModule: function(characterName) {
            const name = characterName || this.characterName;
            if (!name) return;
            
            const moduleName = `模块:卡牌/${name}`;
            
            new mw.Api().get({
                action: 'query',
                prop: 'revisions',
                titles: moduleName,
                rvprop: 'content',
                rvslots: 'main'
            }).done((data) => {
                const pages = data.query.pages;
                const page = pages[Object.keys(pages)[0]];
                
                if (page.revisions) {
                    const content = page.revisions[0].slots.main['*'];
                    this.parseLuaCode(content);
                    mw.notify(`成功从 ${moduleName} 导入数据`, {type: 'success'});
                } else {
                    mw.notify('模块不存在', {type: 'error'});
                }
            }).fail(() => {
                mw.notify('加载失败', {type: 'error'});
            });
        },
        
        // 解析Lua代码
        parseLuaCode: function(content) {
            try {
                this.cards = [];
                
                const orderMatch = content.match(/local\s+cardOrder\s*=\s*\{([^}]+)\}/s);
                const cardOrder = [];
                if (orderMatch) {
                    const names = orderMatch[1].match(/"([^"]+)"/g);
                    if (names) {
                        names.forEach(name => {
                            cardOrder.push(name.replace(/"/g, ''));
                        });
                    }
                }
                
                const cardMatch = content.match(/local\s+card\s*=\s*\{(.+)\}\s*p\.card/s);
                if (!cardMatch) {
                    mw.notify('无法解析卡牌数据', {type: 'error'});
                    return;
                }
                
                const cardContent = cardMatch[1];
                const cardPattern = /\["([^"]+)"\]\s*=\s*\{/g;
                let match;
                const allCardNames = [];
                
                while ((match = cardPattern.exec(cardContent)) !== null) {
                    const cardName = match[1];
                    if (!allCardNames.includes(cardName)) {
                        allCardNames.push(cardName);
                    }
                }
                
                const sortedNames = [];
                cardOrder.forEach(name => {
                    if (allCardNames.includes(name)) {
                        sortedNames.push(name);
                        allCardNames.splice(allCardNames.indexOf(name), 1);
                    }
                });
                sortedNames.push(...allCardNames);
                
                sortedNames.forEach(cardName => {
                    const variants = this.extractCardVariants(cardContent, cardName);
                    if (variants.length > 0) {
                        this.cards.push({
                            name: cardName,
                            variants: variants
                        });
                    }
                });
                
                this.updateCardList();
                this.updateVariantList();
                this.updatePreview();
                this.clearForm();
                
            } catch (e) {
                console.error('解析错误:', e);
                mw.notify('解析失败: ' + e.message, {type: 'error'});
            }
        },
        
        extractCardVariants: function(content, cardName) {
            const variants = [];
            const escapedName = cardName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            const pattern = new RegExp(`\\["${escapedName}"\\]\\s*=\\s*\\{`, 'g');
            const match = pattern.exec(content);
            
            if (!match) return variants;
            
            let pos = match.index + match[0].length;
            let braceCount = 1;
            let variantStart = -1;
            let inString = false;
            let escape = false;
            
            while (pos < content.length && braceCount > 0) {
                const char = content[pos];
                
                if (escape) {
                    escape = false;
                    pos++;
                    continue;
                }
                
                if (char === '\\' && inString) {
                    escape = true;
                    pos++;
                    continue;
                }
                
                if (char === '"') {
                    inString = !inString;
                }
                
                if (!inString) {
                    if (char === '{') {
                        if (braceCount === 1 && variantStart === -1) {
                            variantStart = pos + 1;
                        }
                        braceCount++;
                    } else if (char === '}') {
                        braceCount--;
                        if (braceCount === 1 && variantStart !== -1) {
                            const variantContent = content.substring(variantStart, pos);
                            const variant = this.parseVariant(variantContent);
                            variants.push(variant);
                            variantStart = -1;
                        }
                    }
                }
                
                pos++;
            }
            
            return variants;
        },
        
        parseVariant: function(content) {
            const variant = {};
            const fieldPattern = /\["([^"]+)"\]\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|(-?\d+|[XxA-Za-z]+))/g;
            let match;
            
            while ((match = fieldPattern.exec(content)) !== null) {
                const fieldName = match[1];
                let value;
                
                if (match[2].startsWith('"')) {
                    value = match[3]
                        .replace(/\\"/g, '"')
                        .replace(/\\\\/g, '\\')
                        .replace(/\\n/g, '\n');
                } else {
                    const val = match[5];
                    if (/^-?\d+$/.test(val)) {
                        value = parseInt(val);
                    } else {
                        value = val;
                    }
                }
                
                variant[fieldName] = value;
            }
            
            return variant;
        }
    };
    
    mw.loader.using(['mediawiki.api', 'mediawiki.notify']).then(() => {
        if (mw.config.get('wgPageName') === 'MediaWiki:Card') {
            CardManager.init();
        }
    });
    
    window.CardManager = CardManager;
})();