基于uni-app+vue3渲染markdown格式|uniapp软键盘顶起问题解决方案
<p>前些时候有给大家分享一篇uni-app+vite4+uview-plus搭建跨端项目。今天主要分享下在uniapp中渲染markdown语法及uniapp中软键盘弹起,页面tabbar或顶部自定义navbar导航栏被撑起挤压的问题。</p><p><img src="https://img2023.cnblogs.com/blog/1289798/202306/1289798-20230625081854178-1749525320.jpg"></p>
<p><img src="https://img2023.cnblogs.com/blog/1289798/202306/1289798-20230625081623909-996917660.jpg"></p>
<p>如上图:支持<strong>h5+小程序+App端markdown解析</strong>渲染。</p>
<p><img src="https://img2023.cnblogs.com/blog/1289798/202306/1289798-20230625082127594-1754336575.gif"></p>
<p><img src="https://img2023.cnblogs.com/blog/1289798/202306/1289798-20230625082232994-503235810.gif"></p>
<p>上面则是演示了在App端+小程序端键盘弹起,整体页面及自定义导航条不会被顶跑的问题。</p>
<p>好了,话不多说,接下来就主要介绍下如何实现的吧。</p>
<h3>uniapp+vue3解析markdown语法/高亮</h3>
<p>虽说uniapp插件市场也有一些markdown渲染插件,但大多兼容性不好。于是就使用了markdown-it及highlight.js插件进行了简单的封装处理。经调试目前可以支持h5/小程序/App端的markdown语法解析。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入markdown-it和highlight.js插件</span>
import MarkdownIt from '@/plugins/markdown-it.min.js'<span style="color: rgba(0, 0, 0, 1)">
import hljs from </span>'@/plugins/highlight/highlight.min.js'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> import '@/plugins/highlight/github-dark.min.css'</span>
import '@/plugins/highlight/atom-one-light.css'<span style="color: rgba(0, 0, 0, 1)">
import parseHtml from </span>'@/plugins/html-parser.js'</pre>
</div>
<p>highlight.js高亮样式大家可以根据需要自行下载,这里使用的浅色样式。</p>
<ul>
<li><strong>初始化markdownIt插件</strong></li>
</ul>
<p>接下来是初始化markdown及语法高亮、增加代码行号功能。</p>
<div class="cnblogs_code">
<pre>const markdown =<span style="color: rgba(0, 0, 0, 1)"> MarkdownIt({
html: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
highlight: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(str, lang) {
let preCode </span>= ""
<span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
preCode </span>=<span style="color: rgba(0, 0, 0, 1)"> hljs.highlightAuto(str).value
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
preCode </span>=<span style="color: rgba(0, 0, 0, 1)"> markdown.utils.escapeHtml(str);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自定义行号</span>
const lines = preCode.split(/\n/).slice(0, -1<span style="color: rgba(0, 0, 0, 1)">)
let html </span>= lines.map((item, index) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 去掉空行</span>
<span style="color: rgba(0, 0, 255, 1)">if</span>( item == ''<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ''<span style="color: rgba(0, 0, 0, 1)">
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item +'</li>'<span style="color: rgba(0, 0, 0, 1)">
}).join(</span>''<span style="color: rgba(0, 0, 0, 1)">)
html </span>= '<ol style="padding: 0px 30px;">' + html + '</ol>'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代码复制</span>
<span style="color: rgba(0, 0, 0, 1)"> copyCode.push(str)
let htmlCode </span>= `<div class="markdown-wrap"><span style="color: rgba(0, 0, 0, 1)">`
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #ifndef MP-WEIXIN</span>
htmlCode += `<div style="color: #aaa;text-align: right;font-size: 12px;padding:8px;"><span style="color: rgba(0, 0, 0, 1)">`
htmlCode </span>+= `${lang}<a class="copy-btn" code-data-index="${copyCode.length - 1}" style="margin-left: 8px;">复制代码</a>`
htmlCode += `</div>`
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #endif</span>
htmlCode += `<pre class="hljs" style="padding:0 8px;margin-bottom:5px;overflow: auto;border-radius: 5px;"><code>${html}</code></pre><span style="color: rgba(0, 0, 0, 1)">`;
htmlCode </span>+= '</div>'
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> htmlCode
}
})</span></pre>
</div>
<ul>
<li><strong>渲染markdown结构</strong></li>
</ul>
<div class="cnblogs_code">
<pre>const parseNodes = (value) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(!value) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
let htmlString </span>= ''
<span style="color: rgba(0, 0, 255, 1)">if</span> (value.split("```").length % 2<span style="color: rgba(0, 0, 0, 1)">) {
let msgContent </span>=<span style="color: rgba(0, 0, 0, 1)"> value
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(msgContent != '\n'<span style="color: rgba(0, 0, 0, 1)">){
msgContent </span>+= '\n'<span style="color: rgba(0, 0, 0, 1)">
}
htmlString </span>=<span style="color: rgba(0, 0, 0, 1)"> markdown.render(msgContent)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
htmlString </span>=<span style="color: rgba(0, 0, 0, 1)"> markdown.render(msgContent.value)
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #ifndef APP-NVUE</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> htmlString
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #endif</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> nvue模式下将htmlString转成htmlArray,其他情况rich-text内部转</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注:本示例项目还没使用nvue编译</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #ifdef APP-NVUE</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> parseHtml(htmlString)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #endif</span>
}</pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/1289798/202306/1289798-20230625083602081-1912030801.jpg"></p>
<p>最后使用rich-text组件来渲染处理后的结果。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">rich-text </span><span style="color: rgba(255, 0, 0, 1)">space</span><span style="color: rgba(0, 0, 255, 1)">="nbsp"</span><span style="color: rgba(255, 0, 0, 1)"> :nodes</span><span style="color: rgba(0, 0, 255, 1)">="parseNodes(item.content)"</span><span style="color: rgba(255, 0, 0, 1)"> @itemclick</span><span style="color: rgba(0, 0, 255, 1)">="handleItemClick"</span><span style="color: rgba(0, 0, 255, 1)">></</span><span style="color: rgba(128, 0, 0, 1)">rich-text</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<p>代码复制功能则是使用rich-text提供的itemclick方法来实现。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 复制代码</span>
const handleItemClick = (e) =><span style="color: rgba(0, 0, 0, 1)"> {
let {attrs} </span>=<span style="color: rgba(0, 0, 0, 1)"> e.detail.node
let {</span>"code-data-index": codeDataIndex, "class": className} =<span style="color: rgba(0, 0, 0, 1)"> attrs
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(className == 'copy-btn'<span style="color: rgba(0, 0, 0, 1)">){
uni.setClipboardData({
data:copyCode,
showToast:</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
success() {
uni.showToast({
title: </span>'复制成功'<span style="color: rgba(0, 0, 0, 1)">,
icon: </span>'none'<span style="color: rgba(0, 0, 0, 1)">
});
}
})
}
}</span></pre>
</div>
<p>通过上面几步,基本就实现了解析markdown语法了。</p>
<blockquote>
<p><span style="font-size: 18px; font-family: 宋体, "Songti SC"; color: rgba(255, 0, 0, 1); background-color: rgba(255, 255, 0, 1)">uniapp markdown组件已经发布插件市场,欢迎免费下载使用。</span></p>
<p><span style="color: rgba(0, 0, 255, 1)"><span style="color: rgba(0, 0, 255, 1)">https://ext.dcloud.net.cn/plugin?id=13307</span></span></p>
</blockquote>
<p>如果大家有一些其它不错的解决方案,欢迎交流讨论分享哈~~</p>
<p><img src="https://img2023.cnblogs.com/blog/1289798/202307/1289798-20230702004144717-291673688.png"></p>
<p> </p>
<h3>uni-app软键盘撑起顶跑问题</h3>
<p>在使用uniapp开发的时候,经常会遇到input输入框键盘会顶跑页面。导致顶部自定义导航栏会不见了。</p>
<p>接下来就介绍一种简单的方法,经测试是可行的,如果大家有其它方法,也欢迎交流分享。</p>
<p><img src="https://img2023.cnblogs.com/blog/1289798/202306/1289798-20230625084656847-1517998451.png"></p>
<p>说白了,就是在input编辑框外层加一个view标签,然后给设置padding-bottom为键盘弹起高度。</p>
<p>uniapp也提供了监听键盘高度变化函数 <span class="cnblogs_code">uni.onKeyboardHeightChange</span> </p>
<div class="cnblogs_code">
<pre>const fixPaddingBottom = computed(() =><span style="color: rgba(0, 0, 0, 1)"> {
let keyH </span>= keyboardHeight.value > 50 ? keyboardHeight.value - 50<span style="color: rgba(0, 0, 0, 1)"> : keyboardHeight.value
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (keyH || 10) + 'px'<span style="color: rgba(0, 0, 0, 1)">
})</span></pre>
</div>
<p>这里减去50是底部有自定义tabbar,大家可以根据实际情况调整。</p>
<div class="cnblogs_code">
<pre>onMounted(() =><span style="color: rgba(0, 0, 0, 1)"> {
nextTick(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
scrollToLast()
})
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #ifndef H5</span>
uni.onKeyboardHeightChange(e =><span style="color: rgba(0, 0, 0, 1)"> {
keyboardHeight.value </span>=<span style="color: rgba(0, 0, 0, 1)"> e.height
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在dom渲染完毕 滚动到最后一条消息</span>
nextTick(() =><span style="color: rgba(0, 0, 0, 1)"> {
scrollToLast()
})
})
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> #endif</span>
})</pre>
</div>
<p>目前通过这种方法解决了键盘撑起问题。如果大家有其它解决方法,欢迎下方留言讨论哈~~💝</p>
<h3><span style="background-color: rgba(255, 255, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">Uniapp-DeepSeek跨三端AI助手|uniapp+vue3+deepseek-v3流式ai聊天模板</span></span></h3>
<h3><span style="background-color: rgba(204, 255, 204, 1)"><span style="background-color: rgba(204, 255, 204, 1)">vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果</span></span></h3>
<p><img src="https://img2023.cnblogs.com/blog/1289798/202306/1289798-20230625085415872-266031454.gif"></p>
<p> </p>
</div>
<div id="MySignature" role="contentinfo">
本文为博主原创文章,未经博主允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)<br><br>
来源:https://www.cnblogs.com/xiaoyan2017/p/17502105.html
頁:
[1]