knip智能工具箱
首页
工具中心
作品展示
讨论区
资讯
登录
注册
截图转表格
PDF工具
admin · 2026-10-13 · 使用 3
点赞
0
收藏
0
分享
反馈
粘贴截图自动识别文字并保留表格结构导出Excel
截图转表格
放大后操作
预览
代码
放大后操作(预览仅供参考)
纯前端工具,无后端代码
换行
HTML
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>截图转表格 - 图片文字识别导出</title> <style> *{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent} body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;background:#f0f2f5;color:#1f2937;min-height:100vh;padding:12px;overflow-x:hidden} .app{max-width:960px;margin:0 auto} header{text-align:center;padding:16px 0 12px} header h1{font-size:22px;font-weight:700;color:#111827;letter-spacing:.5px} header p{font-size:13px;color:#6b7280;margin-top:6px} .card{background:#fff;border-radius:14px;box-shadow:0 2px 12px rgba(0,0,0,.06);padding:16px;margin-bottom:14px} .dropzone{border:2px dashed #c7d2fe;border-radius:12px;background:#f8faff;padding:28px 16px;text-align:center;cursor:pointer;transition:.2s;position:relative} .dropzone:hover,.dropzone.drag{border-color:#6366f1;background:#eef2ff} .dropzone .icon{font-size:40px;line-height:1} .dropzone .t1{font-size:15px;font-weight:600;color:#374151;margin-top:10px} .dropzone .t2{font-size:12px;color:#9ca3af;margin-top:6px} .dropzone input{display:none} .thumbs{display:flex;flex-wrap:wrap;gap:10px;margin-top:14px} .thumb{position:relative;width:96px;height:96px;border-radius:10px;overflow:hidden;border:1px solid #e5e7eb;background:#f9fafb} .thumb img{width:100%;height:100%;object-fit:cover;display:block} .thumb .del{position:absolute;top:3px;right:3px;width:22px;height:22px;border-radius:50%;background:rgba(0,0,0,.6);color:#fff;border:none;font-size:13px;line-height:22px;text-align:center;cursor:pointer;padding:0} .thumb .name{position:absolute;bottom:0;left:0;right:0;background:rgba(0,0,0,.55);color:#fff;font-size:10px;padding:2px 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .btnrow{display:flex;gap:10px;margin-top:14px} .btn{flex:1;min-height:44px;border:none;border-radius:10px;font-size:15px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;text-align:center;transition:.15s;padding:0 8px;font-family:inherit} .btn:active{transform:scale(.98)} .btn:disabled{opacity:.45;cursor:not-allowed} .btn-primary{background:linear-gradient(135deg,#6366f1,#4f46e5);color:#fff} .btn-primary:hover:not(:disabled){background:linear-gradient(135deg,#4f46e5,#4338ca)} .btn-ghost{background:#f3f4f6;color:#374151;border:1px solid #e5e7eb} .btn-ghost:hover:not(:disabled){background:#e5e7eb} .btn-green{background:linear-gradient(135deg,#10b981,#059669);color:#fff} .btn-green:hover:not(:disabled){background:linear-gradient(135deg,#059669,#047857)} .progress{height:8px;background:#e5e7eb;border-radius:4px;overflow:hidden;margin-top:14px;display:none} .progress.show{display:block} .progress .bar{height:100%;width:0;background:linear-gradient(90deg,#6366f1,#8b5cf6);transition:width .3s;border-radius:4px} .status{font-size:13px;color:#6b7280;text-align:center;margin-top:10px;min-height:18px} .status.err{color:#dc2626} .status.ok{color:#059669} .result-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px;flex-wrap:wrap} .result-head h2{font-size:16px;font-weight:700;color:#111827} .result-head .count{font-size:12px;color:#6b7280;background:#f3f4f6;padding:3px 10px;border-radius:20px} .tabs{display:flex;gap:6px;margin-bottom:12px;background:#f3f4f6;padding:4px;border-radius:10px} .tab{flex:1;min-height:38px;border:none;background:transparent;border-radius:8px;font-size:14px;font-weight:600;color:#6b7280;cursor:pointer;font-family:inherit;display:flex;align-items:center;justify-content:center} .tab.active{background:#fff;color:#4f46e5;box-shadow:0 1px 4px rgba(0,0,0,.08)} .tablewrap{overflow:auto;max-height:420px;border:1px solid #e5e7eb;border-radius:10px;-webkit-overflow-scrolling:touch} table{border-collapse:collapse;width:100%;font-size:13px;min-width:100%} table th,table td{border:1px solid #e5e7eb;padding:8px 10px;text-align:left;white-space:nowrap;max-width:220px;overflow:hidden;text-overflow:ellipsis} table th{background:#f9fafb;font-weight:600;color:#374151;position:sticky;top:0;z-index:1} table td[contenteditable]:focus{outline:2px solid #6366f1;outline-offset:-2px;background:#eef2ff} table tr:nth-child(even) td{background:#fcfcfd} table tr:nth-child(even) td[contenteditable]:focus{background:#eef2ff} .rawtext{width:100%;min-height:200px;border:1px solid #e5e7eb;border-radius:10px;padding:12px;font-size:13px;font-family:ui-monospace,Menlo,Consolas,monospace;resize:vertical;line-height:1.6;color:#374151} .rawtext:focus{outline:2px solid #6366f1;outline-offset:-1px} .empty{text-align:center;padding:36px 16px;color:#9ca3af} .empty .icon{font-size:44px;margin-bottom:10px} .empty p{font-size:14px} .tips{font-size:12px;color:#9ca3af;line-height:1.7;margin-top:10px} .tips b{color:#6b7280} .toast{position:fixed;left:50%;bottom:28px;transform:translateX(-50%) translateY(20px);background:#1f2937;color:#fff;padding:11px 20px;border-radius:10px;font-size:14px;opacity:0;pointer-events:none;transition:.25s;z-index:99;max-width:90vw;text-align:center} .toast.show{opacity:1;transform:translateX(-50%) translateY(0)} .hidden{display:none!important} @media(max-width:520px){ body{padding:8px} header h1{font-size:19px} .card{padding:13px;border-radius:12px} .dropzone{padding:22px 12px} .thumb{width:76px;height:76px} .btn{font-size:14px;min-height:44px} table th,table td{padding:7px 8px;font-size:12px} } </style> </head> <body> <div class="app"> <header> <h1>📸 截图转表格</h1> <p>粘贴或上传截图,自动识别文字并保留表格结构,一键导出 Excel</p> </header> <div class="card"> <div class="dropzone" id="dropzone"> <div class="icon">🖼️</div> <div class="t1">点击选择图片,或直接 Ctrl+V 粘贴截图</div> <div class="t2">支持 PNG / JPG / WEBP,可一次选多张批量识别</div> <input type="file" id="fileInput" accept="image/*" multiple> </div> <div class="thumbs" id="thumbs"></div> <div class="btnrow"> <button class="btn btn-primary" id="btnRecognize" disabled>🔍 开始识别</button> <button class="btn btn-ghost" id="btnClear" disabled>🗑️ 清空</button> </div> <div class="progress" id="progress"><div class="bar" id="bar"></div></div> <div class="status" id="status">等待添加图片…</div> </div> <div class="card" id="resultCard"> <div class="result-head"> <h2>识别结果</h2> <span class="count" id="cellCount">0 个单元格</span> </div> <div class="tabs"> <button class="tab active" data-tab="table">📊 表格视图</button> <button class="tab" data-tab="raw">📝 纯文本</button> </div> <div id="tabTable"> <div class="tablewrap" id="tablewrap"> <div class="empty"><div class="icon">📋</div><p>识别完成后,表格会显示在这里<br>单元格可直接点击修改</p></div> </div> </div> <div id="tabRaw" class="hidden"> <textarea class="rawtext" id="rawtext" placeholder="识别出的纯文本会显示在这里,可自由编辑…"></textarea> </div> <div class="btnrow"> <button class="btn btn-green" id="btnExcel" disabled>📥 导出 Excel</button> <button class="btn btn-ghost" id="btnCsv" disabled>📄 导出 CSV</button> <button class="btn btn-ghost" id="btnCopy" disabled>📋 复制表格</button> </div> <div class="tips"> <b>小提示:</b>识别结果基于浏览器内置文字识别能力,识别后请核对关键数据;表格单元格支持直接双击编辑,改完再导出即可。 </div> </div> </div> <div class="toast" id="toast"></div> <script> (function(){ 'use strict'; var images = []; // {id, name, dataUrl} var rows = []; // 二维数组 var uid = 0; var ocrSupported = ('TextDetector' in window); var $ = function(id){ return document.getElementById(id); }; var dropzone = $('dropzone'), fileInput = $('fileInput'), thumbs = $('thumbs'); var btnRecognize = $('btnRecognize'), btnClear = $('btnClear'); var progress = $('progress'), bar = $('bar'), statusEl = $('status'); var tablewrap = $('tablewrap'), rawtext = $('rawtext'), cellCount = $('cellCount'); var btnExcel = $('btnExcel'), btnCsv = $('btnCsv'), btnCopy = $('btnCopy'); var toastEl = $('toast'); function toast(msg){ toastEl.textContent = msg; toastEl.classList.add('show'); clearTimeout(toastEl._t); toastEl._t = setTimeout(function(){ toastEl.classList.remove('show'); }, 2200); } function setStatus(msg, type){ statusEl.textContent = msg; statusEl.className = 'status' + (type ? ' ' + type : ''); } function updateButtons(){ var has = images.length > 0; btnRecognize.disabled = !has; btnClear.disabled = !has; } function renderThumbs(){ thumbs.innerHTML = ''; images.forEach(function(img){ var d = document.createElement('div'); d.className = 'thumb'; var im = document.createElement('img'); im.src = img.dataUrl; im.alt = img.name; var del = document.createElement('button'); del.className = 'del'; del.textContent = '×'; del.title = '移除'; del.onclick = function(e){ e.stopPropagation(); removeImage(img.id); }; var nm = document.createElement('div'); nm.className = 'name'; nm.textContent = img.name; d.appendChild(im); d.appendChild(del); d.appendChild(nm); thumbs.appendChild(d); }); updateButtons(); } function removeImage(id){ images = images.filter(function(i){ return i.id !== id; }); renderThumbs(); setStatus(images.length ? ('已添加 ' + images.length + ' 张图片,可开始识别') : '等待添加图片…'); } function addFiles(files){ var list = Array.prototype.slice.call(files).filter(function(f){ return f && f.type && f.type.indexOf('image/') === 0; }); if(!list.length){ toast('请选择图片文件'); return; } var loaded = 0; list.forEach(function(f){ var reader = new FileReader(); reader.onload = function(e){ images.push({ id: ++uid, name: f.name || ('截图' + uid), dataUrl: e.target.result }); loaded++; if(loaded === list.length){ renderThumbs(); setStatus('已添加 ' + images.length + ' 张图片,可开始识别'); } }; reader.onerror = function(){ toast('读取图片失败:' + (f.name || '')); }; reader.readAsDataURL(f); }); } dropzone.addEventListener('click', function(){ fileInput.click(); }); fileInput.addEventListener('change', function(e){ addFiles(e.target.files); fileInput.value = ''; }); ['dragenter','dragover'].forEach(function(ev){ dropzone.addEventListener(ev, function(e){ e.preventDefault(); e.stopPropagation(); dropzone.classList.add('drag'); }); }); ['dragleave','drop'].forEach(function(ev){ dropzone.addEventListener(ev, function(e){ e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('drag'); }); }); dropzone.addEventListener('drop', function(e){ if(e.dataTransfer && e.dataTransfer.files) addFiles(e.dataTransfer.files); }); document.addEventListener('paste', function(e){ var items = (e.clipboardData && e.clipboardData.items) || []; var files = []; for(var i=0;i<items.length;i++){ if(items[i].type && items[i].type.indexOf('image/') === 0){ var f = items[i].getAsFile(); if(f) files.push(f); } } if(files.length){ e.preventDefault(); addFiles(files); toast('已粘贴 ' + files.length + ' 张截图'); } }); btnClear.addEventListener('click', function(){ images = []; rows = []; renderThumbs(); renderTable(); rawtext.value = ''; setStatus('等待添加图片…'); progress.classList.remove('show'); bar.style.width = '0'; updateExportButtons(); }); // ---------- 识别 ---------- function loadImage(dataUrl){ return new Promise(function(resolve, reject){ var im = new Image(); im.onload = function(){ resolve(im); }; im.onerror = function(){ reject(new Error('图片加载失败')); }; im.src = dataUrl; }); } function detectBlocks(img){ var detector = new window.TextDetector(); return detector.detect(img).then(function(list){ return list.map(function(b){ var r = b.boundingBox; return { text: (b.rawValue || '').trim(), x: r.x, y: r.y, w: r.width, h: r.height }; }).filter(function(b){ return b.text; }); }); } function groupIntoRows(blocks){ if(!blocks.length) return []; var sorted = blocks.slice().sort(function(a,b){ return a.y - b.y; }); var heights = sorted.map(function(b){ return b.h; }).sort(function(a,b){ return a-b; }); var medH = heights[Math.floor(heights.length/2)] || 16; var tol = Math.max(8, medH * 0.6); var lines = []; sorted.forEach(function(b){ var cy = b.y + b.h/2; var line = null; for(var i=0;i<lines.length;i++){ if(Math.abs(lines[i].cy - cy) <= tol){ line = lines[i]; break; } } if(!line){ line = { cy: cy, items: [] }; lines.push(line); } line.items.push(b); line.cy = (line.cy * (line.items.length-1) + cy) / line.items.length; }); lines.sort(function(a,b){ return a.cy - b.cy; }); lines.forEach(function(l){ l.items.sort(function(a,b){ return a.x - b.x; }); }); return lines; } function buildGrid(lines){ if(!lines.length) return []; var all = []; lines.forEach(function(l){ l.items.forEach(function(b){ all.push(b); }); }); var widths = all.map(function(b){ return b.w; }).sort(function(a,b){ return a-b; }); var medW = widths[Math.floor(widths.length/2)] || 60; var gapTol = Math.max(12, medW * 0.9); var maxCols = 1; lines.forEach(function(l){ var cols = 1; for(var i=1;i<l.items.length;i++){ var prev = l.items[i-1]; var gap = l.items[i].x - (prev.x + prev.w); if(gap > gapTol) cols++; } l.cols = cols; if(cols > maxCols) maxCols = cols; }); var grid = lines.map(function(l){ var cells = []; var cur = l.items[0].text; for(var i=1;i<l.items.length;i++){ var prev = l.items[i-1]; var gap = l.items[i].x - (prev.x + prev.w); if(gap > gapTol){ cells.push(cur); cur = l.items[i].text; } else { cur += ' ' + l.items[i].text; } } cells.push(cur); while(cells.length < maxCols) cells.push(''); return cells; }); return grid; } function blocksToGrid(blocks){ var lines = groupIntoRows(blocks); return buildGrid(lines); } function recognizeOne(dataUrl){ return loadImage(dataUrl).then(function(img){ if(ocrSupported){ return detectBlocks(img).then(function(blocks){ if(blocks.length) return blocksToGrid(blocks); return null; }); } return null; }); } function mergeGrids(grids){ var out = []; grids.forEach(function(g){ if(!g || !g.length) return; if(out.length) out.push([]); g.forEach(function(r){ out.push(r.slice()); }); }); return out; } btnRecognize.addEventListener('click', function(){ if(!images.length) return; btnRecognize.disabled = true; btnClear.disabled = true; progress.classList.add('show'); bar.style.width = '0'; setStatus('正在识别…'); var results = []; var done = 0; var failed = 0; function step(){ if(done >= images.length){ finish(); return; } var img = images[done]; setStatus('正在识别第 ' + (done+1) + ' / ' + images.length + ' 张…'); recognizeOne(img.dataUrl).then(function(grid){ if(grid && grid.length) results.push(grid); else failed++; }).catch(function(){ failed++; }).then(function(){ done++; bar.style.width = Math.round(done / images.length * 100) + '%'; setTimeout(step, 60); }); } function finish(){ progress.classList.remove('show'); updateButtons(); if(!results.length){ setStatus('未能识别出文字,请换一张更清晰的截图重试', 'err'); toast('识别失败,请尝试更清晰的截图'); return; } rows = mergeGrids(results); renderTable(); renderRaw(); updateExportButtons(); var msg = '识别完成,共 ' + rows.length + ' 行'; if(failed) msg += '(' + failed + ' 张未识别出内容)'; setStatus(msg, 'ok'); toast('识别完成'); } step(); }); // ---------- 渲染 ---------- function renderTable(){ if(!rows.length){ tablewrap.innerHTML = '<div class="empty"><div class="icon">📋</div><p>识别完成后,表格会显示在这里<br>单元格可直接点击修改</p></div>'; cellCount.textContent = '0 个单元格'; return; } var maxCols = 0; rows.forEach(function(r){ if(r.length > maxCols) maxCols = r.length; }); var html = '<table><thead><tr>'; for(var c=0;c<maxCols;c++) html += '<th>列' + (c+1) + '</th>'; html += '</tr></thead><tbody>'; rows.forEach(function(r, ri){ html += '<tr>'; for(var c=0;c<maxCols;c++){ var v = r[c] == null ? '' : r[c]; html += '<td contenteditable="true" data-r="' + ri + '" data-c="' + c + '">' + escapeHtml(v) + '</td>'; } html += '</tr>'; }); html += '</tbody></table>'; tablewrap.innerHTML = html; cellCount.textContent = (rows.length * maxCols) + ' 个单元格'; tablewrap.querySelectorAll('td[contenteditable]').forEach(function(td){ td.addEventListener('blur', function(){ var r = +td.getAttribute('data-r'), c = +td.getAttribute('data-c'); if(rows[r]) rows[r][c] = td.textContent.trim(); renderRaw(); }); td.addEventListener('keydown', function(e){ if(e.key === 'Enter'){ e.preventDefault(); td.blur(); } }); }); } function escapeHtml(s){ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); } function renderRaw(){ rawtext.value = rows.map(function(r){ return r.join('\t'); }).join('\n'); } function updateExportButtons(){ var has = rows.length > 0; btnExcel.disabled = !has; btnCsv.disabled = !has; btnCopy.disabled = !has; } // ---------- 标签切换 ---------- document.querySelectorAll('.tab').forEach(function(tab){ tab.addEventListener('click', function(){ document.querySelectorAll('.tab').forEach(function(t){ t.classList.remove('active'); }); tab.classList.add('active'); var name = tab.getAttribute('data-tab'); $('tabTable').classList.toggle('hidden', name !== 'table'); $('tabRaw').classList.toggle('hidden', name !== 'raw'); }); }); // ---------- 导出 ---------- function downloadBlob(blob, filename){ var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); setTimeout(function(){ document.body.removeChild(a); URL.revokeObjectURL(url); }, 200); } function csvEscape(v){ v = v == null ? '' : String(v); if(/[",\n\r\t]/.test(v)) return '"' + v.replace(/"/g,'""') + '"'; return v; } btnCsv.addEventListener('click', function(){ if(!rows.length) return; var csv = rows.map(function(r){ return r.map(csvEscape).join(','); }).join('\r\n'); var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }); downloadBlob(blob, '识别表格.csv'); toast('CSV 已导出'); }); btnExcel.addEventListener('click', function(){ if(!rows.length) return; var maxCols = 0; rows.forEach(function(r){ if(r.length > maxCols) maxCols = r.length; }); var html = '<html xmlns:x="urn:schemas-microsoft-com:office:excel"><head><meta charset="utf-8">'; html += '<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>'; html += '<x:Name>识别结果</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>'; html += '</x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->'; html += '<style>td{border:1px solid #ccc;padding:4px;mso-number-format:"\\@";}</style></head><body><table>'; rows.forEach(function(r){ html += '<tr>'; for(var c=0;c<maxCols;c++){ var v = r[c] == null ? '' : r[c]; html += '<td>' + escapeHtml(v) + '</td>'; } html += '</tr>'; }); html += '</table></body></html>'; var blob = new Blob(['\ufeff' + html], { type: 'application/vnd.ms-excel;charset=utf-8' }); downloadBlob(blob, '识别表格.xls'); toast('Excel 已导出'); }); btnCopy.addEventListener('click', function(){ if(!rows.length) return; var text = rows.map(function(r){ return r.join('\t'); }).join('\n'); function fallback(){ var ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); try{ document.execCommand('copy'); toast('已复制到剪贴板'); } catch(err){ toast('复制失败,请手动选择文本'); } document.body.removeChild(ta); } if(navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(text).then(function(){ toast('已复制到剪贴板'); }).catch(fallback); } else fallback(); }); // 初始化 if(!ocrSupported){ setStatus('当前浏览器不支持内置文字识别,建议使用最新版 Chrome / Edge', 'err'); } else { setStatus('等待添加图片…'); } renderThumbs(); updateExportButtons(); })(); </script> </body> </html>
评论(0)
暂无评论,来说两句吧
分享「截图转表格」
微信扫一扫,分享给朋友
复制链接
QQ 分享
微博分享
微信内也可以点右上角
菜单分享
反馈「截图转表格」问题
工具哪里不好用、报错或结果不对?反馈会直接通知作品作者处理。
0 / 500
无需登录,可直接提交
暂无评论,来说两句吧