当前位置: 首页 > news >正文

鸿蒙OSUniApp开发富文本编辑器组件#三方框架 #Uniapp

使用UniApp开发富文本编辑器组件

富文本编辑在各类应用中非常常见,无论是内容创作平台还是社交软件,都需要提供良好的富文本编辑体验。本文记录了我使用UniApp开发一个跨平台富文本编辑器组件的过程,希望对有类似需求的开发者有所启发。

背景

前段时间接到一个需求,要求在我们的跨平台应用中加入富文本编辑功能,支持基础的文本格式化、插入图片、链接等功能。考虑到项目使用UniApp开发,需要兼容多个平台,市面上现成的富文本编辑器要么不支持跨平台,要么功能过于复杂。于是我决定自己动手,开发一个功能适中、性能良好的富文本编辑器组件。

技术选型

为何不直接使用现有组件?

首先,我调研了几个流行的富文本编辑器:

  1. quill.js - 功能强大,但在小程序环境中存在兼容性问题
  2. wangeditor - 针对Web端优化,小程序支持不佳
  3. mp-html - 专注于小程序,但编辑功能有限

UniApp官方提供的rich-text组件只具备富文本展示能力,不支持编辑。所以最终决定基于原生能力自己封装一个轻量级的富文本编辑器组件。

核心技术点

  • 使用uni.createSelectorQuery获取DOM节点
  • 基于contenteditable特性实现编辑功能
  • 自定义文本选区和格式化操作
  • 跨平台样式处理
  • 图片上传和展示

开发实现

1. 创建基础组件结构

首先,我们需要创建一个基础的编辑器组件结构:

