微件:Excel2Page
准备中...
生成结果(0 个页面)
'
+ '
';
}
function bindItemEvents(div, item) {
var toggleBtn = div.querySelector('.e2p-item-toggle');
if (toggleBtn) {
toggleBtn.addEventListener('click', function (e) {
e.stopPropagation();
var expanded = div.classList.toggle('expanded');
toggleBtn.textContent = expanded ? '收起' : '展开';
});
}
var cb = div.querySelector('.e2p-result-checkbox');
if (cb) {
cb.addEventListener('change', function () {
item.checked = cb.checked;
updateSubmitBtn();
});
}
}
function refreshItemEl(item) {
var div = $('e2p-item-' + item.idx);
if (!div) return;
var wasExpanded = div.classList.contains('expanded');
div.className = 'e2p-result-item status-' + item.status + (wasExpanded ? ' expanded' : '');
div.innerHTML = buildItemHtml(item);
bindItemEvents(div, item);
if (wasExpanded) {
var tb = div.querySelector('.e2p-item-toggle');
if (tb) tb.textContent = '收起';
}
}
/* ═══════════════════════════════════════════
统计
═══════════════════════════════════════════ */
function updateStats() {
var s = { pending: 0, running: 0, success: 0, error: 0, skipped: 0 };
state.items.forEach(function (it) { s[it.status] = (s[it.status] || 0) + 1; });
state.stats = s;
$('e2p-stat-pending').textContent = s.pending + ' 待处理';
$('e2p-stat-running').textContent = s.running + ' 处理中';
$('e2p-stat-success').textContent = s.success + ' 成功';
$('e2p-stat-error').textContent = s.error + ' 失败';
$('e2p-stat-skipped').textContent = s.skipped + ' 跳过';
}
function updateSubmitBtn() {
var n = state.items.filter(function (it) {
return it.checked && it.status === 'pending';
}).length;
submitBtn.disabled = (n === 0) || state.isRunning;
submitBtn.textContent = '提交选中页面(' + n + ')';
}
/* ═══════════════════════════════════════════
全选 / 取消全选
═══════════════════════════════════════════ */
selectAllBtn.addEventListener('click', function () {
state.items.forEach(function (item) {
if (item.status !== 'pending') return;
item.checked = true;
var cb = document.querySelector('#e2p-item-' + item.idx + ' .e2p-result-checkbox');
if (cb) cb.checked = true;
});
updateSubmitBtn();
});
deselectAllBtn.addEventListener('click', function () {
state.items.forEach(function (item) {
if (item.status !== 'pending') return;
item.checked = false;
var cb = document.querySelector('#e2p-item-' + item.idx + ' .e2p-result-checkbox');
if (cb) cb.checked = false;
});
updateSubmitBtn();
});
/* ═══════════════════════════════════════════
展开 / 折叠全部
═══════════════════════════════════════════ */
toggleAllBtn.addEventListener('click', function () {
state.allExpanded = !state.allExpanded;
toggleAllBtn.textContent = state.allExpanded ? '折叠全部' : '展开全部';
resultList.querySelectorAll('.e2p-result-item').forEach(function (div) {
div.classList.toggle('expanded', state.allExpanded);
var btn = div.querySelector('.e2p-item-toggle');
if (btn) btn.textContent = state.allExpanded ? '收起' : '展开';
});
});
/* ═══════════════════════════════════════════
返回 / 重置
═══════════════════════════════════════════ */
backBtn.addEventListener('click', function () {
if (state.isRunning) return;
showStep('input');
});
resetBtn.addEventListener('click', function () {
if (state.isRunning) {
if (!confirm('当前正在运行中,确定要停止并重置吗?')) return;
state.stopFlag = true;
}
pasteArea.value = '';
previewSection.style.display = 'none';
parseHint.textContent = '';
parseHint.style.color = '';
state.parsed = null;
state.items = [];
resultList.innerHTML = '';
progressWrap.style.display = 'none';
showStep('input');
updateStats();
updateSubmitBtn();
});
/* ═══════════════════════════════════════════
MediaWiki API
═══════════════════════════════════════════ */
function getToken() {
return new mw.Api().getToken('csrf');
}
function pageExists(title) {
return new mw.Api().get({
action: 'query', titles: title, formatversion: 2
}).then(function (data) {
var pages = data.query.pages;
return pages && pages.length > 0 && pages[0].pageid > 0;
});
}
function editPage(token, title, content, summary, append) {
var params = {
action: 'edit', title: title,
summary: summary, token: token, formatversion: 2
};
if (append) { params.appendtext = content; }
else { params.text = content; }
return new mw.Api().post(params);
}
/* ═══════════════════════════════════════════
提交流程
═══════════════════════════════════════════ */
submitBtn.addEventListener('click', function () {
if (state.isRunning) return;
var toProcess = state.items.filter(function (it) {
return it.checked && it.status === 'pending';
});
if (toProcess.length === 0) return;
/* 仅预览模式 */
if (optPreviewOnly.checked) {
toProcess.forEach(function (item) {
var div = $('e2p-item-' + item.idx);
if (!div) return;
div.classList.add('expanded');
var btn = div.querySelector('.e2p-item-toggle');
if (btn) btn.textContent = '收起';
});
alert('预览模式:已展开 ' + toProcess.length + ' 个条目的 Wikitext,未实际提交。');
return;
}
if (!confirm('将提交 ' + toProcess.length + ' 个页面,确认吗?')) return;
/* 启动 */
state.isRunning = true;
state.stopFlag = false;
submitBtn.style.display = 'none';
stopBtn.style.display = '';
selectAllBtn.style.display = 'none';
deselectAllBtn.style.display = 'none';
backBtn.disabled = true;
progressWrap.style.display = '';
progressBar.style.width = '0%';
progressText.textContent = '正在获取 Token...';
var summary = summaryInput.value || '通过 Excel2Page 批量创建';
var skipExist = optSkipExist.checked;
var appendMode = optAppend.checked;
getToken().then(function (token) {
return processItems(toProcess, token, summary, skipExist, appendMode);
}).then(function () {
finishRun();
}).catch(function (err) {
progressText.textContent = '发生错误:' + String(err);
finishRun();
});
});
stopBtn.addEventListener('click', function () {
state.stopFlag = true;
progressText.textContent = '正在停止...';
});
function finishRun() {
state.isRunning = false;
stopBtn.style.display = 'none';
submitBtn.style.display = '';
selectAllBtn.style.display = '';
deselectAllBtn.style.display = '';
backBtn.disabled = false;
updateSubmitBtn();
}
/** 顺序处理,避免并发限流 */
function processItems(items, token, summary, skipExist, appendMode) {
var total = items.length;
var done = 0;
function next(idx) {
if (idx >= items.length) {
updateStats();
return Promise.resolve();
}
/* 停止标志 */
if (state.stopFlag) {
for (var k = idx; k < items.length; k++) {
items[k].status = 'skipped';
items[k].statusMsg = '已停止';
refreshItemEl(items[k]);
}
updateStats();
return Promise.resolve();
}
var item = items[idx];
item.status = 'running';
item.statusMsg = '处理中...';
refreshItemEl(item);
updateStats();
progressBar.style.width = Math.round(done / total * 100) + '%';
progressText.textContent = '处理中 ' + (done + 1) + ' / ' + total + ':' + item.page;
var p;
if (skipExist) {
p = pageExists(item.page).then(function (exists) {
if (exists) {
item.status = 'skipped';
item.statusMsg = '页面已存在,已跳过';
refreshItemEl(item);
done++;
updateStats();
return next(idx + 1);
}
return doEdit(item, token, summary, appendMode).then(function () {
done++; updateStats(); return next(idx + 1);
});
});
} else {
p = doEdit(item, token, summary, appendMode).then(function () {
done++; updateStats(); return next(idx + 1);
});
}
return p.catch(function (err) {
item.status = 'error';
item.statusMsg = '错误:' + String(err);
refreshItemEl(item);
done++; updateStats();
return next(idx + 1);
});
}
return next(0).then(function () {
progressBar.style.width = '100%';
var s = state.stats;
progressText.textContent =
'完成!成功 ' + s.success + ',跳过 ' + s.skipped + ',失败 ' + s.error;
});
}
function doEdit(item, token, summary, appendMode) {
return editPage(token, item.page, item.wikitext, summary, appendMode)
.then(function (data) {
if (data && data.edit &&
(data.edit.result === 'Success' || data.edit.nochange !== undefined)) {
item.status = 'success';
item.statusMsg = data.edit.nochange !== undefined
? '内容未变化(已跳过)' : '提交成功';
} else {
item.status = 'error';
item.statusMsg = '提交失败:' + JSON.stringify(data);
}
refreshItemEl(item);
})
.catch(function (err) {
item.status = 'error';
item.statusMsg = 'API 错误:' + String(err);
refreshItemEl(item);
throw err;
});
}
/* ═══════════════════════════════════════════
初始化
═══════════════════════════════════════════ */
showStep('input');
updateStats();
updateSubmitBtn();
}());
'
+ '' + esc(item.page) + ''
+ ' {{' + esc(item.temple) + '}}'
+ ' '
+ '
'
+ '' + esc(item.wikitext) + '
'
+ statusHtml
+ '