yinji/index.html
wenpai 0a35b14d3d 重构:代码清理 + 职责拆分 + 文案润色
代码清理:
- 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 处文案修改
2026-04-05 07:49:04 +08:00

407 lines
21 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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">&times;</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>