打开/关闭菜单
443
1222
41
4822
植物大战僵尸杂交版Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

微件:BatchUpload:修订间差异

来自植物大战僵尸杂交版Wiki
创建页面,内容为“<includeonly> <style> .batch-upload-container { margin: 20px 0; padding: 20px; border: 2px dashed #ccc; border-radius: 12px; text-align: center; background: #fafafa; } .batch-upload-container.dragover { border-color: #4CAF50; background: #f0fff0; } .batch-upload-input { display: none; } .batch-upload-btn { display: inline-block; padding: 10px 30px; background: #4CAF50; color: #fff; border: none; border…”
 
无编辑摘要
第12行: 第12行:
     border-color: #4CAF50;
     border-color: #4CAF50;
     background: #f0fff0;
     background: #f0fff0;
}
.batch-upload-input {
    display: none;
}
}
.batch-upload-btn {
.batch-upload-btn {
     display: inline-block;
     display: inline-block;
     padding: 10px 30px;
     padding: 12px 30px;
     background: #4CAF50;
     background: #4CAF50;
     color: #fff;
     color: #fff;
第25行: 第22行:
     font-size: 16px;
     font-size: 16px;
     cursor: pointer;
     cursor: pointer;
     margin: 10px;
     margin: 8px;
}
.batch-upload-btn:hover {
    background: #45a049;
}
.batch-upload-btn:disabled {
    background: #ccc;
    cursor: not-allowed;
}
}
.batch-upload-btn:hover { background: #45a049; }
.batch-upload-btn:disabled { background: #ccc; cursor: not-allowed; }
.batch-upload-list {
.batch-upload-list {
     margin-top: 15px;
     margin-top: 15px;
第48行: 第40行:
     gap: 10px;
     gap: 10px;
}
}
.batch-upload-item .file-info {
.batch-upload-item .file-info { flex: 1; min-width: 0; }
    flex: 1;
.batch-upload-item .file-original { font-size: 12px; color: #999; word-break: break-all; }
    min-width: 0;
}
.batch-upload-item .file-original {
    font-size: 12px;
    color: #999;
    word-break: break-all;
}
.batch-upload-item .file-rename {
.batch-upload-item .file-rename {
     width: 200px;
     width: 200px; padding: 4px 8px; border: 1px solid #ccc;
    padding: 4px 8px;
     border-radius: 4px; font-size: 13px;
    border: 1px solid #ccc;
     border-radius: 4px;
    font-size: 13px;
}
.batch-upload-item .file-rename:focus {
    border-color: #4CAF50;
    outline: none;
}
.batch-upload-item .file-status {
    font-size: 12px;
    min-width: 60px;
    text-align: right;
}
}
.batch-upload-item .file-rename:focus { border-color: #4CAF50; outline: none; }
.batch-upload-item .file-status { font-size: 12px; min-width: 60px; text-align: right; }
.status-waiting { color: #999; }
.status-waiting { color: #999; }
.status-uploading { color: #2196F3; }
.status-uploading { color: #2196F3; }
第78行: 第53行:
.status-error { color: #f44336; }
.status-error { color: #f44336; }
.batch-upload-progress {
.batch-upload-progress {
     width: 100%;
     width: 100%; height: 6px; background: #e0e0e0;
    height: 6px;
     border-radius: 3px; margin: 15px 0; overflow: hidden; display: none;
    background: #e0e0e0;
     border-radius: 3px;
    margin: 15px 0;
    overflow: hidden;
    display: none;
}
}
.batch-upload-progress-bar {
.batch-upload-progress-bar {
     height: 100%;
     height: 100%; background: #4CAF50; border-radius: 3px;
    background: #4CAF50;
     transition: width 0.3s; width: 0%;
    border-radius: 3px;
     transition: width 0.3s;
    width: 0%;
}
.batch-upload-summary {
    margin-top: 10px;
    font-size: 14px;
    color: #555;
    display: none;
}
.batch-upload-actions {
    margin-top: 10px;
    display: flex;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
}
.batch-upload-remove {
    color: #f44336;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
}
.batch-upload-remove:hover {
    color: #d32f2f;
}
.batch-upload-ext {
    font-size: 12px;
    color: #999;
    margin-left: 2px;
}
}
.batch-upload-summary { margin-top: 10px; font-size: 14px; color: #555; display: none; }
.batch-upload-actions { margin-top: 10px; display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; }
.batch-upload-remove { color: #f44336; cursor: pointer; font-size: 18px; line-height: 1; }
.batch-upload-remove:hover { color: #d32f2f; }
.batch-upload-ext { font-size: 12px; color: #999; margin-left: 2px; }
</style>
</style>


第125行: 第70行:
     <h3>📤 批量上传图片</h3>
     <h3>📤 批量上传图片</h3>
     <p style="color:#888;font-size:14px;">支持一次选择多张图片,或拖拽图片到此处</p>
     <p style="color:#888;font-size:14px;">支持一次选择多张图片,或拖拽图片到此处</p>
    <input type="file" class="batch-upload-input" id="batch-upload-input" multiple accept="image/*">
     <div class="batch-upload-actions">
     <div class="batch-upload-actions">
         <button class="batch-upload-btn" id="batch-upload-select">选择图片</button>
         <button class="batch-upload-btn" id="batch-upload-select">选择图片</button>
第140行: 第84行:
<script>
<script>
(function() {
(function() {
     var $container = $('#batch-upload-container');
     var $container = document.getElementById('batch-upload-container');
    var $input = $('#batch-upload-input');
     var $selectBtn = document.getElementById('batch-upload-select');
     var $selectBtn = $('#batch-upload-select');
     var $clearBtn = document.getElementById('batch-upload-clear');
     var $clearBtn = $('#batch-upload-clear');
     var $startBtn = document.getElementById('batch-upload-start');
     var $startBtn = $('#batch-upload-start');
     var $list = document.getElementById('batch-upload-list');
     var $list = $('#batch-upload-list');
     var $progress = document.getElementById('batch-upload-progress');
     var $progress = $('#batch-upload-progress');
     var $progressBar = document.getElementById('batch-upload-progress-bar');
     var $progressBar = $('#batch-upload-progress-bar');
     var $summary = document.getElementById('batch-upload-summary');
     var $summary = $('#batch-upload-summary');


     var files = [];
     var files = [];
第155行: 第98行:
     var isUploading = false;
     var isUploading = false;


    // 获取文件扩展名
     function getExt(filename) {
     function getExt(filename) {
         var lastDot = filename.lastIndexOf('.');
         var lastDot = filename.lastIndexOf('.');
第161行: 第103行:
     }
     }


    // 获取不带扩展名的文件名
     function getNameWithoutExt(filename) {
     function getNameWithoutExt(filename) {
         var lastDot = filename.lastIndexOf('.');
         var lastDot = filename.lastIndexOf('.');
第167行: 第108行:
     }
     }


     $selectBtn.click(function() {
     // 创建隐藏的 input[type=file]
         $input.click();
    var fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.multiple = true;
    fileInput.accept = 'image/*';
    fileInput.style.display = 'none';
    document.body.appendChild(fileInput);
 
    fileInput.addEventListener('change', function() {
         addFiles(this.files);
        this.value = '';
     });
     });


     $input.change(function() {
     $selectBtn.addEventListener('click', function() {
         addFiles(this.files);
         fileInput.click();
        $input.val('');
     });
     });


     $container.on('dragover', function(e) {
    // 拖拽
     $container.addEventListener('dragover', function(e) {
         e.preventDefault();
         e.preventDefault();
         $container.addClass('dragover');
         $container.classList.add('dragover');
     });
     });
 
     $container.addEventListener('dragleave', function() {
     $container.on('dragleave', function() {
         $container.classList.remove('dragover');
         $container.removeClass('dragover');
     });
     });
 
     $container.addEventListener('drop', function(e) {
     $container.on('drop', function(e) {
         e.preventDefault();
         e.preventDefault();
         $container.removeClass('dragover');
         $container.classList.remove('dragover');
         addFiles(e.originalEvent.dataTransfer.files);
         addFiles(e.dataTransfer.files);
     });
     });


     $clearBtn.click(function() {
     $clearBtn.addEventListener('click', function() {
         files = [];
         files = [];
         renderList();
         renderList();
         updateStartButton();
         updateStartButton();
         $summary.hide();
         $summary.style.display = 'none';
     });
     });


第202行: 第150行:
             var file = newFiles[i];
             var file = newFiles[i];
             if (file.type.match(/^image\//)) {
             if (file.type.match(/^image\//)) {
                // 存储原始文件和目标文件名
                 file._targetName = getNameWithoutExt(file.name);
                 file._targetName = getNameWithoutExt(file.name);
                 file._ext = getExt(file.name);
                 file._ext = getExt(file.name);
第213行: 第160行:


     function renderList() {
     function renderList() {
         $list.empty();
         $list.innerHTML = '';
         if (files.length === 0) {
         if (files.length === 0) {
             $list.append('<p style="color:#999;text-align:center;">暂无文件</p>');
             $list.innerHTML = '<p style="color:#999;text-align:center;">暂无文件</p>';
             return;
             return;
         }
         }
         files.forEach(function(file, index) {
         files.forEach(function(file, index) {
             var sizeStr = file.size > 1024 * 1024 ? (file.size / (1024 * 1024)).toFixed(1) + ' MB' : (file.size / 1024).toFixed(0) + ' KB';
             var sizeStr = file.size > 1024 * 1024 ? (file.size / (1024 * 1024)).toFixed(1) + ' MB' : (file.size / 1024).toFixed(0) + ' KB';
             var $item = $('<div>', { class: 'batch-upload-item' });
             var item = document.createElement('div');
 
            item.className = 'batch-upload-item';
             // 文件信息
             item.innerHTML =
            var $info = $('<div>', { class: 'file-info' });
                '<div class="file-info">' +
            $info.append('<input type="text" class="file-rename" value="' + file._targetName + '" data-index="' + index + '">');
                    '<input type="text" class="file-rename" value="' + file._targetName + '" data-index="' + index + '">' +
            $info.append('<span class="batch-upload-ext">' + file._ext + '</span>');
                    '<span class="batch-upload-ext">' + file._ext + '</span>' +
            $info.append('<div class="file-original">原始: ' + file.name + ' (' + sizeStr + ')</div>');
                    '<div class="file-original">原始: ' + file.name + ' (' + sizeStr + ')</div>' +
            $item.append($info);
                '</div>' +
 
                '<span class="file-status status-waiting" data-index="' + index + '">等待上传</span>' +
            // 状态
                '<span class="batch-upload-remove" data-index="' + index + '" title="移除">×</span>';
            $item.append('<span class="file-status status-waiting" data-index="' + index + '">等待上传</span>');
             $list.appendChild(item);
 
            // 删除
            $item.append('<span class="batch-upload-remove" data-index="' + index + '" title="移除">×</span>');
 
             $list.append($item);
         });
         });


         // 绑定改名事件
         // 改名
         $('.file-rename').off('input').on('input', function() {
         $list.querySelectorAll('.file-rename').forEach(function(input) {
            var idx = parseInt($(this).data('index'));
            input.addEventListener('input', function() {
            if (files[idx]) {
                var idx = parseInt(this.getAttribute('data-index'));
                files[idx]._targetName = $(this).val().trim();
                if (files[idx]) files[idx]._targetName = this.value.trim();
             }
             });
         });
         });


         // 绑定删除事件
         // 删除
         $('.batch-upload-remove').off('click').click(function() {
         $list.querySelectorAll('.batch-upload-remove').forEach(function(btn) {
            var idx = parseInt($(this).data('index'));
            btn.addEventListener('click', function() {
            files.splice(idx, 1);
                var idx = parseInt(this.getAttribute('data-index'));
            renderList();
                files.splice(idx, 1);
            updateStartButton();
                renderList();
                updateStartButton();
            });
         });
         });
     }
     }


     function updateStartButton() {
     function updateStartButton() {
         $startBtn.prop('disabled', files.length === 0 || isUploading);
         $startBtn.disabled = files.length === 0 || isUploading;
     }
     }


第264行: 第208行:


     function uploadFile(file, index) {
     function uploadFile(file, index) {
         return new Promise(function(resolve, reject) {
         return new Promise(function(resolve) {
             var targetName = (file._targetName || getNameWithoutExt(file.name)) + file._ext;
             var targetName = (file._targetName || getNameWithoutExt(file.name)) + file._ext;
             var formData = new FormData();
             var formData = new FormData();
第274行: 第218行:
             formData.append('ignorewarnings', '1');
             formData.append('ignorewarnings', '1');


             $.ajax({
             var xhr = new XMLHttpRequest();
                url: mw.util.wikiScript('api'),
            xhr.open('POST', mw.util.wikiScript('api'), true);
                 type: 'POST',
            xhr.onload = function() {
                data: formData,
                 try {
                processData: false,
                    var data = JSON.parse(xhr.responseText);
                contentType: false,
                success: function(data) {
                     if (data.upload && data.upload.result === 'Success') {
                     if (data.upload && data.upload.result === 'Success') {
                         resolve({ success: true, name: targetName });
                         resolve({ success: true, name: targetName });
                     } else {
                     } else {
                         var errMsg = '未知错误';
                         var errMsg = '未知错误';
                         if (data.upload && data.upload.warnings) {
                         if (data.upload && data.upload.warnings) errMsg = JSON.stringify(data.upload.warnings);
                            errMsg = JSON.stringify(data.upload.warnings);
                         else if (data.error) errMsg = data.error.info || '未知错误';
                         } else if (data.error) {
                            errMsg = data.error.info || '未知错误';
                        }
                         resolve({ success: false, name: targetName, error: errMsg });
                         resolve({ success: false, name: targetName, error: errMsg });
                     }
                     }
                 },
                 } catch(e) {
                error: function(xhr) {
                     resolve({ success: false, name: targetName, error: '解析响应失败' });
                     resolve({ success: false, name: targetName, error: 'HTTP ' + xhr.status });
                 }
                 }
             });
             };
            xhr.onerror = function() {
                resolve({ success: false, name: targetName, error: '网络错误' });
            };
            xhr.send(formData);
         });
         });
     }
     }


     $startBtn.click(function() {
     $startBtn.addEventListener('click', function() {
         if (isUploading || files.length === 0) return;
         if (isUploading || files.length === 0) return;
         isUploading = true;
         isUploading = true;
第306行: 第248行:
         totalCount = files.length;
         totalCount = files.length;
         updateStartButton();
         updateStartButton();
         $selectBtn.prop('disabled', true);
         $selectBtn.disabled = true;
         $clearBtn.prop('disabled', true);
         $clearBtn.disabled = true;
         $progress.show();
         $progress.style.display = 'block';
         $summary.hide();
         $summary.style.display = 'none';
         $progressBar.css('width', '0%');
         $progressBar.style.width = '0%';
         $('.file-rename').prop('disabled', true);
         $list.querySelectorAll('.file-rename').forEach(function(i) { i.disabled = true; });
         $('.batch-upload-remove').hide();
         $list.querySelectorAll('.batch-upload-remove').forEach(function(i) { i.style.display = 'none'; });


         var uploadQueue = Promise.resolve();
         var uploadQueue = Promise.resolve();
         files.forEach(function(file, index) {
         files.forEach(function(file, index) {
             uploadQueue = uploadQueue.then(function() {
             uploadQueue = uploadQueue.then(function() {
                 $('.file-status[data-index="' + index + '"]')
                 var statusEl = $list.querySelector('.file-status[data-index="' + index + '"]');
                     .removeClass('status-waiting')
                if (statusEl) {
                     .addClass('status-uploading')
                     statusEl.classList.remove('status-waiting');
                     .text('上传中...');
                     statusEl.classList.add('status-uploading');
 
                     statusEl.textContent = '上传中...';
                }
                 return uploadFile(file, index).then(function(result) {
                 return uploadFile(file, index).then(function(result) {
                     uploadedCount++;
                     uploadedCount++;
                     var progress = Math.round((uploadedCount / totalCount) * 100);
                     var progress = Math.round((uploadedCount / totalCount) * 100);
                     $progressBar.css('width', progress + '%');
                     $progressBar.style.width = progress + '%';
 
                     if (statusEl) {
                     if (result.success) {
                         statusEl.classList.remove('status-uploading');
                         $('.file-status[data-index="' + index + '"]')
                        if (result.success) {
                            .removeClass('status-uploading')
                             statusEl.classList.add('status-success');
                             .addClass('status-success')
                             statusEl.textContent = '✅ 成功';
                             .text('✅ 成功');
                        } else {
                    } else {
                             statusEl.classList.add('status-error');
                        $('.file-status[data-index="' + index + '"]')
                             statusEl.textContent = '❌ 失败';
                             .removeClass('status-uploading')
                         }
                            .addClass('status-error')
                             .text('❌ 失败');
                         console.log('上传失败:', result.name, result.error);
                     }
                     }
                 });
                 });
第345行: 第285行:
         uploadQueue.then(function() {
         uploadQueue.then(function() {
             isUploading = false;
             isUploading = false;
             $selectBtn.prop('disabled', false);
             $selectBtn.disabled = false;
             $clearBtn.prop('disabled', false);
             $clearBtn.disabled = false;
             updateStartButton();
             updateStartButton();
             var successCount = $('.status-success').length;
             var successCount = $list.querySelectorAll('.status-success').length;
             var failCount = $('.status-error').length;
             var failCount = $list.querySelectorAll('.status-error').length;
             $summary.show().html('上传完成!成功 ' + successCount + ' 张,失败 ' + failCount + ' 张。');
             $summary.style.display = 'block';
            $summary.innerHTML = '上传完成!成功 ' + successCount + ' 张,失败 ' + failCount + ' 张。';
             files = [];
             files = [];
            // 保留列表显示结果,5 秒后可清空
             setTimeout(function() {
             setTimeout(function() {
                 if (files.length === 0 && !isUploading) {
                 if (files.length === 0 && !isUploading) {
                     renderList();
                     renderList();
                     $progress.hide();
                     $progress.style.display = 'none';
                 }
                 }
             }, 5000);
             }, 5000);

2026年6月10日 (三) 11:39的版本