首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,572 阅读
2
Mac打印机设置黑白打印
4,882 阅读
3
修改elementUI中el-table树形结构图标
4,861 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,641 阅读
5
intelliJ Idea 2022.2.X破解
4,310 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
MyBatis
WordPress
Nacos
Spring Cloud Alibaba
Mybatis-Plus
jQuery
MacOS
Java Script
asp.net
MySQL
IntelliJ IDEA
微信小程序
Typecho
Sentinel
UniApp
asp.net core
Laughing
累计撰写
611
篇文章
累计收到
1,427
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
109
篇与
的结果
2025-04-03
若依前后端分离项目白名单支持携带参数
RuoYi是一个后台管理系统,基于经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf)主要目的让开发者注重专注业务,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量。若依前端框架通过permission.js文件中的whiteList进行配置的,比如登录、注册登连接,需要能直接跳转,只需要在whiteList中配置上对应的路由即可。const whiteList = ['/login', '/register']然后在路由打开前,判断路由是否在白名单内。但是这个判断逻辑存在一个问题,就是判断方法使用的whiteList.indexOf(to.path),这样如果我们路由携带参数的话,会无法实现,因为参数是动态的,我们无法配置白名单。为了实现跳转,我们需要替换whiteList.indexOf(to.path)方法,通过正则表达式的方式进行匹配。增加正则表达式校验方法在utils\validate.js文件中,增加正则校验方法/** * 路径匹配器 * @param {string} pattern * @param {string} path * @returns {Boolean} */ export function isPathMatch(pattern, path) { const regexPattern = pattern.replace(/\//g, '\\/').replace(/\*\*/g, '.*').replace(/\*/g, '[^\\/]*') const regex = new RegExp(`^${regexPattern}$`) return regex.test(path) }修改permission.js在permission.js引入新增的方法,并将原来的whiteList.indexOf(to.path)替换成新方法。import { isPathMatch } from '@/utils/validate' const isWhiteList = (path) => { return whiteList.some(pattern => isPathMatch(pattern, path)) }
2025年04月03日
0 阅读
0 评论
0 点赞
2025-03-30
VSCode配置Java(Spring Boot)开发环境
{message type="info" content="VSCode简介"/} 大名鼎鼎的VS Code不用过多介绍吧。VS Code全称Visual Studio Code,是由微软开发的一款免费、开源、跨平台的代码编辑器,支持Windows、macOS和Linux系统。它以其轻量级、高性能和丰富的扩展生态广受开发者欢迎,适用于多种编程语言和开发场景。 如果你也厌倦了一成不变的IntelliJ IDEA开发环境,想换换开发风格,不妨体验一下在VSCode中开发Spring Boot程序。{message type="success" content="前置条件"/} 本文基于Mac OS进行介绍,在Windows及Linux下配置也是类似的,为了顺利完成环境搭建,你需要提前准备好以前软件及配置。JDK并完成环境变量配置。Maven并完成环境变量配置。VSCode软件{message type="warn" content="安装插件"/} 其实所谓的配置环境,不过就是安装一堆Java插件。 打开VS Code,找到【插件】页签,我们需要安装以下插件。Debugger for JavaMaven for JavaLanguage Support for Java(TM) by Red HatExtension Pack for Java{message type="error" content="使用"/} 你可以找一个现有的Spring Boot环境,或者使用脚手架大家一个简单的Spring Boot程序。{alert type="info"} 官方脚手架目前已经不支持JDK1.8,如果使用JDK1.8的话,可以使用阿里云提供的脚手架,地址为: start.aliyun.com {/alert} 使用VS Code打开代码,找到启用程序,如下图 可以点击Run运行程序或者点击Debug调试程序。 也可以打短点调试程序,这里我在启动方法中打一个短点,然后点击Debug调试程序,之后程序就会进入断点。 我们也可以安装依赖,比如下面安装lombok依赖 如果需要打包,可以在控制台输入mvn package命令通过原生Maven的方式进行打包,因为我们上面安装了Maven插件,在VS Code左下角(默认)有个Maven选项,也可以打包,与Idea基本一致。 好了,以上就是在VS Code下搭建Java(Spring Boot)开发环境的完整步骤,感兴趣的童鞋可以去尝试一下。
2025年03月30日
31 阅读
0 评论
0 点赞
2025-03-22
网站ICP备案、公安备案仿Github徽章美化
在网站建设过程中,ICP备案和公安备案是必不可少的环节。然而,传统的备案信息展示方式往往显得单调乏味,与网站整体风格格格不入。为了让你的网站更加美观专业,我们可以借鉴 Github 徽章的风格,对备案信息进行美化。整体的实现效果可以查看李森的博客下方页面,如下图整体实现过程是很简单的,注意将下面代码中的备案信息换成自己的。在网站需要显示备案信息的地方插入下面代码<div class="github-badge"> <a href="./" title="©2025 李森的博客"> <span class="badge-subject">Copyright</span> <span class="badge-value bg-purple">©2025 李森的博客</span> </a> </div> | <div class="github-badge"> <a href="https://beian.miit.gov.cn" ; target="_blank" title="鲁ICP备16035197号-4"> <span class="badge-subject">鲁ICP备</span> <span class="badge-value bg-green">16035197号-4</span> </a> </div> | <div class="github-badge"> <a href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=37011202002327" ; target="_blank" title="鲁公网安备37011202002327号"> <span class="badge-subject">鲁公网安备</span> <span class="badge-value bg-orange">37011202002327号</span> </a> </div>在网站css文件中放入以下代码/* 底部版权、备案等信息美化开始 */ .github-badge { display: inline-block; border-radius: 4px; text-shadow: none; font-size: 11px; color: #fff; line-height: 15px; background-color: #abbac3; margin-bottom: 5px } .github-badge .badge-subject { display: inline-block; background-color: #4d4d4d; color: #fff; padding: 2px 4px 2px 6px; border-top-left-radius: 4px; border-bottom-left-radius: 4px } .github-badge .badge-value { display: inline-block; color: #fff; padding: 2px 6px 2px 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px } .github-badge .bg-blue { background-color: #007ec6 } .github-badge .bg-orange { background-color: #ffa500 } .github-badge .bg-red { background-color: #f00 } .github-badge .bg-green { background-color: #3bca6e } .github-badge .bg-purple { background-color: #ab34e9 } /* 底部版权、备案等信息美化结束 */
2025年03月22日
86 阅读
0 评论
1 点赞
2025-03-21
AISeo:一款Typecho主题Joe借助AI生成SEO关键词和SEO描述语的插件
一个网页的keywords及description对于网站的SEO是非常重要的,因此,我们在发布文章时,需要针对文章设置一个合理的keywords及description。但是如果每个文章,我们都手工设置keywords及description,势必会浪费我们极大的精力,在这个全名疯狂的AI时代,不利用AI生成一下keywords及description感觉自己不属于这个时代似的。{mtitle title="实现原理"/}其实整个实现过程是非常简单的,在发布文章页面,增加一个按钮,按钮点击时,通过http的方式调用对应模型的接口,并要求模型返回对应的keywords及description,使用正则表达式对返回的内容进行拆分,获取keywords及description信息,赋值到对应的字段上。{mtitle title="环境介绍"/}本文实现是有条件的,当然你可以在本文的基础上自行修改,进行更多的适配。李森的博客使用的是轻量高效的开源博客程序-Typecho,主题使用的是Joe,因此本文所有介绍的内容都是基于这两个前提的,当然,我们后续的代码会进行说明,方便你在其他地方使用或者使用了Typecho但是不是使用的Joe主题时,也能顺畅的使用。{mtitle title="GPT-API-free介绍"/}本着免费的原则,本文使用的AI工具是GPT-API-free。GPT-API-free是一个提供免费ChatGPT API密钥的开源项目,支持多种模型并允许开发者低成本接入GPT服务。特点支持Models, Embedding, text-davinci(免费版不支持), GPT-3.5-Turbo, GPT-3.5-Turbo-16K(免费版不支持), GPT-4, DALLE(免费版不支持), Whisper(免费版不支持)。(免费版就可以支持AutoGPT, gpt_academic, langchain等)免费版支持gpt-4,一天3次;支持gpt-4o-mini,和gpt-3.5-turbo共享一天200次。与官方完全一致的接口标准,兼容各种软件/插件。支持流式响应。国内线路使用动态加速,体验远优于使用代理连接官方。无需科学上网,国内环境直接可用。个人完全免费使用。免费使用申请领取内测免费API Key,申请地址 https://api.chatanywhere.org/v1/oauth/free/renderHost: https://api.chatanywhere.tech (国内中转,延时更低){mtitle title="博客引入AI"/}{alert type="error"}本插件只适用于Typecho并且需要使用Joe插件,{/alert}下载插件我已经将需要使用的插件封装好,可以直接下载使用{cloud title="AISeo.zip" type="bd" url="https://pan.baidu.com/s/1Nuoce_U8vkI9IG-60MEFlw?pwd=ccdd" password="ccdd"/}下载插件后解压,得到AISeo文件夹,将文件夹整体上传到网站的/usr/plugins文件夹内启用并配置插件进入网站后台,找到并启用AISeo插件点击设置按钮,进入设置页面模型名:gpt-3.5-turboAPI KEY:通过申请领取内测免费API Key介绍的网址免费申请。注意:申请API Key需要Github授权。输入地址:https://api.chatanywhere.tech其他保持默认即可,然后保存插件设置。配置文章编辑页面到admin目录下,找到write-post.php文件。在标题下方插入一个按钮<button type="button" class="generate-seo" onclick="generateSeoKeywords()">生成SEO关键词和描述</button>然后在最后一个div标签后面,添加以下代码<script> function generateSeoKeywords() { // 获取标题和内容 const title = document.querySelector('input[name="title"]').value; const text = document.querySelector('textarea[name="text"]').value; // 获取插件设置 const apiUrl = "<?php echo rtrim(Typecho_Widget::widget('Widget_Options')->plugin('AISeo')->apiUrl, '/') . '/v1/chat/completions'; ?>"; const keyValue = "<?php echo Typecho_Widget::widget('Widget_Options')->plugin('AISeo')->keyValue; ?>"; const modelName = "<?php echo Typecho_Widget::widget('Widget_Options')->plugin('AISeo')->modelName; ?>"; const maxLength = "<?php echo Typecho_Widget::widget('Widget_Options')->plugin('AISeo')->maxLength; ?>"; // 获取UUID const uuid = "<?php echo AISeo_Plugin::getUuid(); ?>"; // 从插件中获取UUID // 发起 AJAX 请求 fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + keyValue, 'X-Request-ID': uuid // 添加UUID到请求头 }, body: JSON.stringify({ model: modelName, messages: [{ role: "system", content: "请你扮演一个文本SEO关键词和SEO描述生成器,下面是一篇关于 '" + title + "' 的文章,请你根据文章标题生成 " + maxLength + " 字左右的SEO关键词和SEO描述,格式使用SEO关键词:SEO描述:,关键词使用英文逗号分隔,除了你生成的内容,请不要输出其他任何无关内容" }, { role: "user", content: text }], temperature: 0 }) }) .then(response => response.json()) .then(data => { if (data.choices && data.choices.length > 0) { const content = data.choices[0].message.content; // 使用特定的分隔符进行分割 const keywordsMatch = content.match(/SEO关键词:(.*?)SEO描述:(.*)/s); if (keywordsMatch && keywordsMatch.length === 3) { const keywords = keywordsMatch[1].replace(/\s+/g, ''); // 关键词 const description = keywordsMatch[2].trim(); // 描述 const keywordField = document.querySelector('input[name="fields[keywords]"]'); const descriptionField = document.querySelector('textarea[name="fields[description]"]'); // 始终覆盖自定义字段 keywordField.value = keywords; descriptionField.value = description; // 使用 cocoMessage 显示成功提示 console.success('SEO关键词和描述生成成功!'); } else { // 使用 cocoMessage 显示失败提示 console.error('生成失败,请重试。'); } } else { // 使用 cocoMessage 显示失败提示 console.error('生成失败,请重试。'); } }) .catch(error => { // 捕获并处理错误 console.error('请求失败:' + error.message); }); } </script>配置按钮样式找到admin/css/style.css,在任意位置,增加以下样式.generate-seo { padding: 8px 10px; /* 缩小内边距,减少按钮高度 */ background-color: #4CAF50; /* 绿色背景 */ color: white; /* 字体颜色为白色 */ border: none; /* 去除边框 */ border-radius: 4px; /* 圆角边框 */ cursor: pointer; /* 鼠标悬停时显示为指针 */ font-size: 14px; /* 字体大小 */ transition: background-color 0.5s ease; /* 背景颜色渐变效果 */ margin-top: 0px; /* 仅调整此按钮的顶部外边距 */ } .generate-seo:hover { background-color: #45a049; /* 悬停时更深的绿色 */ }
2025年03月21日
66 阅读
0 评论
0 点赞
2025-03-21
Typecho主题Joe首页底部增加友情链接
Joe是typecho下一款非常优秀的主题,本站也是使用的Joe的主题,只不过进行了一些改动。{message type="warning" content="本文实现方式只只用于Joe主题,其他主题需要自己修改"/}增加首页友情链接文件在主题public文件夹下增加homefriend.php文件,文件内容如下<!-- * 博客底部-友情链接 * Joe主题在想要显示的地方直接引入即可,其他主题需根据主题自身情况修改。 * $this->need('public/homefriend.php'); * * @Author:李森的博客 * @site:https://lisen.cc --> <style> .ls_friend_home { border-radius: 5px; margin: 0 auto; overflow: hidden } .ls_sites { overflow: hidden; padding-top: 10px } .ls_sites li { width: 31%; float: left; text-align: center; border-radius: 5px; line-height: 35px; margin: 0 3px; margin-bottom: 10px; position: relative } .ls_sites li a { text-decoration: none; color: #fff; display: block; text-overflow: ellipsis; white-space: nowrap; overflow: hidden } .ls_sites li:before, .ls_sites li:after { content: ''; position: absolute; top: 0; right: 0; height: 3px; width: 0; background: #fff; transition: 400ms ease all } .ls_sites li:after { right: inherit; top: inherit; left: 0; bottom: 0 } .ls_sites li:hover:before, .ls_sites li:hover:after { width: 100%; transition: 800ms ease all } @media(min-width:576px) { .ls_friend_home { max-width: 540px } .ls_sites li { width: 30% } } @media(min-width:768px) { .ls_friend_home { max-width: 720px } .ls_sites li { width: 30% } } @media(min-width:992px) { .ls_friend_home { max-width: 960px } .ls_sites li { width: 16.6% } } @media(min-width:1200px) { .ls_friend_home { max-width: 1140px } .ls_sites li { width: 16.6% } } @media(min-width:1400px) { .ls_friend_home { max-width: 1320px } .ls_sites li { width: 16.6% } } </style> <?php /*获取友情链接【这里Joe主题可直接使用,其他主题需要自己重写,原理相同,获取到链接进行循环。】*/ $friends = []; $friends_color = ['#F8D800','#0396FF','#EA5455','#7367F0','#32CCBC','#F6416C','#28C76F','#9F44D3','#F55555','#736EFE','#E96D71','#DE4313','#D939CD','#4C83FF','#F072B6','#C346C2','#5961F9','#FD6585','#465EFB','#FFC600','#FA742B','#5151E5','#BB4E75','#FF52E5','#49C628','#00EAFF','#F067B4','#F067B4','#ff9a9e','#00f2fe','#4facfe','#f093fb','#6fa3ef','#bc99c4','#46c47c','#f9bb3c','#e8583d','#f68e5f']; $friends_text = $this->options->JFriends; if ($friends_text) { $friends_arr = explode("\r\n", $friends_text); if (count($friends_arr) > 0) { for ($i = 0; $i < count($friends_arr); $i++) { $name = explode("||", $friends_arr[$i])[0]; $url = explode("||", $friends_arr[$i])[1]; $avatar = explode("||", $friends_arr[$i])[2]; $desc = explode("||", $friends_arr[$i])[3]; $friends[] = array("name" => trim($name), "url" => trim($url), "avatar" => trim($avatar),"desc" => trim($desc)); }; } } ?> <?php if (sizeof($friends) > 0) : ?> <div class="ls_friend_home"> <ul class="ls_sites"> <?php foreach ($friends as $item) : ?> <li style="background: <?php echo $friends_color[mt_rand(0, count($friends_color) - 1)] ?>"> <a href="<?php echo $item['url']; ?>" target="_blank" rel="noopener noreferrer" title="<?php echo $item['desc']; ?>"> <img width="20" height="20" class="lazyload" style="border-radius: 50%;object-fit: cover;" src="<?php _getAvatarLazyload(); ?>" data-src="<?php echo $item['avatar']; ?>" alt="<?php echo $item['name']; ?>" /> <?php echo $item['name']; ?> </a> </li> <?php endforeach; ?> </ul> </div> <?php endif; ?>在需要展示的地方,引入友情链接文件如果你想全站显示,建议放到public/footer.php的合适位置。因为我只想在首页显示,所以我是在index.php文件引入的,位置的话,放到了最底部<!-- 友情链接 --> <?php $this->need('public/homefriend.php'); ?>
2025年03月21日
73 阅读
0 评论
0 点赞
2025-03-19
云闪付登录逻辑:用户发短信?这是什么神仙操作?
云闪付是一种非现金收付款移动交易结算工具,是在中国人民银行的指导下,由中国银联携手各商业银行、支付机构等产业各方共同开发建设、共同维护运营的移动支付APP,于2017年12月11日正式发布。最近想购买扩展坞,用云闪付可以直接国补抵扣,在使用云闪付时发现它的手机号码登录逻辑简直让人哭笑不得。作为一个支付工具,安全性和便捷性本该是核心,但云闪付的登录流程却让我怀疑人生——竟然需要用户主动发短信才能登录!而且它还是给你发送一个短信,告诉你需要给它发送一个短信进行验证。。。这是什么神仙操作?今天就来吐槽一下这个让人摸不着头脑的设计。登录流程:用户发短信?认真的吗?正常的移动App通常的登录流程是:输入手机号 → 接收验证码 → 输入验证码 → 登录成功。简单直接,一气呵成。但云闪付的流程却是:输入手机号 → 系统给你发一条短信提示你发一条短信到指定号码 → 发完短信 → 等待系统验证 → 登录成功。What? 我不仅要输入手机号,还要自己发短信?这操作简直让人一脸懵。难道云闪付觉得用户的时间不值钱还是觉得用户发短信不需要钱,还是觉得发短信是一件很有趣的事情?用户体验:繁琐又反人类步骤繁琐: 本来一键接收验证码就能搞定的事情,非要让用户手动发短信。操作复杂: 尤其是对不太熟悉手机操作的中老年用户,发短信可能比接收验证码难多了。浪费时间: 发短信、等待验证,整个过程比普通登录多花了好几倍时间。浪费金钱: 用户想要注册或者登录你的产品,你还得要用户掏钱去认证?云闪付的产品经理是不是觉得用户都太闲了? 这种设计简直是把用户体验按在地上摩擦。安全性:真的更安全吗?可能有人会说,这种设计是为了安全。但仔细想想,发短信真的比接收验证码更安全吗?短信拦截风险:如果用户的手机被恶意软件控制,短信内容同样可能被截获。用户体验牺牲:为了所谓的安全,牺牲了用户体验,真的值得吗?对比其他App:差距明显看看支付宝、微信支付,甚至其他普通的App,哪个不是一键验证码登录?简单、快捷、安全,这才是用户需要的。而云闪付却偏偏要走一条“与众不同”的路,结果就是让用户感到困惑和不便。希望云闪付的产品团队能好好反思一下,用户需要的是便捷和安全,而不是折腾和困惑。如果下次登录还要我发短信,我可能真的要考虑卸载了。最后,灵魂一问:云闪付,你是认真的吗?还是说,这只是为了让我多花点短信费?(手动狗头)
2025年03月19日
43 阅读
0 评论
0 点赞
2024-07-21
提取Html内容生成文章目录
最近在开发一款在线知识库系统,为了方便使用,提供了两种编辑器,一种是基于markdown的,一种是基于富文本的。基于markdown的编辑器,我们使用了MavonEditor插件,同时也是使用该插件进行markdown内容的展示,这个插件自带了目录功能,我们可以直接使用,感兴趣的可以看一下Vue使用mavon-editor插件实现Markdown文件编辑及预览为了保证使用体验的一致性,基于富文本编辑器的内容,我们也给他添加一个目录功能,目录结构的展示我们使用elementUI框架的el-tree组件。template代码template代码比较简单,因为是基于elementUI框架的el-tree组件的,所以需要先安装elementUI。<el-tree class="toc-tree" ref="tree" node-key="uuid" :data="tocTreeData" :props="tocDefaultProps" v-if="tocTreeData && tocTreeData.length>0" default-expand-all> <div class="custom-tree-node" slot-scope="{ node, data }"> <div @click="toDiv(data)">{{ data.text }}</div> </div> </el-tree>data代码其实看到上面template的代码,我们大概能猜测到data里面需要的内容tocTreeData: [], tocDefaultProps: { label: 'text', children: 'children' },tocTreeData是我们存储的目录树形的结构tocDefaultProps是el-tree组件用来配置展示内容及子节点的。js代码getCatalog()是入口方法,当我们在前端完成富文本内容的渲染后,就可以调用此方法,创建目录数据,进行展示。当然,提取目录的层级是根据我们的<h>标签进行提取的,可以自己定义提取的深度,比如我这里提取到了<h6>。toDiv()是点击目录时,用于页面滚动到对应位置的方法。getCatalog() { const h = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] const elements = document.querySelectorAll(h) let hElements = [] for (const key of elements) { if (h.indexOf(key.localName) > -1) { let text if (key.children && key.children.length) { text = this.getText(key.children) } else { text = key.innerHTML } hElements.push({ hLevel: parseInt(key.localName[1]), text, id: key.localName, uuid: key.id, offsetTop: key.offsetTop }) } } this.tocTreeData = this.toTree(hElements) }, getText(arr) { let result = null if (!arr.length) return for (let i = 0; i < arr.length; i++) { if (arr[i].children && arr[i].children.length) { result = this.getText(arr[i].children) } else { result = arr[i].innerHTML } } return result }, toTree(flatArr) { const tree = [] const copyArr = flatArr.map(function(item) { return item }) // 根据指定级别查找该级别的子孙级,并删除掉已经查找到的子孙级 const getChildrenByLevel = function(currentLevelItem, arr, level) { if (!currentLevelItem) { return } // 将level值转成负数,再进行比较 const minusCurrentLevel = -currentLevelItem.hLevel const children = [] let i = 0, len = arr.length for (; i < len; i++) { const levelItem = arr[i] if (-levelItem.hLevel < minusCurrentLevel) { children.push(levelItem) } else { // 只找最近那些子孙级 break } } // 从数组中删除已经找到的那些子孙级,以免影响到其他子孙级的查找 if (children.length > 0) { arr.splice(0, children.length) } return children } const getTree = function(result, arr, level) { // 首先将数组第一位移除掉,并添加到结果集中 let currentItem = arr.shift() currentItem.level = level result.push(currentItem) while (arr.length > 0) { if (!currentItem) { return } // 根据当前级别获取它的子孙级 const children = getChildrenByLevel(currentItem, arr, level) // 如果当前级别没有子孙级则开始下一个 if (children.length === 0) { currentItem = arr.shift() currentItem.level = level if (currentItem) { result.push(currentItem) } continue } currentItem.children = [] // 查找到的子孙级继续查找子孙级 getTree(currentItem.children, children, level + 1) } } getTree(tree, copyArr, 1) return tree }, toDiv(data) { document.getElementById('contentMainId').scrollTop = data.offsetTop - 20 },
2024年07月21日
886 阅读
0 评论
1 点赞
2024-07-19
150款CSS实现的加载中动画
每个里面可能都有多种动画,可以直接运行查看150款CSS实现的加载中动画.zip
2024年07月19日
838 阅读
0 评论
0 点赞
2024-07-17
Vue使用mavon-editor插件实现Markdown文件编辑及预览
因为开发在线知识库系统,因为主要面向研发人员,所以在文档编辑器上,更加倾向于使用markdown,基于vue的markdown编辑器有很多,在经过实际体验后,从易用性、美观性等综合考量,最重选择了mavon-editor。MavonEditor简介MavonEditor是一款基于Vue的Markdown编辑器,它结合了Markdown语法和Typora的实时预览功能,提供了一个所见即所得(WYSIWYG)的编辑体验。MavonEditor不仅支持基本的Markdown语法,还支持LaTeX公式、代码高亮、目录生成、自动目录锚点链接等功能,使其成为撰写技术文档、博客文章等的理想工具。但是MavonEditor也有一个缺点,就是它依赖的highlightjs、katex插件,默认是基于CDN的,当然我们可以改成基于本地的,这个后面再详细介绍。安装插件npm install mavon-editor --save引入因为我涉及在后端编辑,前端预览,所以直接选择了全局引入,编辑main.js// mavonEditor全局注册 import mavonEditor from 'mavon-editor' import 'mavon-editor/dist/css/index.css' Vue.use(mavonEditor)后端编辑页面<mavon-editor ref="md" v-model="form.content" @imgAdd="markdownImageAdd" :toolbars="toolbars" @imgDel="markdownImageDelete" class="content-show" codeStyle="docco" :ishljs="true" :externalLink="externalLink"/>v-model是绑定的内容imgAdd是图片上传后的回调,mavon-editor上传图片后,会转成base64,在回调方法内,我们可以拿到base64编码的图片信息,上传到服务器或OSS后进行存储。imgDel是删除图片后的回调,这里需要注意一点,如果我们是将图片上传到自己服务器,需要有个记录表记录一下,因为在后期编辑文档的时候,工具栏是没有删除图片按钮的,如果我们需要删除图片的话,就需要一个专门维护的地方。toolbars是配置工具栏按钮codeStyle配置代码高亮样式。可选的配色方案可以参考https://hinesboy.github.io/mavonEditor/src/lib/core/hljs/lang.hljs.css.jsishljs是否开启代码高亮。externalLink配置外链应用,如果我们不使用CDN时,需要配置这个data() { return { toolbars: { bold: true, // 粗体 italic: true, // 斜体 header: true, // 标题 underline: true, // 下划线 strikethrough: true, // 中划线 mark: true, // 标记 superscript: true, // 上角标 subscript: true, // 下角标 quote: true, // 引用 ol: true, // 有序列表 ul: true, // 无序列表 link: true, // 链接 imagelink: true, // 图片链接 code: true, // code table: true, // 表格 fullscreen: true, // 全屏编辑 readmodel: false, // 沉浸式阅读 htmlcode: true, // 展示html源码 help: false, // 帮助 /* 1.3.5 */ undo: true, // 上一步 redo: true, // 下一步 trash: true, // 清空 save: false, // 保存(触发events中的save事件) /* 1.4.2 */ navigation: false, // 导航目录 /* 2.1.8 */ alignleft: true, // 左对齐 aligncenter: true, // 居中 alignright: true, // 右对齐 /* 2.2.1 */ subfield: true, // 单双栏模式 preview: true, // 预览 boxShadow: false }, //加载本地资源 externalLink: { markdown_css: function() { // 这是你的markdown css文件路径 return '/markdown/github-markdown.min.css' }, hljs_js: function() { // 这是你的hljs文件路径 return '/highlightjs/highlight.min.js' }, hljs_css: function(css) { // 这是你的代码高亮配色文件路径 return '/highlightjs/styles/' + css + '.min.css' }, hljs_lang: function(lang) { // 这是你的代码高亮语言解析路径 return '/highlightjs/languages/' + lang + '.min.js' }, katex_css: function() { // 这是你的katex配色方案路径路径 return '/katex/katex.min.css' }, katex_js: function() { // 这是你的katex.js路径 return '/katex/katex.min.js' } }, } },methods: { //编辑器图片删除 markdownImageDelete(file) { let filePath = file[0] if (filePath.indexOf(this.baseApi) === 0) { filePath = filePath.replace(this.baseApi, '') } delImage(filePath).then(response => { if (response.code === '200') { this.$modal.msgSuccess('图片删除成功') } }) }, //编辑器插入图片 markdownImageAdd(fileIndex, file) { if (!file.articleId) { file.articleId = this.form.articleId } uploadImage(JSON.stringify(file)).then((res) => { if (res.code === 200) { this.$refs.md.$img2Url(fileIndex, this.baseApi + res.data) this.$modal.msgSuccess('图片上传成功') } else { this.$refs.md.$img2Url(fileIndex, '') } }).catch(() => { this.$refs.md.$img2Url(fileIndex, '') }) } }至此,我们后端编辑功能基本完成了前端预览界面<mavon-editor ref="mavonEditor" :editable="false" v-model="article.content" :defaultOpen="'preview'" :subfield="false" :toolbarsFlag="false" :navigation="true" codeStyle="docco" :ishljs="true" :scroll-style="true" :box-shadow="false" preview-background="#ffffff" :externalLink="externalLink"/>编辑页面相同的属性就不介绍了,介绍一下其他的。editable是否可编辑,因为我们是预览界面,所以设置成不可编辑defaultOpen在单栏(subfield=false)时默认展示区域, edit: 默认展示编辑区域,preview: 默认展示预览区域。subfield单栏还是双栏暂时,true: 双栏(编辑预览同屏), false: 单栏(编辑预览分屏)toolbarsFlag是否显示工具栏navigation是否展示目录scroll-style开启滚动条样式box-shadow开启边框阴影preview-background预览框背景颜色配置本地外链加载如果你想自己引入而不希望mavon-editor加载的话,可以将externalLink设置为false.如果想本地按需加载,你需要安装copy-webpack-plugin插件(npm install copy-webpack-plugin -D) 配置webpack如下所示: (假定webpack配置文件位于项目的/webpack/webpack.js, 而你希望将hljs以及markdown相关文件导出位于项目的/dist/highlightjs以及/dist/markdown目录之下, katex和上面一样)var CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { // ... plugins: [ // ... new CopyWebpackPlugin([{ from: 'node_modules/mavon-editor/dist/highlightjs', to: path.resolve(__dirname, '../dist/highlightjs'), // 插件将会把文件导出于/dist/highlightjs之下 }, { from: 'node_modules/mavon-editor/dist/markdown', to: path.resolve(__dirname, '../dist/markdown'), // 插件将会把文件导出于/dist/markdown之下 }, { from: 'node_modules/mavon-editor/dist/katex', // 插件将会把文件导出 to: path.resolve(__dirname, '../dist/katex') }]), // ... ], // ... }然后你需要给mavon-editor设置externalLink 相关代码如下所示: (假定你的web根目录位于项目的/dist/, 你的网站是www.site.com, 那么 markdown, hljs_js, hljs_css, hljs_lang, katex_css, katex_js返回的是你的网站对应文件位置, 比如www.site.com/markdown/github-markdown.min.css 对应的文件应该位于项目的/dist/markdown/github-markdown.min.css)<template> <div id="app"> <mavon-editor :subfield = "subfield" :code_style="code_style" :ishljs="true" :externalLink="externalLink" ></mavon-editor> </div> </template> <script> export default { data () { return { subfield: true, code_style: 'solarized-dark', externalLink: { markdown_css: function() { // 这是你的markdown css文件路径 return '/markdown/github-markdown.min.css'; }, hljs_js: function() { // 这是你的hljs文件路径 return '/highlightjs/highlight.min.js'; }, hljs_css: function(css) { // 这是你的代码高亮配色文件路径 return '/highlightjs/styles/' + css + '.min.css'; }, hljs_lang: function(lang) { // 这是你的代码高亮语言解析路径 return '/highlightjs/languages/' + lang + '.min.js'; }, katex_css: function() { // 这是你的katex配色方案路径路径 return '/katex/katex.min.css'; }, katex_js: function() { // 这是你的katex.js路径 return '/katex/katex.min.js'; }, } } }, } </script>Notice: 如果你想禁用mavon-editor的自动加载, 可以将externalLink设置为false或externalLink中的某函数值设置为false 如:export default { // ... data() { return { externalLink: false, // 这里只能为`true`/`false`和一个`Object`, 如果为`true`代表全使用外链且自动加载, 如果为`false`则禁用,如果为`Object`则如上所示 } } // ... }或者:export default { // ... data() { return { externalLink: { hljs_css: function(css) { // 这是你的代码高亮配色文件路径 return '/highlightjs/styles/' + css + '.min.css'; }, katex_css: false, // `false`表示禁用自动加载,它也可以是个函数,如果它是个函数,那么这个函数应该返回一个可访问的`katex`的css路径字符串 // 我们没有设置`katex_js`, `hljs_js`, `hljs_lang`, `markdown_css`, `mavon-editor`会认为它的值为`true`,它会默认使用`cdnjs`相关外链加载 }, } } // ... }
2024年07月17日
980 阅读
0 评论
0 点赞
2024-07-01
推荐几款可以当做在线知识库的系统
在这个信息爆炸的时代,知识成为推动社会进步与个人成长的重要力量。而在线知识库,作为信息时代的智慧宝库,正日益成为学习、研究与创新不可或缺的平台。无论是企业内部的知识管理,还是面向公众的知识分享,一个结构化、易访问、内容丰富的在线知识库都是关键。本文将深入探讨如何高效构建和维护一个在线知识库,以促进知识的有效传播与利用。这篇文章,我们简要的介绍几款免费的可以当做在线知识库的系统,这里面有些是免费开源的,有些不是开源的还有一些有开源版本及收费版本,后续我们会针对每一个系统的使用方式进行详细的介绍。MrDoc觅思文档第一款要介绍的就是MrDoc觅思文档,这是我个人非常推荐的一个在线知识库系统,支持自托管、私有部署的。觅思文档分为两个版本【开源版】还有【专业版】,个人觉得开源版已经能够满足我们日常使用,开源版是基于GPLv3协议开源的。觅思文档的优点:支持创建在线文本文档、在线表格文档、在线 Office 文档(OnlyOffice),文档的创作简便且高效。丰富的权限控制。支持私有化部署。支持素材管理,也与第三方OSS支持集成。界面美观。觅思文档的缺点:不支持评论。Python版本较低,不知道现在有没有升级,当时因为低版本Python存在漏洞,被公司扫描出来,高版本的Python部署报错,因此放弃的。docsify这个在之前的文章中有介绍过,这里就不过多介绍了,感兴趣的可以移步dosify一个神奇的文档网站生成工具WordPressWordPress虽然不是专业的知识库系统,但是WordPress胜在丰富的插件支持,我们可以借助插件将WordPress打造成一个在线知识库系统。下面为大家分享几大常见的WordPress知识库插件BetterDocs是一个功能强大的WordPress知识库插件,易于使用且功能全面;带有可立即使用的WordPress知识库模板。BetterDocs还包括对带有小工具的页面构建器的支持。您还可以使用简码创建自己的布局,并在任何地方插入知识库文章。BetterDocs包括功能强大的搜索,浮动目录,一个知识库机器人,该机器人自动尝试查找答案以减少支持请求。该插件还包括基于用户角色的控制和见解,以查看用户正在寻找的内容。Heroic KB是一个易于使用的知识库WordPress插件,功能强大且功能齐全;可以在现有站点中添加可搜索的知识库,以便访问者可以轻松找到问题的答案。大家可以从功能强大的知识库插件获得所需的所有功能,例如内容分类,拖放内容排序,文章附件,显示小部件等。Heroic KB内置分析和用户反馈功能可以收集见解以改进现有文档,从而可以减少支持,节省时间并提高销量。weDocs是另一个出色的WordPress知识库和文档插件,易于使用,并允许您将文档页面分为小节,分层文档和标签;具有一个更简单的界面,可从一个屏幕管理所有文档。该插件不包括其他模板,但可以与所有流行的WordPress主题配合使用。weDocs知识库的布局简单而有效,并带有侧边栏导航和面包屑菜单。Echo Knowledge Base是 WordPress 最好的知识库插件之一,带有一个简单的设置向导,可以引导您完成插件设置;还可以获得多种布局供您选择,并提供自定义各个方面的选项。大家可以从基本、选项卡或以类别为中心的布局中进行选择,每个布局顶部都有一个快速搜索栏,可帮助用户快速找到答案;可以进一步按类别和标签组织文章。Encyclopedia / Glossary / Wiki是一个灵活的 WordPress 知识库插件,允许以词汇表格式组织文档;可以按字母顺序或按类别和标签组织文档和文章。Encyclopedia / Glossary / Wiki包括一个简单的模板来列出所有词汇表项,可以添加搜索小部件,为个别文章启用评论,并添加过滤索引以按字母表对项目进行排序。插件提供了一个很好的选项,可以以词汇表或百科全书的形式组织支持页面;支持WPML,可用于多语言网站。BasePress是一个有用的WordPress知识库和文档管理插件,可以轻松地为产品或服务构建支持页面和文档部分。BasePress带有三个模板,并带有一个高级即时搜索栏,可以帮助用户快速找到答案;可以使用简单的拖放界面组织文章,类别和标签中的文章,以对文章重新排序。BasePress可以为每个部分添加图像和描述,从而创建一个非常整洁的索引页面供用户浏览,还提供了简单的自定义选项,可以控制文章页面的外观。wiki.jsWiki.js 是一个基于 NodeJS 的现代、轻量级和强大的 wiki 应用程序。支持全文检索、用户权限管理、页面定制等功能,还具备良好的用户体验。其灵活的扩展性使得我们可以根据项目的实际需求对其进行定制和扩展。这个我没实际体验过,这里只大概介绍一下,感兴趣的可以安装体验一下。MinDoc基于beeego开发,原作者已经不再维护,基于MinDoc比较出名的站点我觉得可能就是书栈网了,并且书栈网也已经开源了。感兴趣的可以研究一下https://gitee.com/truthhun/BookStackWCPWCP 是一套BS架构的开源知识管理系统、知识库系统。它能提供团队知识库建设的一整套功能,从知识创建、知识更新、知识推送到知识评价、知识激励、知识统计以及基于以上功能权限控制等功能。WCP开源版本采用springMVC、spring、hibernate框架实现主要架构功能,由lucene提供全文检索功能,并使用了其他若干主流开源项目。数据库管理系统默认使用mysql。可以部署在tomcat等主流中间件服务器上。WCP可以通过对大语言模型接口的调用来为用户提供智能问答服务,该服务可以依赖知识库中的知识进行问题回答。其他CMS系统其实像网钛CMS、帝国CMS这些内容管理系统,我觉得都可以作为在线知识库使用。
2024年07月01日
362 阅读
0 评论
0 点赞
2024-05-30
ElementUI中一个页面多个el-descriptions列不对齐问题处理
使用ElementUI框架时,如果我们希望列表形式展示多个字段。可能经常使用Descriptions描述列表,如果一个页面只有一个Descriptions时,列宽显示是没有问题的,但是如果有多个Descriptions,你会发现,各个Descriptions之间的列宽,可能显示的不一致。为了保持各列宽度一致,我们可以设置contentStyle属性。<el-descriptions border :label-style="{ width: '110px' }" :contentStyle="content_style"> </el-descriptions> 然后在data里面,设置具体的content_stylecontent_style: { // 居左 'text-align': 'left', // 设置长度 width: '400px', // 排列第二行 'word-break': 'break-all' }
2024年05月30日
986 阅读
0 评论
1 点赞
2024-05-29
ElementUI中el-input-number实时监听值变化
在前端开发过程中,我们经常会遇到将金额转大写的情况,我们要实现的效果,是只要输入就实时计算大写,而不是等全部输入完成失去焦点后。如果使用ElementUI框架的el-input-number控件的@input事件,是无法达到实时显示效果的。html代码<el-col :span="24"> <el-form-item label="本次报价" prop="quotationPrice"> <el-input-number :precision="2" :step="100" :max="10000000" :min="0" v-model="form.quotationPrice" style="width: 100%" controls-position="right" data-unit="元" placeholder="请输入本次报价" ref="refQuotationPrice" @input.native="convertPrice2CnyMoney" @change="convertPrice2CnyMoney4Change" /> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="总金额" prop="cnyMoney"> <div class="cnyMoney">{{ cnyMoney }}</div> </el-form-item> </el-col> JavaScript代码//本次报价金额实时转大写 convertPrice2CnyMoney(event) { let value = this.$refs.refQuotationPrice.displayValue; this.cnyMoney = numToCny(value); }, //本次报价金额实时转大写 convertPrice2CnyMoney4Change(val) { this.cnyMoney = numToCny(val); }
2024年05月29日
831 阅读
0 评论
0 点赞
1
2
...
10