MediaWiki

Partner.js:修订间差异

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献
无编辑摘要
律Rhyme留言 | 贡献
无编辑摘要
 
(未显示同一用户的5个中间版本)
第1行: 第1行:
/**
* MediaWiki伙伴管理器
* 用于创建、编辑和管理伙伴页面
*/
(function() {
(function() {
     'use strict';
     'use strict';
   
     // 加载CSS
     // 只在MediaWiki:Partner页面运行
     mw.loader.load('/index.php?title=Template:Editor/styles.css&action=raw&ctype=text/css', 'text/css');
     if (mw.config.get('wgPageName') !== 'MediaWiki:Partner') {
 
        return;
    }
   
     var PartnerManager = {
     var PartnerManager = {
         api: new mw.Api(),
         api: new mw.Api(),
第25行: 第18行:
                     label: '稀有度',  
                     label: '稀有度',  
                     type: 'select',
                     type: 'select',
                     options: ['', '5', '4']
                     options: ['', '5', '4', '3']
                 },
                 },
                 {  
                 {  
第56行: 第49行:
                 { name: '生日', label: '生日', type: 'text' },
                 { name: '生日', label: '生日', type: 'text' },
                 { name: '异能', label: '异能', type: 'text' },
                 { name: '异能', label: '异能', type: 'text' },
                 { name: '所属势力', label: '所属势力', type: 'text' },
                 {  
                 { name: '档案', label: '档案', type: 'textarea' }
                    name: '所属势力',  
                    label: '所属势力',  
                    type: 'text',
                    toolbar: [
                        { label: '换行', action: 'insertText', text: '<br>' }
                    ]
                },
                 {  
                    name: '档案',  
                    label: '档案',  
                    type: 'textarea',
                    toolbar: [
                        { label: '加粗', action: 'wrapSelection', before: '<b>', after: '</b>' },
                        { label: '换行', action: 'insertText', text: '<br>' }
                    ]
                }
             ],
             ],
             skills: [
             skills: [
                 { name: '被动', label: '被动技能', type: 'text' },
                 { name: '被动', label: '被动技能', type: 'text' },
                 { name: '被动_描述', label: '被动描述', type: 'textarea' },
                 {  
                    name: '被动_描述',  
                    label: '被动描述',  
                    type: 'textarea',
                    toolbar: [
                        { label: '换行', action: 'insertText', text: '<br>' },
                    ]
                },
                 { name: '自我意识技能', label: '自我意识技能', type: 'text' },
                 { name: '自我意识技能', label: '自我意识技能', type: 'text' },
                 { name: '自我意识技能_消耗', label: '自我意识技能消耗', type: 'text' },
                 { name: '自我意识技能_消耗', label: '自我意识技能消耗', type: 'text' },
                 { name: '自我意识技能_描述', label: '自我意识技能描述', type: 'textarea' }
                 {  
                    name: '自我意识技能_描述',  
                    label: '自我意识技能描述',  
                    type: 'textarea',
                    toolbar: [
                        { label: '换行', action: 'insertText', text: '<br>' },
                        { label: '蓝色文本', action: 'wrapSelection', before: '{{文本|蓝|', after: '}}' },
                        { label: '词典', action: 'wrapSelection', before: '{{词典|', after: '}}' }
                    ]
                }
             ],
             ],
             stats: [
             stats: [
第282行: 第306行:
             html += '<div class="agent-field-row">';
             html += '<div class="agent-field-row">';
             this.templateFields.basic.forEach(function(field) {
             this.templateFields.basic.forEach(function(field) {
                 html += self.renderField(field, data[field.name] || '');
                 html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'basic');
             });
             });
             html += '</div>';
             html += '</div>';
第290行: 第314行:
             html += '<div class="agent-field-row">';
             html += '<div class="agent-field-row">';
             this.templateFields.image.forEach(function(field) {
             this.templateFields.image.forEach(function(field) {
                 html += self.renderField(field, data[field.name] || '');
                 html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'image');
             });
             });
             html += '</div>';
             html += '</div>';
第298行: 第322行:
             html += '<div class="agent-field-row">';
             html += '<div class="agent-field-row">';
             this.templateFields.voice.forEach(function(field) {
             this.templateFields.voice.forEach(function(field) {
                 html += self.renderField(field, data[field.name] || '');
                 html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'voice');
             });
             });
             html += '</div>';
             html += '</div>';
第305行: 第329行:
             html += '<h4>角色档案</h4>';
             html += '<h4>角色档案</h4>';
             this.templateFields.profile.forEach(function(field) {
             this.templateFields.profile.forEach(function(field) {
                 html += self.renderField(field, data[field.name] || '');
                 html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'profile');
             });
             });
              
              
第311行: 第335行:
             html += '<h4>技能信息</h4>';
             html += '<h4>技能信息</h4>';
             this.templateFields.skills.forEach(function(field) {
             this.templateFields.skills.forEach(function(field) {
                 html += self.renderField(field, data[field.name] || '');
                 html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'skills');
             });
             });
              
              
第318行: 第342行:
             html += '<div class="agent-field-row">';
             html += '<div class="agent-field-row">';
             this.templateFields.stats.forEach(function(field) {
             this.templateFields.stats.forEach(function(field) {
                 html += self.renderField(field, data[field.name] || '');
                 html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'stats');
             });
             });
             html += '</div>';
             html += '</div>';
