查看“︁微件:Excel2Page”︁的源代码
←
微件:Excel2Page
跳转到导航
跳转到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您没有权限编辑
微件
命名空间内的页面。
您可以查看和复制此页面的源代码。
<includeonly> <style> :root { --e2p-primary: #0066cc; --e2p-primary-hover: #0052a3; --e2p-border: #ddd; --e2p-bg: #f9f9f9; --e2p-hover: #f0f0f0; --e2p-text: #333; --e2p-max-height: 900px; } /* ── 根容器 ── */ #excel2page-root { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 20px 0; } /* ── 外框 ── */ .e2p-container { border: 1px solid var(--e2p-border); border-radius: 8px; overflow: hidden; background: white; max-height: var(--e2p-max-height); display: flex; flex-direction: column; } /* ── 工具栏 ── */ .e2p-toolbar { padding: 8px 12px; border-bottom: 1px solid var(--e2p-border); display: flex; align-items: center; flex-wrap: wrap; flex-shrink: 0; background: var(--e2p-bg); gap: 8px; } .e2p-toolbar-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; width: 100%; padding: 4px 0; } .e2p-summary-label { font-size: 13px; color: var(--e2p-text); white-space: nowrap; font-weight: 500; } .e2p-summary-input { padding: 5px 10px; border: 1px solid var(--e2p-border); border-radius: 4px; font-size: 13px; width: 260px; } .e2p-summary-input:focus { outline: none; border-color: var(--e2p-primary); } /* ── 按钮 ── */ .e2p-btn { padding: 6px 12px; border: 1px solid var(--e2p-border); border-radius: 4px; background: white; cursor: pointer; font-size: 13px; transition: all 0.2s; white-space: nowrap; } .e2p-btn:hover:not(:disabled) { background: var(--e2p-primary); color: white; border-color: var(--e2p-primary); } .e2p-btn:disabled { opacity: 0.5; cursor: not-allowed; } .e2p-btn-primary { background: var(--e2p-primary); color: white; border-color: var(--e2p-primary); } .e2p-btn-primary:hover:not(:disabled) { background: var(--e2p-primary-hover); border-color: var(--e2p-primary-hover); } .e2p-btn-danger { background: #d32f2f; color: white; border-color: #d32f2f; } .e2p-btn-danger:hover:not(:disabled) { background: #b71c1c; border-color: #b71c1c; } .e2p-btn-warning { background: #f57c00; color: white; border-color: #f57c00; } .e2p-btn-warning:hover:not(:disabled) { background: #e65100; border-color: #e65100; } .e2p-btn-success { background: #388e3c; color: white; border-color: #388e3c; } .e2p-btn-success:hover:not(:disabled) { background: #2e7d32; border-color: #2e7d32; } .e2p-btn-sm { font-size: 11px; padding: 2px 8px; } /* ── 进度条 ── */ .e2p-progress-wrap { padding: 10px 12px; display: none; border-bottom: 1px solid var(--e2p-border); background: var(--e2p-bg); flex-shrink: 0; } .e2p-progress-track { width: 100%; height: 8px; background: #e0e0e0; border-radius: 4px; overflow: hidden; } .e2p-progress-bar { height: 100%; background: var(--e2p-primary); border-radius: 4px; transition: width 0.3s; width: 0%; } .e2p-progress-text { font-size: 13px; color: #666; margin-top: 6px; text-align: center; } /* ── 主体区 ── */ .e2p-main { display: flex; flex-direction: column; min-width: 0; flex: 1; min-height: 0; overflow: hidden; } .e2p-content { flex: 1; overflow-y: auto; padding: 12px; } .e2p-content::-webkit-scrollbar { width: 8px; } .e2p-content::-webkit-scrollbar-track { background: var(--e2p-bg); } .e2p-content::-webkit-scrollbar-thumb { background: #ccc; border-radius: 4px; } .e2p-content::-webkit-scrollbar-thumb:hover { background: #999; } /* ── 底部工具栏 ── */ .e2p-footer { padding: 8px 12px; border-top: 1px solid var(--e2p-border); background: var(--e2p-bg); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex-shrink: 0; } .e2p-footer-right { margin-left: auto; display: flex; align-items: center; gap: 8px; } /* ── 步骤面板 ── */ .e2p-step { display: none; } .e2p-step.active { display: block; } /* ── 粘贴区 ── */ .e2p-paste-wrap { margin-bottom: 12px; } .e2p-paste-wrap label { font-size: 14px; font-weight: 500; color: var(--e2p-text); display: block; margin-bottom: 6px; } .e2p-paste-area { width: 100%; min-height: 120px; padding: 10px; border: 2px dashed var(--e2p-border); border-radius: 6px; font-size: 13px; font-family: monospace; resize: vertical; box-sizing: border-box; transition: border-color 0.2s; outline: none; color: var(--e2p-text); } .e2p-paste-area:focus { border-color: var(--e2p-primary); border-style: solid; } .e2p-paste-area::placeholder { color: #aaa; } /* ── 选项行 ── */ .e2p-options-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 8px 0; } .e2p-options-row label { font-size: 13px; color: var(--e2p-text); cursor: pointer; display: flex; align-items: center; gap: 5px; } .e2p-options-row input[type="checkbox"] { cursor: pointer; width: 14px; height: 14px; } /* ── 预览表格 ── */ .e2p-preview-wrap { margin-top: 10px; overflow-x: auto; border: 1px solid var(--e2p-border); border-radius: 6px; } .e2p-preview-table { width: 100%; border-collapse: collapse; font-size: 13px; } .e2p-preview-table th { background: var(--e2p-bg); padding: 7px 12px; text-align: left; font-weight: 600; color: var(--e2p-text); border-bottom: 2px solid var(--e2p-border); white-space: nowrap; } .e2p-preview-table td { padding: 6px 12px; border-bottom: 1px solid #eee; color: #444; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .e2p-preview-table tr:last-child td { border-bottom: none; } .e2p-preview-table tr:hover td { background: var(--e2p-hover); } .e2p-preview-table th:first-child, .e2p-preview-table td:first-child { color: var(--e2p-primary); font-weight: 500; } /* ── 区块标题 ── */ .e2p-section-title { font-size: 14px; font-weight: 600; color: var(--e2p-text); margin: 0 0 6px 0; } /* ── 统计栏 ── */ .e2p-stat-bar { display: flex; gap: 16px; font-size: 13px; flex-wrap: wrap; margin-bottom: 10px; } .e2p-stat-item { display: flex; align-items: center; gap: 5px; } .e2p-stat-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } .e2p-stat-dot.pending { background: #bdbdbd; } .e2p-stat-dot.running { background: #ffc107; } .e2p-stat-dot.success { background: #4caf50; } .e2p-stat-dot.error { background: #f44336; } .e2p-stat-dot.skipped { background: #9e9e9e; } /* ── 结果列表 ── */ .e2p-result-list { display: flex; flex-direction: column; gap: 6px; } .e2p-result-item { padding: 10px 12px; border: 1px solid var(--e2p-border); border-radius: 4px; transition: all 0.2s; background: white; display: flex; align-items: flex-start; gap: 10px; } .e2p-result-item.status-pending { background: white; } .e2p-result-item.status-running { background: #fff8e1; border-color: #ffc107; } .e2p-result-item.status-success { background: #e8f5e9; border-color: #4caf50; } .e2p-result-item.status-error { background: #ffebee; border-color: #d32f2f; } .e2p-result-item.status-skipped { background: #f5f5f5; border-color: #9e9e9e; } .e2p-result-checkbox { margin-top: 2px; cursor: pointer; flex-shrink: 0; } .e2p-result-info { flex: 1; min-width: 0; overflow: hidden; } .e2p-result-title { font-size: 14px; font-weight: 500; color: var(--e2p-text); margin-bottom: 4px; } .e2p-result-title a { color: var(--e2p-primary); text-decoration: none; } .e2p-result-title a:hover { text-decoration: underline; } /* wikitext 折叠区 */ .e2p-result-wikitext { display: none; font-size: 12px; color: #555; line-height: 1.7; font-family: monospace; white-space: pre-wrap; word-break: break-all; background: #f5f5f5; padding: 8px 10px; border-radius: 4px; margin-top: 4px; max-height: 160px; overflow-y: auto; border: 1px solid #e0e0e0; } .e2p-result-item.expanded .e2p-result-wikitext { display: block; } .e2p-result-status-msg { font-size: 12px; margin-top: 4px; font-weight: 500; } .e2p-result-status-msg.status-success { color: #2e7d32; } .e2p-result-status-msg.status-error { color: #d32f2f; } .e2p-result-status-msg.status-running { color: #f57c00; } .e2p-result-status-msg.status-skipped { color: #757575; } /* ── 通用提示 ── */ .e2p-hint { font-size: 13px; color: #999; } .e2p-loading { text-align: center; padding: 40px; color: #666; font-size: 14px; } .e2p-error { text-align: center; padding: 20px; color: #d32f2f; font-size: 14px; background: #ffebee; border-radius: 4px; margin: 8px 0; } .e2p-empty { text-align: center; padding: 40px; color: #999; font-size: 14px; } /* ── 响应式 ── */ @media (max-width: 768px) { .e2p-toolbar-row { flex-direction: column; align-items: stretch; } .e2p-footer { flex-direction: column; align-items: stretch; } .e2p-footer-right { margin-left: 0; justify-content: flex-end; } .e2p-summary-input { width: 100%; } } </style> <div id="excel2page-root"> <div class="e2p-container"> <!-- 工具栏(含编辑摘要) --> <div class="e2p-toolbar"> <div class="e2p-toolbar-row"> <label class="e2p-summary-label">编辑摘要:</label> <input type="text" class="e2p-summary-input" id="e2p-summary" value="通过 Excel2Page 批量创建" placeholder="编辑摘要"> <button class="e2p-btn" id="e2p-reset-btn" style="margin-left:auto;">重置</button> </div> </div> <!-- 进度条 --> <div class="e2p-progress-wrap" id="e2p-progress-wrap"> <div class="e2p-progress-track"> <div class="e2p-progress-bar" id="e2p-progress-bar"></div> </div> <div class="e2p-progress-text" id="e2p-progress-text">准备中...</div> </div> <!-- 主内容区 --> <div class="e2p-main"> <div class="e2p-content" id="e2p-main-content"> <!-- Step 1:粘贴与预览 --> <div class="e2p-step active" id="e2p-step-input"> <div class="e2p-paste-wrap"> <label for="e2p-paste-area">粘贴 Excel 内容(含表头行)</label> <textarea class="e2p-paste-area" id="e2p-paste-area" rows="8" placeholder="粘贴从 Excel 复制的内容,第一行为表头,必须包含 page 和 temple 列 例: page temple card_id char_id art Card:c_1049_srt1 Cards c_1049_srt1 1049 start_1049_01.png" ></textarea> </div> <div class="e2p-options-row" style="border-top:1px solid var(--e2p-border);margin-top:4px;"> <label title="若目标页面已存在则跳过,不覆盖"> <input type="checkbox" id="e2p-opt-skip-exist" checked> 跳过已存在页面 </label> <label title="写入前先预览 wikitext,不自动提交"> <input type="checkbox" id="e2p-opt-preview-only"> 仅预览,不提交 </label> <label title="在模板末尾追加内容而非覆盖"> <input type="checkbox" id="e2p-opt-append"> 追加模式(不覆盖原内容) </label> </div> <div style="margin-top:12px;display:flex;gap:8px;align-items:center;"> <button class="e2p-btn e2p-btn-primary" id="e2p-parse-btn">解析预览</button> <span class="e2p-hint" id="e2p-parse-hint"></span> </div> <!-- 数据预览 --> <div id="e2p-preview-section" style="display:none;margin-top:14px;"> <p class="e2p-section-title"> 数据预览(共 <span id="e2p-row-count">0</span> 行) </p> <div class="e2p-preview-wrap"> <table class="e2p-preview-table"> <thead id="e2p-preview-thead"></thead> <tbody id="e2p-preview-tbody"></tbody> </table> </div> <div style="margin-top:12px;display:flex;gap:8px;align-items:center;"> <button class="e2p-btn e2p-btn-success" id="e2p-next-btn"> 生成 Wikitext </button> <span class="e2p-hint">将根据表格数据生成对应页面内容</span> </div> </div> </div><!-- /step-input --> <!-- Step 2:结果列表 --> <div class="e2p-step" id="e2p-step-result"> <div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap;"> <p class="e2p-section-title" style="margin:0;"> 生成结果(<span id="e2p-result-count">0</span> 个页面) </p> <button class="e2p-btn" id="e2p-toggle-all-btn">展开全部</button> <button class="e2p-btn" id="e2p-back-btn" style="margin-left:auto;">返回编辑</button> </div> <div class="e2p-stat-bar" id="e2p-stat-bar"> <div class="e2p-stat-item"> <span class="e2p-stat-dot pending"></span> <span id="e2p-stat-pending">0 待处理</span> </div> <div class="e2p-stat-item"> <span class="e2p-stat-dot running"></span> <span id="e2p-stat-running">0 处理中</span> </div> <div class="e2p-stat-item"> <span class="e2p-stat-dot success"></span> <span id="e2p-stat-success">0 成功</span> </div> <div class="e2p-stat-item"> <span class="e2p-stat-dot error"></span> <span id="e2p-stat-error">0 失败</span> </div> <div class="e2p-stat-item"> <span class="e2p-stat-dot skipped"></span> <span id="e2p-stat-skipped">0 跳过</span> </div> </div> <div class="e2p-result-list" id="e2p-result-list"></div> </div><!-- /step-result --> </div><!-- /e2p-content --> </div><!-- /e2p-main --> <!-- 底部操作栏 --> <div class="e2p-footer" id="e2p-footer"> <div class="e2p-footer-right"> <button class="e2p-btn e2p-btn-warning" id="e2p-select-all-btn" style="display:none;">全选</button> <button class="e2p-btn e2p-btn-danger" id="e2p-deselect-all-btn" style="display:none;">取消全选</button> <button class="e2p-btn e2p-btn-primary" id="e2p-submit-btn" style="display:none;" disabled>提交选中页面</button> <button class="e2p-btn" id="e2p-stop-btn" style="display:none;">停止</button> </div> </div> </div><!-- /e2p-container --> </div><!-- /excel2page-root --> <script> (function () { 'use strict'; /* ═══════════════════════════════════════════ 工具函数 ═══════════════════════════════════════════ */ /** 解析 TSV,返回 { headers, rows } 或 null */ function parseTSV(text) { var lines = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n') .filter(function (l) { return l.trim() !== ''; }); if (lines.length < 2) return null; var headers = lines[0].split('\t').map(function (h) { return h.trim(); }); var pageIdx = headers.indexOf('page'); var templeIdx = headers.indexOf('temple'); if (pageIdx === -1 || templeIdx === -1) return null; var rows = []; for (var i = 1; i < lines.length; i++) { var cells = lines[i].split('\t'); var page = (cells[pageIdx] || '').trim(); var temple = (cells[templeIdx] || '').trim(); if (!page || !temple) continue; var params = []; for (var j = 0; j < headers.length; j++) { if (j === pageIdx || j === templeIdx) continue; var key = headers[j].trim(); var val = (cells[j] || '').trim(); if (!key || val === '') continue; params.push({ key: key, val: val }); } rows.push({ page: page, temple: temple, params: params, rawCells: cells, rawHeaders: headers }); } return { headers: headers, rows: rows }; } /** 生成 Wikitext */ function buildWikitext(row) { var lines = ['{{' + row.temple]; for (var i = 0; i < row.params.length; i++) { lines.push('| ' + row.params[i].key + ' = ' + row.params[i].val); } lines.push('}}'); return lines.join('\n'); } /** HTML 转义 */ function esc(s) { return String(s) .replace(/&/g, '&').replace(/</g, '<') .replace(/>/g, '>').replace(/"/g, '"'); } /* ═══════════════════════════════════════════ 状态 ═══════════════════════════════════════════ */ var state = { parsed: null, items: [], isRunning: false, stopFlag: false, allExpanded: false, stats: { pending: 0, running: 0, success: 0, error: 0, skipped: 0 } }; /* ═══════════════════════════════════════════ DOM 引用 ═══════════════════════════════════════════ */ function $(id) { return document.getElementById(id); } var pasteArea = $('e2p-paste-area'); var parseBtn = $('e2p-parse-btn'); var parseHint = $('e2p-parse-hint'); var previewSection = $('e2p-preview-section'); var previewThead = $('e2p-preview-thead'); var previewTbody = $('e2p-preview-tbody'); var rowCount = $('e2p-row-count'); var nextBtn = $('e2p-next-btn'); var backBtn = $('e2p-back-btn'); var stepInput = $('e2p-step-input'); var stepResult = $('e2p-step-result'); var resultList = $('e2p-result-list'); var resultCount = $('e2p-result-count'); var toggleAllBtn = $('e2p-toggle-all-btn'); var submitBtn = $('e2p-submit-btn'); var stopBtn = $('e2p-stop-btn'); var selectAllBtn = $('e2p-select-all-btn'); var deselectAllBtn = $('e2p-deselect-all-btn'); var summaryInput = $('e2p-summary'); var progressWrap = $('e2p-progress-wrap'); var progressBar = $('e2p-progress-bar'); var progressText = $('e2p-progress-text'); var resetBtn = $('e2p-reset-btn'); var optSkipExist = $('e2p-opt-skip-exist'); var optPreviewOnly = $('e2p-opt-preview-only'); var optAppend = $('e2p-opt-append'); /* ═══════════════════════════════════════════ 步骤切换 ═══════════════════════════════════════════ */ function showStep(name) { var inResult = name === 'result'; stepInput.classList.toggle('active', !inResult); stepResult.classList.toggle('active', inResult); submitBtn.style.display = inResult ? '' : 'none'; stopBtn.style.display = 'none'; selectAllBtn.style.display = inResult ? '' : 'none'; deselectAllBtn.style.display = inResult ? '' : 'none'; } /* ═══════════════════════════════════════════ 解析预览 ═══════════════════════════════════════════ */ parseBtn.addEventListener('click', function () { var text = pasteArea.value; if (!text.trim()) { setHint('请先粘贴 Excel 内容', 'warn'); return; } var result = parseTSV(text); if (!result) { setHint('解析失败:未找到 page 或 temple 列,请确认表头正确', 'error'); previewSection.style.display = 'none'; return; } if (result.rows.length === 0) { setHint('未解析到有效数据行(page 或 temple 为空的行已跳过)', 'warn'); previewSection.style.display = 'none'; return; } state.parsed = result; setHint('解析成功,共 ' + result.rows.length + ' 行', 'ok'); renderPreviewTable(result); previewSection.style.display = ''; }); function setHint(msg, level) { var colors = { ok: '#2e7d32', warn: '#f57c00', error: '#d32f2f' }; parseHint.textContent = msg; parseHint.style.color = colors[level] || '#999'; } function renderPreviewTable(result) { rowCount.textContent = result.rows.length; var thHtml = '<tr>'; result.headers.forEach(function (h) { thHtml += '<th>' + esc(h) + '</th>'; }); thHtml += '</tr>'; previewThead.innerHTML = thHtml; var tbHtml = ''; var showRows = result.rows.slice(0, 20); showRows.forEach(function (row) { tbHtml += '<tr>'; row.rawHeaders.forEach(function (h, idx) { var val = (row.rawCells[idx] || '').trim(); tbHtml += '<td title="' + esc(val) + '">' + esc(val) + '</td>'; }); tbHtml += '</tr>'; }); if (result.rows.length > 20) { tbHtml += '<tr><td colspan="' + result.headers.length + '" ' + 'style="text-align:center;color:#999;font-style:italic;">' + '仅显示前 20 行,共 ' + result.rows.length + ' 行</td></tr>'; } previewTbody.innerHTML = tbHtml; } /* ═══════════════════════════════════════════ 生成结果列表 ═══════════════════════════════════════════ */ nextBtn.addEventListener('click', function () { if (!state.parsed || state.parsed.rows.length === 0) return; buildResultItems(); showStep('result'); updateStats(); updateSubmitBtn(); }); function buildResultItems() { state.items = []; resultList.innerHTML = ''; resultCount.textContent = state.parsed.rows.length; state.parsed.rows.forEach(function (row, idx) { var item = { idx: idx, page: row.page, temple: row.temple, wikitext: buildWikitext(row), checked: true, status: 'pending', statusMsg: '' }; state.items.push(item); resultList.appendChild(createResultEl(item)); }); state.stats = { pending: state.items.length, running: 0, success: 0, error: 0, skipped: 0 }; } function createResultEl(item) { var div = document.createElement('div'); div.className = 'e2p-result-item status-' + item.status; div.id = 'e2p-item-' + item.idx; div.innerHTML = buildItemHtml(item); bindItemEvents(div, item); return div; } function buildItemHtml(item) { var wikiUrl = (typeof mw !== 'undefined' && mw.util) ? mw.util.getUrl(item.page) : '#'; var checkedA = item.checked ? ' checked' : ''; var disabledA = (item.status === 'success' || item.status === 'skipped') ? ' disabled' : ''; var statusHtml = item.statusMsg ? '<div class="e2p-result-status-msg status-' + item.status + '">' + esc(item.statusMsg) + '</div>' : ''; return '<input type="checkbox" class="e2p-result-checkbox"' + checkedA + disabledA + '>' + '<div class="e2p-result-info">' + '<div class="e2p-result-title">' + '<a href="' + esc(wikiUrl) + '" target="_blank">' + esc(item.page) + '</a>' + ' <span style="color:#999;font-weight:400;font-size:12px;">{{' + esc(item.temple) + '}}</span>' + ' <button class="e2p-btn e2p-btn-sm e2p-item-toggle">展开</button>' + '</div>' + '<div class="e2p-result-wikitext">' + esc(item.wikitext) + '</div>' + statusHtml + '</div>'; } 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(); }()); </script></includeonly><noinclude> {{Path|微件}} {{#微件:Excel2Page}} [[分类:微件]] </noinclude>
该页面使用的模板:
模板:Path
(
查看源代码
)
返回
微件:Excel2Page
。
导航菜单
个人工具
登录
命名空间
微件
讨论
大陆简体
查看
阅读
查看源代码
查看历史
更多
搜索
首页
回到首页
刷新首页
卡厄思梦境BWIKI
官方
国服官网
图鉴
主战员
辅战员
装备
卡牌
事件
小工具
PT计算器
卡组模拟器
维基管理
配置菜单栏
配置全站样式
配置全站脚本
页面管理
批量上传
批量刷新
替换文字
Excel2Page
常用页面
沙盒
最近更改
文件列表