https://www.zuoan.com.cn/News3/3487.html
https://www.zuoan.com.cn/News3/3490.html
距离上面发布的文章,在发布文章和编辑文章时添加预览HTML的功能之后,我突然又想在列表上直接预览文章。
第一步:
apps/admin/model/content/ContentModel.php
获取文章列表的地方添加:
'a.content',
其他几处也相应添加,要不然后台列表处无法获取到数据。
第二步:在后台模板文件中添加代码,最终形成如下。
{if($value->status)} {if(!$value->outlink)} <input type="hidden" name="urls[[value->id]]" value="{php}echo $link{/php}"> <a href="{php}echo $link{/php}" class="layui-btn layui-btn-xs layui-btn-primary" target="_blank">查看</a> {else} <a href="[value->outlink]" class="layui-btn layui-btn-xs layui-btn-primary" target="_blank">查看</a> {/if} <!-- 添加的代码 --> <input type="hidden" name="content[[value->id]]" value="[value->content]"> <a href="javascript:;" class="layui-btn layui-btn-xs btn-preview" data-id="[value->id]">列表预览</a> <script> layui.use(['layer', 'jquery'], function () { var $ = layui.jquery; var layer = layui.layer; // 只绑定一次点击事件 $(document).off('click', '.btn-preview').on('click', '.btn-preview', function () { var id = $(this).data('id'); var $input = $("input[name='content[" + id + "]']"); if (!$input.length) { layer.alert("未找到 ID=" + id + " 的内容!"); return; } var rawHtml = $input.val(); var previewHtml = `<iframe id="previewFrame" style="width:100%;height:100%;border:none;"></iframe>`; layer.open({ type: 1, title: '文章内容预览', area: ['90%', '90%'], content: previewHtml, success: function (layero, index) { var iframe = document.getElementById('previewFrame'); var doc = iframe.contentDocument || iframe.contentWindow.document; var fullHtml = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> 此处可引入你前端的CSS样式,以便预览的时候显示的是你前端的效果,而不是加载后端的样式。 <style> body { padding: 20px; font-family: "微软雅黑", sans-serif; } </style> </head> <body> ${rawHtml} </body> </html> `; doc.open(); doc.write(fullHtml); doc.close(); } }); }); }); </script> <!-- 添加的代码 --> {else} <a href="javascript:;" class="layui-btn layui-btn-xs layui-btn-disabled">查看</a> {/if}
最后我们来看效果:
使用上面的方法 呢,可能我们会担心一个问题,就是在一个列表中,用
<input type="hidden" name="content[[value->id]]" value="[value->content]">
去获取的数据会不会太大了。假如列表是15篇文章,那就相当于有15篇文章的内容在这个页面中。会造成这个列表数据庞大,会不会卡顿呢?
1. 页面卡顿可能性
每篇文章内容完整HTML都写在了隐藏input的value属性里,浏览器会把这些内容都加载、解析。
如果内容很长,尤其有大量HTML、图片、样式等,页面源码会变得很大,初始加载时浏览器解析负担会增大,加载时间变长。
隐藏字段的value存放大量HTML其实并不太合适,可能导致:
页面体积变大,加载慢
浏览器渲染缓慢
内存占用增加
如果还有JS对这些字段做操作,性能压力更大
2. 用户体验
首屏加载时间变长,影响用户体验。
如果用户只点击少数几篇文章预览,却加载了所有文章内容,浪费资源。
改进方案 方案A:按需加载内容
页面隐藏域只保存文章的 ID、标题等轻量信息,不保存完整内容。
点击“预览”按钮时,通过 Ajax 请求后台接口,传文章ID,服务器返回该文章完整内容(HTML)。
前端收到内容后,再打开弹窗展示。
这样初始页面只加载少量数据,提升首屏性能,点击时才加载详细内容。
所以我们第一步在后台打开API接口。
第二步修改代码。
<!-- 添加的代码 --> <input type="hidden" name="content[[value->id]]" > <a href="javascript:;" class="layui-btn layui-btn-xs btn-preview" data-id="[value->id]">预览</a> <script> layui.use(['layer', 'jquery'], function () { var $ = layui.jquery; var layer = layui.layer; $(document).off('click.preview').on('click.preview', '.btn-preview', function () { var id = $(this).data('id'); if (!id) { layer.alert("未获取到文章ID!"); return; } var url = '/api.php/content/' + id; var loading = layer.load(2); $.ajax({ type: 'GET', // 改为GET请求 url: url, dataType: 'json', data: { appid: "{pboot:appid}", timestamp: "{pboot:timestamp}", signature: "{pboot:signature}" }, success: function (res) { layer.close(loading); if (!res || !res.data || !res.data.content) { layer.alert("未获取到文章内容!"); return; } var rawHtml = res.data.content; var iframeHtml = '<iframe id="previewFrame" style="width:100%;height:100%;border:none;"></iframe>'; layer.open({ type: 1, title: '文章预览', area: ['90%', '90%'], content: iframeHtml, success: function () { var iframe = document.getElementById('previewFrame'); var doc = iframe.contentDocument || iframe.contentWindow.document; var fullHtml = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/template/peoplesj/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/template/peoplesj/css/style.css"> <style>body { padding: 20px; font-family: "微软雅黑", sans-serif; }</style> </head> <body> ${rawHtml} </body> </html> `; doc.open(); doc.write(fullHtml); doc.close(); } }); }, error: function (xhr, status, error) { layer.close(loading); console.error("请求失败:", error); layer.alert("请求失败:" + error); } }); }); }); </script>
按道理,这样我们连MODE都不用修改了。未作测试,诸君可自行测试一下。
但是有一个问题,我在本地测试的时候,获取不到API,只有放在服务器上才可以。