<template><view class="rich-editor"><view class="toolbar"><view v-for="(item, index) in tools" :key="index"class="tool-item":class="{active: activeFormats[item.format]}"@tap="handleFormat(item.format, item.value)"><text class="iconfont" :class="item.icon"></text></view></view><!-- 编辑区域 --><view class="editor-container":style="{ height: editorHeight + 'px' }"><viewclass="editor-body"contenteditable="true"@input="onInput"@blur="onBlur"@focus="onFocus"id="editor"ref="editor"></view></view><!-- 底部工具栏 --><view class="bottom-tools"><view class="tool-item" @tap="insertImage"><text class="iconfont icon-image"></text></view><view class="tool-item" @tap="insertLink"><text class="iconfont icon-link"></text></view></view></view>
</template><script>
export default {name: 'RichEditor',props: {value: {type: String,default: ''},height: {type: Number,default: 300},placeholder: {type: String,default: '请输入内容...'}},data() {return {editorHeight: 300,editorContent: '',selectionRange: null,activeFormats: {bold: false,italic: false,underline: false,strikethrough: false,alignLeft: true,alignCenter: false,alignRight: false},tools: [{ format: 'bold', icon: 'icon-bold', value: 'bold' },{ format: 'italic', icon: 'icon-italic', value: 'italic' },{ format: 'underline', icon: 'icon-underline', value: 'underline' },{ format: 'strikethrough', icon: 'icon-strikethrough', value: 'line-through' },{ format: 'alignLeft', icon: 'icon-align-left', value: 'left' },{ format: 'alignCenter', icon: 'icon-align-center', value: 'center' },{ format: 'alignRight', icon: 'icon-align-right', value: 'right' }]}},created() {this.editorHeight = this.heightthis.editorContent = this.value},mounted() {this.initEditor()},methods: {initEditor() {const editor = this.$refs.editorif (editor) {editor.innerHTML = this.value || `<p><br></p>`}// 设置placeholderif (!this.value && this.placeholder) {this.$nextTick(() => {editor.setAttribute('data-placeholder', this.placeholder)})}},// 监听输入onInput(e) {// 获取当前内容this.editorContent = e.target.innerHTMLthis.$emit('input', this.editorContent)this.saveSelection()},// 保存当前选区saveSelection() {const selection = window.getSelection()if (selection.rangeCount > 0) {this.selectionRange = selection.getRangeAt(0)}},// 恢复选区restoreSelection() {if (this.selectionRange) {const selection = window.getSelection()selection.removeAllRanges()selection.addRange(this.selectionRange)return true}return false},// 处理格式化handleFormat(format, value) {// 恢复选区if (!this.restoreSelection()) {console.log('No selection to format')return}// 根据不同格式执行不同操作switch(format) {case 'bold':case 'italic':case 'underline':case 'strikethrough':document.execCommand(format, false, null)breakcase 'alignLeft':case 'alignCenter':case 'alignRight':document.execCommand('justify' + format.replace('align', ''), false, null)breakdefault:console.log('未知格式:', format)}// 更新激活状态this.checkActiveFormats()// 触发内容变化this.editorContent = this.$refs.editor.innerHTMLthis.$emit('input', this.editorContent)},// 检查当前激活的格式checkActiveFormats() {this.activeFormats.bold = document.queryCommandState('bold')this.activeFormats.italic = document.queryCommandState('italic')this.activeFormats.underline = document.queryCommandState('underline')this.activeFormats.strikethrough = document.queryCommandState('strikethrough')const alignment = document.queryCommandValue('justifyLeft') ? 'alignLeft' :document.queryCommandValue('justifyCenter') ? 'alignCenter' :document.queryCommandValue('justifyRight') ? 'alignRight' : 'alignLeft'this.activeFormats.alignLeft = alignment === 'alignLeft'this.activeFormats.alignCenter = alignment === 'alignCenter'this.activeFormats.alignRight = alignment === 'alignRight'},// 焦点事件onFocus() {this.saveSelection()this.checkActiveFormats()},onBlur() {this.saveSelection()},// 插入图片insertImage() {uni.chooseImage({count: 1,success: (res) => {const tempFilePath = res.tempFilePaths[0]// 上传图片this.uploadImage(tempFilePath)}})},// 上传图片uploadImage(filePath) {// 这里应该是实际的上传逻辑uni.showLoading({ title: '上传中...' })// 模拟上传过程setTimeout(() => {// 假设这是上传后的图片URLconst imageUrl = filePath// 恢复选区并插入图片this.restoreSelection()document.execCommand('insertHTML', false, `<img src="${imageUrl}" style="max-width:100%;" />`)// 更新内容this.editorContent = this.$refs.editor.innerHTMLthis.$emit('input', this.editorContent)uni.hideLoading()}, 500)},// 插入链接insertLink() {uni.showModal({title: '插入链接',editable: true,placeholderText: 'https://',success: (res) => {if (res.confirm && res.content) {const url = res.content// 恢复选区this.restoreSelection()// 获取选中的文本const selection = window.getSelection()const selectedText = selection.toString()// 如果有选中文本,将其设为链接文本;否则使用URL作为文本const linkText = selectedText || url// 插入链接document.execCommand('insertHTML', false, `<a href="${url}" target="_blank">${linkText}</a>`)// 更新内容this.editorContent = this.$refs.editor.innerHTMLthis.$emit('input', this.editorContent)}}})},// 获取编辑器内容getContent() {return this.editorContent},// 设置编辑器内容setContent(html) {this.editorContent = htmlif (this.$refs.editor) {this.$refs.editor.innerHTML = html}this.$emit('input', html)}}
}
</script><style>
.rich-editor {width: 100%;border: 1rpx solid #eee;border-radius: 10rpx;overflow: hidden;
}.toolbar {display: flex;flex-wrap: wrap;padding: 10rpx;border-bottom: 1rpx solid #eee;background-color: #f8f8f8;
}.tool-item {width: 80rpx;height: 80rpx;display: flex;justify-content: center;align-items: center;font-size: 40rpx;color: #333;
}.tool-item.active {color: #007AFF;background-color: rgba(0, 122, 255, 0.1);border-radius: 8rpx;
}.editor-container {width: 100%;overflow-y: auto;
}.editor-body {min-height: 100%;padding: 20rpx;font-size: 28rpx;line-height: 1.5;outline: none;
}.editor-body[data-placeholder]:empty:before {content: attr(data-placeholder);color: #999;font-style: italic;
}.bottom-tools {display: flex;padding: 10rpx;border-top: 1rpx solid #eee;background-color: #f8f8f8;
}/* 引入字体图标库 (需要自行配置) */
@font-face {font-family: 'iconfont';src: url('data:font/woff2;charset=utf-8;base64,...') format('woff2');
}
.iconfont {font-family: "iconfont" !important;font-style: normal;
}
</style>

2. 处理平台差异

UniApp支持多个平台,但在富文本编辑方面存在平台差异,特别是小程序限制较多。下面是一些关键的跨平台适配处理:

// 跨平台选区处理
saveSelection() {// #ifdef H5const selection = window.getSelection()if (selection.rangeCount > 0) {this.selectionRange = selection.getRangeAt(0)}// #endif// #ifdef MP-WEIXIN// 微信小程序不支持DOM选区,需使用特殊方法this.getEditContext().getSelectionRange({success: (res) => {this.selectionRange = res}})// #endif
},// 获取编辑器上下文(微信小程序)
getEditContext() {// #ifdef MP-WEIXINreturn this.editorCtx || wx.createSelectorQuery().in(this).select('#editor').context(res => {this.editorCtx = res.context}).exec()// #endifreturn null
}

3. 增强图片处理能力

富文本编辑器的一个关键功能是图片处理,我们需要增强这方面的能力:

// 增强版图片上传处理
uploadImage(filePath) {uni.showLoading({ title: '上传中...' })// 压缩图片uni.compressImage({src: filePath,quality: 80,success: res => {const compressedPath = res.tempFilePath// 上传到服务器uni.uploadFile({url: 'https://your-upload-endpoint.com/upload',filePath: compressedPath,name: 'file',success: uploadRes => {try {const data = JSON.parse(uploadRes.data)const imageUrl = data.url// 插入图片this.insertImageToEditor(imageUrl)} catch (e) {uni.showToast({title: '上传失败',icon: 'none'})}},fail: () => {uni.showToast({title: '上传失败',icon: 'none'})},complete: () => {uni.hideLoading()}})},fail: () => {// 压缩失败,使用原图this.doUploadFile(filePath)}})
},// 插入图片到编辑器
insertImageToEditor(imageUrl) {// #ifdef H5this.restoreSelection()document.execCommand('insertHTML', false, `<img src="${imageUrl}" style="max-width:100%;" />`)// #endif// #ifdef MP-WEIXINthis.getEditContext().insertImage({src: imageUrl,width: '100%',success: () => {console.log('插入图片成功')}})// #endif// 更新内容this.$nextTick(() => {// #ifdef H5this.editorContent = this.$refs.editor.innerHTML// #endif// #ifdef MP-WEIXINthis.getEditContext().getContents({success: res => {this.editorContent = res.html}})// #endifthis.$emit('input', this.editorContent)})
}

4. 实现HTML与富文本互转

编辑器需要支持HTML格式的导入导出,以便存储和展示:

// HTML转富文本对象
htmlToJson(html) {const tempDiv = document.createElement('div')tempDiv.innerHTML = htmlconst parseNode = (node) => {if (node.nodeType === 3) { // 文本节点return {type: 'text',text: node.textContent}}if (node.nodeType === 1) { // 元素节点const result = {type: node.nodeName.toLowerCase(),children: []}// 处理元素属性if (node.attributes && node.attributes.length > 0) {result.attrs = {}for (let i = 0; i < node.attributes.length; i++) {const attr = node.attributes[i]result.attrs[attr.name] = attr.value}}// 处理样式if (node.style && node.style.cssText) {result.styles = {}const styles = node.style.cssText.split(';')styles.forEach(style => {if (style.trim()) {const [key, value] = style.split(':')if (key && value) {result.styles[key.trim()] = value.trim()}}})}// 递归处理子节点for (let i = 0; i < node.childNodes.length; i++) {const childResult = parseNode(node.childNodes[i])if (childResult) {result.children.push(childResult)}}return result}return null}const result = []for (let i = 0; i < tempDiv.childNodes.length; i++) {const nodeResult = parseNode(tempDiv.childNodes[i])if (nodeResult) {result.push(nodeResult)}}return result
},// 富文本对象转HTML
jsonToHtml(json) {if (!json || !Array.isArray(json)) return ''const renderNode = (node) => {if (node.type === 'text') {return node.text}// 处理元素节点let html = `<${node.type}`// 添加属性if (node.attrs) {Object.keys(node.attrs).forEach(key => {html += ` ${key}="${node.attrs[key]}"`})}// 添加样式if (node.styles) {let styleStr = ''Object.keys(node.styles).forEach(key => {styleStr += `${key}: ${node.styles[key]};`})if (styleStr) {html += ` style="${styleStr}"`}}html += '>'// 处理子节点if (node.children && node.children.length > 0) {node.children.forEach(child => {html += renderNode(child)})}// 关闭标签html += `</${node.type}>`return html}let result = ''json.forEach(node => {result += renderNode(node)})return result
}

实战案例:评论编辑器

下面是一个简化版的评论编辑器实现,可以在社区或博客应用中使用:

<template><view class="comment-editor"><view class="editor-title"><text>发表评论</text></view><rich-editorv-model="commentContent":height="200"placeholder="说点什么吧..."ref="editor"></rich-editor><view class="action-bar"><view class="action-btn cancel" @tap="cancel">取消</view><view class="action-btn submit" @tap="submitComment">发布</view></view></view>
</template><script>
import RichEditor from '@/components/rich-editor/rich-editor.vue'export default {components: {RichEditor},data() {return {commentContent: '',replyTo: null}},props: {articleId: {type: [String, Number],required: true}},methods: {cancel() {this.commentContent = ''this.$refs.editor.setContent('')this.$emit('cancel')},submitComment() {if (!this.commentContent.trim()) {uni.showToast({title: '评论内容不能为空',icon: 'none'})return}uni.showLoading({ title: '发布中...' })// 提交评论this.$api.comment.add({article_id: this.articleId,content: this.commentContent,reply_to: this.replyTo}).then(res => {uni.hideLoading()if (res.code === 0) {uni.showToast({title: '评论发布成功',icon: 'success'})// 清空编辑器this.commentContent = ''this.$refs.editor.setContent('')// 通知父组件刷新评论列表this.$emit('submit-success', res.data)} else {uni.showToast({title: res.msg || '评论发布失败',icon: 'none'})}}).catch(() => {uni.hideLoading()uni.showToast({title: '网络错误,请重试',icon: 'none'})})},// 回复某条评论replyComment(comment) {this.replyTo = comment.idthis.$refs.editor.setContent(`<p>回复 @${comment.user.nickname}:</p>`)this.$refs.editor.focus()}}
}
</script><style>
.comment-editor {padding: 20rpx;background-color: #fff;border-radius: 10rpx;
}.editor-title {margin-bottom: 20rpx;font-size: 32rpx;font-weight: bold;
}.action-bar {display: flex;justify-content: flex-end;margin-top: 20rpx;
}.action-btn {padding: 10rpx 30rpx;border-radius: 30rpx;font-size: 28rpx;margin-left: 20rpx;
}.cancel {color: #666;background-color: #f3f3f3;
}.submit {color: #fff;background-color: #007AFF;
}
</style>

踩坑记录

开发过程中遇到了不少坑,这里分享几个关键问题及解决方案:

1. 小程序富文本能力受限

小程序不支持通过contenteditable实现的富文本编辑,需要使用平台提供的editor组件。解决方案是使用条件编译,H5使用contenteditable,小程序使用官方editor组件。

<!-- H5编辑器 -->
<!-- #ifdef H5 -->
<div class="editor-body"contenteditable="true"@input="onInput"id="editor"ref="editor"
></div>
<!-- #endif --><!-- 小程序编辑器 -->
<!-- #ifdef MP-WEIXIN -->
<editor id="editor" class="editor-body" :placeholder="placeholder"@ready="onEditorReady"@input="onInput"
></editor>
<!-- #endif -->

2. 选区处理差异

不同平台的选区API差异很大,需要分别处理:

// 处理选区问题
getSelectionRange() {return new Promise((resolve) => {// #ifdef H5const selection = window.getSelection()if (selection.rangeCount > 0) {resolve(selection.getRangeAt(0))} else {resolve(null)}// #endif// #ifdef MP-WEIXINthis.editorCtx.getSelectionRange({success: (res) => {resolve(res)},fail: () => {resolve(null)}})// #endif})
}

3. 图片上传大小限制

多端应用中,图片上传和展示需要考虑不同平台的限制:

// 处理图片大小限制
async handleImageUpload(file) {// 检查文件大小if (file.size > 5 * 1024 * 1024) { // 5MBuni.showToast({title: '图片不能超过5MB',icon: 'none'})return null}// 压缩图片try {// H5与小程序压缩方式不同// #ifdef H5const compressedFile = await this.compressImageH5(file)return compressedFile// #endif// #ifdef MPconst compressedPath = await this.compressImageMP(file.path)return { path: compressedPath }// #endif} catch (e) {console.error('图片压缩失败', e)return file // 失败时使用原图}
}

性能优化

为了让编辑器运行更流畅,我做了以下优化:

  1. 输入防抖 - 减少频繁更新导致的性能问题
  2. 延迟加载图片 - 使用懒加载机制
  3. 减少DOM操作 - 尽量批量更新DOM
  4. 使用虚拟DOM - 在复杂场景下考虑使用Vue的虚拟DOM机制
// 输入防抖处理
onInput(e) {if (this.inputTimer) {clearTimeout(this.inputTimer)}this.inputTimer = setTimeout(() => {// #ifdef H5this.editorContent = this.$refs.editor.innerHTML// #endif// #ifdef MP-WEIXINthis.editorContent = e.detail.html// #endifthis.$emit('input', this.editorContent)}, 300)
}

总结

通过这次开发实践,我实现了一个跨平台的富文本编辑器组件,总结几点经验:

  1. 平台差异是最大挑战,需要利用条件编译提供各平台最佳实现
  2. 功能要适中,不是所有Web富文本功能都适合移动端
  3. 性能优化很重要,尤其是在低端设备上
  4. 良好的用户体验需要细节打磨,如适当的反馈、容错处理等

富文本编辑是一个复杂的课题,即使是成熟的Web编辑器也有各种问题。在移动端和小程序环境中,受限更多。我们的方案虽然不完美,但通过合理的取舍和平台适配,已经能满足大部分应用场景的需求。

后续还可以继续完善这个组件,比如添加表格支持、代码高亮、Markdown转换等高级功能。希望本文对你有所启发,欢迎在评论区交流讨论!

参考资料

  1. UniApp官方文档
  2. execCommand API参考
  3. ContentEditable详解

相关文章:

鸿蒙OSUniApp开发富文本编辑器组件#三方框架 #Uniapp

使用UniApp开发富文本编辑器组件 富文本编辑在各类应用中非常常见&#xff0c;无论是内容创作平台还是社交软件&#xff0c;都需要提供良好的富文本编辑体验。本文记录了我使用UniApp开发一个跨平台富文本编辑器组件的过程&#xff0c;希望对有类似需求的开发者有所启发。 背景…...

W5500使用SocketTool工具测试

W5500使用SocketTool工具测试 1、按“WINR” 2、输入“IPCONFIG”&#xff0c;得到计算机的IP地址&#xff0c;子网掩码和网关 3、设置W5500设备网络参数如下&#xff1a; 本地网关&#xff1a;192.168.1.1 本地子网掩码: 255.255.255.0 本地物理地址&#xff1a;0C 2…...

《Python星球日记》 第71天:命名实体识别(NER)与关系抽取

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、命名实体识别&#xff08;NER&#xff09;基础1. 什么是命名实体识别&#…...

双向长短期记忆网络-BiLSTM

5月14日复盘 二、BiLSTM 1. 概述 双向长短期记忆网络&#xff08;Bi-directional Long Short-Term Memory&#xff0c;BiLSTM&#xff09;是一种扩展自长短期记忆网络&#xff08;LSTM&#xff09;的结构&#xff0c;旨在解决传统 LSTM 模型只能考虑到过去信息的问题。BiLST…...

CentOS7原有磁盘扩容实战记录(LVM非LVM)【针对GPT分区】

一、环境 二、命令及含义 fdisk ‌ ‌ fdisk‌是一个较老的分区表创建和管理工具&#xff0c;主要支持MBR&#xff08;Master Boot Record&#xff09;格式的分区表。MBR分区表支持的硬盘单个分区最大容量为2TB&#xff0c;最多可以有4个主分区。fdisk通过命令行界面进行操…...

如何在终端/命令行中把PDF的每一页转换成图片(PNG)

今天被对象安排了一个任务&#xff1a; 之前自己其实也有这个需要&#xff0c;但是吧&#xff0c;我懒&#xff1a;量少拖拽&#xff0c;量大就放弃。但这次躲不过去了&#xff0c;所以研究了一下有什么工具可以做到这个需求。 本文记录我这次发现的使用 XpdfReader 的方法。…...

【0415】Postgres内核 释放指定 memory context 中所有内存 ④

1. frees all memory (memory context) Postgres内核中由函数 AllocSetReset() 完成该功能。即 “释放给定set中分配的所有内存。” 它应当将所有已分配的chunks标记为已释放,但不一定需要归还set所拥有的全部资源。我们的实际实现是,除了“保留”块(“keeper” block)(…...

2025年Flutter初级工程师技能要求

在2025年&#xff0c;随着移动应用市场的持续增长和跨平台开发需求的不断增加&#xff0c;Flutter已经成为许多公司构建高性能、跨平台应用的首选框架。对于初入职场的Flutter初级工程师来说&#xff0c;掌握以下技能要求是必不可少的。这些技能不仅能够帮助你在工作中快速上手…...

AWS技术助力企业满足GDPR合规要求

GDPR(通用数据保护条例)作为欧盟严格的数据保护法规,给许多企业带来了合规挑战。本文将探讨如何利用AWS(亚马逊云服务)的相关技术来满足GDPR的核心要求,帮助企业实现数据保护合规。 一、GDPR核心要求概览 GDPR的主要目标是保护欧盟公民的个人数据和隐私权。其核心要求包括: 数…...

MVCC:数据库并发控制的利器

在并发环境下&#xff0c;数据库需要处理多个事务同时访问和修改数据的情况。为了保证数据的一致性和隔离性&#xff0c;数据库需要采用一些并发控制机制。MVCC (Multi-Version Concurrency Control&#xff0c;多版本并发控制) 就是一种常用的并发控制技术&#xff0c;它通过维…...

第二章、Isaaclab强化学习包装器(3):SKRL Wrapper

0 前言 官方文档&#xff1a;https://isaac-sim.github.io/IsaacLab/main/source/api/lab_rl/isaaclab_rl.html#module-isaaclab_rl.skrl https://skrl.readthedocs.io/en/latest/intro/getting_started.html 在本节中&#xff0c;您将学习如何使用 skrl 库的各种组件来创建强…...

AI数字人实现原理

随着人工智能与数字技术的快速发展&#xff0c;AI数字人&#xff08;Digital Human&#xff09;作为新一代人机交互媒介&#xff0c;正在多个行业中快速落地。无论是在虚拟主播、在线客服、教育培训&#xff0c;还是在数字代言、元宇宙中&#xff0c;AI数字人都扮演着越来越重要…...

RBTree的模拟实现

1&#xff1a;红黑树的概念 红⿊树是⼀棵⼆叉搜索树&#xff0c;他的每个结点增加⼀个存储位来表⽰结点的颜⾊&#xff0c;可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束&#xff0c;红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍&#xff0c;因…...

ssh connect to remote gitlab without authority

ssh connect to remote gitlab without authority 1 this command can produce a ssh key for authority ssh-keygen -t ed25519 -C "your_emailexample.com"2 this command can get the comment about the key cat ~/.ssh/id_ed25519.pubcopy all content !!!...

gitlab提交测试分支的命令和流程

写在前面 先npm run lint:eslint 先走一遍代码校验然后再提交先把检验跑了再add commit push那些注意一下这个问题:git commit规范不对导致报错subject may not be empty[subject-empty]type may not be empty[type-empty]. 配置lint检查后&#xff0c; 使用commitlint之后报…...

序列化和反序列化hadoop实现

### Hadoop 中序列化与反序列化的实现机制 Hadoop 提供了自己的轻量级序列化接口 Writable&#xff0c;用于高效地在网络中传输数据或将其存储到磁盘。以下是关于其核心概念和实现方式的详细介绍&#xff1a; --- #### 1. **Hadoop 序列化的核心原理** Hadoop 的序列化是一…...

[操作系统] 策略模式进行日志模块设计

文章目录 [toc]一、什么是设计模式&#xff1f;二、日志系统的基本构成三、策略模式在日志系统中的落地实现✦ 1. 策略基类 LogStrategy✦ 2. 具体策略类▸ 控制台输出&#xff1a;ConsoleLogStrategy▸ 文件输出&#xff1a;FileLogStrategy 四、日志等级枚举与转换函数五、日…...

LeetCode 每日一题 3341. 到达最后一个房间的最少时间 I + II

3341. 到达最后一个房间的最少时间 I II 有一个地窖&#xff0c;地窖中有 n x m 个房间&#xff0c;它们呈网格状排布。 给你一个大小为 n x m 的二维数组 moveTime &#xff0c;其中 moveTime[i][j] 表示在这个时刻 以后 你才可以 开始 往这个房间 移动 。你在时刻 t 0 时从…...

《Python星球日记》 第68天:BERT 与预训练模型

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、BERT模型基础1. 什么是BERT?2. BERT 的结构3.预训练和微调对比二、BERT 的预训练任务1. 掩码语言模型 (MLM)2. 下一句预测 (NSP)三、微调 …...

Angular 知识框架

一、Angular 基础 1. Angular 简介 Angular 是什么&#xff1f; 基于 TypeScript 的前端框架&#xff08;Google 维护&#xff09;。 适用于构建单页应用&#xff08;SPA&#xff09;。 核心特性 组件化架构 双向数据绑定 依赖注入&#xff08;DI&#xff09; 模块化设计…...

python三方库sqlalchemy

SQLAlchemy 是 Python 中最强大、最受欢迎的 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它允许你使用 Python 对象来操作数据库&#xff0c;而不需要直接编写 SQL 语句。同时&#xff0c;它也提供了对底层 SQL 的完全控制能力&#xff0c;适用于从简单脚本到大型企…...

【SSL部署与优化​】​​如何为网站启用HTTPS:从Let‘s Encrypt免费证书到Nginx配置​​

网站启用HTTPS 的完整实战指南&#xff0c;涵盖从 Let’s Encrypt 免费证书申请到 Nginx 配置的详细步骤&#xff0c;包括重定向、HSTS 设置及常见问题排查&#xff1a; 一、准备工作 1. 确保域名解析正确 • 在 DNS 管理后台&#xff0c;将域名&#xff08;如 example.com&…...

Kubernetes控制平面组件:Kubelet详解(四):gRPC 与 CRI gRPC实现

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

电商平台自动化

为什么要进行独立站自动化 纯人工测试人力成本高&#xff0c;相对效率低 回归测试在通用模块重复进行人工测试&#xff0c;测试效率低 前期调研备选自动化框架&#xff08;工具&#xff09;&#xff1a; Katalon Applitools Testim 阿里云EMAS Playwright Appium Cypress 相关…...

【kafka】kafka概念,使用技巧go示例

1. Kafka基础概念 1.1 什么是Kafka&#xff1f; Kafka是一个分布式流处理平台&#xff0c;用于构建实时数据管道和流式应用。核心特点&#xff1a; 高吞吐量&#xff1a;每秒可处理百万级消息持久化存储&#xff1a;消息按Topic分区存储在磁盘分布式架构&#xff1a;支持水平…...

计算机系统结构——Cache性能分析

一、实验目的 加深对Cache的基本概念、基本组织结构以及基本工作原理的理解。掌握Cache容量、相联度、块大小对Cache性能的影响。掌握降低Cache不命中率的各种方法以及这些方法对提高Cache性能的好处。理解LRU与随机法的基本思想以及它们对Cache性能的影响。 二、实验平台 实…...

Spring Web MVC————入门(2)

1&#xff0c;请求 我们接下来继续讲请求的部分&#xff0c;上期将过很多了&#xff0c;我们来给请求收个尾。 还记得Cookie和Seesion吗&#xff0c;我们在HTTP讲请求和响应报文的时候讲过&#xff0c;现在再给大家讲一遍&#xff0c;我们HTTP是无状态的协议&#xff0c;这次的…...

Adobe DC 2025安装教程

一.软件下载 点此下载 二.软件安装...

W1电力线载波通信技术

CK_Label_W1 产品型号&#xff1a;CK_Label_W1 尺寸&#xff1a;37*65*33.7mm 按键&#xff1a;1 指示灯&#xff1a;1 RGB灯(红/绿/蓝/黄/紫/白/青) 外观颜色&#xff1a;白色 合规认证&#xff1a;CE, RoHS 工作温度&#xff1a;0-50℃ 提示功能&#xff1a;蜂鸣器声音…...

现代 Web 自动化测试框架对比:Playwright 与 Selenium 的深度剖析

现代 Web 自动化测试框架对比&#xff1a;Playwright 与 Selenium 的深度剖析 摘要&#xff1a;本文对 Playwright 与 Selenium 在开发适配性、使用难度、场景适用性及性能表现等方面进行了全面深入的对比分析。通过详细的技术实现细节阐述与实测数据支撑&#xff0c;为开发者…...

第二章:CSS秘典 · 色彩与布局的力量

剧情承接&#xff1a;色彩失衡的荒原 林昊穿过 HTML 大门&#xff0c;眼前却是一片 灰白扭曲的荒原。所有页面元素如同幽灵般漂浮&#xff0c;没有色彩、没有结构&#xff0c;错乱无章。 “这是失控的样式荒原。” 零号导师的声音再次响起&#xff0c; “HTML 给了你骨架&…...

ubuntu studio 系统详解

Ubuntu Studio 系统详解&#xff1a;面向多媒体创作的专业 Linux 发行版 一、定位与目标用户 Ubuntu Studio 是 Ubuntu 的官方衍生版本&#xff08;Flavor&#xff09;&#xff0c;专为 音频、视频、图形设计、音乐制作、影视后期 等多媒体创作场景设计。目标用户包括&#x…...

在 Ubuntu 20.04.6 LTS 中将 SCons 从 3.1.2 升级到 4.9.1

在 Ubuntu 20.04.6 LTS 中将 SCons 从 3.1.2 升级到 4.9.1&#xff0c;可以通过以下步骤完成&#xff1a; 方法 1&#xff1a;使用 pip 安装&#xff08;推荐&#xff09; 步骤 1&#xff1a;卸载旧版本 SCons # 如果通过 apt 安装的旧版本&#xff0c;先卸载 sudo apt remov…...

边缘计算网关工业物联网应用:空压机远程运维监控管理

边缘计算网关在空压机远程运维监控管理中的工业物联网应用&#xff0c;主要体现在数据采集与处理、设备监控、故障诊断与预警、远程控制等方面&#xff0c;以下是具体介绍&#xff1a; 数据采集与处理 多源数据采集&#xff1a;边缘计算网关能连接空压机的各类传感器&#xf…...

【大模型面试每日一题】Day 18:大模型中KV Cache的作用是什么?如何通过Window Attention优化其内存占用?

【大模型面试每日一题】Day 18&#xff1a;大模型中KV Cache的作用是什么&#xff1f;如何通过Window Attention优化其内存占用&#xff1f; &#x1f4cc; 题目重现 &#x1f31f;&#x1f31f; 面试官:大模型中KV Cache的作用是什么&#xff1f;如何通过Window Attention优…...

Spring的 @Validate注解详细分析

在 Spring Boot 中&#xff0c;参数校验是保证数据合法性的重要手段。除了前面提到的NotNull、Size等基础注解外&#xff0c;JSR-303&#xff08;Bean Validation 1.0&#xff09;、JSR-349&#xff08;Bean Validation 1.1&#xff09;和 JSR-380&#xff08;Bean Validation …...

现代计算机图形学Games101入门笔记(三)

三维变换 具体形式缩放&#xff0c;平移 特殊点旋转。这里涉及到坐标系&#xff0c;先统一定义右手坐标系&#xff0c;根据叉乘和右手螺旋判定方向。这里还能法线Ry Sina 正负与其他两个旋转不一样。这里可以用右手螺旋&#xff0c;x叉乘z&#xff0c;发现大拇指朝下&#xff0…...

AI时代的弯道超车之第八章:具体分享几个AI实际操作方法和案例

在这个AI重塑世界的时代,你还在原地观望吗?是时候弯道超车,抢占先机了! 李尚龙倾力打造——《AI时代的弯道超车:用人工智能逆袭人生》专栏,带你系统掌握AI知识,从入门到实战,全方位提升认知与竞争力! 内容亮点: AI基础 + 核心技术讲解 职场赋能 + 创业路径揭秘 打破…...

企业网络新选择:软件定义架构下的MPLS

随着现代企业园区网络和运营商级基础设施的不断发展&#xff0c;多协议标签交换 &#xff08;MPLS&#xff09; 已成为一项基础技术&#xff0c;这要归功于其高效的数据包转发、高级流量工程功能以及对多租户环境的强大支持。 什么是MPLS&#xff1f; MPLS&#xff08;多协议…...

SparkSQL操作Mysql

&#xff08;一&#xff09;准备mysql环境 我们计划在hadoop001这台设备上安装mysql服务器&#xff0c;&#xff08;当然也可以重新使用一台全新的虚拟机&#xff09;。 以下是具体步骤&#xff1a; 使用finalshell连接hadoop001.查看是否已安装MySQL。命令是: rpm -qa|grep…...

【论文阅读】UNIT: Backdoor Mitigation via Automated Neural Distribution Tightening

ECCV2024 https://github.com/Megum1/UNIT 我们的主要贡献总结如下&#xff1a; 我们引入了UNIT&#xff08;“AUtomated Neural DIstribution Tightening”&#xff09;&#xff0c;这是一种创新的后门缓解方法&#xff0c;它为每个神经元近似独特的分布边界&#xff0c;用于…...

Android逆向学习(十) IDA逆向编辑Android so文件

Android逆向学习&#xff08;十&#xff09; IDA逆向编辑Android so文件 一、 写在前面 这是吾爱破解论坛正己大大的第10个教程 native code在我之前的博客中讲到过&#xff0c;所以这里就不讲了 简单来说&#xff0c;native code就是在android中使用c或c语言进行开发 这样…...

OpenCV + PyAutoGUI + Tkinter + FastAPI + Requests 实现的远程控制软件设计方案

以下是基于 OpenCV PyAutoGUI Tkinter FastAPI Requests 实现的远程控制软件设计方案。该方案分为 被控端&#xff08;服务端&#xff09; 和 控制端&#xff08;客户端&#xff09;&#xff0c;支持屏幕实时查看、键盘映射和鼠标操作。 1. 系统架构 ------------------- …...

C++.神经网络与深度学习(赶工版)(会二次修改)

神经网络与深度学习 1. 神经网络基础1.1 神经元模型与激活函数1.2 神经网络结构与前向传播2.1 损失函数与优化算法均方误差损失函数交叉熵损失函数梯度下降优化算法 2.2 反向传播与梯度计算神经元的反向传播 3.1 神经元类设计与实现神经元类代码实现代码思路 3.2 神经网络类构建…...

砷化镓太阳能电池:开启多元领域能源新篇

砷化镓太阳能电池作为一种高性能的光伏产品&#xff0c;具有诸多独特优势。其中&#xff0c;锗衬底砷化镓太阳能电池表现尤为突出&#xff0c;它具备高转化效率、耐辐照和高电压等特性。在空间供电电源领域&#xff0c;这些优势使其成为人造卫星、太空站、太空探测器和登陆探测…...

[Linux] vim及gcc工具

目录 一、vim 1.vim的模式 2.vim的命令集 (1):命令模式 (2):底行模式 3.vim配置 二、gcc 1.gcc格式及选项 2.工作布置 三、自动化构建工具makefile 1.基本使用方法 2.配置文件解析 3.拓展 在linux操作系统的常用工具中&#xff0c;常用vim来进行程序的编写&#xff1b…...

java加强 -stream流

Stream流是jdk8开始新增的一套api&#xff0c;可以用于操作集合或数组的内容。 Stream流大量的结合了Lambda的语法风格来编程&#xff0c;功能强大&#xff0c;性能高效&#xff0c;代码简洁&#xff0c;可读性好。 体验Stream流 把集合中所有以三开头并且三个字的元素存储到…...

RHCE认证通过率

红帽RHCE考试总体通过率38%&#xff08;2023年数据&#xff09;&#xff0c;细分数据显示自学者通过率18%&#xff0c;参加官方培训者47%&#xff0c;企业团体考生53%。通过率差异由备考资源和考试策略决定。 RHCE考试重点考Ansible自动化运维&#xff0c;需在3.5小时内完成12…...

OpenEvidence AI临床决策支持工具平台研究报告

平台概述 OpenEvidence是一个专为医疗专业人士设计的临床决策支持工具,旨在通过整合各类临床计算器和先进的人工智能技术,提高医生的诊疗决策效率和准确性。作为一款综合性医疗平台,OpenEvidence将复杂的医学计算流程简化,同时提供个性化的临床建议,使医生能够更快、更准…...

gd32e230c8t6 keil6工程模板

下载固件gd32e230c8t6固件官方下载&#xff08;需登录&#xff09; 或 蓝奏云 新建一个文件夹&#xff0c;把固件压缩包里的里的Firmware和Template拖进去 keil新建gd32e230c8工程 必须勾选CMSIS-CORE 新建一个文件夹&#xff0c;双击任意改名 点击manage project it…...