打开/关闭菜单
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…”
 
无编辑摘要
 
(未显示同一用户的2个中间版本)
第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: 85px; 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-retrying { color: #FF9800; }
.status-conflict { color: #FF9800; }
.status-skipped { color: #9E9E9E; }
.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 {
.batch-upload-summary { margin-top: 10px; font-size: 14px; color: #555; display: none; }
    margin-top: 10px;
.batch-upload-actions { margin-top: 10px; display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; }
    display: flex;
.batch-upload-remove { color: #f44336; cursor: pointer; font-size: 18px; line-height: 1; }
    justify-content: center;
.batch-upload-remove:hover { color: #d32f2f; }
    gap: 10px;
.batch-upload-ext { font-size: 12px; color: #999; margin-left: 2px; }
    flex-wrap: wrap;
.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;
}
}
.batch-upload-remove {
.conflict-btn.overwrite { border-color: #4CAF50; color: #4CAF50; }
    color: #f44336;
.conflict-btn.overwrite:hover { background: #4CAF50; color: #fff; }
    cursor: pointer;
.conflict-btn.skip { border-color: #f44336; color: #f44336; }
     font-size: 18px;
.conflict-btn.skip:hover { background: #f44336; color: #fff; }
     line-height: 1;
.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;
}
}
.batch-upload-remove:hover {
.conflict-notice button {
     color: #d32f2f;
     padding: 6px 15px; margin: 5px 5px 0 0; border: none;
}
    border-radius: 5px; cursor: pointer; font-size: 13px; color: #fff;
.batch-upload-ext {
    font-size: 12px;
    color: #999;
    margin-left: 2px;
}
}
.conflict-notice .btn-overwrite-all { background: #4CAF50; }
.conflict-notice .btn-skip-all { background: #f44336; }
</style>
</style>


第125行: 第92行:
     <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>
         <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">
第135行: 第105行:
     <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>


<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 $conflictNotice = document.getElementById('conflict-notice');
    var $conflictGlobal = document.getElementById('conflict-actions-global');


     var files = [];
     var files = [];
第154行: 第125行:
     var totalCount = 0;
     var totalCount = 0;
     var isUploading = false;
     var isUploading = false;
    var conflictResolved = false;
    var MAX_RETRIES = 3;
    var retryCounts = {};
    var currentFailedFiles = [];


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


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


     $selectBtn.click(function() {
     var fileInput = document.createElement('input');
         $input.click();
    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 = [];
        conflictResolved = false;
        retryCounts = {};
        currentFailedFiles = [];
        $conflictNotice.style.display = 'none';
         renderList();
         renderList();
         updateStartButton();
         updateStartButton();
         $summary.hide();
         $summary.style.display = 'none';
     });
     });


第202行: 第184行:
             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);
                file._conflictAction = 'ask';
                file._fileId = Date.now() + '_' + i + '_' + Math.random().toString(36).substr(2, 5);
                 files.push(file);
                 files.push(file);
                retryCounts[file._fileId] = 0;
             }
             }
         }
         }
        conflictResolved = false;
        $conflictNotice.style.display = 'none';
         renderList();
         renderList();
         updateStartButton();
         updateStartButton();
第213行: 第199行:


     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.setAttribute('data-index', index);


            // 文件信息
             var actionHtml = '';
             var $info = $('<div>', { class: 'file-info' });
             if (file._conflictAction === 'overwrite') {
             $info.append('<input type="text" class="file-rename" value="' + file._targetName + '" data-index="' + index + '">');
                actionHtml = '<span class="conflict-actions"><span style="color:#4CAF50;font-size:12px;">覆盖</span></span>';
            $info.append('<span class="batch-upload-ext">' + file._ext + '</span>');
             } else if (file._conflictAction === 'skip') {
             $info.append('<div class="file-original">原始: ' + file.name + ' (' + sizeStr + ')</div>');
                actionHtml = '<span class="conflict-actions"><span style="color:#f44336;font-size:12px;">跳过</span></span>';
             $item.append($info);
             }


             // 状态
             item.innerHTML =
            $item.append('<span class="file-status status-waiting" data-index="' + index + '">等待上传</span>');
                '<div class="file-info">' +
 
                    '<input type="text" class="file-rename" value="' + file._targetName + '" data-index="' + index + '">' +
            // 删除
                    '<span class="batch-upload-ext">' + file._ext + '</span>' +
            $item.append('<span class="batch-upload-remove" data-index="' + index + '" title="移除">×</span>');
                    '<div class="file-original">原始: ' + file.name + ' (' + sizeStr + ')</div>' +
 
                '</div>' +
             $list.append($item);
                actionHtml +
                '<span class="file-status status-waiting" data-index="' + index + '">等待检测</span>' +
                '<span class="batch-upload-remove" data-index="' + index + '" title="移除">×</span>';
             $list.appendChild(item);
         });
         });


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


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


     function updateStartButton() {
     function updateStartButton() {
         $startBtn.prop('disabled', files.length === 0 || isUploading);
         if (currentFailedFiles.length > 0) {
            $startBtn.disabled = false;
            $startBtn.textContent = '重试失败文件 (' + currentFailedFiles.length + ')';
            $startBtn.style.background = '#FF9800';
        } else if (conflictResolved) {
            $startBtn.disabled = false;
            $startBtn.textContent = '开始上传';
            $startBtn.style.background = '#4CAF50';
        } else {
            $startBtn.disabled = files.length === 0 || isUploading;
            $startBtn.textContent = '检测并上传';
            $startBtn.style.background = '#4CAF50';
        }
     }
     }


     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();
        });
     }
     }


     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行: 第302行:
             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, fileId: file._fileId });
                     } 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) {
                         resolve({ success: false, name: targetName, error: errMsg, fileId: file._fileId });
                            errMsg = data.error.info || '未知错误';
                    }
                         }
                } catch(e) {
                        resolve({ success: false, name: targetName, error: errMsg });
                    resolve({ success: false, name: targetName, error: '解析响应失败', fileId: file._fileId });
                }
            };
            xhr.onerror = function() {
                resolve({ success: false, name: targetName, error: '网络错误', fileId: file._fileId });
            };
            xhr.send(formData);
        });
    }
 
    function delay(ms) {
        return new Promise(function(resolve) { setTimeout(resolve, ms); });
    }
 
    function uploadWithRetry(file, index, attemptNumber) {
        attemptNumber = attemptNumber || 1;
        var statusEl = $list.querySelector('.file-status[data-index="' + index + '"]');
 
        if (attemptNumber > 1 && statusEl) {
            statusEl.classList.remove('status-error', 'status-uploading');
            statusEl.classList.add('status-retrying');
            statusEl.textContent = '重试 ' + (attemptNumber - 1) + '/' + MAX_RETRIES;
        }
 
        return uploadFile(file, index).then(function(result) {
            if (result.success) {
                if (statusEl) {
                    statusEl.classList.remove('status-uploading', 'status-retrying', 'status-error');
                    statusEl.classList.add('status-success');
                    statusEl.textContent = '✅ 成功';
                }
                retryCounts[file._fileId] = 0;
                uploadedCount++;
                var progress = Math.round((uploadedCount / totalCount) * 100);
                $progressBar.style.width = progress + '%';
                return { success: true, index: index };
            } else {
                if (attemptNumber < MAX_RETRIES) {
                    if (statusEl) {
                        statusEl.classList.remove('status-uploading', 'status-error');
                        statusEl.classList.add('status-retrying');
                        statusEl.textContent = '重试 ' + attemptNumber + '/' + MAX_RETRIES;
                    }
                    var waitTime = attemptNumber * 2000;
                    return delay(waitTime).then(function() {
                        return uploadWithRetry(file, index, attemptNumber + 1);
                    });
                } else {
                    if (statusEl) {
                        statusEl.classList.remove('status-uploading', 'status-retrying');
                        statusEl.classList.add('status-error');
                        statusEl.textContent = '❌ 失败(' + MAX_RETRIES + '次)';
                     }
                     }
                },
                    retryCounts[file._fileId] = MAX_RETRIES;
                error: function(xhr) {
                    uploadedCount++;
                     resolve({ success: false, name: targetName, error: 'HTTP ' + xhr.status });
                    var progress = Math.round((uploadedCount / totalCount) * 100);
                    $progressBar.style.width = progress + '%';
                    currentFailedFiles.push({ file: file, index: index, error: result.error });
                     return { success: false, index: index, error: result.error };
                 }
                 }
            }
        });
    }
    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.click(function() {
     $startBtn.addEventListener('click', function() {
        if (isUploading) return;
 
        // 如果有失败文件,点击重试
        if (currentFailedFiles.length > 0) {
            retryFailedFiles();
            return;
        }
 
        if (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;
        currentFailedFiles = [];
         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'; });
        $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) {
            finishUpload();
            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() {
                 $('.file-status[data-index="' + index + '"]')
                 var statusEl = $list.querySelector('.file-status[data-index="' + item.index + '"]');
                     .removeClass('status-waiting')
                if (statusEl) {
                     .addClass('status-uploading')
                     statusEl.classList.remove('status-waiting', 'status-conflict', 'status-skipped');
                     .text('上传中...');
                     statusEl.classList.add('status-uploading');
                     statusEl.textContent = '上传中...';
                }
                return uploadWithRetry(item.file, item.index);
            });
        });
 
        uploadQueue.then(function() {
            finishUpload();
        });
    }
 
    function retryFailedFiles() {
        if (isUploading || currentFailedFiles.length === 0) return;
        isUploading = true;
        $selectBtn.disabled = true;
        $clearBtn.disabled = true;
        $progress.style.display = 'block';
        $progressBar.style.width = '0%';
        uploadedCount = 0;
        totalCount = currentFailedFiles.length;
        updateStartButton();


                return uploadFile(file, index).then(function(result) {
        var toRetry = currentFailedFiles.slice();
                    uploadedCount++;
        currentFailedFiles = [];
                    var progress = Math.round((uploadedCount / totalCount) * 100);
                    $progressBar.css('width', progress + '%');


                    if (result.success) {
        var uploadQueue = Promise.resolve();
                        $('.file-status[data-index="' + index + '"]')
        toRetry.forEach(function(item) {
                            .removeClass('status-uploading')
            uploadQueue = uploadQueue.then(function() {
                            .addClass('status-success')
                retryCounts[item.file._fileId] = 0;
                            .text('✅ 成功');
                var statusEl = $list.querySelector('.file-status[data-index="' + item.index + '"]');
                    } else {
                if (statusEl) {
                        $('.file-status[data-index="' + index + '"]')
                    statusEl.classList.remove('status-error');
                            .removeClass('status-uploading')
                    statusEl.classList.add('status-uploading');
                            .addClass('status-error')
                    statusEl.textContent = '重试中...';
                            .text('❌ 失败');
                }
                        console.log('上传失败:', result.name, result.error);
                 return uploadWithRetry(item.file, item.index);
                    }
                 });
             });
             });
         });
         });


         uploadQueue.then(function() {
         uploadQueue.then(function() {
             isUploading = false;
             finishUpload();
            $selectBtn.prop('disabled', false);
        });
            $clearBtn.prop('disabled', false);
    }
            updateStartButton();
 
            var successCount = $('.status-success').length;
    function finishUpload() {
            var failCount = $('.status-error').length;
        isUploading = false;
            $summary.show().html('上传完成!成功 ' + successCount + ' 张,失败 ' + failCount + ' 张。');
        $selectBtn.disabled = false;
        $clearBtn.disabled = false;
        updateStartButton();
 
        var successCount = $list.querySelectorAll('.status-success').length;
        var failCount = $list.querySelectorAll('.status-error').length;
        var skipCount = $list.querySelectorAll('.status-skipped').length;
 
        var msgParts = [];
        if (successCount > 0) msgParts.push('成功 ' + successCount + ' 张');
        if (failCount > 0) msgParts.push('失败 ' + failCount + ' 张');
        if (skipCount > 0) msgParts.push('跳过 ' + skipCount + ' 张');
 
        $summary.style.display = 'block';
        $summary.innerHTML = '上传完成!' + msgParts.join(',') + '。';
 
        if (currentFailedFiles.length > 0) {
            $summary.innerHTML += ' <a href="javascript:void(0)" id="retry-link" style="color:#FF9800;font-weight:bold;">点击重试失败文件</a>';
        }
 
        if (currentFailedFiles.length === 0) {
             files = [];
             files = [];
             // 保留列表显示结果,5 秒后可清空
             conflictResolved = false;
             setTimeout(function() {
             setTimeout(function() {
                 if (files.length === 0 && !isUploading) {
                 if (files.length === 0 && !isUploading && currentFailedFiles.length === 0) {
                     renderList();
                     renderList();
                     $progress.hide();
                     $progress.style.display = 'none';
                 }
                 }
             }, 5000);
             }, 5000);
         });
         }
    });
 
        // 绑定重试链接
        var retryLink = document.getElementById('retry-link');
        if (retryLink) {
            retryLink.addEventListener('click', function() {
                retryFailedFiles();
            });
        }
    }
})();
})();
</script>
</script>
</includeonly>
</includeonly>

2026年6月12日 (五) 12:25的最新版本