首页
归档
留言
广告合作
友链
美女主播
Search
1
博瑞GE车机升级/降级
5,146 阅读
2
Mac打印机设置黑白打印
4,517 阅读
3
修改elementUI中el-table树形结构图标
4,516 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,351 阅读
5
intelliJ Idea 2022.2.X破解
4,060 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
mybatis
WordPress
Nacos
Spring Cloud Alibaba
Mybatis-Plus
jQuery
Java Script
asp.net
微信小程序
Sentinel
UniApp
MySQL
asp.net core
IntelliJ IDEA
Jpa
树莓派
Laughing
累计撰写
570
篇文章
累计收到
1,424
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
广告合作
友链
美女主播
搜索到
570
篇与
的结果
2024-07-19
150款CSS实现的加载中动画
每个里面可能都有多种动画,可以直接运行查看150款CSS实现的加载中动画.zip
2024年07月19日
822 阅读
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日
940 阅读
0 评论
0 点赞
2024-07-06
Sprint Boot接入阿里通义千问
阿里通义千问是阿里巴巴推出的大规模语言模型,由达摩院研发。它是基于先进的自然语言处理技术构建的,旨在提供高质量的文本生成和理解能力。通义千问的特点包括:多语言支持:通义千问能够理解和生成多种语言的文本,包括但不限于中文、英文、日文、法文、西班牙文和德文等,这使得它具有全球化的交流能力。训练数据丰富:它的训练数据来自阿里巴巴内部的大量语言和文本资源,涵盖了文学、历史、科学、艺术等各种主题,旨在提供广泛的知识基础。应用场景广泛:通义千问不仅可以用于日常对话和信息查询,还可以为企业和个人用户提供定制化服务,如行业咨询、文档撰写、智能助手等,帮助用户生成内容或解答问题。与阿里巴巴产品整合:2023年4月,阿里巴巴宣布其所有产品将接入通义千问,这意味着用户可以在钉钉、天猫精灵等平台上直接体验到该模型的服务,企业也可以利用阿里云的能力来定制自己的行业专属大模型。合规性:2023年9月13日,通义千问通过了相关备案并正式对公众开放,表明其在遵守法律法规的前提下提供服务。商业价值:张勇(阿里巴巴集团董事会主席兼CEO)强调了通义千问对于提升阿里巴巴产品和服务的智能化水平,以及帮助企业利用人工智能进行创新。如果你是基于Python或Java开发,那么通义千问支持的SDK还是比较完善的,本文已Spring Boot接入阿里通义千问为例进行说明。壹、申请Key进入阿里云官网,定位到【API-KEY管理】,如果已经有Key的话,可以直接使用,如果没有可以创建一个新的。贰、创建Spring Boot工程具体怎么创建工程就不过多介绍了,现在主要说说创建完之后的配置及开发工作。2.1、添加依赖<dependency> <groupId>com.alibaba</groupId> <artifactId>dashscope-sdk-java</artifactId> <version>2.14.0</version> </dependency>2.2、修改配置文件在配置文件application.yml中添加我们申请到的Key注意替换成你实际的keyqwen: ai-api-key: sk-XXXX2.3、创建配置文件,读取配置信息@Component @ConfigurationProperties(prefix = "qwen") @Data public class QWenConfig { private String aiApiKey; }2.4、创建通义千问的配置文件@Configuration public class AliQWenConfig { @Bean public Generation generation() { return new Generation(); } }2.5、创建请求@RestController @RequestMapping("ai") public class QWenController { @Resource private Generation generation; @Resource private QWenConfig qWenConfig; /** * 测试demo * * @param content 用书输入文本内容 */ @PostMapping(value = "qwen") public String send(@RequestBody String content) throws NoApiKeyException, InputRequiredException { //用户与模型的对话历史。list中的每个元素形式为{“role”:角色, “content”: 内容}。 Message userMessage = Message.builder() .role(Role.USER.getValue()) .content(content) .build(); GenerationParam param = GenerationParam.builder() //指定用于对话的通义千问模型名 .model("qwen-turbo") .messages(Collections.singletonList(userMessage)) // .resultFormat(GenerationParam.ResultFormat.MESSAGE) //生成过程中核采样方法概率阈值,例如,取值为0.8时,仅保留概率加起来大于等于0.8的最可能token的最小集合作为候选集。 // 取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的确定性越高。 .topP(0.8) //阿里云控制台DASHSCOPE获取的api-key .apiKey(qWenConfig.getAiApiKey()) //启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。 .enableSearch(true) .build(); GenerationResult generationResult = generation.call(param); return generationResult.getOutput().getChoices().get(0).getMessage().getContent(); } }叁、测试使用ApiFox测试一下
2024年07月06日
1,064 阅读
0 评论
0 点赞
2024-07-04
使用thumbnailator实现图片压缩
Thumbnailator是一个用于Java的强大的图片处理库,主要用于创建、缩放和裁剪图片的缩略图。它设计得既简单又功能强大,提供了一系列丰富的特性:尺寸调整:Thumbnailator能够根据指定的宽度或高度来调整图片大小。裁剪:可以将图片裁剪为特定的尺寸或长宽比。水印添加:可以在图片上添加文本或图片形式的水印。格式支持:支持多种图像格式,如JPEG、PNG、BMP、GIF等。Thumbnailator的使用非常直观。你只需要在你的项目中添加Thumbnailator的依赖(比如在Maven或Gradle的构建文件中),然后就可以在代码中调用其提供的方法了。下面结合项目示例,介绍一下使用Thumbnailator的方法,当然,关于添加水印、裁剪等功能你可以查看其github仓库https://github.com/coobird/thumbnailator壹、添加依赖我这里使用的0.4.20版本。 <!--thumbnailator图片处理--> <dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.20</version> </dependency>贰、创建一个公共类这里简单介绍一下,getAccuracy是根据原图片大小,判断压缩的比例,压缩比例介于[0,1],比例越小,压缩的越小,当然相应的图片就会越模糊,所以这个比例可以根据实际情况进行调整,尽量保证压缩比例小的情况下,别造成图片失真。compressPicForScale方法,第一个参数代表源图片字节数组,第二个参数desFileSize代表要压缩到的大小,单位是kb,compressPicForScale方法对输入的图片循环压缩,直至压缩后文件大小<= desFileSizepackage net.xiangcaowuyu.common.utils.file; import net.coobird.thumbnailator.Thumbnails; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; /** * description: 图片压缩 * * @author: leeframe * DateTime: 2024-07-04 11:54 */ public class PicUtils { //以下是常量 private static final Logger logger = LoggerFactory.getLogger(PicUtils.class); private static final Integer ZERO = 0; private static final Integer ONE_ZERO_TWO_FOUR = 1024; private static final Integer NINE_ZERO_ZERO = 900; private static final Integer THREE_TWO_SEVEN_FIVE = 3275; private static final Integer TWO_ZERO_FOUR_SEVEN = 2047; private static final Double ZERO_EIGHT_FIVE = 0.85; private static final Double ZERO_SEVEN_FIVE = 0.75; private static final Double ZERO_FOUR_FOUR = 0.44; private static final Double ZERO_FOUR = 0.4; /** * 根据指定大小压缩图片 * * @param imageBytes 源图片字节数组 * @param desFileSize 指定图片大小,单位kb * @return 压缩质量后的图片字节数组 */ public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize) { if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) { return imageBytes; } long srcSize = imageBytes.length; double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR); try { while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) { ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length); Thumbnails.of(inputStream) .scale(accuracy) .outputQuality(accuracy) .toOutputStream(outputStream); imageBytes = outputStream.toByteArray(); } logger.info("图片原大小={}kb | 压缩后大小={}kb", srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR); } catch (Exception e) { logger.error("【图片压缩】msg=图片压缩失败!", e); } return imageBytes; } /** * 自动调节精度(经验数值) * * @param size 源图片大小 * @return 图片压缩质量比 */ private static double getAccuracy(long size) { double accuracy; if (size < NINE_ZERO_ZERO) { accuracy = ZERO_EIGHT_FIVE; } else if (size < TWO_ZERO_FOUR_SEVEN) { accuracy = ZERO_SEVEN_FIVE; } else if (size < THREE_TWO_SEVEN_FIVE) { accuracy = ZERO_FOUR_FOUR; } else { accuracy = ZERO_FOUR; } return accuracy; } } 叁、使用我这里是借助阿里云OSS,然后下载的网络图片/** * 阿里云文件上传(中保创) * * @param extension 文件后缀 * @param ownerDirectory 目录 */ public String upload(String extension, String ownerDirectory, String url) throws InvalidExtensionException, IOException { // 填写网络流地址。 try (InputStream inputStream = new URL(url).openStream()) { // 校验格式、大小等 String filePath = getFilePath(extension, ownerDirectory); byte[] bytesOriginal = IOUtils.toByteArray(inputStream); byte[] bytes = PicUtils.compressPicForScale(bytesOriginal, 400); ByteArrayInputStream inputStreamCompress = new ByteArrayInputStream(bytes); // 上传到阿里云 ossClient.putObject(aliyunConfig.getBucketName(), filePath, inputStreamCompress); //this.aliyunConfig.getUrlPrefix() + filePath 文件路径需要保持数据库 return aliyunConfig.getUrlPrefix() + filePath; } }
2024年07月04日
863 阅读
0 评论
0 点赞
2024-07-04
druid discard long time none received connection
阿里巴巴的Druid是一个Java数据库连接池(JDBC connection pool)组件,由阿里巴巴开发并开源。它不仅是一个数据库连接管理器,还提供数据源代理,SQL解析,监控等功能。Druid的主要特性包括:高效性:Druid使用了高效的连接池实现,减少了创建和销毁连接的开销。监控功能:Druid可以监控应用程序中的SQL执行情况,帮助开发者优化数据库操作。SQL解析:Druid能够解析SQL语句,对于一些复杂的SQL语句,可以进行优化或改写。防SQL注入:Druid通过SQL解析,能有效防止SQL注入攻击。高可用性:Druid支持主从读写分离,负载均衡等高级功能,提高系统的稳定性和性能。兼容性:Druid对主流的JDBC驱动和数据库都有很好的兼容性。当我们执行Sql时,如果当前执行时间与上一次执行Sql的时间间隔60s以上,在日志中就会有一条日志discard long time none received connection. , jdbcUrl : jdbc:mysql://xxx:3306/xxx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC, version : 1.2.5, lastPacketReceivedIdleMillis : 62681这个信息,不影响程序正常运行,如果我们想屏蔽掉这个消息,可以在启动类中,加一个静态代码块 static { System.getProperties().put("druid.mysql.usePingMethod", "false"); }再次启动测试,间隔60s以上请求
2024年07月04日
911 阅读
0 评论
0 点赞
2024-07-03
DataGrip连接国产数据库
我们前面的文章在介绍Navicat Premium Lite 17 初体验的时候,Navicat其中一个令人诟病的地方就是不支持国产数据库。如果使用DataGrip的话就不存在这个问题了。下面我们介绍一下使用DataGrip连接国产数据库的方式。其实,DataGrip与IntelliJ IDEA出自一家公司,所以可以猜测,DataGrip连接数据库基本上就是基于JDBC的方式,只要国产数据库提供了JDBC驱动(我相信都有),我们都是可以连接的。下面我们以国产达梦数据库进行说明,其他像神通、人大金仓等国产数据库,原理都是一样的。壹、准备达梦JDBC驱动在达梦官网下载JDBC驱动https://eco.dameng.com/eco-file-server/file/eco/download/20221215101545VDTCQHAM5JD0C86Q5D我们重点关注的是DmJdbcDriver18.jar为了统一管理,建议将jar移动到DataGrip的驱动文件夹内,比如我Mac电脑位置在/Users/用户名/Library/Application Support/JetBrains/DataGrip2024.1/jdbc-drivers 其中用户名为你Mac的用户名称 DataGrip2024.1版本号可以在里面建个文件夹,比如我创建的是DM,并将jar移动进去贰、DataGrip配置驱动打开DataGrip,点击➕,选择Driver可以修改Name,然后点➕,选址Custom JARS... 找到我们的驱动,加载进来Class选择dm.jdbc.driver.DmDriver可以配置URL templatesjdbc:dm://{host::localhost}?[:{port::5236}][/DataBaseName?schema={database}] 配置完成后,点[OK]叁、配置连接配置完驱动后,再次点➕,选择Data Source,然后选择我们创建的达梦主要配置以下信息Host:IP或域名User:用户名Password:密码Database:数据库名配置完成后点【Test Connection】,如果配置的没问题,会提示【Succeeded】
2024年07月03日
308 阅读
0 评论
0 点赞
2024-07-03
Spring Boot动态修改刷新application.yaml文件
YAML(YAML Ain't Markup Language)是一种数据序列化格式,设计用于人类阅读和编写。它在功能上类似于JSON,但在表达复杂的数据结构时更为灵活和强大。YAML 的语法简洁明了,可以轻松地表示嵌套的列表和字典。以下是一些YAML的特点:易于阅读:YAML 的设计使其非常易于阅读和理解。例如,使用缩进而不是括号或大括号来表示层级关系,这使得YAML文件看起来更像自然语言。支持复杂的数据类型:YAML 可以表示复杂的嵌套数据结构,包括数组、字典和自定义类型。可扩展性:YAML 支持自定义标签,允许你定义自己的数据类型和表示形式。广泛的应用:YAML 被广泛用于配置文件,因为它的语法比其他格式如XML或JSON更容易编写和维护。此外,YAML 也常用于数据交换和存储。多语言支持:YAML 有多种编程语言的库支持,包括Python、Ruby、JavaScript等,这使得在不同环境中使用YAML变得容易。有时候我们可能在系统运行过程中,动态修改application.yaml的内容,并且在修改内容后,加载最新内容。为了修改application.yaml内容,我们可以借助snakeyaml进行修改,借助spring-cloud-context的ContextRefresher实现上下文的刷新。[alt type="warning"]借助ContextRefresher刷新有个限制。Spring Boot不能高于2.4.0版本,因为去掉了ConfigurationBeanFactoryMetadata,否则会提示下面的报错[/alt]java.lang.ClassNotFoundException: org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata壹、添加依赖添加依赖的时候,一定要注意版本,版本不兼容,会出现这个报错。<properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.6.13</spring-boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-context</artifactId> <version>2.1.2.RELEASE</version> </dependency> </dependencies>贰、增加配置文件用于测试application.yaml# 测试修改application.yaml application-modify: name: 测试叁、增加配置类,用于读取配置文件ApplicationModifyConfiguration.javapackage net.xiangcaowuyu.application.modify.config; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * description: * * @author: Laughing * DateTime: 2024-07-03 10:01 */ @Component @ConfigurationProperties(prefix = "application-modify") @Getter @Setter public class ApplicationModifyConfiguration { private String name; }肆、实际修改与读取配置文件代码package net.xiangcaowuyu.application.modify.controller; import lombok.extern.slf4j.Slf4j; import net.xiangcaowuyu.application.modify.config.ApplicationModifyConfiguration; import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.util.ClassUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.yaml.snakeyaml.Yaml; import javax.annotation.Resource; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.util.Map; import java.util.Objects; /** * description: * * @author: Laughing * DateTime: 2024-07-03 10:05 */ @RestController @RequestMapping("application/yaml") @Slf4j public class ApplicationModifyController { @Resource private ApplicationModifyConfiguration applicationModifyConfiguration; @Resource private ContextRefresher contextRefresher; @GetMapping("/modify") @SuppressWarnings({"unchecked"}) public void modifyApplicationYaml() throws IOException { String path = Objects.requireNonNull(Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource("application.yaml")).getPath(); Yaml yaml = new Yaml(); FileWriter fileWriter = null; try (FileInputStream fileInputStream = new FileInputStream(path)) { Map<String, Object> yamlMap = yaml.load(fileInputStream); Map<String,Object> applicationModifyMap = (Map<String, Object>) yamlMap.get("application-modify"); applicationModifyMap.put("name", "张三"); fileWriter = new FileWriter(path); fileWriter.write(yaml.dumpAsMap(yamlMap)); fileWriter.flush(); // 刷新配置文件 contextRefresher.refresh(); } catch (Exception exception) { log.error(exception.getLocalizedMessage()); } finally { log.info("finally"); if (fileWriter != null) { fileWriter.close(); } } } @GetMapping("/get") public String getApplicationYaml() { // 使用刷新后的配置 return applicationModifyConfiguration.getName(); } }修改完成后,可以请求测试一下
2024年07月03日
478 阅读
0 评论
0 点赞
2024-07-02
Navicat Premium Lite 17 初体验
在Navicat的最新版本17中,Navicat终于退出了免费版本Navicat Premium Lite 17Navicat Premium Lite 是 Navicat 的精简版,它包含了用户执行主要的基本数据库操作所需的核心功能。它允许你同时连接到各种数据库平台,包括 MySQL、PostgreSQL、SQL Server、Oracle、MariaDB,以及 Redis 和 MongoDB 等NoSQL 数据库,所有这些操作都可以通过一个应用程序来完成。Navicat Premium Lite 可用于商业和非商业目的,使其成为从小型企业到个体开发者等用户的实用选择。Navicat支持的数据库种类很多,多到一屏放不下,像国产数据库Gauss DB、Ocean Base也都默认支持了。但是像神通、达梦、瀚高等国产数据库仍然不支持,所以有XC要求的还是不太适用。最近也下载试用了一下,如果只是用来简单的查询还可以,如果真的作为日常的数据库连接工具,感觉还是算了,各种功能都限制企业版才能使用。甚至说美话Sql、查看单行记录我认为这种基本功能都要企业版才能使用。
2024年07月02日
399 阅读
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日
309 阅读
0 评论
0 点赞
2024-06-30
常见的后端性能优化方法
软件的响应速度和稳定性直接影响到用户的满意度和留存率。如果一个应用加载缓慢或频繁卡顿,用户可能会选择卸载并转向竞争对手的产品。良好的性能是保证用户界面流畅、操作响应快速的基础,有助于提升用户粘性和正面评价。本文不是教条式的指导,比如优化索引、重构代码等等这种形而上学的东西,而是重在动手实践,根据个人在日常开发中遇到的性能问题,通过具体的手段进行优化,比如加索引,到底索引加在哪,或者重构代码,应该在什么位置重构,代码应该怎么写。一、通过索引优化性能在数据库中加索引,能够优化查询、更新、删除的性能,但是索引并不是越多越好,这个还是需要根据实际情况的,举个例子,我们如果有如下的Sqlselect email,phone from user where name = '张三' 上面的sql很简单,查询姓名是张三的手机号、邮箱,因为我们需要通过name进行过滤,所以我们可以给name添加一个索引,避免触发全标扫描。二、留意隐式转换导致的性能问题隐式转换是指在进行查询、比较、计算或数据连接等操作时,如果涉及到的操作数具有不同的数据类型,数据库管理系统(DBMS)会自动将其中一个或多个操作数的数据类型转换为兼容的类型,以便这些操作可以顺利完成。这种转换过程是自动发生的,无需用户在查询语句中显式指定转换类型。这是一个在开发中不太容易犯错,但是一点出现问题极不容易发现的点。比如我们有一个表sys_dict_data其中有一个排序字段dict_sort,因为是排序字段,我们定义成int类型。如果我们代码里面写了下面一段Sqlselect dict_value, dict_label from sys_dict_data where dict_type = 'car_usage_nature' and dict_sort >= '3'这个Sql是完全正确的,但是有一个问题,我们在where条件里面写的是dict_sort >= '3',也就是字符串'3',这样就导致了一个隐式转换。所以我们应该正确写dict_sort >= 3三、避免大事务在Spring Boot开发中,我们经常使用@Transactional进行事务注解,因为注解是基于切面的,所以在方法开始就会开启事务,如果我们方法内部逻辑比较复杂,相对的事务就会比较长。举个不太恰当的栗子,我们有个需求是从数据库A表查询数据,查出来之后,对数据进行加工,加工之后,将数据插入到B表。数据查询方法比如是selectMethod()、数据计算方法是computeMethod()、数据插入方法是insertMethod(),如果我们有以下代码@Transactonal(rollbackFor = Exception.class) public void baseMethod(){ selectMethod(); computeMethod(); insertMethod(); }在selectMethod()方法中,因为我们只是查询数据,所以是不需要事务的,比如上面的方法,我们实际上就把事务给放大了。为了减小事务,我们可以把selectMethod()、computeMethod()放到事务外面。public void baseMethod(){ selectMethod(); computeMethod(); } @Transactonal(rollbackFor = Exception.class) public void insertMethod(){ }当然,因为事务是基于切面的,我们需要注意需要在外面调用insertMethod()避免事务失效。四、减少数据库查询数据这个如果是平时开发可能遇到的比较少,但是基于敏捷开发平台的遇到的会相对多点。比如我们现在的敏捷开发平台,平时创建表单时,会基于模型,但是因为表单涉及增删改查,所以模型上会有所有的表、字段。有时候与别的模块对接,为了省事,直接使用模型取数,但是对接过程中,我们只使用主表数据,子表取数就白白的消耗掉了性能。这种情况其实很好解决,我们在取数时,做到按需取数即可。五、减少重复取数这种情况,我觉得一般出现在取系统参数的过程中。在系统不断的迭代过程中,我们不断增加新的参数,如果增加一个参数就取一遍数据,也会导致大量的sql。针对这种情况,我们可以提取一个公共方法,将要获取的参数都放里面,尽量一次性取出来,并对参数进行缓存。避免重复从数据库取数。六、使用Redis缓存针对全局参数、一些配置参数等,因为改动很少,我们可以借助Redis对数据进行缓存。在获取参数时,先从Redis获取,如果获取到直接返回,如果获取不到,再从数据库获取,并将获取到的数据缓存到Redis中,在参数保存时,清掉Redis缓存的数据。七、合理使用order byorder by是一个成本很高的操作,如果非必要尽量不要使用order by,如果需要使用order by,尽可能在order by子句使用索引字段,减少排序的成本。八、减少使用临时表这里说的临时表,指的不仅仅是全局临时表也包括实表临时表(也就是一个表当临时表用),临时表如果使用不当,比如频繁地创建和销毁临时表,或者临时表设计不合理导致索引缺失,可能降低整体的数据库性能。另外,现在数据库种类很多,不同数据库对临时表的语法不尽相同,会增加程序移植的复杂度。涉及使用临时表的地方,可以考虑使用left join或inner join代替。九、关于exists关键字在我们的开发思维中,我们一般认为在大数据量时,exists的性能要比in的性能高,但是在国产神通数据库中,exists的性能却是特别的差(当然in也不高)。针对这种,我们可能需要针对特定数据库对sql进行优化,像神通数据库,我们可以改成left join并判断主键是否是null。十、关于or关键字当一个字段只有某几个值时,我们可能会这样查询select * from table where filed = '1' or filed = '2'针对这种,我们可以尝试使用union allselect * from table where filed = '1' union all select * from table where filed = '2'十一、串行改并行当函数串行时,整个计算过程耗时是所有函数耗时的和,改成并行后,理论上,耗时是最慢的一个函数的耗时。比如A调用B\C\D三个方法,其中B方法耗时1s,C方法耗时1.5s,D方法耗时2s,如果串行的话,不考虑A自身耗时,那么调用B\C\D的耗时为4.5s = 1s + 1.5s + 2s,如果我们改成并行,那么理论上耗时为 2s,也就是耗时最长的D方法的耗时。当然,如果涉及到多线程之间的同步,我们可以借助CountDownLatch等并发工具类,实现线程之间的同步。下面是一个简单的示例。@Slf4j public class CountDownLatchCase1 { public static void main(String[] args) throws InterruptedException { // 创建 CountDownLatch 对象,需要等待 3 个线程完成任务 CountDownLatch latch = new CountDownLatch(3); // 创建 3 个线程 Worker worker1 = new Worker(latch, "worker1"); Worker worker2 = new Worker(latch, "worker2"); Worker worker3 = new Worker(latch, "worker3"); // 启动 3 个线程 worker1.start(); worker2.start(); worker3.start(); // 等待 3 个线程完成任务 latch.await(); // 所有线程完成任务后,执行下面的代码 log.info("All workers have finished their jobs!"); } } class Worker extends Thread { private final CountDownLatch latch; public String name; public Worker(CountDownLatch latch, String name) { this.latch = latch; this.name = name; } @Override public void run() { try { // 模拟任务耗时 TimeUnit.MILLISECONDS.sleep(1000); log.info("{} has finished the job!", name); } catch (InterruptedException e) { log.error(e.getMessage(), e); } finally { // 一定要保证每个线程执行完毕或者异常后调用countDown()方法 // 如果不调用会导致其他线程一直等待, 无法继续执行 // 建议放在finally代码块中, 防止异常情况下未调用countDown()方法 latch.countDown(); } } }十二、分库分表当数据库中表的数据量过大时,可以考虑分库分表。比如每个月的数据量都比较大,我们考虑按月或者按年进行分表,每个月或者每年一张表。当然分库分表会增加后续查询的复杂度。十三、分页其实原本是不打算写这条的,因为分页是一个程序必备的东西,后来想想还是加上了,其实不管自己写Sql也好,各个ORM框架也都提供了分页的组件。
2024年06月30日
377 阅读
0 评论
0 点赞
2024-06-30
UniApp开发小程序生成海报
具体实现效果就是上面这个图片。里面的元素都是自己根据自己实际情况拼接进去的。在开发小程序时,我们经常会遇到【分享】这个功能,在实现分享功能时,我们一般会基于当前页面,生成一个海报进行分享。在实现时,基本上是通过canvas画一个图片,然后生成一个图片,具体的可以看下面的代码。视图代码<!-- 报价按钮 --> <view :style="{marginTop: '30rpx'}"> <view class="container-price"> <view class="container-price-left"> <view style="display: flex;flex-direction: column;align-items: center;margin-left: 30rpx;" @click="shareing"> <view> <image src="../../static/img/share.svg" style="height: 46rpx;width: 46rpx;"></image> </view> <view style="font-size: 16rpx;height: 30rpx;">分享</view> </view> <view style="display: flex;flex-direction: column;align-items: center;margin-left: 30rpx;" @click="subscribeMsg4Bid"> <view> <image src="../../static/img/subscribe.svg" style="height: 46rpx;width: 46rpx;"> </image> </view> <view style="font-size: 16rpx;height: 30rpx;">订阅</view> </view> </view> </view> </view> <!-- 生成海报 --> <canvas v-if="ifGeneratingPosters" :style="{height: pupopHeight + 'px',width: pupopWidth + 'px'}" canvas-id="myCanvas"></canvas> <uni-popup ref="popupPosters" type="bottom" border-radius="10px 10px 0 0"> <view class="popup-posters-wrap"> <image :src="posterImg" mode="aspectFill" :style="{height:'420px',width:'100%'}"> </image> <view class="popup-footer" :style="{marginBottom : (0-safeAreaHeight) + 'px'}"> <view style="text-align: center;font-size: 20rpx;">立即分享</view> <view style="display: flex;flex-direction: row;font-size: 20rpx;margin: 0 20rpx;"> <view style="display: flex;flex-direction: column;align-items: center;"> <button class="noneButton" data-name="shareBtn" open-type="share"> <u-icon size="80" color="#03de6d" name="weixin-circle-fill"></u-icon> </button> <view>微信好友</view> </view> <view style="display: flex;flex-direction: column;align-items: center;margin-left: 20rpx;"> <view @click="saveToLocal"> <u-icon size="80" color="#18a5f0" name="photo-fill"></u-icon> </view> <view>保存海报</view> </view> </view> <view :style="{bottom:(safeAreaHeight+5)+'px',left:'50%',position:'fixed',fontSize:'24rpx',transform:'translate(-50%,-50%)',padding:'5rpx 15rpx'}" @click="this.$refs.popupPosters.close()"> <view>取消</view> </view> </view> </view> </uni-popup>js代码//生成海报--微信端 createPoster() { let _this = this const canvasId = "myCanvas" let ctx = uni.createCanvasContext(canvasId, _this) // 自定义组件中 一定要传this ,这里一开始没加,困惑很久,一定要写一下 // 填充背景 ctx.setFillStyle('#FFFFFF') ctx.fillRect(0, 0, _this.pupopWidth, _this.pupopHeight); ctx.save() //生成车辆图册 uni.getImageInfo({ src: _this.imageFileList[0], //这里的banner是展示的商品图 success(image) { let bannerW = _this.pupopWidth let bannerH = 400 * _this.pixelRatio let bannerX = 0 * _this.pixelRatio let bannerY = 20 * _this.pixelRatio // 将banner到画布上 ctx.drawImage(image.path, bannerX, bannerY, bannerW, bannerH) ctx.restore() ctx.save() //车辆描述 ctx.setFontSize(18 * _this.pixelRatio) ctx.setFillStyle("#000") let bannerTextX = 20 * _this.pixelRatio let bannerTextY = bannerY + bannerH + 20 * _this.pixelRatio + 20 let title = _this.inquiry.vehicleBrand + _this.inquiry.vehicleSeries + _this.inquiry.vehicleYear + _this.inquiry.vehicleModel if (title.length > 20) { title = title.slice(0, 20) + '...' } ctx.fillText(title, bannerTextX, bannerTextY, _this.pupopWidth); // 第二行文字 ctx.setFontSize(14 * _this.pixelRatio) ctx.setFillStyle("#cb171d") //事故类型 let titleAccidentType = _this.inquiry.accidentTypeName ctx.fillText(titleAccidentType, bannerTextX, bannerTextY + 30 * _this.pixelRatio, _this .pupopWidth); let accidentTypeWidth = ctx.measureText(titleAccidentType).width //所有人性质 let vehicleOwnerNature = _this.inquiry.vehicleOwnerNatureName ctx.fillText(vehicleOwnerNature, bannerTextX + accidentTypeWidth + 5 * _this.pixelRatio, bannerTextY + 30 * _this.pixelRatio, _this .pupopWidth); let vehicleOwnerNatureWidth = ctx.measureText(vehicleOwnerNature).width // 使用性质 let vehicleUsageNature = _this.inquiry.vehicleUsageNatureName ctx.fillText(vehicleUsageNature, bannerTextX + accidentTypeWidth + 5 * _this.pixelRatio + vehicleOwnerNatureWidth + 5 * _this.pixelRatio, bannerTextY + 30 * _this.pixelRatio, _this .pupopWidth); let vehicleUsageNatureWidth = ctx.measureText(vehicleUsageNature).width // 初登日期及地方位置 ctx.setFontSize(12 * _this.pixelRatio) ctx.setFillStyle("#b3b3b3") let vehicleRegisterDate = '初登日期 ' + _this.inquiry.vehicleRegisterDate let position = '停放地 ' + _this.inquiry.parkingPositionProvinceName + _this.inquiry .parkingPositionCityName let vehicleRegisterDateWithPosition = vehicleRegisterDate + " / " + position ctx.fillText(vehicleRegisterDateWithPosition, bannerTextX, bannerTextY + 30 * _this.pixelRatio + 20 * _this.pixelRatio, _this.pupopWidth); let vehicleRegisterDateWithPositionWidth = ctx.measureText(vehicleRegisterDateWithPosition) .width //画间隔线 ctx.moveTo(40 * _this.pixelRatio, bannerTextY + 20 * _this.pixelRatio + 20 * _this.pixelRatio + 20 * _this.pixelRatio); //设置起点状态 ctx.lineTo(_this.pupopWidth - 40 * _this.pixelRatio, bannerTextY + 20 * _this.pixelRatio + 20 * _this.pixelRatio + 20 * _this.pixelRatio); //设置末端状态 ctx.lineWidth = 1 * _this.pixelRatio; //设置线宽状态 ctx.strokeStyle = '#EEEEEE'; //设置线的颜色状态 ctx.stroke(); //二维码 uni.getImageInfo({ src: 'https://oss.guochewang.cn/banner/app/wechat_app.jpg', success(res) { // 画当前页面的二维码 const img_x = 20 * _this.pixelRatio const img_w = 60 * _this.pixelRatio const img_y = bannerTextY + 20 * _this.pixelRatio + 20 * _this.pixelRatio + 20 * _this.pixelRatio + 20 * _this.pixelRatio ctx.drawImage(res.path, img_x, img_y, img_w, img_w) //画提示文字 const tiptextX = img_x + img_w + 20 * _this.pixelRatio const tiptext1Y = img_y + 20 * _this.pixelRatio const tiptext2Y = img_y + 20 * _this.pixelRatio + 20 * _this .pixelRatio + 12 * _this.pixelRatio const tiptext1 = '国车网 ' const tiptext1_1 = '一个专注事故车的交易平台 ' const tiptext2 = '识别二维码查看车辆更多详情' ctx.setFontSize(16 * _this.pixelRatio) ctx.setFillStyle("#cb171d") ctx.fillText(tiptext1, tiptextX, tiptext1Y); ctx.stroke(); let tiptext1Width = ctx.measureText(tiptext1).width ctx.setFontSize(12 * _this.pixelRatio) ctx.setFillStyle("#333333") ctx.fillText(tiptext1_1, tiptextX + tiptext1Width, tiptext1Y); ctx.stroke(); ctx.setFontSize(12 * _this.pixelRatio) ctx.setFillStyle("#333333") ctx.fillText(tiptext2, tiptextX, tiptext2Y); ctx.stroke(); ctx.draw(false, () => { uni.canvasToTempFilePath({ width: _this.pupopWidth, height: _this.pupopHeight, destWidth: _this.pupopWidth, destHeight: _this.pupopHeight, canvasId: canvasId, fileType: 'png', quality: 1, success: function(res) { _this.posterImg = res .tempFilePath; //最终将canvas转换为图片 _this.$refs.popupPosters .open(); uni.hideLoading() _this.ifGeneratingPosters = false }, fail(error) { console.log('4', error) uni.showToast({ title: '生成海报失败,请稍后重试!' }) setTimeout(() => { uni.hideLoading() _this.ifGeneratingPosters = false }, 2000) } }, _this) }) }, fail(error) { console.log('获取二维码失败', error) uni.showToast({ title: '生成海报失败,获取二维码失败' }) setTimeout(() => { uni.hideLoading() _this.ifGeneratingPosters = false }, 2000) } }) }, fail(error) { console.log('生成商品图失败', error) uni.showToast({ title: '生成海报失败,获取商品图失败' }) setTimeout(() => { uni.hideLoading() _this.ifGeneratingPosters = false }, 2000) } }); }, //将图片保存到本地相册 saveToLocal() { //#ifdef MP-WEIXIN uni.saveImageToPhotosAlbum({ filePath: this.posterImg, success: () => { uni.showToast({ icon: 'success', title: '保存到相册成功' }) this.$refs.popupPosters.close() }, fail: (err) => { console.log("保存到相册失败", err) } }); //#endif },
2024年06月30日
549 阅读
0 评论
0 点赞
2024-06-30
Office 2021 for Mac 各版本破解版下载
建议直接下载16.82版本,里面有安装包及激活工具。Office 2021 for Mac介绍Microsoft Office 2021 for Mac 是专为苹果Mac电脑设计的一套办公软件,包含了Word、Excel、PowerPoint、Outlook等经典办公应用程序的最新版本。这一版本聚焦于提升用户体验、增加生产力工具,并且优化了Mac平台的性能表现。以下是Office 2021 for Mac的一些主要特点和更新:兼容性和性能优化:针对MacOS进行了优化,确保软件在包括搭载M1芯片的Mac上都能流畅运行,同时提高了启动速度和整体响应能力。实时协作功能:允许用户与他人同时编辑Word文档、Excel电子表格或PowerPoint演示文稿,提升了团队合作效率。新增和改进的功能:引入了新的数据类型、函数、翻译工具和编辑工具,使得数据分析和内容创建更加高效。例如,在Excel中增加了动态数组和新的图表类型,Word和PowerPoint则获得了更强大的排版和设计工具。深色模式支持:全面支持MacOS的深色模式,提供更加统一和舒适的视觉体验。永久授权模式:作为Office的一个非订阅版本,购买后可永久使用,无需持续付费,对于不希望采用订阅制的用户来说是一个不错的选择。安全性增强:更新了安全性和隐私保护措施,确保用户的数据安全。跨平台兼容性:文档和项目可以在不同平台(包括Windows、Mac、Web和移动设备)之间无缝切换和协作。安装过程如果你下载的是16.82版本,下载完成后,直接双击setup.pkg安装即可。当然,安装过程中,可以选择自己需要的组件进行安装,比如只安装Word、Excel、Power Point。激活如果你下载的是16.82版本,双击Crack2021.dmg,然后双击LTSC_2021_VL_Serializer.pkg,按照指引一路继续即可。下载链接: https://pan.baidu.com/s/1a1YV7h5GlkBYGtcG7VsQxA?pwd=2kxb 提取码: 2kxb[tag type="success"]如果链接失效,可以留言通知我更新,感谢[/tag]
2024年06月30日
430 阅读
0 评论
0 点赞
1
2
3
...
48