第335行: 第359行:
              
              
             container.innerHTML = html;
             container.innerHTML = html;
           
            // 绑定工具栏事件
            this.bindToolbarEvents();
              
              
             // 绑定保存事件
             // 绑定保存事件
第359行: 第386行:
         },
         },
          
          
         // 渲染表单字段
         // 使用唯一ID渲染字段
         renderField: function(field, value) {
         renderFieldWithUniqueId: function(field, value, groupName) {
             var escapedValue = mw.html.escape(String(value));
             var escapedValue = mw.html.escape(String(value));
            // 使用分组名和字段名生成唯一ID
            var fieldId = 'field-' + groupName + '-' + encodeURIComponent(field.name).replace(/%/g, '_');
             var html = '<div class="agent-form-group">';
             var html = '<div class="agent-form-group">';
             html += '<label>' + mw.html.escape(field.label) + '</label>';
             html += '<label>' + mw.html.escape(field.label) + '</label>';
           
            // 渲染工具栏
            if (field.toolbar && field.toolbar.length > 0) {
                html += '<div class="editor-toolbar" data-target="' + fieldId + '">';
                field.toolbar.forEach(function(button, index) {
                    html += '<span class="toolbar-button" data-action="' + button.action + '" ';
                    if (button.text) {
                        html += 'data-text="' + mw.html.escape(button.text) + '" ';
                    }
                    if (button.before) {
                        html += 'data-before="' + mw.html.escape(button.before) + '" ';
                    }
                    if (button.after) {
                        html += 'data-after="' + mw.html.escape(button.after) + '" ';
                    }
                    html += '>' + mw.html.escape(button.label) + '</span>';
                });
                html += '</div>';
            }
              
              
             if (field.type === 'select') {
             if (field.type === 'select') {
                 html += '<select class="agent-select" data-field="' + mw.html.escape(field.name) + '">';
                 html += '<select class="agent-select" id="' + fieldId + '" data-field="' + mw.html.escape(field.name) + '">';
                 field.options.forEach(function(option) {
                 field.options.forEach(function(option) {
                     var selected = (value === option) ? ' selected' : '';
                     var selected = (value === option) ? ' selected' : '';
第374行: 第422行:
                 html += '</select>';
                 html += '</select>';
             } else if (field.type === 'textarea') {
             } else if (field.type === 'textarea') {
                 html += '<textarea class="agent-textarea" data-field="' +  
                 html += '<textarea class="agent-textarea" id="' + fieldId + '" data-field="' +  
                         mw.html.escape(field.name) + '">' + escapedValue + '</textarea>';
                         mw.html.escape(field.name) + '">' + escapedValue + '</textarea>';
             } else {
             } else {
                 html += '<input type="text" class="agent-input" data-field="' +  
                 html += '<input type="text" class="agent-input" id="' + fieldId + '" data-field="' +  
                         mw.html.escape(field.name) + '" value="' + escapedValue + '">';
                         mw.html.escape(field.name) + '" value="' + escapedValue + '">';
             }
             }
第383行: 第431行:
             html += '</div>';
             html += '</div>';
             return html;
             return html;
        },
       
        // 绑定工具栏事件
        bindToolbarEvents: function() {
            var self = this;
           
            document.querySelectorAll('.toolbar-button').forEach(function(button) {
                button.addEventListener('click', function() {
                    var toolbar = this.parentElement;
                    var targetId = toolbar.getAttribute('data-target');
                    var targetElement = document.getElementById(targetId);
                   
                    if (!targetElement) {
                        console.error('找不到目标元素:', targetId);
                        return;
                    }
                   
                    var action = this.getAttribute('data-action');
                   
                    if (action === 'insertText') {
                        var text = self.unescapeHtml(this.getAttribute('data-text'));
                        self.insertTextAtCursor(targetElement, text);
                    } else if (action === 'wrapSelection') {
                        var before = self.unescapeHtml(this.getAttribute('data-before'));
                        var after = self.unescapeHtml(this.getAttribute('data-after'));
                        self.wrapSelection(targetElement, before, after);
                    }
                   
                    targetElement.focus();
                });
            });
        },
       
        // 在光标位置插入文本
        insertTextAtCursor: function(element, text) {
            var startPos = element.selectionStart;
            var endPos = element.selectionEnd;
            var scrollTop = element.scrollTop;
           
            var value = element.value;
            element.value = value.substring(0, startPos) + text + value.substring(endPos);
           
            // 设置光标位置
            element.selectionStart = element.selectionEnd = startPos + text.length;
            element.scrollTop = scrollTop;
           
            // 触发 change 事件
            var event = new Event('input', { bubbles: true });
            element.dispatchEvent(event);
        },
       
        // 包裹选中文本
        wrapSelection: function(element, before, after) {
            var startPos = element.selectionStart;
            var endPos = element.selectionEnd;
            var scrollTop = element.scrollTop;
           
            var value = element.value;
            var selectedText = value.substring(startPos, endPos);
           
            // 如果没有选中文本,使用默认文本
            if (selectedText === '') {
                selectedText = '请选择文本';
            }
           
            var replacement = before + selectedText + after;
            element.value = value.substring(0, startPos) + replacement + value.substring(endPos);
           
            // 选中替换后的内容
            element.selectionStart = startPos;
            element.selectionEnd = startPos + replacement.length;
            element.scrollTop = scrollTop;
           
            // 触发 change 事件
            var event = new Event('input', { bubbles: true });
            element.dispatchEvent(event);
        },
       
        // 反转义 HTML 实体
        unescapeHtml: function(text) {
            var textarea = document.createElement('textarea');
            textarea.innerHTML = text;
            return textarea.value;
         },
         },
          
          
第636行: 第767行:
              
              
             Promise.all(promises).then(function() {
             Promise.all(promises).then(function() {
                 var html = '<h4>模板检查结果</h4>';
                 var html = '<h4>模板完整性检查结果</h4>';
                 if (issues.length === 0) {
                 if (issues.length === 0) {
                     html += '<div class="agent-message success">所有伙伴模板完整!</div>';
                     html += '<div class="agent-message success">所有伙伴模板完整!</div>';

2025年10月9日 (四) 13:25的最新版本

(function() {
    'use strict';
    // 加载CSS
    mw.loader.load('/index.php?title=Template:Editor/styles.css&action=raw&ctype=text/css', 'text/css');

    var PartnerManager = {
        api: new mw.Api(),
        currentPartner: null,
        partnerList: [],
        
        // 模板字段定义
        templateFields: {
            basic: [
                { name: 'id', label: 'ID', type: 'text' },
                { name: '名称', label: '名称', type: 'text' },
                { 
                    name: '稀有度', 
                    label: '稀有度', 
                    type: 'select',
                    options: ['', '5', '4', '3']
                },
                { 
                    name: '职业', 
                    label: '职业', 
                    type: 'select',
                    options: ['', '决斗家', '先锋', '游侠', '猎人', '心灵术士', '控制师']
                },
                { name: '实装日期', label: '实装日期', type: 'text' }
            ],
            image: [
                { name: 'size', label: 'Size', type: 'text' },
                { name: 'right', label: 'Right', type: 'text' },
                { name: 'bottom', label: 'Bottom', type: 'text' }
            ],
            voice: [
                { name: '声优_韩', label: '韩语声优', type: 'text' },
                { name: '声优_日', label: '日语声优', type: 'text' },
                { name: '声优_英', label: '英语声优', type: 'text' },
                { name: '声优_中', label: '中文声优', type: 'text' }
            ],
            profile: [
                { name: '种族', label: '种族', type: 'text' },
                { 
                    name: '性别', 
                    label: '性别', 
                    type: 'select',
                    options: ['', '男', '女']
                },
                { name: '生日', label: '生日', type: 'text' },
                { name: '异能', label: '异能', type: 'text' },
                { 
                    name: '所属势力', 
                    label: '所属势力', 
                    type: 'text',
                    toolbar: [
                        { label: '换行', action: 'insertText', text: '<br>' }
                    ]
                },
                { 
                    name: '档案', 
                    label: '档案', 
                    type: 'textarea',
                    toolbar: [
                        { label: '加粗', action: 'wrapSelection', before: '<b>', after: '</b>' },
                        { label: '换行', action: 'insertText', text: '<br>' }
                    ]
                }
            ],
            skills: [
                { name: '被动', label: '被动技能', type: 'text' },
                { 
                    name: '被动_描述', 
                    label: '被动描述', 
                    type: 'textarea',
                    toolbar: [
                        { label: '换行', action: 'insertText', text: '<br>' },
                    ]
                },
                { name: '自我意识技能', label: '自我意识技能', type: 'text' },
                { name: '自我意识技能_消耗', label: '自我意识技能消耗', type: 'text' },
                { 
                    name: '自我意识技能_描述', 
                    label: '自我意识技能描述', 
                    type: 'textarea',
                    toolbar: [
                        { label: '换行', action: 'insertText', text: '<br>' },
                        { label: '蓝色文本', action: 'wrapSelection', before: '{{文本|蓝|', after: '}}' },
                        { label: '词典', action: 'wrapSelection', before: '{{词典|', after: '}}' }
                    ]
                }
            ],
            stats: [
                { name: '攻击力_1级', label: '攻击力(1级)', type: 'text' },
                { name: '攻击力_成长', label: '攻击力成长', type: 'text' },
                { name: '防御力_1级', label: '防御力(1级)', type: 'text' },
                { name: '防御力_成长', label: '防御力成长', type: 'text' },
                { name: 'HP_1级', label: 'HP(1级)', type: 'text' },
                { name: 'HP_成长', label: 'HP成长', type: 'text' }
            ]
        },
        
        // 初始化
        init: function() {
            this.createUI();
            this.loadPartnerList();
        },
        
        // 创建UI
        createUI: function() {
            var container = document.createElement('div');
            container.className = 'agent-manager';
            container.innerHTML = `
                <div class="agent-section">
                    <h3>伙伴管理器</h3>
                    <div id="partner-message"></div>
                    
                    <div class="agent-tabs">
                        <div class="agent-tab active" data-tab="list">伙伴列表</div>
                        <div class="agent-tab" data-tab="edit">编辑/新建</div>
                        <div class="agent-tab" data-tab="batch">批量操作</div>
                    </div>
                    
                    <div id="tab-list" class="agent-tab-content active">
                        <div class="agent-button" id="refresh-list">刷新列表</div>
                        <div class="agent-button" id="create-new">新建伙伴</div>
                        <div id="partner-list" class="agent-loading">加载中...</div>
                    </div>
                    
                    <div id="tab-edit" class="agent-tab-content">
                        <div id="editor-container"></div>
                    </div>
                    
                    <div id="tab-batch" class="agent-tab-content">
                        <div class="agent-form-group">
                            <label>批量操作</label>
                            <div class="agent-button" id="export-all">导出所有伙伴</div>
                            <div class="agent-button secondary" id="check-templates">检查模板完整性</div>
                        </div>
                        <div id="batch-result"></div>
                    </div>
                </div>
            `;
            
            var content = document.getElementById('mw-content-text');
            content.innerHTML = '';
            content.appendChild(container);
            
            this.bindEvents();
        },
        
        // 绑定事件
        bindEvents: function() {
            var self = this;
            
            // 标签切换
            document.querySelectorAll('.agent-tab').forEach(function(tab) {
                tab.addEventListener('click', function() {
                    var tabName = this.getAttribute('data-tab');
                    self.switchTab(tabName);
                });
            });
            
            // 刷新列表
            document.getElementById('refresh-list').addEventListener('click', function() {
                self.loadPartnerList();
            });
            
            // 新建伙伴
            document.getElementById('create-new').addEventListener('click', function() {
                self.createNewPartner();
            });
            
            // 导出所有
            document.getElementById('export-all').addEventListener('click', function() {
                self.exportAll();
            });
            
            // 检查模板
            document.getElementById('check-templates').addEventListener('click', function() {
                self.checkTemplates();
            });
        },
        
        // 切换标签
        switchTab: function(tabName) {
            document.querySelectorAll('.agent-tab').forEach(function(tab) {
                tab.classList.remove('active');
            });
            document.querySelectorAll('.agent-tab-content').forEach(function(content) {
                content.classList.remove('active');
            });
            
            document.querySelector('[data-tab="' + tabName + '"]').classList.add('active');
            document.getElementById('tab-' + tabName).classList.add('active');
        },
        
        // 加载伙伴列表
        loadPartnerList: function() {
            var self = this;
            var listContainer = document.getElementById('partner-list');
            listContainer.innerHTML = '<div class="agent-loading">加载中...</div>';
            
            this.api.get({
                action: 'query',
                list: 'categorymembers',
                cmtitle: 'Category:伙伴',
                cmlimit: 500,
                format: 'json'
            }).done(function(data) {
                self.partnerList = data.query.categorymembers;
                self.renderPartnerList();
            }).fail(function() {
                self.showMessage('加载失败,请检查分类是否存在', 'error');
            });
        },
        
        // 渲染伙伴列表
        renderPartnerList: function() {
            var listContainer = document.getElementById('partner-list');
            
            if (this.partnerList.length === 0) {
                listContainer.innerHTML = '<div class="agent-message info">暂无伙伴,点击"新建伙伴"开始创建</div>';
                return;
            }
            
            var html = '<div class="agent-grid">';
            this.partnerList.forEach(function(partner) {
                html += '<div class="agent-list-item" data-title="' + mw.html.escape(partner.title) + '">' + 
                        mw.html.escape(partner.title) + '</div>';
            });
            html += '</div>';
            
            listContainer.innerHTML = html;
            
            var self = this;
            document.querySelectorAll('.agent-list-item').forEach(function(item) {
                item.addEventListener('click', function() {
                    var title = this.getAttribute('data-title');
                    self.loadPartner(title);
                });
            });
        },
        
        // 加载伙伴数据
        loadPartner: function(title) {
            var self = this;
            this.showMessage('加载中...', 'info');
            
            this.api.get({
                action: 'query',
                titles: title,
                prop: 'revisions',
                rvprop: 'content',
                rvslots: 'main',
                format: 'json'
            }).done(function(data) {
                var pages = data.query.pages;
                var pageId = Object.keys(pages)[0];
                
                if (pageId === '-1') {
                    self.showMessage('页面不存在', 'error');
                    return;
                }
                
                var content = pages[pageId].revisions[0].slots.main['*'];
                
                self.currentPartner = {
                    title: title,
                    content: content
                };
                
                self.switchTab('edit');
                self.renderEditor();
                self.showMessage('已加载: ' + title, 'success');
            }).fail(function(error) {
                console.error('加载失败:', error);
                self.showMessage('加载失败', 'error');
            });
        },
        
        // 创建新伙伴
        createNewPartner: function() {
            this.currentPartner = {
                title: '',
                content: this.generateTemplate({})
            };
            this.switchTab('edit');
            this.renderEditor();
            this.showMessage('请填写伙伴信息', 'info');
        },
        
        // 渲染编辑器
        renderEditor: function() {
            var self = this;
            var container = document.getElementById('editor-container');
            var data = this.parseTemplate(this.currentPartner.content);
            
            var html = '<div class="agent-form-group">';
            html += '<label>页面标题</label>';
            html += '<input type="text" class="agent-input" id="page-title" value="' + 
                    mw.html.escape(this.currentPartner.title || '') + '" placeholder="例如: 伙伴/李四">';
            html += '</div>';
            
            // 基础信息
            html += '<h4>基础信息</h4>';
            html += '<div class="agent-field-row">';
            this.templateFields.basic.forEach(function(field) {
                html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'basic');
            });
            html += '</div>';
            
            // 图片位置
            html += '<h4>图片位置</h4>';
            html += '<div class="agent-field-row">';
            this.templateFields.image.forEach(function(field) {
                html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'image');
            });
            html += '</div>';
            
            // 声优信息
            html += '<h4>声优信息</h4>';
            html += '<div class="agent-field-row">';
            this.templateFields.voice.forEach(function(field) {
                html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'voice');
            });
            html += '</div>';
            
            // 角色档案
            html += '<h4>角色档案</h4>';
            this.templateFields.profile.forEach(function(field) {
                html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'profile');
            });
            
            // 技能信息
            html += '<h4>技能信息</h4>';
            this.templateFields.skills.forEach(function(field) {
                html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'skills');
            });
            
            // 属性数值
            html += '<h4>属性数值</h4>';
            html += '<div class="agent-field-row">';
            this.templateFields.stats.forEach(function(field) {
                html += self.renderFieldWithUniqueId(field, data[field.name] || '', 'stats');
            });
            html += '</div>';
            
            // 操作按钮
            html += '<div style="margin-top: 20px;">';
            html += '<div class="agent-button" id="save-partner">保存伙伴</div>';
            html += '<div class="agent-button secondary" id="preview-partner">预览</div>';
            html += '<div class="agent-button secondary" id="view-source">查看源码</div>';
            if (this.currentPartner.title) {
                html += '<div class="agent-button secondary" id="view-page">查看页面</div>';
            }
            html += '</div>';
            
            html += '<div id="preview-container" style="margin-top: 20px;"></div>';
            
            container.innerHTML = html;
            
            // 绑定工具栏事件
            this.bindToolbarEvents();
            
            // 绑定保存事件
            document.getElementById('save-partner').addEventListener('click', function() {
                self.savePartner();
            });
            
            // 绑定预览事件
            document.getElementById('preview-partner').addEventListener('click', function() {
                self.previewPartner();
            });
            
            // 绑定查看源码事件
            document.getElementById('view-source').addEventListener('click', function() {
                self.viewSource();
            });
            
            // 绑定查看页面事件
            if (this.currentPartner.title) {
                document.getElementById('view-page').addEventListener('click', function() {
                    window.open(mw.util.getUrl(self.currentPartner.title), '_blank');
                });
            }
        },
        
        // 使用唯一ID渲染字段
        renderFieldWithUniqueId: function(field, value, groupName) {
            var escapedValue = mw.html.escape(String(value));
            // 使用分组名和字段名生成唯一ID
            var fieldId = 'field-' + groupName + '-' + encodeURIComponent(field.name).replace(/%/g, '_');
            var html = '<div class="agent-form-group">';
            html += '<label>' + mw.html.escape(field.label) + '</label>';
            
            // 渲染工具栏
            if (field.toolbar && field.toolbar.length > 0) {
                html += '<div class="editor-toolbar" data-target="' + fieldId + '">';
                field.toolbar.forEach(function(button, index) {
                    html += '<span class="toolbar-button" data-action="' + button.action + '" ';
                    if (button.text) {
                        html += 'data-text="' + mw.html.escape(button.text) + '" ';
                    }
                    if (button.before) {
                        html += 'data-before="' + mw.html.escape(button.before) + '" ';
                    }
                    if (button.after) {
                        html += 'data-after="' + mw.html.escape(button.after) + '" ';
                    }
                    html += '>' + mw.html.escape(button.label) + '</span>';
                });
                html += '</div>';
            }
            
            if (field.type === 'select') {
                html += '<select class="agent-select" id="' + fieldId + '" data-field="' + mw.html.escape(field.name) + '">';
                field.options.forEach(function(option) {
                    var selected = (value === option) ? ' selected' : '';
                    html += '<option value="' + mw.html.escape(option) + '"' + selected + '>' + 
                            mw.html.escape(option || '请选择') + '</option>';
                });
                html += '</select>';
            } else if (field.type === 'textarea') {
                html += '<textarea class="agent-textarea" id="' + fieldId + '" data-field="' + 
                        mw.html.escape(field.name) + '">' + escapedValue + '</textarea>';
            } else {
                html += '<input type="text" class="agent-input" id="' + fieldId + '" data-field="' + 
                        mw.html.escape(field.name) + '" value="' + escapedValue + '">';
            }
            
            html += '</div>';
            return html;
        },
        
        // 绑定工具栏事件
        bindToolbarEvents: function() {
            var self = this;
            
            document.querySelectorAll('.toolbar-button').forEach(function(button) {
                button.addEventListener('click', function() {
                    var toolbar = this.parentElement;
                    var targetId = toolbar.getAttribute('data-target');
                    var targetElement = document.getElementById(targetId);
                    
                    if (!targetElement) {
                        console.error('找不到目标元素:', targetId);
                        return;
                    }
                    
                    var action = this.getAttribute('data-action');
                    
                    if (action === 'insertText') {
                        var text = self.unescapeHtml(this.getAttribute('data-text'));
                        self.insertTextAtCursor(targetElement, text);
                    } else if (action === 'wrapSelection') {
                        var before = self.unescapeHtml(this.getAttribute('data-before'));
                        var after = self.unescapeHtml(this.getAttribute('data-after'));
                        self.wrapSelection(targetElement, before, after);
                    }
                    
                    targetElement.focus();
                });
            });
        },
        
        // 在光标位置插入文本
        insertTextAtCursor: function(element, text) {
            var startPos = element.selectionStart;
            var endPos = element.selectionEnd;
            var scrollTop = element.scrollTop;
            
            var value = element.value;
            element.value = value.substring(0, startPos) + text + value.substring(endPos);
            
            // 设置光标位置
            element.selectionStart = element.selectionEnd = startPos + text.length;
            element.scrollTop = scrollTop;
            
            // 触发 change 事件
            var event = new Event('input', { bubbles: true });
            element.dispatchEvent(event);
        },
        
        // 包裹选中文本
        wrapSelection: function(element, before, after) {
            var startPos = element.selectionStart;
            var endPos = element.selectionEnd;
            var scrollTop = element.scrollTop;
            
            var value = element.value;
            var selectedText = value.substring(startPos, endPos);
            
            // 如果没有选中文本,使用默认文本
            if (selectedText === '') {
                selectedText = '请选择文本';
            }
            
            var replacement = before + selectedText + after;
            element.value = value.substring(0, startPos) + replacement + value.substring(endPos);
            
            // 选中替换后的内容
            element.selectionStart = startPos;
            element.selectionEnd = startPos + replacement.length;
            element.scrollTop = scrollTop;
            
            // 触发 change 事件
            var event = new Event('input', { bubbles: true });
            element.dispatchEvent(event);
        },
        
        // 反转义 HTML 实体
        unescapeHtml: function(text) {
            var textarea = document.createElement('textarea');
            textarea.innerHTML = text;
            return textarea.value;
        },
        
        // 解析模板
        parseTemplate: function(content) {
            var data = {};
            
            // 查找伙伴模板
            var templateStart = content.indexOf('{{伙伴');
            var templateEnd = content.lastIndexOf('}}');
            
            if (templateStart === -1 || templateEnd === -1) {
                return data;
            }
            
            // 提取模板内容
            var templateContent = content.substring(templateStart, templateEnd + 2);
            
            // 移除开头的 {{伙伴 和结尾的 }}
            templateContent = templateContent.replace(/^\{\{伙伴\s*/, '').replace(/\s*\}\}$/, '');
            
            // 按行分割
            var lines = templateContent.split('\n');
            
            for (var i = 0; i < lines.length; i++) {
                var line = lines[i].trim();
                
                // 跳过空行和只有|的行
                if (!line || line === '|') {
                    continue;
                }
                
                // 移除开头的 |
                if (line.charAt(0) === '|') {
                    line = line.substring(1);
                }
                
                // 查找第一个 = 的位置
                var equalPos = line.indexOf('=');
                
                if (equalPos > 0) {
                    var key = line.substring(0, equalPos).trim();
                    var value = line.substring(equalPos + 1).trim();
                    
                    if (key) {
                        data[key] = value;
                    }
                }
            }
            
            return data;
        },
        
        // 生成模板(只输出非空字段)
        generateTemplate: function(data) {
            var self = this;
            var template = '{{面包屑|伙伴图鉴}}\n{{伙伴\n';
            
            // 辅助函数:添加字段(只添加非空字段)
            var addFields = function(fields) {
                var hasContent = false;
                var output = '';
                fields.forEach(function(field) {
                    var value = data[field.name] || '';
                    // 只有当值不为空时才输出该字段
                    if (value.trim() !== '') {
                        output += '|' + field.name + '=' + value + '\n';
                        hasContent = true;
                    }
                });
                if (hasContent) {
                    output += '\n';
                }
                return output;
            };
            
            // 基础信息
            template += addFields(this.templateFields.basic);
            
            // 图片位置
            template += addFields(this.templateFields.image);
            
            // 声优信息
            template += addFields(this.templateFields.voice);
            
            // 角色档案
            template += addFields(this.templateFields.profile);
            
            // 技能信息
            template += addFields(this.templateFields.skills);
            
            // 属性数值
            template += addFields(this.templateFields.stats);
            
            // 移除末尾多余的空行
            template = template.replace(/\n+$/, '\n');
            
            template += '}}\n[[分类:伙伴]]';
            return template;
        },
        
        // 收集表单数据
        collectFormData: function() {
            var data = {};
            document.querySelectorAll('[data-field]').forEach(function(input) {
                var field = input.getAttribute('data-field');
                data[field] = input.value;
            });
            return data;
        },
        
        // 保存伙伴
        savePartner: function() {
            var self = this;
            var title = document.getElementById('page-title').value.trim();
            
            if (!title) {
                this.showMessage('请输入页面标题', 'error');
                return;
            }
            
            var data = this.collectFormData();
            var content = this.generateTemplate(data);
            
            this.showMessage('保存中...', 'info');
            
            this.api.postWithToken('csrf', {
                action: 'edit',
                title: title,
                text: content,
                summary: '通过伙伴管理器更新',
                format: 'json'
            }).done(function() {
                self.showMessage('保存成功!', 'success');
                self.currentPartner.title = title;
                self.currentPartner.content = content;
                self.loadPartnerList();
            }).fail(function(code, error) {
                console.error('保存失败:', code, error);
                self.showMessage('保存失败: ' + (error.error ? error.error.info : '未知错误'), 'error');
            });
        },
        
        // 预览
        previewPartner: function() {
            var data = this.collectFormData();
            var content = this.generateTemplate(data);
            
            var preview = document.getElementById('preview-container');
            preview.innerHTML = '<h4>预览</h4><pre style="background: #f5f5f5; padding: 10px; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;">' + 
                                mw.html.escape(content) + '</pre>';
        },
        
        // 查看源码
        viewSource: function() {
            var data = this.collectFormData();
            var content = this.generateTemplate(data);
            
            var preview = document.getElementById('preview-container');
            preview.innerHTML = '<h4>Wiki源码</h4>' +
                                '<textarea class="agent-textarea" style="min-height: 400px;">' + 
                                mw.html.escape(content) + '</textarea>';
        },
        
        // 导出所有
        exportAll: function() {
            var self = this;
            var result = document.getElementById('batch-result');
            result.innerHTML = '<div class="agent-loading">导出中...</div>';
            
            var exportData = [];
            var promises = [];
            
            this.partnerList.forEach(function(partner) {
                var promise = self.api.get({
                    action: 'query',
                    titles: partner.title,
                    prop: 'revisions',
                    rvprop: 'content',
                    rvslots: 'main',
                    format: 'json'
                }).then(function(data) {
                    var pages = data.query.pages;
                    var pageId = Object.keys(pages)[0];
                    if (pageId !== '-1') {
                        var content = pages[pageId].revisions[0].slots.main['*'];
                        exportData.push({
                            title: partner.title,
                            content: content
                        });
                    }
                });
                promises.push(promise);
            });
            
            Promise.all(promises).then(function() {
                var json = JSON.stringify(exportData, null, 2);
                result.innerHTML = '<h4>导出结果 (' + exportData.length + '个伙伴)</h4>' +
                                   '<textarea class="agent-textarea" style="min-height: 400px;">' + 
                                   mw.html.escape(json) + '</textarea>';
            });
        },
        
        // 检查模板完整性
        checkTemplates: function() {
            var self = this;
            var result = document.getElementById('batch-result');
            result.innerHTML = '<div class="agent-loading">检查中...</div>';
            
            var issues = [];
            var promises = [];
            
            this.partnerList.forEach(function(partner) {
                var promise = self.api.get({
                    action: 'query',
                    titles: partner.title,
                    prop: 'revisions',
                    rvprop: 'content',
                    rvslots: 'main',
                    format: 'json'
                }).then(function(data) {
                    var pages = data.query.pages;
                    var pageId = Object.keys(pages)[0];
                    if (pageId !== '-1') {
                        var content = pages[pageId].revisions[0].slots.main['*'];
                        var parsedData = self.parseTemplate(content);
                        
                        var missingFields = [];
                        // 只检查关键字段
                        var requiredFields = [
                            { name: 'id', label: 'ID' },
                            { name: '名称', label: '名称' },
                            { name: '稀有度', label: '稀有度' },
                            { name: '职业', label: '职业' }
                        ];
                        
                        requiredFields.forEach(function(field) {
                            if (!parsedData[field.name] || parsedData[field.name].trim() === '') {
                                missingFields.push(field.label);
                            }
                        });
                        
                        if (missingFields.length > 0) {
                            issues.push({
                                title: partner.title,
                                missing: missingFields
                            });
                        }
                    }
                });
                promises.push(promise);
            });
            
            Promise.all(promises).then(function() {
                var html = '<h4>模板完整性检查结果</h4>';
                if (issues.length === 0) {
                    html += '<div class="agent-message success">所有伙伴模板完整!</div>';
                } else {
                    html += '<div class="agent-message error">发现 ' + issues.length + ' 个页面存在问题:</div>';
                    html += '<div style="max-height: 400px; overflow-y: auto;">';
                    issues.forEach(function(issue) {
                        html += '<div style="margin: 10px 0; padding: 10px; background: white; border: 1px solid #ddd;">';
                        html += '<strong>' + mw.html.escape(issue.title) + '</strong><br>';
                        html += '缺失字段: ' + mw.html.escape(issue.missing.join(', '));
                        html += '</div>';
                    });
                    html += '</div>';
                }
                result.innerHTML = html;
            });
        },
        
        // 显示消息
        showMessage: function(message, type) {
            var container = document.getElementById('partner-message');
            container.innerHTML = '<div class="agent-message ' + type + '">' + mw.html.escape(message) + '</div>';
            
            setTimeout(function() {
                container.innerHTML = '';
            }, 5000);
        }
    };
    
    // 页面加载完成后初始化
    $(function() {
        PartnerManager.init();
    });
    
})();