test(rich-text): 添加富文本渲染测试页面
- 添加 v-html 渲染测试页面 (index.vue) - 添加 mp-html 组件测试页面 (index1.vue) - 测试内容:HTML实体解析、a标签替换为div、图片预览、文件链接点击 - 使用 useFileOperation 处理 PDF 文件打开 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Showing
3 changed files
with
726 additions
and
0 deletions
| ... | @@ -37,6 +37,7 @@ const pages = [ | ... | @@ -37,6 +37,7 @@ const pages = [ |
| 37 | 37 | ||
| 38 | if (process.env.NODE_ENV === 'development') { | 38 | if (process.env.NODE_ENV === 'development') { |
| 39 | pages.push('pages/test-tabs/index') | 39 | pages.push('pages/test-tabs/index') |
| 40 | + pages.push('pages/rich-text-test/index') // 富文本渲染测试页面 | ||
| 40 | // pages.push('pages/nfcTest/index') | 41 | // pages.push('pages/nfcTest/index') |
| 41 | // pages.push('pages/tailwindTest/index') | 42 | // pages.push('pages/tailwindTest/index') |
| 42 | } | 43 | } | ... | ... |
src/pages/rich-text-test/index.config.js
0 → 100755
| 1 | +/* | ||
| 2 | + * @Date: 2022-09-26 14:36:57 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2022-09-26 14:41:01 | ||
| 5 | + * @FilePath: /swx/src/pages/activityDetail/index.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + navigationBarTitleText: '活动详情', | ||
| 10 | + usingComponents: { | ||
| 11 | + }, | ||
| 12 | +} |
src/pages/rich-text-test/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2026-02-27 | ||
| 3 | + * @Description: v-html 测试页面 - 验证 v-html 在 Taro 中是否可用 | ||
| 4 | + * | ||
| 5 | + * @purpose | ||
| 6 | + * 验证 v-html 指令在 Taro + Vue3 小程序中是否能正常运行 | ||
| 7 | + * 对比 index1.vue 中 mp-html 组件的绑定方式 | ||
| 8 | + * | ||
| 9 | + * @test-cases | ||
| 10 | + * 1. v-html 渲染响应式变量 | ||
| 11 | + * 2. 内容切换响应性 | ||
| 12 | + * 3. HTML 标签解析 | ||
| 13 | +--> | ||
| 14 | +<template> | ||
| 15 | + <view class="rich-text-test-page"> | ||
| 16 | + <!-- 导航栏 --> | ||
| 17 | + <NavHeader title="v-html 测试" /> | ||
| 18 | + | ||
| 19 | + <view class="test-container"> | ||
| 20 | + <!-- 测试说明 --> | ||
| 21 | + <view class="test-info"> | ||
| 22 | + <text class="test-title">测试项目:</text> | ||
| 23 | + <text class="test-item">1. v-html 渲染 {{ renderTest ? '✅' : '❌' }}</text> | ||
| 24 | + <text class="test-item">2. 内容切换 {{ contentSwitchTest ? '✅' : '❌' }}</text> | ||
| 25 | + <text class="test-item">3. HTML 解析 {{ htmlParseTest ? '✅' : '❌' }}</text> | ||
| 26 | + </view> | ||
| 27 | + | ||
| 28 | + <!-- v-html 渲染区域 --> | ||
| 29 | + <view class="v-html-container"> | ||
| 30 | + <text class="container-title">v-html 绑定区域:</text> | ||
| 31 | + <view id="taro_html" v-html="testHtml" class="taro_html"></view> | ||
| 32 | + </view> | ||
| 33 | + | ||
| 34 | + <!-- 测试结果 --> | ||
| 35 | + <view class="test-results"> | ||
| 36 | + <text class="result-title">测试结果:</text> | ||
| 37 | + <text class="result-item">当前内容: {{ currentContentType }}</text> | ||
| 38 | + <text class="result-item">内容长度: {{ contentLength }} 字符</text> | ||
| 39 | + <text class="result-item">切换次数: {{ switchCount }}</text> | ||
| 40 | + <text class="result-item">最后操作: {{ lastAction }}</text> | ||
| 41 | + </view> | ||
| 42 | + | ||
| 43 | + <!-- 切换测试内容 --> | ||
| 44 | + <view class="test-actions"> | ||
| 45 | + <view class="action-btn primary" @tap="switchTestContent(1)">测试1: 纯文本</view> | ||
| 46 | + <view class="action-btn primary" @tap="switchTestContent(2)">测试2: HTML标签</view> | ||
| 47 | + <view class="action-btn primary" @tap="switchTestContent(3)">测试3: 图片</view> | ||
| 48 | + <view class="action-btn primary" @tap="switchTestContent(4)">测试4: 链接</view> | ||
| 49 | + <view class="action-btn warning" @tap="clearContent">清空内容</view> | ||
| 50 | + <view class="action-btn success" @tap="loadFromAPI">模拟API加载</view> | ||
| 51 | + <view class="action-btn danger" @tap="loadRealApiData">📄 真实API</view> | ||
| 52 | + <view class="action-btn info" @tap="loadRealApiDataWithDiv">📄 真实API(a→div)</view> | ||
| 53 | + </view> | ||
| 54 | + | ||
| 55 | + <!-- 对比说明 --> | ||
| 56 | + <view class="compare-note"> | ||
| 57 | + <text class="note-title">与 index1.vue 对比:</text> | ||
| 58 | + <text class="note-item">• index1.vue 使用 <mp-html> 组件 + :content="testHtml"</text> | ||
| 59 | + <text class="note-item">• 本页使用 v-html="testHtml" 指令</text> | ||
| 60 | + <text class="note-item">• 两者都绑定响应式变量 testHtml</text> | ||
| 61 | + </view> | ||
| 62 | + </view> | ||
| 63 | + </view> | ||
| 64 | +</template> | ||
| 65 | + | ||
| 66 | +<script setup> | ||
| 67 | +import { ref, onMounted, nextTick, watch } from 'vue' | ||
| 68 | +import Taro, { definePageConfig } from '@tarojs/taro' | ||
| 69 | +import NavHeader from '@/components/navigation/NavHeader.vue' | ||
| 70 | +import { $ } from '@tarojs/extend' | ||
| 71 | +import { useFileOperation } from '@/composables/useFileOperation' | ||
| 72 | +// v-html 需要导入 Taro 的 HTML 样式 | ||
| 73 | +import '@tarojs/taro/html.css' | ||
| 74 | + | ||
| 75 | +// 文件操作 | ||
| 76 | +const { viewFile } = useFileOperation() | ||
| 77 | + | ||
| 78 | +// 配置页面 | ||
| 79 | +definePageConfig({ | ||
| 80 | + navigationBarTitleText: 'v-html 测试' | ||
| 81 | +}) | ||
| 82 | + | ||
| 83 | +// 测试状态 | ||
| 84 | +const renderTest = ref(false) | ||
| 85 | +const contentSwitchTest = ref(false) | ||
| 86 | +const htmlParseTest = ref(false) | ||
| 87 | +const switchCount = ref(0) | ||
| 88 | +const currentContentType = ref('未加载') | ||
| 89 | +const contentLength = ref(0) | ||
| 90 | +const lastAction = ref('页面初始化') | ||
| 91 | + | ||
| 92 | +// 测试 HTML 内容 | ||
| 93 | +const testHtml = ref('') | ||
| 94 | + | ||
| 95 | +// 真实 API 数据(来自 raw.md) | ||
| 96 | +const realApiData = { | ||
| 97 | + id: 3185667, | ||
| 98 | + post_title: "IANG Profile入职指引", | ||
| 99 | + post_content: `<p><img src="https://cdn.ipadbiz.cn/space_3079606/微信图片_20260227170749_FhZ6SHYxM9yVKOPo1LeqVO25I35q.jpg" alt="微信图片_20260227170749.jpg" width="800" height="340" /></p> | ||
| 100 | +<p> </p> | ||
| 101 | +<p>📝IANG Profile入职指引💡</p> | ||
| 102 | +<hr /> | ||
| 103 | +<p> </p> | ||
| 104 | +<p>符合IANG 计划的招募对象👥</p> | ||
| 105 | +<p>1️⃣年龄:21-55岁</p> | ||
| 106 | +<p>2️⃣持IANG、高才通B/C类 VISA及大学毕业或以上学历</p> | ||
| 107 | +<p>3️⃣非香港永久性居民</p> | ||
| 108 | +<p>4️⃣并非宏利前代理人</p> | ||
| 109 | +<p> </p> | ||
| 110 | +<hr /> | ||
| 111 | +<p> </p> | ||
| 112 | +<p>📑📌IANG Profile需注意事项:</p> | ||
| 113 | +<p>1.此计划提供18个月每月津贴(底薪),并设有3年约束期;每月津贴申请金额:本科毕业生每月津贴为港元 10,000-港元 20,000; 硕士毕业生每月津贴为港元 15,000-港元 25,000; 博士毕业生:每月津贴为港元 20,000-港元 30,000</p> | ||
| 114 | +<p>2.如代理人业绩未达到该月的业绩规定,将不會获发放该月的每月津貼。代理人可于第三个、第六个、第九个、第十二个、第十五个及第十八个合約月份结束时达到相应的的业绩规定及课程规定(第四个合约月内完成财富管家课程),将可获补发未获发放的每月津贴;</p> | ||
| 115 | +<p>3.若于第36个合约月内合约因任何原因被终止,需全数退还已收取的每月津贴;</p> | ||
| 116 | +<p>4.若首 18 个合约月份代理人所销售保单续保率为55%以上及 80%以下,须按比例退回已收取的每月津贴;</p> | ||
| 117 | +<p>5. 若首 12 个合约月份代理人所销售保单续保率为55%以上及 80%以下,须按比例退回已收取的特别业绩表现奖金 I;</p> | ||
| 118 | +<p>6. 若首 13个合约月份至第24个合约月份代理人所销售保单续保率为55%以上及 80%以下,须按比例退回已收取的特别业绩表现奖金 II 。</p> | ||
| 119 | +<p> </p> | ||
| 120 | +<p>其余规定请查看以下公司IANG计划之规定文件!</p> | ||
| 121 | +<p><a class="_file_list" style="display: inline-block; padding: 12px; border: 1px solid #e1e5e9; border-radius: 8px; margin: 8px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 400px; transition: all 0.3s ease; text-decoration: none; color: inherit; cursor: pointer; font-family: -apple-system,BlinkMacSystemFont,;" href="https://cdn.ipadbiz.cn/space_3079606/2-hk-prog_IANG_Profile_Program_C_Fj2lebJPQj71S8LhhOtgmcYVnpBD.pdf" target="_blank" rel="noopener"><span style="display: inline-block; font-size: 24px; margin-right: 12px; color: #6c757d; vertical-align: middle;"><img style="width: 30px; height: 30px; vertical-align: text-bottom;" src="https://cdn.ipadbiz.cn/img%2Ftinymce_icon%2Foffice-pdf.png" /></span><span style="display: inline-block; vertical-align: middle;"><span style="display: block; font-weight: 600; font-size: 14px; color: #2c3e50; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2;">2-hk-prog_IANG_Profile....pdf</span><span style="display: block; font-size: 12px; color: #6c757d; margin-top: 4px; line-height: 1.2;">384.34 KB</span></span></a></p> | ||
| 122 | +<p> </p> | ||
| 123 | +<p><a class="_file_list" style="display: inline-block; padding: 12px; border: 1px solid #e1e5e9; border-radius: 8px; margin: 8px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 400px; transition: all 0.3s ease; text-decoration: none; color: inherit; cursor: pointer; font-family: -apple-system,BlinkMacSystemFont,;" href="https://cdn.ipadbiz.cn/space_3079606/IANG Profile illustration_FriOxAVusDKilewqIqFo_OHdc84X.pdf" target="_blank" rel="noopener"><span style="display: inline-block; font-size: 24px; margin-right: 12px; color: #6c757d; vertical-align: middle;"><img style="width: 30px; height: 30px; vertical-align: text-bottom;" src="https://cdn.ipadbiz.cn/img%2Ftinymce_icon%2Foffice-pdf.png" /></span><span style="display: inline-block; vertical-align: middle;"><span style="display: block; font-weight: 600; font-size: 14px; color: #2c3e50; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2;">IANG Profile illustration.pdf</span><span style="display: block; font-size: 12px; color: #6c757d; margin-top: 4px; line-height: 1.2;">1.82 MB</span></span></a></p> | ||
| 124 | +<p> </p> | ||
| 125 | +<p><a class="_file_list" style="display: inline-block; padding: 12px; border: 1px solid #e1e5e9; border-radius: 8px; margin: 8px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 400px; transition: all 0.3s ease; text-decoration: none; color: inherit; cursor: pointer; font-family: -apple-system,BlinkMacSystemFont,;" href="https://cdn.ipadbiz.cn/space_3079606/IANG对数表_Fq3IQGJuM0SP4zAkKZDK803xVeQK.pdf" target="_blank" rel="noopener"><span style="display: inline-block; font-size: 24px; margin-right: 12px; color: #6c757d; vertical-align: middle;"><img style="width: 30px; height: 30px; vertical-align: text-bottom;" src="https://cdn.ipadbiz.cn/img%2Ftinymce_icon%2Foffice-pdf.png" /></span><span style="display: inline-block; vertical-align: middle;"><span style="display: block; font-weight: 600; font-size: 14px; color: #2c3e50; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2;">IANG对数表.pdf</span><span style="display: block; font-size: 12px; color: #6c757d; margin-top: 4px; line-height: 1.2;">65.73 KB</span></span></a></p> | ||
| 126 | +<p> </p> | ||
| 127 | +<hr /> | ||
| 128 | +<p> </p> | ||
| 129 | +<p>📑📌IANG Profile入职资料</p> | ||
| 130 | +<p>1.香港身份证</p> | ||
| 131 | +<p>2.Visa</p> | ||
| 132 | +<p>3.首次入境小票</p> | ||
| 133 | +<p>4.申请VISA时登记的有效港澳通行证正反面或护照个人信息页</p> | ||
| 134 | +<p>5.近三个月内由港府、香港银行、香港电话公司发出的【香港住宅英文地址证明】(注1️⃣)</p> | ||
| 135 | +<p>6.毕业证及近三个月学信网学历认证,国外学历须提供留服认证或香港资历架构认证(注2️⃣)</p> | ||
| 136 | +<p>7.卷一及卷三合格证明</p> | ||
| 137 | +<p>8.香港电话号码(注3️⃣)</p> | ||
| 138 | +<p>9.在港申请的香港银行卡</p> | ||
| 139 | +<p>10.一寸证件照两张,底色无要求</p> | ||
| 140 | +<p>11.简历</p> | ||
| 141 | +<p> </p> | ||
| 142 | +<hr /> | ||
| 143 | +<p> </p> | ||
| 144 | +<p>⚠️入职资料注意事项:</p> | ||
| 145 | +<p>1️⃣香港住宅英文地址证明,例如:水电煤气通知单、银行月结单或电话费月结单等;证券接单不可作为有效地址证明。</p> | ||
| 146 | +<p>2️⃣艺术类,语言类或文科专业毕业,还需提供成绩单,且成绩单里包含中文、英语及数学成绩、成绩须达第二级/达E级或以上,IA看情况要求提供成绩认证。</p> | ||
| 147 | +<p>3️⃣香港电话号码尽量不提供在内地申请的两地号码,两地号码在内地没办法收验证码,不便于办理挂牌手续。</p> | ||
| 148 | +<p> </p> | ||
| 149 | +<hr /> | ||
| 150 | +<p> </p> | ||
| 151 | +<p>入职办理流程⤵️:</p> | ||
| 152 | +<p>1.准招募递交入职申请后,由DD核实审批</p> | ||
| 153 | +<p>2.审批完毕后,入职流程递交至AR部门,AR部门核实签证类型,没问题后Pass至AD部门</p> | ||
| 154 | +<p>3.AD核实后,出Offer Letter</p> | ||
| 155 | +<p>4.准招募签署完Offer Letter后,流程Pass至C&B部门做资料综合审核及背景调查</p> | ||
| 156 | +<p>5.审查完毕,C&B同事帮准招募向保監提出挂牌申请</p> | ||
| 157 | +<p>6.准招募收到保監发出的【保險中介一站通-啟動個人賬戶】郵件及公司發出的纸本AOP啟動碼</p> | ||
| 158 | +<p>7.联系秘书填写一站通资料</p> | ||
| 159 | +<p>8.公司审核一站通资料无误后,准招募支付挂牌费用(3年挂牌费为港元810)</p> | ||
| 160 | +<p>9.挂牌后,公司生成专属Agent code,并发出代理人合约及奖金合约</p> | ||
| 161 | +<p>10.准招募签署合约并递交给公司</p> | ||
| 162 | +<p>11.收到公司配送的Welcome Kit,内含工牌、门卡及邮件激活密码</p> | ||
| 163 | +<p>12.激活邮件及Manutouch账号后,可开始签单</p> | ||
| 164 | +<p> </p> | ||
| 165 | +<p><img src="https://cdn.ipadbiz.cn/space_3079606/微信图片_20260227171202_FhZfGYQAKK_04-E_Td5TzNosjlXm.jpg" alt="微信图片_20260227171202.jpg" width="1200" height="596" /></p> | ||
| 166 | +<hr /> | ||
| 167 | +<p> </p> | ||
| 168 | +<p>⭐入职需完成课程✅:</p> | ||
| 169 | +<p>1.【新人课】,完成后方可发第1期底薪,建议在递交完入职资料,等待出Code过程中,联系秘书报名,最迟在入职第一个合约月内完成,完成后方可发第1个月底薪。</p> | ||
| 170 | +<p>2.【财富管家课程】,入职后4个月内需上完,否则会影响底薪及佣金发放,建议出code后,尽快安排时间联系秘书报名。</p> | ||
| 171 | +<p> </p> | ||
| 172 | +<p>🕹️签署合约后,每月需交给公司200元/月行政费!</p> | ||
| 173 | +<hr /> | ||
| 174 | +<p> </p> | ||
| 175 | +<p>入职资料Sample</p> | ||
| 176 | +<p><a class="_file_list" style="display: inline-block; padding: 12px; border: 1px solid #e1e5e9; border-radius: 8px; margin: 8px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 400px; transition: all 0.3s ease; text-decoration: none; color: inherit; cursor: pointer; font-family: -apple-system,BlinkMacSystemFont,;" href="https://cdn.ipadbiz.cn/space_3079606/需提供的部分个人资料SAMPLE(不含流水SAMPLE)_扫描版_lkAqOTlyK-jZ7RbBN43oiG5QPfr-.pdf" target="_blank" rel="noopener"><span style="display: inline-block; font-size: 24px; margin-right: 12px; color: #6c757d; vertical-align: middle;"><img style="width: 30px; height: 30px; vertical-align: text-bottom;" src="https://cdn.ipadbiz.cn/img%2Ftinymce_icon%2Foffice-pdf.png" /></span><span style="display: inline-block; vertical-align: middle;"><span style="display: block; font-weight: 600; font-size: 14px; color: #2c3e50; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2;">需提供的部分个人资料SAMPLE(不含流水S....pdf</span><span style="display: block; font-size: 12px; color: #6c757d; margin-top: 4px; line-height: 1.2;">101.91 MB</span></span></a></p> | ||
| 177 | +<p> </p>` | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +// 测试内容模板 | ||
| 181 | +const testContents = { | ||
| 182 | + 1: { | ||
| 183 | + name: '纯文本', | ||
| 184 | + content: ` | ||
| 185 | + <h1>测试1:纯文本渲染</h1> | ||
| 186 | + <p>这是一段纯文本内容,测试 v-html 是否能正确渲染基本的 HTML 结构。</p> | ||
| 187 | + <p><strong>加粗文本</strong>和<em>斜体文本</em>测试。</p> | ||
| 188 | + ` | ||
| 189 | + }, | ||
| 190 | + 2: { | ||
| 191 | + name: 'HTML标签', | ||
| 192 | + content: ` | ||
| 193 | + <h1>测试2:HTML 标签解析</h1> | ||
| 194 | + <h2>二级标题</h2> | ||
| 195 | + <h3>三级标题</h3> | ||
| 196 | + <ul> | ||
| 197 | + <li>列表项 1</li> | ||
| 198 | + <li>列表项 2</li> | ||
| 199 | + <li>列表项 3</li> | ||
| 200 | + </ul> | ||
| 201 | + <p>段落测试:<span style="color: red;">红色文字</span>和<span style="color: blue;">蓝色文字</span></p> | ||
| 202 | + ` | ||
| 203 | + }, | ||
| 204 | + 3: { | ||
| 205 | + name: '图片', | ||
| 206 | + content: ` | ||
| 207 | + <h1>测试3:图片渲染</h1> | ||
| 208 | + <p>点击图片测试长按预览功能:</p> | ||
| 209 | + <p class="h5-p"> | ||
| 210 | + <img class="h5-img" src="https://cdn.ipadbiz.cn/space_3079606/微信图片_20260227170749_FhZ6SHYxM9yVKOPo1LeqVO25I35q.jpg" alt="测试图片" style="max-width: 100%;" /> | ||
| 211 | + </p> | ||
| 212 | + ` | ||
| 213 | + }, | ||
| 214 | + 4: { | ||
| 215 | + name: '链接', | ||
| 216 | + content: ` | ||
| 217 | + <h1>测试4:链接渲染</h1> | ||
| 218 | + <p>测试链接是否正确渲染:</p> | ||
| 219 | + <p> | ||
| 220 | + <a href="https://www.baidu.com">百度链接</a> | | ||
| 221 | + <a href="https://www.github.com">GitHub 链接</a> | ||
| 222 | + </p> | ||
| 223 | + <p>PDF 链接测试: | ||
| 224 | + <a href="https://cdn.ipadbiz.cn/space_3079606/需提供的部分个人资料SAMPLE(不含流水SAMPLE)_扫描版_lkAqOTlyK-jZ7RbBN43oiG5QPfr-.pdf">PDF 文档</a> | ||
| 225 | + </p> | ||
| 226 | + ` | ||
| 227 | + } | ||
| 228 | +} | ||
| 229 | + | ||
| 230 | +// 切换测试内容 | ||
| 231 | +const switchTestContent = (index) => { | ||
| 232 | + const template = testContents[index] | ||
| 233 | + if (template) { | ||
| 234 | + testHtml.value = template.content | ||
| 235 | + currentContentType.value = template.name | ||
| 236 | + contentLength.value = template.content.length | ||
| 237 | + switchCount.value++ | ||
| 238 | + lastAction.value = `切换到测试${index}: ${template.name}` | ||
| 239 | + contentSwitchTest.value = true | ||
| 240 | + | ||
| 241 | + // 绑定图片长按事件 | ||
| 242 | + nextTick(() => { | ||
| 243 | + bindImageEvents() | ||
| 244 | + }) | ||
| 245 | + } | ||
| 246 | +} | ||
| 247 | + | ||
| 248 | +// 清空内容 | ||
| 249 | +const clearContent = () => { | ||
| 250 | + testHtml.value = '' | ||
| 251 | + currentContentType.value = '已清空' | ||
| 252 | + contentLength.value = 0 | ||
| 253 | + lastAction.value = '清空内容' | ||
| 254 | +} | ||
| 255 | + | ||
| 256 | +// 模拟 API 加载 | ||
| 257 | +const loadFromAPI = () => { | ||
| 258 | + lastAction.value = '模拟 API 加载中...' | ||
| 259 | + Taro.showLoading({ title: '加载中...' }) | ||
| 260 | + | ||
| 261 | + setTimeout(() => { | ||
| 262 | + // 模拟 API 返回的数据 | ||
| 263 | + const apiData = ` | ||
| 264 | + <div class="api-content"> | ||
| 265 | + <h1>从 API 加载的内容</h1> | ||
| 266 | + <p>这是模拟从后端 API 获取的富文本内容。</p> | ||
| 267 | + <p>加载时间:${new Date().toLocaleString()}</p> | ||
| 268 | + <p class="h5-p"> | ||
| 269 | + <img class="h5-img" src="https://cdn.ipadbiz.cn/space_3079606/微信图片_20260227170749_FhZ6SHYxM9yVKOPo1LeqVO25I35q.jpg" alt="API图片" /> | ||
| 270 | + </p> | ||
| 271 | + </div> | ||
| 272 | + ` | ||
| 273 | + testHtml.value = apiData | ||
| 274 | + currentContentType.value = 'API 数据' | ||
| 275 | + contentLength.value = apiData.length | ||
| 276 | + lastAction.value = 'API 加载完成' | ||
| 277 | + Taro.hideLoading() | ||
| 278 | + | ||
| 279 | + nextTick(() => { | ||
| 280 | + bindImageEvents() | ||
| 281 | + }) | ||
| 282 | + }, 1000) | ||
| 283 | +} | ||
| 284 | + | ||
| 285 | +// 加载真实 API 数据(原始版本,包含 <a> 标签) | ||
| 286 | +const loadRealApiData = () => { | ||
| 287 | + lastAction.value = '加载真实 API 数据(原始)...' | ||
| 288 | + Taro.showLoading({ title: '加载中...' }) | ||
| 289 | + | ||
| 290 | + setTimeout(() => { | ||
| 291 | + testHtml.value = realApiData.post_content | ||
| 292 | + currentContentType.value = `真实文章(原始<a>): ${realApiData.post_title}` | ||
| 293 | + contentLength.value = realApiData.post_content.length | ||
| 294 | + switchCount.value++ | ||
| 295 | + lastAction.value = `加载文章 ID: ${realApiData.id} (原始<a>标签)` | ||
| 296 | + Taro.hideLoading() | ||
| 297 | + | ||
| 298 | + nextTick(() => { | ||
| 299 | + bindImageEvents() | ||
| 300 | + }) | ||
| 301 | + | ||
| 302 | + Taro.showToast({ | ||
| 303 | + title: '原始 <a> 标签', | ||
| 304 | + icon: 'none' | ||
| 305 | + }) | ||
| 306 | + }, 500) | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +// 加载真实 API 数据(<a> 替换为 <div> 版本) | ||
| 310 | +const loadRealApiDataWithDiv = () => { | ||
| 311 | + lastAction.value = '加载真实 API 数据(a→div)...' | ||
| 312 | + Taro.showLoading({ title: '加载中...' }) | ||
| 313 | + | ||
| 314 | + setTimeout(() => { | ||
| 315 | + // 将 <a> 标签替换为 <div> 标签,保留 href 为 data-href | ||
| 316 | + let content = realApiData.post_content | ||
| 317 | + | ||
| 318 | + console.log('[VHtmlTest] 原始内容中的 <a> 标签示例:') | ||
| 319 | + const aTagMatch = content.match(/<a[^>]*>/) | ||
| 320 | + if (aTagMatch) { | ||
| 321 | + console.log('[VHtmlTest]', aTagMatch[0]) | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + // 替换 HTML 实体 | ||
| 325 | + content = content.replace(/ /g, ' ') // 空格 | ||
| 326 | + content = content.replace(/&/g, '&') // & | ||
| 327 | + content = content.replace(/</g, '<') // < | ||
| 328 | + content = content.replace(/>/g, '>') // > | ||
| 329 | + content = content.replace(/"/g, '"') // " | ||
| 330 | + content = content.replace(/'/g, "'") // ' | ||
| 331 | + | ||
| 332 | + // 替换 <a ... href="..."> 为 <div ... data-href="..." data-is-link="true"> | ||
| 333 | + content = content.replace(/<a\s+/g, '<div data-is-link="true" ') | ||
| 334 | + content = content.replace(/href=/g, 'data-href=') | ||
| 335 | + content = content.replace(/<\/a>/g, '</div>') | ||
| 336 | + | ||
| 337 | + console.log('[VHtmlTest] 替换后的 div 标签示例:') | ||
| 338 | + const divTagMatch = content.match(/<div[^>]*data-is-link="true"[^>]*>/) | ||
| 339 | + if (divTagMatch) { | ||
| 340 | + console.log('[VHtmlTest]', divTagMatch[0]) | ||
| 341 | + } | ||
| 342 | + | ||
| 343 | + // 统计 data-href 数量 | ||
| 344 | + const hrefCount = (content.match(/data-href=/g) || []).length | ||
| 345 | + console.log('[VHtmlTest] 找到 data-href 数量:', hrefCount) | ||
| 346 | + | ||
| 347 | + testHtml.value = content | ||
| 348 | + currentContentType.value = `真实文章(<a>→<div>): ${realApiData.post_title}` | ||
| 349 | + contentLength.value = content.length | ||
| 350 | + switchCount.value++ | ||
| 351 | + lastAction.value = `加载文章 ID: ${realApiData.id} (a标签已替换为div, href=${hrefCount})` | ||
| 352 | + Taro.hideLoading() | ||
| 353 | + | ||
| 354 | + nextTick(() => { | ||
| 355 | + bindImageEvents() | ||
| 356 | + // 绑定文件链接点击事件 | ||
| 357 | + const linkCount = bindFileLinkEvents() | ||
| 358 | + console.log('[VHtmlTest] 绑定了', linkCount, '个文件链接') | ||
| 359 | + }) | ||
| 360 | + | ||
| 361 | + Taro.showToast({ | ||
| 362 | + title: `已绑定 ${hrefCount} 个文件链接`, | ||
| 363 | + icon: 'success' | ||
| 364 | + }) | ||
| 365 | + }, 500) | ||
| 366 | +} | ||
| 367 | + | ||
| 368 | +// 绑定图片长按预览事件(类似 activityDetail 页面的实现) | ||
| 369 | +const bindImageEvents = () => { | ||
| 370 | + const imgs = $('#taro_html').children('.h5-p').children('.h5-img') | ||
| 371 | + console.log('[VHtmlTest] 找到图片数量:', imgs.length) | ||
| 372 | + | ||
| 373 | + imgs.forEach(function (img) { | ||
| 374 | + $(img).on('longpress', function () { | ||
| 375 | + const src = $(img).attr('src') | ||
| 376 | + console.log('[VHtmlTest] 图片长按事件:', src) | ||
| 377 | + | ||
| 378 | + Taro.previewImage({ | ||
| 379 | + urls: [src], | ||
| 380 | + current: src, | ||
| 381 | + indicator: 'default', | ||
| 382 | + loop: false, | ||
| 383 | + success: res => { | ||
| 384 | + console.log('[VHtmlTest] 预览成功:', res) | ||
| 385 | + lastAction.value = '图片预览触发' | ||
| 386 | + }, | ||
| 387 | + fail: err => { | ||
| 388 | + console.error('[VHtmlTest] 预览失败:', err) | ||
| 389 | + } | ||
| 390 | + }) | ||
| 391 | + }) | ||
| 392 | + }) | ||
| 393 | + | ||
| 394 | + htmlParseTest.value = imgs.length > 0 | ||
| 395 | +} | ||
| 396 | + | ||
| 397 | +// 绑定文件链接点击事件 | ||
| 398 | +const bindFileLinkEvents = () => { | ||
| 399 | + // 先检查 #taro_html 容器是否存在 | ||
| 400 | + const container = $('#taro_html') | ||
| 401 | + console.log('[VHtmlTest] 容器存在?', container.length > 0) | ||
| 402 | + | ||
| 403 | + // 检查容器内所有 div | ||
| 404 | + const allDivs = container.find('div') | ||
| 405 | + console.log('[VHtmlTest] 容器内所有 div 数量:', allDivs.length) | ||
| 406 | + | ||
| 407 | + // 检查带 data-is-link 属性的 div | ||
| 408 | + const fileLinks = container.find('div[data-is-link="true"]') | ||
| 409 | + console.log('[VHtmlTest] 找到文件链接数量 (data-is-link=true):', fileLinks.length) | ||
| 410 | + | ||
| 411 | + // 如果找不到,尝试用 class 查找 | ||
| 412 | + if (fileLinks.length === 0) { | ||
| 413 | + console.log('[VHtmlTest] 尝试用 _file_list class 查找...') | ||
| 414 | + const classLinks = container.find('._file_list') | ||
| 415 | + console.log('[VHtmlTest] 找到 _file_list 数量:', classLinks.length) | ||
| 416 | + | ||
| 417 | + // 检查这些元素的属性 | ||
| 418 | + classLinks.each(function (idx, el) { | ||
| 419 | + console.log('[VHtmlTest] _file_list[' + idx + '] 属性:', { | ||
| 420 | + tagName: el.tagName, | ||
| 421 | + className: el.className, | ||
| 422 | + 'data-href': el.getAttribute('data-href'), | ||
| 423 | + 'data-is-link': el.getAttribute('data-is-link'), | ||
| 424 | + href: el.getAttribute('href'), | ||
| 425 | + innerHTML: el.innerHTML.substring(0, 100) | ||
| 426 | + }) | ||
| 427 | + }) | ||
| 428 | + | ||
| 429 | + // 如果用 class 找到了,绑定到这些元素上 | ||
| 430 | + classLinks.each(function (idx, el) { | ||
| 431 | + const $el = $(el) | ||
| 432 | + const dataHref = $el.attr('data-href') | ||
| 433 | + console.log('[VHtmlTest] _file_list[' + idx + '] data-href:', dataHref) | ||
| 434 | + | ||
| 435 | + if (dataHref) { | ||
| 436 | + $el.on('tap', function () { | ||
| 437 | + const fileName = $el.find('span span span').first().text() || 'document.pdf' | ||
| 438 | + console.log('[VHtmlTest] 文件链接点击:', { dataHref, fileName }) | ||
| 439 | + | ||
| 440 | + lastAction.value = `打开文件: ${fileName}` | ||
| 441 | + viewFile({ | ||
| 442 | + downloadUrl: dataHref, | ||
| 443 | + fileName: fileName | ||
| 444 | + }) | ||
| 445 | + }) | ||
| 446 | + } | ||
| 447 | + }) | ||
| 448 | + | ||
| 449 | + return classLinks.length | ||
| 450 | + } | ||
| 451 | + | ||
| 452 | + // 原始逻辑:使用 data-is-link 查找 | ||
| 453 | + fileLinks.forEach(function (link) { | ||
| 454 | + const $link = $(link) | ||
| 455 | + const href = $link.attr('data-href') | ||
| 456 | + | ||
| 457 | + console.log('[VHtmlTest] 文件链接 data-href:', href) | ||
| 458 | + | ||
| 459 | + $link.on('tap', function () { | ||
| 460 | + const fileName = $link.find('span span span').first().text() || 'document.pdf' | ||
| 461 | + | ||
| 462 | + console.log('[VHtmlTest] 文件链接点击:', { href, fileName }) | ||
| 463 | + | ||
| 464 | + if (href) { | ||
| 465 | + lastAction.value = `打开文件: ${fileName}` | ||
| 466 | + | ||
| 467 | + // 使用 useFileOperation 打开文件 | ||
| 468 | + viewFile({ | ||
| 469 | + downloadUrl: href, | ||
| 470 | + fileName: fileName | ||
| 471 | + }) | ||
| 472 | + } else { | ||
| 473 | + console.warn('[VHtmlTest] 链接没有 data-href 属性') | ||
| 474 | + } | ||
| 475 | + }) | ||
| 476 | + }) | ||
| 477 | + | ||
| 478 | + return fileLinks.length | ||
| 479 | +} | ||
| 480 | + | ||
| 481 | +// 监听 testHtml 变化 | ||
| 482 | +watch(testHtml, (newVal, oldVal) => { | ||
| 483 | + console.log('[VHtmlTest] testHtml 变化:', { | ||
| 484 | + oldLength: oldVal?.length || 0, | ||
| 485 | + newLength: newVal?.length || 0 | ||
| 486 | + }) | ||
| 487 | + renderTest.value = true | ||
| 488 | +}) | ||
| 489 | + | ||
| 490 | +// 初始化 | ||
| 491 | +onMounted(() => { | ||
| 492 | + console.log('[VHtmlTest] 组件已挂载') | ||
| 493 | + | ||
| 494 | + // 加载默认测试内容 | ||
| 495 | + switchTestContent(1) | ||
| 496 | + | ||
| 497 | + console.log('[VHtmlTest] 初始化完成,v-html 应该已绑定到响应式变量 testHtml') | ||
| 498 | +}) | ||
| 499 | +</script> | ||
| 500 | + | ||
| 501 | +<style lang="less"> | ||
| 502 | +.rich-text-test-page { | ||
| 503 | + min-height: 100vh; | ||
| 504 | + background-color: #f5f5f5; | ||
| 505 | +} | ||
| 506 | + | ||
| 507 | +.test-container { | ||
| 508 | + padding: 32rpx; | ||
| 509 | +} | ||
| 510 | + | ||
| 511 | +.test-info { | ||
| 512 | + background: #fff; | ||
| 513 | + padding: 24rpx; | ||
| 514 | + border-radius: 12rpx; | ||
| 515 | + margin-bottom: 24rpx; | ||
| 516 | +} | ||
| 517 | + | ||
| 518 | +.test-title { | ||
| 519 | + display: block; | ||
| 520 | + font-size: 32rpx; | ||
| 521 | + font-weight: bold; | ||
| 522 | + margin-bottom: 16rpx; | ||
| 523 | +} | ||
| 524 | + | ||
| 525 | +.test-item { | ||
| 526 | + display: block; | ||
| 527 | + font-size: 28rpx; | ||
| 528 | + color: #666; | ||
| 529 | + margin: 8rpx 0; | ||
| 530 | +} | ||
| 531 | + | ||
| 532 | +.v-html-container { | ||
| 533 | + background: #fff; | ||
| 534 | + padding: 24rpx; | ||
| 535 | + border-radius: 12rpx; | ||
| 536 | + margin-bottom: 24rpx; | ||
| 537 | + min-height: 200rpx; | ||
| 538 | +} | ||
| 539 | + | ||
| 540 | +.container-title { | ||
| 541 | + display: block; | ||
| 542 | + font-size: 28rpx; | ||
| 543 | + color: #999; | ||
| 544 | + margin-bottom: 16rpx; | ||
| 545 | +} | ||
| 546 | + | ||
| 547 | +.taro_html { | ||
| 548 | + // Taro html.css 样式 | ||
| 549 | + :deep(.h5-p) { | ||
| 550 | + margin: 16rpx 0; | ||
| 551 | + } | ||
| 552 | + | ||
| 553 | + :deep(.h5-img) { | ||
| 554 | + max-width: 100%; | ||
| 555 | + display: block; | ||
| 556 | + } | ||
| 557 | + | ||
| 558 | + :deep(h1) { | ||
| 559 | + font-size: 48rpx; | ||
| 560 | + font-weight: bold; | ||
| 561 | + margin: 32rpx 0 16rpx; | ||
| 562 | + } | ||
| 563 | + | ||
| 564 | + :deep(h2) { | ||
| 565 | + font-size: 40rpx; | ||
| 566 | + font-weight: bold; | ||
| 567 | + margin: 24rpx 0 12rpx; | ||
| 568 | + } | ||
| 569 | + | ||
| 570 | + :deep(h3) { | ||
| 571 | + font-size: 36rpx; | ||
| 572 | + font-weight: bold; | ||
| 573 | + margin: 20rpx 0 12rpx; | ||
| 574 | + } | ||
| 575 | + | ||
| 576 | + :deep(p) { | ||
| 577 | + font-size: 28rpx; | ||
| 578 | + line-height: 1.6; | ||
| 579 | + margin: 12rpx 0; | ||
| 580 | + color: #333; | ||
| 581 | + } | ||
| 582 | + | ||
| 583 | + :deep(ul) { | ||
| 584 | + padding-left: 48rpx; | ||
| 585 | + margin: 16rpx 0; | ||
| 586 | + } | ||
| 587 | + | ||
| 588 | + :deep(li) { | ||
| 589 | + font-size: 28rpx; | ||
| 590 | + line-height: 1.6; | ||
| 591 | + margin: 8rpx 0; | ||
| 592 | + } | ||
| 593 | + | ||
| 594 | + :deep(a) { | ||
| 595 | + color: #1989fa; | ||
| 596 | + text-decoration: underline; | ||
| 597 | + } | ||
| 598 | + | ||
| 599 | + :deep(strong) { | ||
| 600 | + font-weight: bold; | ||
| 601 | + } | ||
| 602 | + | ||
| 603 | + :deep(em) { | ||
| 604 | + font-style: italic; | ||
| 605 | + } | ||
| 606 | + | ||
| 607 | + :deep(span) { | ||
| 608 | + font-size: 28rpx; | ||
| 609 | + } | ||
| 610 | + | ||
| 611 | + // 文件链接卡片样式 | ||
| 612 | + :deep(._file_list) { | ||
| 613 | + display: inline-block !important; | ||
| 614 | + padding: 12px; | ||
| 615 | + border: 1px solid #e1e5e9; | ||
| 616 | + border-radius: 8px; | ||
| 617 | + margin: 8px; | ||
| 618 | + background: #f8f9fa; | ||
| 619 | + box-shadow: 0 2px 4px rgba(0,0,0,0.1); | ||
| 620 | + max-width: 400px; | ||
| 621 | + transition: all 0.3s ease; | ||
| 622 | + text-decoration: none; | ||
| 623 | + color: inherit; | ||
| 624 | + cursor: pointer; | ||
| 625 | + width: 100%; | ||
| 626 | + box-sizing: border-box; | ||
| 627 | + } | ||
| 628 | +} | ||
| 629 | + | ||
| 630 | +.test-results { | ||
| 631 | + background: #fff; | ||
| 632 | + padding: 24rpx; | ||
| 633 | + border-radius: 12rpx; | ||
| 634 | + margin-bottom: 24rpx; | ||
| 635 | +} | ||
| 636 | + | ||
| 637 | +.result-title { | ||
| 638 | + display: block; | ||
| 639 | + font-size: 32rpx; | ||
| 640 | + font-weight: bold; | ||
| 641 | + margin-bottom: 16rpx; | ||
| 642 | +} | ||
| 643 | + | ||
| 644 | +.result-item { | ||
| 645 | + display: block; | ||
| 646 | + font-size: 28rpx; | ||
| 647 | + color: #666; | ||
| 648 | + margin: 8rpx 0; | ||
| 649 | +} | ||
| 650 | + | ||
| 651 | +.test-actions { | ||
| 652 | + display: flex; | ||
| 653 | + flex-wrap: wrap; | ||
| 654 | + gap: 16rpx; | ||
| 655 | + margin-bottom: 24rpx; | ||
| 656 | +} | ||
| 657 | + | ||
| 658 | +.action-btn { | ||
| 659 | + padding: 16rpx 32rpx; | ||
| 660 | + border-radius: 8rpx; | ||
| 661 | + font-size: 28rpx; | ||
| 662 | + text-align: center; | ||
| 663 | + | ||
| 664 | + &.primary { | ||
| 665 | + background: #1989fa; | ||
| 666 | + color: #fff; | ||
| 667 | + } | ||
| 668 | + | ||
| 669 | + &.warning { | ||
| 670 | + background: #ff976a; | ||
| 671 | + color: #fff; | ||
| 672 | + } | ||
| 673 | + | ||
| 674 | + &.success { | ||
| 675 | + background: #07c160; | ||
| 676 | + color: #fff; | ||
| 677 | + } | ||
| 678 | + | ||
| 679 | + &.danger { | ||
| 680 | + background: #ee0a24; | ||
| 681 | + color: #fff; | ||
| 682 | + font-weight: bold; | ||
| 683 | + } | ||
| 684 | + | ||
| 685 | + &.info { | ||
| 686 | + background: #7232dd; | ||
| 687 | + color: #fff; | ||
| 688 | + } | ||
| 689 | +} | ||
| 690 | + | ||
| 691 | +.compare-note { | ||
| 692 | + background: #f0f9ff; | ||
| 693 | + padding: 24rpx; | ||
| 694 | + border-radius: 12rpx; | ||
| 695 | + border-left: 8rpx solid #1989fa; | ||
| 696 | +} | ||
| 697 | + | ||
| 698 | +.note-title { | ||
| 699 | + display: block; | ||
| 700 | + font-size: 28rpx; | ||
| 701 | + font-weight: bold; | ||
| 702 | + color: #1989fa; | ||
| 703 | + margin-bottom: 16rpx; | ||
| 704 | +} | ||
| 705 | + | ||
| 706 | +.note-item { | ||
| 707 | + display: block; | ||
| 708 | + font-size: 24rpx; | ||
| 709 | + color: #666; | ||
| 710 | + margin: 8rpx 0; | ||
| 711 | + line-height: 1.5; | ||
| 712 | +} | ||
| 713 | +</style> |
-
Please register or login to post a comment