d05a6cd529
演示页面增强: - 引入 html2canvas 库支持导出为 PNG - 添加顶部工具栏和批量导出按钮 - 图片点击放大/缩小交互 - 单页导出按钮在每个卡片头部 - 兼容 file:// 和 HTTP 协议的导出处理 - 本地文件模式下显示协议限制提示
643 lines
28 KiB
HTML
643 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>阿尼坊小程序 - 用户指南(公众号截图版)</title>
|
||
<!-- html2canvas 库 - 用于导出PNG -->
|
||
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
||
<style>
|
||
:root {
|
||
--primary-color: #6366f1;
|
||
--text-main: #1f2937;
|
||
--text-muted: #4b5563;
|
||
--bg-color: #1e293b; /* 暗色背景凸显截图卡片 */
|
||
--bg-content: #ffffff;
|
||
--border-color: #e5e7eb;
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
body {
|
||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
|
||
color: var(--text-main);
|
||
background-color: var(--bg-color);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 20px 0;
|
||
line-height: 1.7;
|
||
}
|
||
|
||
/* 顶部提示工具栏 */
|
||
.toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
width: 393px;
|
||
background-color: var(--bg-content);
|
||
padding: 12px 20px;
|
||
margin-bottom: 20px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||
}
|
||
.toolbar-text {
|
||
color: var(--text-muted);
|
||
font-size: 14px;
|
||
}
|
||
.toolbar-btn {
|
||
background-color: var(--primary-color);
|
||
color: #fff;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
.toolbar-btn:hover {
|
||
background-color: #4f46e5;
|
||
transform: scale(1.02);
|
||
}
|
||
.toolbar-btn:disabled {
|
||
background-color: #94a3b8;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
/* 核心:iPhone16 竖屏卡片 9:19.5 (393x852) - 自适应高度 */
|
||
.iphone-card {
|
||
width: 393px;
|
||
min-height: 852px;
|
||
background-color: var(--bg-content);
|
||
margin-bottom: 40px;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.4);
|
||
/* 直角边缘方便直接矩形截图 */
|
||
border: 1px solid #d1d5db;
|
||
}
|
||
|
||
.card-header {
|
||
background-color: var(--primary-color);
|
||
color: #ffffff;
|
||
height: 54px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0 20px;
|
||
font-weight: 600;
|
||
font-size: 16px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.card-body {
|
||
padding: 28px 24px;
|
||
flex: 1;
|
||
overflow: visible;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.card-footer {
|
||
height: 40px;
|
||
text-align: center;
|
||
color: #94a3b8;
|
||
font-size: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-top: 1px dashed var(--border-color);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 内部排版样式 */
|
||
h1 { font-size: 24px; margin-bottom: 20px; color: var(--primary-color); border-bottom: 2px solid var(--border-color); padding-bottom: 10px; }
|
||
h2 { font-size: 20px; margin: 18px 0 10px; color: var(--text-main); }
|
||
h3 { font-size: 17px; margin: 14px 0 8px; color: var(--text-main); font-weight: 600; }
|
||
p { margin-bottom: 12px; font-size: 16px; color: var(--text-muted); }
|
||
ul, ol { margin-bottom: 14px; padding-left: 22px; font-size: 16px; color: var(--text-muted); }
|
||
li { margin-bottom: 8px; }
|
||
|
||
/* 封面特殊排版 */
|
||
.cover-body { justify-content: center; align-items: center; text-align: center; }
|
||
.cover-icon { font-size: 64px; margin-bottom: 20px; }
|
||
.cover-title { font-size: 28px; color: var(--primary-color); margin-bottom: 12px; border: none;}
|
||
.cover-subtitle { font-size: 17px; color: var(--text-muted); margin-bottom: 40px; }
|
||
.cover-list { text-align: left; background: #f8fafc; padding: 22px 32px; border-radius: 12px; width: 100%; border: 1px solid var(--border-color); }
|
||
.cover-list ol { margin-bottom: 0; }
|
||
.cover-list li { margin-bottom: 14px; font-weight: 500; font-size: 16px;}
|
||
|
||
/* 截图图片控制 - 点击放大功能 */
|
||
.img-wrapper { text-align: center; margin: 10px 0; flex-shrink: 0; }
|
||
.actual-img {
|
||
max-width: 100%;
|
||
border-radius: 6px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
object-fit: contain;
|
||
border: 1px solid #e2e8f0;
|
||
cursor: pointer;
|
||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||
}
|
||
.actual-img:hover {
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
||
}
|
||
.actual-img.enlarged {
|
||
transform: scale(1.8);
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||
z-index: 10;
|
||
position: relative;
|
||
}
|
||
.img-enlarge-hint {
|
||
font-size: 12px;
|
||
color: #94a3b8;
|
||
margin-top: 4px;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
/* 表格样式适配移动端 */
|
||
table { width: 100%; border-collapse: collapse; margin: 14px 0; background-color: white; font-size: 14px; }
|
||
th, td { border: 1px solid var(--border-color); padding: 10px; text-align: left; }
|
||
th { background-color: #f8fafc; font-weight: 600; color: var(--text-main); }
|
||
|
||
/* 提示框样式 */
|
||
.alert { padding: 14px 16px; border-radius: 8px; margin: 14px 0; display: flex; flex-direction: column; font-size: 14px; }
|
||
.alert.warning { background-color: #fffbeb; border-left: 4px solid #f59e0b; color: #92400e; }
|
||
.alert.info { background-color: #eff6ff; border-left: 4px solid #3b82f6; color: #1e40af; }
|
||
.alert strong { margin-bottom: 6px; font-size: 16px; }
|
||
.alert p { margin-bottom: 4px; font-size: 14px; color: inherit; }
|
||
.alert p:last-child { margin-bottom: 0; }
|
||
|
||
/* 代码块样式 */
|
||
pre {
|
||
background-color: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px;
|
||
margin: 12px 0; font-size: 14px; white-space: pre-wrap; word-wrap: break-word;
|
||
}
|
||
code { font-family: ui-monospace, monospace; }
|
||
p code, li code { background-color: #f1f5f9; color: #ef4444; padding: 2px 6px; border-radius: 4px; font-size: 14px; }
|
||
|
||
/* 导出按钮样式 */
|
||
.export-btn {
|
||
background: transparent;
|
||
color: #fff;
|
||
border: 1px solid rgba(255,255,255,0.6);
|
||
padding: 4px 10px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
margin-left: 8px;
|
||
transition: all 0.2s;
|
||
}
|
||
.export-btn:hover {
|
||
background: rgba(255,255,255,0.2);
|
||
border-color: #fff;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="toolbar">
|
||
<span class="toolbar-text">💡 iPhone 16 竖屏比例 (393x852) - 共 7 页</span>
|
||
<button class="toolbar-btn" onclick="exportAllCards()" id="exportAllBtn">📦 批量导出全部</button>
|
||
</div>
|
||
|
||
<!-- Page 1: 封面 -->
|
||
<div class="iphone-card">
|
||
<div class="card-header"><span>🦊 阿尼坊</span><span>01 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
|
||
<div class="card-body cover-body">
|
||
<div class="cover-icon">🦊</div>
|
||
<h1 class="cover-title">阿尼坊小程序</h1>
|
||
<div class="cover-subtitle">用户操作指南</div>
|
||
|
||
<div class="cover-list">
|
||
<ol>
|
||
<li><strong style="color:var(--primary-color)">一、登录流程</strong> - 微信授权登录</li>
|
||
<li><strong style="color:var(--primary-color)">二、实名认证</strong> - 完成身份验证</li>
|
||
<li><strong style="color:var(--primary-color)">三、申摊流程</strong> - 申请摊位参展</li>
|
||
<li><strong style="color:var(--primary-color)">四、预约详情</strong> - 查看申请状态</li>
|
||
</ol>
|
||
</div>
|
||
<p style="margin-top: 30px; font-size: 15px;">欢迎使用阿尼坊!本指南将帮助您快速上手,顺利参与动漫市集。如有问题请通过「我的」-「意见反馈」联系客服。</p>
|
||
</div>
|
||
<div class="card-footer">- 官方运营指南 -</div>
|
||
</div>
|
||
|
||
<!-- Page 2: 登录流程 -->
|
||
<div class="iphone-card">
|
||
<div class="card-header"><span>🦊 阿尼坊</span><span>02 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
|
||
<div class="card-body">
|
||
<h1>一、登录流程</h1>
|
||
<p>首次打开阿尼坊小程序时,系统会引导您完成微信登录。</p>
|
||
|
||
<h3>1. 打开小程序</h3>
|
||
<p>通过微信搜索"阿尼坊"或扫描二维码进入小程序。</p>
|
||
|
||
<h3>2. 触发登录</h3>
|
||
<p>当您点击需要登录的功能(如申摊)时,系统会弹出登录提示框。</p>
|
||
<div class="img-wrapper">
|
||
<img src="screenshots/2_前往登录弹窗.jpg" alt="登录提示弹窗" class="actual-img" crossorigin="anonymous">
|
||
<div class="img-enlarge-hint">点击图片可放大查看</div>
|
||
</div>
|
||
|
||
<div class="alert warning" style="margin-top: auto;">
|
||
<strong>⚠️ 游客用户限制</strong>
|
||
<p>未登录仅可浏览:活动公告、商铺、摊位卡片详情。</p>
|
||
<p>登录后可使用:设施预约、摊位申请、评论点赞、收藏。</p>
|
||
</div>
|
||
</div>
|
||
<div class="card-footer">- 官方运营指南 -</div>
|
||
</div>
|
||
|
||
<!-- Page 3: 登录流程 (续) -->
|
||
<div class="iphone-card">
|
||
<div class="card-header"><span>🦊 阿尼坊</span><span>03 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
|
||
<div class="card-body">
|
||
<h1>一、登录流程 (续)</h1>
|
||
|
||
<h3>3. 填写个人信息</h3>
|
||
<p>登录表单包含头像选择、昵称填写和用户协议勾选。</p>
|
||
<div class="img-wrapper">
|
||
<img src="screenshots/3_填写昵称_头像信息.png" alt="登录表单" class="actual-img" crossorigin="anonymous">
|
||
<div class="img-enlarge-hint">点击图片可放大查看</div>
|
||
</div>
|
||
|
||
<h3>4. 手机号授权</h3>
|
||
<p>系统申请获取微信绑定手机号,用于验证码接收和客服联系。</p>
|
||
<div class="img-wrapper">
|
||
<img src="screenshots/4_登录后授权手机号弹窗.png" alt="手机号授权" class="actual-img" crossorigin="anonymous">
|
||
<div class="img-enlarge-hint">点击图片可放大查看</div>
|
||
</div>
|
||
|
||
<h3>5. 完成登录</h3>
|
||
<p>授权完成后,您的头像和昵称将显示在「我的」页面,系统自动生成唯一 UID。</p>
|
||
</div>
|
||
<div class="card-footer">- 官方运营指南 -</div>
|
||
</div>
|
||
|
||
<!-- Page 4: 实名认证 -->
|
||
<div class="iphone-card">
|
||
<div class="card-header"><span>🦊 阿尼坊</span><span>04 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
|
||
<div class="card-body">
|
||
<h1>二、实名认证</h1>
|
||
<p>实名认证是使用设施预约和摊位申请功能的前置条件。</p>
|
||
|
||
<h3>认证入口</h3>
|
||
<p>进入「我的」页面,点击「前往实名认证」按钮。</p>
|
||
<div class="img-wrapper">
|
||
<img src="screenshots/5_我的界面_前往实名认证.png" alt="实名认证入口" class="actual-img" crossorigin="anonymous">
|
||
<div class="img-enlarge-hint">点击图片可放大查看</div>
|
||
</div>
|
||
|
||
<h3>认证信息填写</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>字段</th><th>说明</th><th>要求</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>真实姓名</td><td>与身份证一致</td><td>2-20个汉字</td></tr>
|
||
<tr><td>身份证号码</td><td>18位身份证号</td><td>正确格式验证</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="img-wrapper">
|
||
<img src="screenshots/6_填写实名认证信息.png" alt="实名认证表单" class="actual-img" crossorigin="anonymous">
|
||
<div class="img-enlarge-hint">点击图片可放大查看</div>
|
||
</div>
|
||
|
||
<div class="alert warning" style="margin-top: auto;">
|
||
<strong>⚠️ 认证后不可修改</strong>
|
||
<p>实名认证完成后,姓名和身份证号不可更改。如需修改请联系客服。</p>
|
||
</div>
|
||
</div>
|
||
<div class="card-footer">- 官方运营指南 -</div>
|
||
</div>
|
||
|
||
<!-- Page 5: 申摊流程 -->
|
||
<div class="iphone-card">
|
||
<div class="card-header"><span>🦊 阿尼坊</span><span>05 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
|
||
<div class="card-body">
|
||
<h1>三、申摊流程</h1>
|
||
<p>申请摊位参加阿尼坊动漫市集。</p>
|
||
<div class="alert info">
|
||
<strong>💡 前置条件</strong>
|
||
<p>申请摊位前请确保:<br>✅ 已完成微信登录<br>✅ 已完成实名认证</p>
|
||
</div>
|
||
|
||
<h3>申请入口</h3>
|
||
<p>进入「市集」页面,点击右上角「申摊」按钮。</p>
|
||
|
||
<h3>填写申请信息</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>字段</th><th>限制</th><th>说明</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>标题</td><td>50字内</td><td>卡片标题</td></tr>
|
||
<tr><td>正文</td><td>500字内</td><td>描述内容,可用 #标签</td></tr>
|
||
<tr><td>图片</td><td>1张,≤5MB</td><td>卡片封面图</td></tr>
|
||
<tr><td>联系QQ</td><td>可修改</td><td>客服联系渠道</td></tr>
|
||
<tr><td>预约场次</td><td>下拉选择</td><td>日期 + 区域</td></tr>
|
||
<tr><td>动漫IP</td><td>1-10个</td><td>必选至少1个标签</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>填写步骤:</strong></p>
|
||
<ol>
|
||
<li>输入标题和内容描述(正文最多500字)</li>
|
||
<li>上传一张清晰图片作为封面(≤5MB)</li>
|
||
<li>选择参展场次(日期+区域)</li>
|
||
<li>选择动漫IP标签(至少1个,最多10个)</li>
|
||
<li>勾选已阅读申摊须知,点击确认提交</li>
|
||
</ol>
|
||
</div>
|
||
<div class="card-footer">- 官方运营指南 -</div>
|
||
</div>
|
||
|
||
<!-- Page 6: 申摊流程 (续) -->
|
||
<div class="iphone-card">
|
||
<div class="card-header"><span>🦊 阿尼坊</span><span>06 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
|
||
<div class="card-body">
|
||
<h1>三、申摊流程 (续)</h1>
|
||
|
||
<h3>申请状态流转</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>状态</th><th>说明</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>待抽签</td><td>申请已提交,等待抽签</td></tr>
|
||
<tr><td>审核中</td><td>抽签中签,等待审核</td></tr>
|
||
<tr><td>待缴费</td><td>审核通过,等待支付</td></tr>
|
||
<tr><td>未中签</td><td>抽签未中</td></tr>
|
||
<tr><td>审核驳回</td><td>审核未通过(显示原因)</td></tr>
|
||
<tr><td>成功</td><td>已公布摊位号</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="alert warning">
|
||
<strong>⚠️ 场次限制</strong>
|
||
<p>• 每天仅能申请一个场次<br>• 申请前请确认场次信息</p>
|
||
</div>
|
||
|
||
<div class="alert info">
|
||
<strong>💡 存稿机制</strong>
|
||
<p>系统自动保存上次填写内容,30天后自动清理存稿。</p>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="card-footer">- 官方运营指南 -</div>
|
||
</div>
|
||
|
||
<!-- Page 7: 预约详情 -->
|
||
<div class="iphone-card">
|
||
<div class="card-header"><span>🦊 阿尼坊</span><span>07 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
|
||
<div class="card-body">
|
||
<h1>四、预约详情</h1>
|
||
<p>查看摊位申请和设施预约记录。</p>
|
||
|
||
<h3>入口位置</h3>
|
||
<p>进入「我的」页面,点击「预约详情」入口。</p>
|
||
|
||
<h3>页面结构</h3>
|
||
<p>页面分为两个栏目:<br><strong>• 摊位栏目</strong> - 摊位申请记录<br><strong>• 设施栏目</strong> - 设施预约记录</p>
|
||
|
||
<h3>摊位申请状态</h3>
|
||
<table>
|
||
<thead>
|
||
<tr><th>状态</th><th>显示内容</th><th>操作</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>待缴费</td><td>订单号、金额、截止时间</td><td>支付 / 取消</td></tr>
|
||
<tr><td>待公布</td><td>订单号、退款提示</td><td>申请退款</td></tr>
|
||
<tr><td>成功</td><td>摊位号 (如: <code>2.1 A区13</code>)</td><td>无</td></tr>
|
||
<tr><td>已失效</td><td>订单号</td><td>无</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="alert warning">
|
||
<strong>⚠️ 支付时限</strong>
|
||
<p>请在截止时间前完成支付,超时订单自动取消。</p>
|
||
</div>
|
||
|
||
<h3>退款申请</h3>
|
||
<p>符合退款条件时点击「申请退款」按钮,退款成功后状态变更为「已退款」。</p>
|
||
</div>
|
||
<div class="card-footer">- 官方运营指南 -</div>
|
||
</div>
|
||
|
||
</body>
|
||
<script>
|
||
// 图片 base64 缓存(解决跨域图片 tainted canvas 问题)
|
||
const imageCache = {};
|
||
let preloadComplete = false;
|
||
let isLocalFile = window.location.protocol === 'file:';
|
||
let allowTaintedCanvas = false;
|
||
|
||
// 显示协议兼容性提示
|
||
function showProtocolWarning() {
|
||
const toolbar = document.querySelector('.toolbar');
|
||
if (toolbar && isLocalFile) {
|
||
const warningDiv = document.createElement('div');
|
||
warningDiv.style.cssText = 'width:393px;background:#fffbeb;border-left:4px solid #f59e0b;padding:12px 16px;margin-bottom:20px;border-radius:8px;font-size:14px;color:#92400e;';
|
||
warningDiv.innerHTML = `
|
||
<strong>⚠️ 本地文件模式限制</strong>
|
||
<p style="margin:6px 0 0;">当前使用 file:// 协议直接打开,图片导出功能受限。</p>
|
||
<p style="margin:4px 0;">推荐方式:启动本地 HTTP 服务器</p>
|
||
<code style="background:#fef3c7;padding:2px 6px;border-radius:4px;">npx serve -l 3000</code>
|
||
<p style="margin:4px 0;">然后访问 <a href="#" onclick="copyToLocalhost()" style="color:#d97706;">http://localhost:3000/...</a></p>
|
||
`;
|
||
toolbar.insertAdjacentElement('beforebegin', warningDiv);
|
||
allowTaintedCanvas = true;
|
||
}
|
||
}
|
||
|
||
// 复制 localhost URL
|
||
function copyToLocalhost() {
|
||
const localhostUrl = 'http://localhost:3000/' + window.location.pathname.split('/').pop();
|
||
navigator.clipboard.writeText(localhostUrl).then(() => {
|
||
alert('已复制: ' + localhostUrl + '\n请启动服务器后访问');
|
||
});
|
||
}
|
||
|
||
// 预加载图片转为 base64(仅 HTTP/HTTPS 协议有效)
|
||
function preloadImages() {
|
||
// file:// 协议下 XMLHttpRequest 被 CORS 阻止,跳过预加载
|
||
if (isLocalFile) {
|
||
console.warn('file:// 协议下无法预加载图片,将使用 allowTaint 模式导出');
|
||
preloadComplete = true;
|
||
allowTaintedCanvas = true;
|
||
return;
|
||
}
|
||
|
||
const images = document.querySelectorAll('.actual-img');
|
||
const promises = [];
|
||
|
||
images.forEach(img => {
|
||
const src = img.getAttribute('src');
|
||
if (src && !imageCache[src]) {
|
||
const promise = new Promise((resolve, reject) => {
|
||
const xhr = new XMLHttpRequest();
|
||
xhr.onload = function() {
|
||
const reader = new FileReader();
|
||
reader.onloadend = function() {
|
||
imageCache[src] = reader.result;
|
||
img.src = reader.result; // 替换为 base64
|
||
resolve();
|
||
};
|
||
reader.readAsDataURL(xhr.response);
|
||
};
|
||
xhr.onerror = function() {
|
||
console.warn('图片预加载失败:', src);
|
||
resolve(); // 即使失败也继续,不阻塞导出
|
||
};
|
||
xhr.open('GET', src);
|
||
xhr.responseType = 'blob';
|
||
xhr.send();
|
||
});
|
||
promises.push(promise);
|
||
}
|
||
});
|
||
|
||
// 所有图片加载完成后标记
|
||
Promise.all(promises).then(() => {
|
||
preloadComplete = true;
|
||
console.log('图片预加载完成,可以安全导出');
|
||
});
|
||
}
|
||
|
||
// 页面加载完成后初始化
|
||
window.addEventListener('load', () => {
|
||
showProtocolWarning();
|
||
preloadImages();
|
||
});
|
||
|
||
// 图片点击放大/缩小切换
|
||
document.querySelectorAll('.actual-img').forEach(img => {
|
||
img.addEventListener('click', function() {
|
||
if (this.classList.contains('enlarged')) {
|
||
// 缩小恢复
|
||
this.classList.remove('enlarged');
|
||
} else {
|
||
// 先缩小其他已放大的图片
|
||
document.querySelectorAll('.actual-img.enlarged').forEach(other => {
|
||
other.classList.remove('enlarged');
|
||
});
|
||
// 放大当前图片
|
||
this.classList.add('enlarged');
|
||
}
|
||
});
|
||
});
|
||
|
||
// 导出单个卡片为PNG
|
||
function exportCard(button) {
|
||
// file:// 协议下跳过预加载检查,使用 allowTaint 模式
|
||
if (!isLocalFile && !preloadComplete) {
|
||
alert('图片正在加载中,请稍后再试导出...\n建议:等待页面完全加载后再点击导出');
|
||
return;
|
||
}
|
||
|
||
// file:// 协议下提示用户
|
||
if (isLocalFile) {
|
||
console.warn('file:// 模式导出:图片可能无法正确渲染,建议使用 HTTP 服务器');
|
||
}
|
||
|
||
const card = button.closest('.iphone-card');
|
||
const pageText = card.querySelector('.card-header span:nth-child(2)').textContent.trim();
|
||
const pageNum = pageText.split('/')[0].trim().replace('0', '');
|
||
const title = getCardTitle(pageNum);
|
||
|
||
html2canvas(card, {
|
||
scale: 2, // 高清导出
|
||
useCORS: !isLocalFile, // HTTP 下启用跨域,file:// 下禁用
|
||
allowTaint: allowTaintedCanvas, // file:// 下允许污染画布
|
||
backgroundColor: '#ffffff',
|
||
logging: false
|
||
}).then(canvas => {
|
||
const link = document.createElement('a');
|
||
link.download = `阿尼坊用户指南_${title}.png`;
|
||
link.href = canvas.toDataURL('image/png');
|
||
link.click();
|
||
}).catch(err => {
|
||
let errorMsg = err.message;
|
||
if (isLocalFile) {
|
||
errorMsg += '\n\nfile:// 协议限制:请使用 HTTP 服务器访问';
|
||
}
|
||
alert('导出失败:' + errorMsg);
|
||
console.error('导出错误:', err);
|
||
});
|
||
}
|
||
|
||
// 根据页码获取卡片标题
|
||
function getCardTitle(pageNum) {
|
||
const titles = {
|
||
'1': '封面',
|
||
'2': '登录流程',
|
||
'3': '登录流程续',
|
||
'4': '实名认证',
|
||
'5': '申摊流程',
|
||
'6': '申摊流程续',
|
||
'7': '预约详情'
|
||
};
|
||
return titles[pageNum] || `第${pageNum}页`;
|
||
}
|
||
|
||
// 批量导出所有卡片
|
||
async function exportAllCards() {
|
||
// file:// 协议下跳过预加载检查,使用 allowTaint 模式
|
||
if (!isLocalFile && !preloadComplete) {
|
||
alert('图片正在加载中,请稍后再试批量导出...\n建议:等待页面完全加载后再点击导出');
|
||
return;
|
||
}
|
||
|
||
// file:// 协议下提示用户
|
||
if (isLocalFile) {
|
||
console.warn('file:// 模式批量导出:图片可能无法正确渲染,建议使用 HTTP 服务器');
|
||
}
|
||
|
||
const btn = document.getElementById('exportAllBtn');
|
||
const cards = document.querySelectorAll('.iphone-card');
|
||
const total = cards.length;
|
||
|
||
btn.disabled = true;
|
||
btn.textContent = `⏳ 正在导出...`;
|
||
|
||
for (let i = 0; i < total; i++) {
|
||
btn.textContent = `⏳ 导出中 (${i+1}/${total})`;
|
||
const card = cards[i];
|
||
const pageNum = (i + 1).toString();
|
||
const title = getCardTitle(pageNum);
|
||
|
||
try {
|
||
const canvas = await html2canvas(card, {
|
||
scale: 2,
|
||
useCORS: !isLocalFile, // HTTP 下启用跨域,file:// 下禁用
|
||
allowTaint: allowTaintedCanvas, // file:// 下允许污染画布
|
||
backgroundColor: '#ffffff',
|
||
logging: false
|
||
});
|
||
|
||
const link = document.createElement('a');
|
||
link.download = `阿尼坊用户指南_${title}.png`;
|
||
link.href = canvas.toDataURL('image/png');
|
||
link.click();
|
||
|
||
// 稍作延迟避免浏览器阻塞
|
||
await new Promise(r => setTimeout(r, 300));
|
||
} catch (err) {
|
||
console.error(`第${pageNum}页导出失败:`, err);
|
||
let errorMsg = err.message;
|
||
if (isLocalFile) {
|
||
errorMsg += ' (file:// 协议限制)';
|
||
}
|
||
alert(`第${pageNum}页导出失败:${errorMsg}`);
|
||
}
|
||
}
|
||
|
||
btn.disabled = false;
|
||
btn.textContent = '📦 批量导出全部';
|
||
alert('全部导出完成!');
|
||
}
|
||
</script>
|
||
</html> |