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

微件:BatchUpload:修订间差异

来自植物大战僵尸杂交版Wiki
无编辑摘要
无编辑摘要
第47行: 第47行:
}
}
.batch-upload-item .file-rename:focus { border-color: #4CAF50; outline: none; }
.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-status { font-size: 12px; min-width: 70px; text-align: right; }
.status-waiting { color: #999; }
.status-waiting { color: #999; }
.status-uploading { color: #2196F3; }
.status-uploading { color: #2196F3; }
.status-success { color: #4CAF50; }
.status-success { color: #4CAF50; }
.status-error { color: #f44336; }
.status-error { color: #f44336; }
.status-conflict { color: #FF9800; }
.status-skipped { color: #9E9E9E; }
.batch-upload-progress {
.batch-upload-progress {
     width: 100%; height: 6px; background: #e0e0e0;
     width: 100%; height: 6px; background: #e0e0e0;
第65行: 第67行:
.batch-upload-remove:hover { color: #d32f2f; }
.batch-upload-remove:hover { color: #d32f2f; }
.batch-upload-ext { font-size: 12px; color: #999; margin-left: 2px; }
.batch-upload-ext { font-size: 12px; color: #999; margin-left: 2px; }
.conflict-actions { display: inline-flex; gap: 4px; margin-left: 8px; }
.conflict-btn {
    padding: 2px 8px; font-size: 11px; border: 1px solid #ccc;
    border-radius: 3px; cursor: pointer; background: #f9f9f9;
}
.conflict-btn.overwrite { border-color: #4CAF50; color: #4CAF50; }
.conflict-btn.overwrite:hover { background: #4CAF50; color: #fff; }
.conflict-btn.skip { border-color: #f44336; color: #f44336; }
.conflict-btn.skip:hover { background: #f44336; color: #fff; }
.conflict-notice {
    background: #fff3e0; border: 1px solid #FF9800; border-radius: 8px;
    padding: 10px 15px; margin: 10px 0; display: none; text-align: left; font-size: 14px;
}
.conflict-notice button {
    padding: 6px 15px; margin: 5px 5px 0 0; border: none;
    border-radius: 5px; cursor: pointer; font-size: 13px; color: #fff;
}
.conflict-notice .btn-overwrite-all { background: #4CAF50; }
.conflict-notice .btn-skip-all { background: #f44336; }
</style>
</style>


第73行: 第94行:
         <button class="batch-upload-btn" id="batch-upload-select">选择图片</button>
         <button class="batch-upload-btn" id="batch-upload-select">选择图片</button>
         <button class="batch-upload-btn" id="batch-upload-clear">清空列表</button>
         <button class="batch-upload-btn" id="batch-upload-clear">清空列表</button>
    </div>
    <div class="conflict-notice" id="conflict-notice">
        <strong>⚠️ 检测到重名文件</strong>,请为每个文件选择"覆盖"或"跳过":
        <div id="conflict-actions-global"></div>
     </div>
     </div>
     <div class="batch-upload-progress" id="batch-upload-progress">
     <div class="batch-upload-progress" id="batch-upload-progress">
第79行: 第104行:
     <div class="batch-upload-list" id="batch-upload-list"></div>
     <div class="batch-upload-list" id="batch-upload-list"></div>
     <div class="batch-upload-summary" id="batch-upload-summary"></div>
     <div class="batch-upload-summary" id="batch-upload-summary"></div>
     <button class="batch-upload-btn" id="batch-upload-start" disabled>开始上传</button>
     <button class="batch-upload-btn" id="batch-upload-start" disabled>检测并上传</button>
</div>
</div>


第92行: 第117行:
     var $progressBar = document.getElementById('batch-upload-progress-bar');
     var $progressBar = document.getElementById('batch-upload-progress-bar');
     var $summary = document.getElementById('batch-upload-summary');
     var $summary = document.getElementById('batch-upload-summary');
    var $conflictNotice = document.getElementById('conflict-notice');
    var $conflictGlobal = document.getElementById('conflict-actions-global');


     var files = [];
     var files = [];
第97行: 第124行:
     var totalCount = 0;
     var totalCount = 0;
     var isUploading = false;
     var isUploading = false;
    var conflictResolved = false;


     function getExt(filename) {
     function getExt(filename) {
第108行: 第136行:
     }
     }


    // 创建隐藏的 input[type=file]
     var fileInput = document.createElement('input');
     var fileInput = document.createElement('input');
     fileInput.type = 'file';
     fileInput.type = 'file';
第125行: 第152行:
     });
     });


    // 拖拽
     $container.addEventListener('dragover', function(e) {
     $container.addEventListener('dragover', function(e) {
         e.preventDefault();
         e.preventDefault();
第141行: 第167行:
     $clearBtn.addEventListener('click', function() {
     $clearBtn.addEventListener('click', function() {
         files = [];
         files = [];
        conflictResolved = false;
        $conflictNotice.style.display = 'none';
         renderList();
         renderList();
         updateStartButton();
         updateStartButton();
第152行: 第180行:
                 file._targetName = getNameWithoutExt(file.name);
                 file._targetName = getNameWithoutExt(file.name);
                 file._ext = getExt(file.name);
                 file._ext = getExt(file.name);
                file._conflictAction = 'ask'; // ask | overwrite | skip
                 files.push(file);
                 files.push(file);
             }
             }
         }
         }
        conflictResolved = false;
        $conflictNotice.style.display = 'none';
         renderList();
         renderList();
         updateStartButton();
         updateStartButton();
第169行: 第200行:
             var item = document.createElement('div');
             var item = document.createElement('div');
             item.className = 'batch-upload-item';
             item.className = 'batch-upload-item';
            item.setAttribute('data-index', index);
            var actionHtml = '';
            if (file._conflictAction === 'overwrite') {
                actionHtml = '<span class="conflict-actions"><span style="color:#4CAF50;font-size:12px;">覆盖</span></span>';
            } else if (file._conflictAction === 'skip') {
                actionHtml = '<span class="conflict-actions"><span style="color:#f44336;font-size:12px;">跳过</span></span>';
            }
             item.innerHTML =
             item.innerHTML =
                 '<div class="file-info">' +
                 '<div class="file-info">' +
第175行: 第215行:
                     '<div class="file-original">原始: ' + file.name + ' (' + sizeStr + ')</div>' +
                     '<div class="file-original">原始: ' + file.name + ' (' + sizeStr + ')</div>' +
                 '</div>' +
                 '</div>' +
                 '<span class="file-status status-waiting" data-index="' + index + '">等待上传</span>' +
                actionHtml +
                 '<span class="file-status status-waiting" data-index="' + index + '">等待检测</span>' +
                 '<span class="batch-upload-remove" data-index="' + index + '" title="移除">×</span>';
                 '<span class="batch-upload-remove" data-index="' + index + '" title="移除">×</span>';
             $list.appendChild(item);
             $list.appendChild(item);
         });
         });


        // 改名
         $list.querySelectorAll('.file-rename').forEach(function(input) {
         $list.querySelectorAll('.file-rename').forEach(function(input) {
             input.addEventListener('input', function() {
             input.addEventListener('input', function() {
                 var idx = parseInt(this.getAttribute('data-index'));
                 var idx = parseInt(this.getAttribute('data-index'));
                 if (files[idx]) files[idx]._targetName = this.value.trim();
                 if (files[idx]) {
                    files[idx]._targetName = this.value.trim();
                    files[idx]._conflictAction = 'ask';
                    conflictResolved = false;
                    $conflictNotice.style.display = 'none';
                }
             });
             });
         });
         });


        // 删除
         $list.querySelectorAll('.batch-upload-remove').forEach(function(btn) {
         $list.querySelectorAll('.batch-upload-remove').forEach(function(btn) {
             btn.addEventListener('click', function() {
             btn.addEventListener('click', function() {
第201行: 第245行:
     function updateStartButton() {
     function updateStartButton() {
         $startBtn.disabled = files.length === 0 || isUploading;
         $startBtn.disabled = files.length === 0 || isUploading;
        if (conflictResolved) {
            $startBtn.textContent = '开始上传';
        } else {
            $startBtn.textContent = '检测并上传';
        }
     }
     }


     function getEditToken() {
     function getEditToken() {
         return mw.user.tokens.get('csrfToken');
         return mw.user.tokens.get('csrfToken');
    }
    // 检查文件是否存在
    function checkFileExists(filename) {
        return new Promise(function(resolve) {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', mw.util.wikiScript('api') + '?action=query&titles=File:' + encodeURIComponent(filename) + '&format=json&origin=*', true);
            xhr.onload = function() {
                try {
                    var data = JSON.parse(xhr.responseText);
                    var pages = data.query.pages;
                    for (var id in pages) {
                        if (parseInt(id) > 0) { resolve(true); return; }
                    }
                    resolve(false);
                } catch(e) { resolve(false); }
            };
            xhr.onerror = function() { resolve(false); };
            xhr.send();
        });
     }
     }


第242行: 第311行:
     }
     }


    // 显示冲突处理 UI
    function showConflictUI(conflicts) {
        $conflictNotice.style.display = 'block';
        $conflictGlobal.innerHTML =
            '<button class="btn-overwrite-all">全部覆盖</button>' +
            '<button class="btn-skip-all">全部跳过</button>';
        $conflictGlobal.querySelector('.btn-overwrite-all').addEventListener('click', function() {
            conflicts.forEach(function(idx) { files[idx]._conflictAction = 'overwrite'; });
            conflictResolved = true;
            $conflictNotice.style.display = 'none';
            renderList();
            updateStartButton();
        });
        $conflictGlobal.querySelector('.btn-skip-all').addEventListener('click', function() {
            conflicts.forEach(function(idx) { files[idx]._conflictAction = 'skip'; });
            conflictResolved = true;
            $conflictNotice.style.display = 'none';
            renderList();
            updateStartButton();
        });
        // 在列表中为每个冲突项添加覆盖/跳过按钮
        conflicts.forEach(function(idx) {
            var item = $list.querySelector('.batch-upload-item[data-index="' + idx + '"]');
            if (!item) return;
            var statusEl = item.querySelector('.file-status');
            if (statusEl) {
                statusEl.classList.remove('status-waiting');
                statusEl.classList.add('status-conflict');
                statusEl.textContent = '⚠️ 重名';
            }
            // 移除旧的动作区
            var oldActions = item.querySelector('.conflict-actions');
            if (oldActions) oldActions.remove();
            var actions = document.createElement('span');
            actions.className = 'conflict-actions';
            actions.innerHTML =
                '<button class="conflict-btn overwrite" data-idx="' + idx + '">覆盖</button>' +
                '<button class="conflict-btn skip" data-idx="' + idx + '">跳过</button>';
            item.appendChild(actions);
        });
        // 绑定单条操作
        $list.querySelectorAll('.conflict-btn.overwrite').forEach(function(btn) {
            btn.addEventListener('click', function(e) {
                e.stopPropagation();
                var idx = parseInt(this.getAttribute('data-idx'));
                files[idx]._conflictAction = 'overwrite';
                var item = $list.querySelector('.batch-upload-item[data-index="' + idx + '"]');
                var statusEl = item.querySelector('.file-status');
                statusEl.classList.remove('status-conflict');
                statusEl.classList.add('status-waiting');
                statusEl.textContent = '等待上传';
                var actionsEl = item.querySelector('.conflict-actions');
                if (actionsEl) actionsEl.innerHTML = '<span style="color:#4CAF50;font-size:12px;">覆盖</span>';
                checkAllConflictsResolved();
            });
        });
        $list.querySelectorAll('.conflict-btn.skip').forEach(function(btn) {
            btn.addEventListener('click', function(e) {
                e.stopPropagation();
                var idx = parseInt(this.getAttribute('data-idx'));
                files[idx]._conflictAction = 'skip';
                var item = $list.querySelector('.batch-upload-item[data-index="' + idx + '"]');
                var statusEl = item.querySelector('.file-status');
                statusEl.classList.remove('status-conflict');
                statusEl.classList.add('status-skipped');
                statusEl.textContent = '⏭️ 跳过';
                var actionsEl = item.querySelector('.conflict-actions');
                if (actionsEl) actionsEl.innerHTML = '<span style="color:#f44336;font-size:12px;">跳过</span>';
                checkAllConflictsResolved();
            });
        });
    }
    function checkAllConflictsResolved() {
        var allResolved = true;
        files.forEach(function(f) {
            if (f._conflictAction === 'ask') allResolved = false;
        });
        if (allResolved) {
            conflictResolved = true;
            $conflictNotice.style.display = 'none';
            updateStartButton();
        }
    }
    // 主流程:先检测再上传
     $startBtn.addEventListener('click', function() {
     $startBtn.addEventListener('click', function() {
        if (isUploading || files.length === 0) return;
        // 如果冲突已解决,直接上传
        if (conflictResolved) {
            startUpload();
            return;
        }
        // 先检测重名
        isUploading = true;
        updateStartButton();
        $selectBtn.disabled = true;
        $clearBtn.disabled = true;
        $startBtn.textContent = '检测中...';
        $list.querySelectorAll('.file-rename').forEach(function(i) { i.disabled = true; });
        $list.querySelectorAll('.batch-upload-remove').forEach(function(i) { i.style.display = 'none'; });
        var checkQueue = Promise.resolve();
        var conflicts = [];
        files.forEach(function(file, index) {
            checkQueue = checkQueue.then(function() {
                var targetName = (file._targetName || getNameWithoutExt(file.name)) + file._ext;
                return checkFileExists(targetName).then(function(exists) {
                    if (exists) {
                        conflicts.push(index);
                    } else {
                        var statusEl = $list.querySelector('.file-status[data-index="' + index + '"]');
                        if (statusEl) {
                            statusEl.classList.remove('status-waiting');
                            statusEl.classList.add('status-waiting');
                            statusEl.textContent = '✅ 新文件';
                        }
                    }
                });
            });
        });
        checkQueue.then(function() {
            isUploading = false;
            $selectBtn.disabled = false;
            $clearBtn.disabled = false;
            updateStartButton();
            if (conflicts.length > 0) {
                showConflictUI(conflicts);
            } else {
                conflictResolved = true;
                updateStartButton();
                startUpload();
            }
        });
    });
    function startUpload() {
         if (isUploading || files.length === 0) return;
         if (isUploading || files.length === 0) return;
         isUploading = true;
         isUploading = true;
         uploadedCount = 0;
         uploadedCount = 0;
         totalCount = files.length;
         totalCount = 0;
         updateStartButton();
         updateStartButton();
         $selectBtn.disabled = true;
         $selectBtn.disabled = true;
第255行: 第469行:
         $list.querySelectorAll('.file-rename').forEach(function(i) { i.disabled = true; });
         $list.querySelectorAll('.file-rename').forEach(function(i) { i.disabled = true; });
         $list.querySelectorAll('.batch-upload-remove').forEach(function(i) { i.style.display = 'none'; });
         $list.querySelectorAll('.batch-upload-remove').forEach(function(i) { i.style.display = 'none'; });
        $list.querySelectorAll('.conflict-actions').forEach(function(i) { i.style.display = 'none'; });
        // 过滤掉跳过的文件
        var toUpload = [];
        files.forEach(function(f, idx) {
            if (f._conflictAction !== 'skip') {
                toUpload.push({ file: f, index: idx });
            }
        });
        totalCount = toUpload.length;
        if (totalCount === 0) {
            isUploading = false;
            $selectBtn.disabled = false;
            $clearBtn.disabled = false;
            updateStartButton();
            $summary.style.display = 'block';
            $summary.innerHTML = '所有文件已跳过,没有上传。';
            $progress.style.display = 'none';
            files = [];
            return;
        }


         var uploadQueue = Promise.resolve();
         var uploadQueue = Promise.resolve();
         files.forEach(function(file, index) {
         toUpload.forEach(function(item) {
             uploadQueue = uploadQueue.then(function() {
             uploadQueue = uploadQueue.then(function() {
                 var statusEl = $list.querySelector('.file-status[data-index="' + index + '"]');
                 var statusEl = $list.querySelector('.file-status[data-index="' + item.index + '"]');
                 if (statusEl) {
                 if (statusEl) {
                     statusEl.classList.remove('status-waiting');
                     statusEl.classList.remove('status-waiting', 'status-conflict', 'status-skipped');
                     statusEl.classList.add('status-uploading');
                     statusEl.classList.add('status-uploading');
                     statusEl.textContent = '上传中...';
                     statusEl.textContent = '上传中...';
                 }
                 }
                 return uploadFile(file, index).then(function(result) {
                 return uploadFile(item.file, item.index).then(function(result) {
                     uploadedCount++;
                     uploadedCount++;
                     var progress = Math.round((uploadedCount / totalCount) * 100);
                     var progress = Math.round((uploadedCount / totalCount) * 100);
第290行: 第526行:
             var successCount = $list.querySelectorAll('.status-success').length;
             var successCount = $list.querySelectorAll('.status-success').length;
             var failCount = $list.querySelectorAll('.status-error').length;
             var failCount = $list.querySelectorAll('.status-error').length;
            var skipCount = $list.querySelectorAll('.status-skipped').length;
            var msg = '上传完成!成功 ' + successCount + ' 张';
            if (failCount > 0) msg += ',失败 ' + failCount + ' 张';
            if (skipCount > 0) msg += ',跳过 ' + skipCount + ' 张';
            msg += '。';
             $summary.style.display = 'block';
             $summary.style.display = 'block';
             $summary.innerHTML = '上传完成!成功 ' + successCount + ' 张,失败 ' + failCount + ' 张。';
             $summary.innerHTML = msg;
             files = [];
             files = [];
            conflictResolved = false;
             setTimeout(function() {
             setTimeout(function() {
                 if (files.length === 0 && !isUploading) {
                 if (files.length === 0 && !isUploading) {
第300行: 第542行:
             }, 5000);
             }, 5000);
         });
         });
     });
     }
})();
})();
</script>
</script>
</includeonly>
</includeonly>

2026年6月11日 (四) 12:43的版本