代码清理: - responsive.js 删除死代码(gestureState、getTouchAngle、optimizeMobileUI) - settings.js 提取对话框系统到 src/ui/dialog.js - window.AppState 统一改为 ES import(batch-processing、seal-library、history) - 删除已废弃文件(main_original.js、pdf-renderer.js、test-multi.pdf、TESTING_CHECKLIST.md) - exporter/pdf-loader/security/object-clipboard/batch-operations 去重与简化 文案润色: - index.html 全部静态文案改为口语化表述 - main.js 动态状态文案(工作流引导、状态卡片、折叠面板) - Toast 消息统一去掉"请先""请"等机械前缀,改为自然语气 - 设置面板 hint 文案简化 - 涉及 12 个 JS 文件约 80 处文案修改
407 lines
21 KiB
HTML
407 lines
21 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<link rel="icon" type="image/svg+xml" href="public/favicon.svg" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<meta
|
||
http-equiv="Content-Security-Policy"
|
||
content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; base-uri 'self'; form-action 'self'"
|
||
/>
|
||
<title>印迹(Yinji)- 本地文件印章工具</title>
|
||
<meta
|
||
name="description"
|
||
content="印迹(Yinji)- 本地文件印章工具,支持 PDF、PNG、JPG 盖章,支持普通印章、骑缝章和批量处理,所有操作均在本地完成。"
|
||
/>
|
||
<meta
|
||
name="keywords"
|
||
content="印迹,Yinji,PDF印章,图片盖章,电子印章,骑缝章,本地工具,PDF处理,批量处理"
|
||
/>
|
||
<link rel="stylesheet" href="src/style.css">
|
||
</head>
|
||
<body>
|
||
<div id="app" class="sidebar-visible no-pdf-loaded">
|
||
<aside class="sidebar" id="sidebar">
|
||
<div class="sidebar-header">
|
||
<div class="sidebar-heading">
|
||
<h2 class="sidebar-title">页面预览</h2>
|
||
<p class="sidebar-subtitle">打开文件后这里会显示缩略图</p>
|
||
</div>
|
||
<div class="sidebar-header-actions">
|
||
<button id="batch-mode-btn" class="btn btn-sm btn-secondary batch-mode-btn" title="一次选择多页">选多页</button>
|
||
<button id="sidebar-toggle" class="sidebar-toggle-btn" title="收起页面预览" aria-label="收起页面预览">
|
||
<i data-lucide="panel-left-close" class="sidebar-toggle-icon" aria-hidden="true"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<!-- 批量操作工具栏 -->
|
||
<div class="batch-toolbar" id="batch-toolbar" style="display:none;">
|
||
<div class="batch-info">
|
||
<span id="selected-pages-list"></span>
|
||
<span class="batch-count">已选 <span id="selected-count">0</span> 页</span>
|
||
</div>
|
||
<div class="batch-actions">
|
||
<button class="btn btn-sm btn-secondary" id="select-all-pages-btn">全选</button>
|
||
<button class="btn btn-sm btn-secondary" id="clear-selection-btn">清除选择</button>
|
||
<button class="btn btn-sm btn-primary" id="batch-add-seal-btn">多页盖章</button>
|
||
<button class="btn btn-sm btn-danger" id="batch-delete-stamps-btn">多页删章</button>
|
||
</div>
|
||
</div>
|
||
<div class="thumbnail-container" id="thumbnail-container">
|
||
<div class="empty-state">
|
||
<i data-lucide="file-up" class="empty-state-icon" aria-hidden="true"></i>
|
||
<h3>还没打开文件</h3>
|
||
<p>打开 PDF 或图片后,页面缩略图会出现在这里。</p>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-collapsed-panel" aria-hidden="true">
|
||
<div class="collapsed-sidebar-pill">
|
||
<span class="collapsed-sidebar-label">预览</span>
|
||
<i data-lucide="file-text" class="collapsed-sidebar-icon" aria-hidden="true"></i>
|
||
<strong id="collapsed-sidebar-primary">空闲</strong>
|
||
<span id="collapsed-sidebar-secondary">还没打开文件</span>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<main class="main-content-wrapper">
|
||
<div class="main-content" id="main-content">
|
||
<div id="drop-zone" class="drop-zone">
|
||
<div class="drop-zone-prompt">
|
||
<div class="drop-zone-eyebrow">开始使用</div>
|
||
<div class="drop-zone-illustration">
|
||
<i data-lucide="file-up" class="drop-zone-icon" aria-hidden="true"></i>
|
||
</div>
|
||
<h2>拖入 PDF 或图片文件</h2>
|
||
<p class="drop-zone-subtitle">支持 PDF、PNG、JPG,文件不会离开你的电脑。</p>
|
||
<div class="drop-zone-actions">
|
||
<button class="btn btn-primary" id="upload-pdf-btn-main">选择文件</button>
|
||
<button class="btn btn-secondary" id="load-sample-pdf-btn" data-load-sample-pdf>加载示例</button>
|
||
<span>或拖到这里</span>
|
||
</div>
|
||
<div class="drop-zone-features">
|
||
<span class="feature-chip">PDF</span>
|
||
<span class="feature-chip">PNG / JPG</span>
|
||
<span class="feature-chip">本地处理</span>
|
||
</div>
|
||
<input type="file" id="pdfInput" accept=".pdf,image/png,image/jpeg,.png,.jpg,.jpeg" hidden />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="main-content-footer">
|
||
<div class="page-navigator">
|
||
<span id="page-indicator">第 - / - 页</span>
|
||
<select id="page-selector" class="page-selector" title="快速跳转页面"></select>
|
||
</div>
|
||
<div class="zoom-controls" title="Ctrl 或 Command 加滚轮,可按鼠标位置缩放预览">
|
||
<label for="zoom-slider">缩放:</label>
|
||
<input type="range" id="zoom-slider" min="0.5" max="2" value="1" step="0.1" list="zoom-markers">
|
||
<span id="zoom-value">100% (Fit-Width)</span>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<datalist id="zoom-markers">
|
||
<option value="1" label="100%"></option>
|
||
</datalist>
|
||
|
||
<aside class="toolbar" id="toolbar">
|
||
<div class="toolbar-header">
|
||
<div class="toolbar-brand">
|
||
<h1>印迹(Yinji)</h1>
|
||
<div class="toolbar-brand-meta">
|
||
<p>给文件盖章,简单好用</p>
|
||
<span class="product-badge">本地处理</span>
|
||
</div>
|
||
</div>
|
||
<div class="header-actions">
|
||
<button id="settingsBtn" class="icon-btn" title="设置"><i data-lucide="settings"></i></button>
|
||
<button id="helpBtn" class="icon-btn" title="键盘快捷键"><i data-lucide="help-circle"></i></button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab Navigation -->
|
||
<div class="tab-navigation">
|
||
<button class="tab-btn active" data-tab="edit"><i data-lucide="edit"></i> 编辑</button>
|
||
<button class="tab-btn" data-tab="export"><i data-lucide="download"></i> 导出</button>
|
||
</div>
|
||
|
||
<!-- Tab Content -->
|
||
<div class="tab-content-wrapper">
|
||
<!-- Tab 1: 编辑 -->
|
||
<div class="tab-panel active" id="tab-edit">
|
||
<div class="toolbar-context-card">
|
||
<div class="toolbar-context-title">三步搞定盖章</div>
|
||
<p class="toolbar-context-copy">选文件 → 选印章 → 放到页面上,就这么简单。支持 PDF 和常见图片格式。</p>
|
||
<div class="toolbar-context-steps">
|
||
<span class="context-step">1 选择文件</span>
|
||
<span class="context-step">2 选择印章</span>
|
||
<span class="context-step">3 盖章并导出</span>
|
||
</div>
|
||
</div>
|
||
<div class="toolbar-status-card">
|
||
<div class="toolbar-status-header">
|
||
<strong id="edit-status-file">还没打开文件</strong>
|
||
<span id="edit-status-page">第 - / - 页</span>
|
||
</div>
|
||
<p class="toolbar-status-note" id="edit-status-summary">打开一个文件试试。</p>
|
||
</div>
|
||
<div class="workflow-card">
|
||
<div class="workflow-card-header">
|
||
<div>
|
||
<div class="workflow-card-title">快速操作</div>
|
||
<p class="workflow-card-copy">跟着按钮走就好。</p>
|
||
</div>
|
||
</div>
|
||
<p class="workflow-next-step" id="edit-next-step">打开一个文件试试。</p>
|
||
<div class="workflow-actions">
|
||
<button type="button" class="btn btn-primary" id="guide-primary-btn">选择文件</button>
|
||
<button type="button" class="btn btn-secondary" id="guide-secondary-btn" hidden>加载示例</button>
|
||
</div>
|
||
<p class="workflow-last-action" id="edit-last-action">操作结果会显示在这里。</p>
|
||
</div>
|
||
<div class="tool-section" data-section-key="edit-seal-source" data-section-default="open">
|
||
<h3>印章与盖章</h3>
|
||
<p class="tool-section-note">选好印章或手写签名后,放到当前页或做骑缝章。</p>
|
||
<div class="seal-preview-container" id="seal-preview-trigger" role="button" tabindex="0" aria-label="选择印章图片">
|
||
<img id="seal-preview" src="" alt="印章预览" class="seal-preview-image hidden">
|
||
<div id="seal-placeholder" class="seal-preview-placeholder">
|
||
<span class="seal-placeholder-badge">还没选素材</span>
|
||
<strong>选一个印章或签名</strong>
|
||
<p>可以用图片、手写签名,或者从常用印章里挑一个。</p>
|
||
</div>
|
||
<div class="seal-preview-overlay" id="seal-preview-overlay" hidden>
|
||
<span class="seal-preview-overlay-text">点击更换印章</span>
|
||
<button class="seal-preview-icon-btn" id="clear-seal-btn" type="button" title="清除当前素材" aria-label="清除当前素材">
|
||
<i data-lucide="trash-2" aria-hidden="true"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="button-group button-grid-2 button-grid-readable">
|
||
<button class="btn btn-primary" id="upload-seal-btn">选择印章图片</button>
|
||
<button class="btn btn-secondary" id="quick-signature-btn">手写签名</button>
|
||
</div>
|
||
<div class="button-group button-grid-2">
|
||
<button id="addSeal" class="btn btn-primary">盖到当前页</button>
|
||
<button id="addStraddle" class="btn btn-secondary">盖骑缝章</button>
|
||
</div>
|
||
<input type="file" id="sealInput" accept="image/*" hidden />
|
||
</div>
|
||
<div class="tool-section" data-section-key="edit-appearance" data-section-default="closed">
|
||
<h3>更多工具</h3>
|
||
<p class="tool-section-note">不常用的功能收在这里。</p>
|
||
<div class="button-group button-grid-2">
|
||
<button class="btn btn-tertiary" id="remove-bg-btn" title="去除图片白色背景,使其透明">去掉白底</button>
|
||
<button class="btn btn-tertiary" id="save-seal-btn">存为常用</button>
|
||
</div>
|
||
<div class="rotation-controls">
|
||
<label for="brightness-slider">亮度:</label>
|
||
<input type="range" id="brightness-slider" min="0" max="200" value="100" step="1">
|
||
<span id="brightness-value">100%</span>
|
||
</div>
|
||
<div class="rotation-controls">
|
||
<label for="rotation-slider">旋转:</label>
|
||
<input type="range" id="rotation-slider" min="-180" max="180" value="0" step="1">
|
||
<input type="number" id="rotation-input" min="-180" max="180" value="0" class="rotation-input-box">
|
||
<span>°</span>
|
||
</div>
|
||
<div class="button-group button-grid-3">
|
||
<button class="btn btn-secondary" id="copy-stamp-btn" title="复制选中 (Ctrl+C)">复制</button>
|
||
<button class="btn btn-secondary" id="paste-stamp-btn" title="粘贴 (Ctrl+V)">粘贴</button>
|
||
<button class="btn btn-secondary" id="lock-stamp-btn" title="锁定/解锁 (Ctrl+L)">锁定</button>
|
||
</div>
|
||
<div class="button-group precision-toggle-row">
|
||
<button type="button" class="btn btn-secondary btn-sm precision-toggle-btn" id="toggle-precision-controls" hidden>微调位置</button>
|
||
</div>
|
||
<div class="precision-panel" id="precision-controls" hidden>
|
||
<div class="precision-panel-header">
|
||
<div class="precision-panel-copy">
|
||
<strong>微调位置和大小</strong>
|
||
<p id="edit-selection-chip">点一下页面上的印章,就能调位置。</p>
|
||
</div>
|
||
<button class="btn btn-secondary btn-sm" id="guideline-toggle-btn" title="拖动印章时显示并吸附到对齐辅助线">辅助线:开</button>
|
||
</div>
|
||
<span id="edit-guideline-chip" hidden>对齐辅助线:已开启</span>
|
||
<div class="coord-controls" id="precision-coordinate-fields" hidden>
|
||
<div class="coord-field">
|
||
<label for="coord-x">X</label>
|
||
<input type="number" id="coord-x" class="coord-input" step="1" min="0" placeholder="0">
|
||
</div>
|
||
<div class="coord-field">
|
||
<label for="coord-y">Y</label>
|
||
<input type="number" id="coord-y" class="coord-input" step="1" min="0" placeholder="0">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="tool-section" data-section-key="edit-library" data-section-default="closed">
|
||
<h3>印章库</h3>
|
||
<p class="tool-section-note">常用印章存在这里,下次直接用。</p>
|
||
<div class="tool-chip-row">
|
||
<span class="tool-chip" id="seal-library-count-chip">已保存 0 个印章</span>
|
||
</div>
|
||
<div id="seal-library-container" class="seal-library-container"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab 2: 导出 -->
|
||
<div class="tab-panel" id="tab-export">
|
||
<div class="toolbar-context-card toolbar-context-card-export">
|
||
<div class="toolbar-context-title">盖好章就能下载</div>
|
||
<p class="toolbar-context-copy">一般在「编辑」盖好章后回来下载。需要同时处理多份 PDF 时,切到批量处理。</p>
|
||
</div>
|
||
<div class="toolbar-status-card">
|
||
<div class="toolbar-status-header">
|
||
<strong id="export-status-file">还没打开文件</strong>
|
||
<span id="export-status-summary">第 - / - 页</span>
|
||
</div>
|
||
<p class="toolbar-status-note" id="export-status-note">去「编辑」盖好章,再来这里下载。</p>
|
||
</div>
|
||
<div class="workflow-card">
|
||
<div class="workflow-card-header">
|
||
<div>
|
||
<div class="workflow-card-title">下载文件</div>
|
||
<p class="workflow-card-copy">盖好章就能下载。</p>
|
||
</div>
|
||
</div>
|
||
<p class="workflow-next-step" id="export-next-step">去「编辑」盖好章后回来就好。</p>
|
||
<p class="workflow-last-action" id="export-last-action">操作结果会显示在这里。</p>
|
||
</div>
|
||
<div class="export-mode-switch" role="tablist" aria-label="导出方式">
|
||
<button type="button" class="export-mode-btn active" id="export-mode-single-btn" data-export-mode="single" aria-selected="true">单份文件</button>
|
||
<button type="button" class="export-mode-btn" id="export-mode-batch-btn" data-export-mode="batch" aria-selected="false">批量处理</button>
|
||
</div>
|
||
<div class="export-mode-panel" data-export-mode-panel="single">
|
||
<div class="tool-section" data-pdf-dependent="true" data-section-key="export-current" data-section-default="open">
|
||
<h3>下载整份文件</h3>
|
||
<p class="tool-section-note">下载盖好章的文件。如果是图片来源,还能导出 PNG。</p>
|
||
<div class="button-group button-grid-2">
|
||
<button id="exportPDF" class="btn btn-primary btn-export">下载 PDF</button>
|
||
<button id="exportImage" class="btn btn-secondary btn-export" hidden>下载 PNG 图片</button>
|
||
</div>
|
||
</div>
|
||
<div class="tool-section" data-pdf-dependent="true" data-section-key="export-pages" data-section-default="closed">
|
||
<h3>按页面下载</h3>
|
||
<p class="tool-section-note">只需要几页时用这个。不选页面就下载全部。</p>
|
||
<div class="button-group button-grid-compact">
|
||
<button class="btn btn-secondary" id="export-selected-btn" title="下载选中的页面">下载选中页</button>
|
||
<button class="btn btn-secondary" id="export-all-btn" title="下载全部页面">下载全部页</button>
|
||
<button class="btn btn-tertiary" id="export-split-btn" title="每页独立下载为单个文件">逐页下载</button>
|
||
</div>
|
||
<p id="export-hint" class="export-hint"></p>
|
||
</div>
|
||
</div>
|
||
<div class="export-mode-panel" data-export-mode-panel="batch" hidden>
|
||
<div class="tool-section" data-section-key="export-batch" data-section-default="open">
|
||
<h3>批量处理多份 PDF</h3>
|
||
<div class="section-meta-line">
|
||
<p class="tool-section-note">一口气给多份 PDF 盖同样的章。</p>
|
||
<span class="section-counter" id="batch-file-count">0 个文件</span>
|
||
</div>
|
||
<div class="button-group">
|
||
<button class="btn btn-secondary" id="upload-batch-btn">添加多个 PDF</button>
|
||
<input type="file" id="batchInput" accept=".pdf" multiple hidden />
|
||
</div>
|
||
<div id="batch-file-list" class="batch-file-list"></div>
|
||
<div class="button-group button-grid-compact button-grid-compact-2">
|
||
<button id="processBatch" class="btn btn-primary">开始批量处理</button>
|
||
<button id="clearBatch" class="btn btn-danger">清空队列</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toolbar-footer">
|
||
<p>所有操作都在你的电脑上完成</p>
|
||
<a href="https://www.modiqi.com" target="_blank" rel="noopener noreferrer">莫蒂奇数字技术(苏州)有限公司</a>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
|
||
<script
|
||
type="module"
|
||
src="vendor/pdf.min.mjs"
|
||
></script>
|
||
<script
|
||
src="vendor/fabric.min.js"
|
||
></script>
|
||
<script
|
||
src="vendor/pdf-lib.min.js"
|
||
></script>
|
||
<script
|
||
src="vendor/lucide.min.js"
|
||
></script>
|
||
<script type="module" src="src/utils/validators.js"></script>
|
||
<script type="module" src="src/ui/notifications.js"></script>
|
||
<script type="module" src="src/ui/loading.js"></script>
|
||
<script type="module" src="src/core/state.js"></script>
|
||
<script type="module" src="src/core/history.js"></script>
|
||
<script type="module" src="src/core/seal-library.js"></script>
|
||
<script type="module" src="src/core/batch-processing.js"></script>
|
||
<script type="module" src="src/core/settings.js"></script>
|
||
<script type="module" src="src/main.js"></script>
|
||
|
||
<!-- 签名模态框 -->
|
||
<div id="signature-modal" class="modal hidden">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>手写签名</h3>
|
||
<button class="modal-close">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<canvas id="signature-canvas" width="600" height="300"></canvas>
|
||
<p class="modal-hint">在上面空白处写你的签名</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button id="clear-signature-btn" class="btn btn-secondary">重写</button>
|
||
<button id="confirm-signature-btn" class="btn btn-primary">插入到当前页</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右键菜单 -->
|
||
<div id="context-menu" class="context-menu hidden">
|
||
<div class="context-menu-item" data-action="delete">
|
||
<i data-lucide="trash-2"></i>
|
||
<span>删除</span>
|
||
<span class="context-menu-shortcut">Del</span>
|
||
</div>
|
||
<div class="context-menu-item" data-action="copy">
|
||
<i data-lucide="copy"></i>
|
||
<span>复制</span>
|
||
<span class="context-menu-shortcut">Ctrl+C</span>
|
||
</div>
|
||
<div class="context-menu-item" data-action="paste">
|
||
<i data-lucide="clipboard"></i>
|
||
<span>粘贴</span>
|
||
<span class="context-menu-shortcut">Ctrl+V</span>
|
||
</div>
|
||
<div class="context-menu-item" data-action="toggle-lock">
|
||
<i data-lucide="lock"></i>
|
||
<span>锁定/解锁</span>
|
||
<span class="context-menu-shortcut">Ctrl+L</span>
|
||
</div>
|
||
<div class="context-menu-divider"></div>
|
||
<div class="context-menu-item" data-action="undo">
|
||
<i data-lucide="undo"></i>
|
||
<span>撤销</span>
|
||
<span class="context-menu-shortcut">Ctrl+Z</span>
|
||
</div>
|
||
<div class="context-menu-item" data-action="redo">
|
||
<i data-lucide="redo"></i>
|
||
<span>重做</span>
|
||
<span class="context-menu-shortcut">Ctrl+Y</span>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 初始化 Lucide 图标
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', () => lucide.createIcons());
|
||
} else {
|
||
lucide.createIcons();
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|