原生API编写简单富文本编辑器003

news2025/1/13 7:58:38

原生API编写简单富文本编辑器003

系列文章快速阅读:
富文本编辑器开发系列-1-基础概念
富文本编辑器开发系列2-document.execCommand 的API
富文本编辑器开发系列3-selection
富文本编辑器开发系列4——Range对象
富文本编辑器开发系列5——浏览器Selection API探究
富文本编辑器开发系列6——Range API 探究
富文本编辑器开发系列7——textRange对象详解
富文本编辑器开发系列8——常用DOM API
原生API编写简单富文本编辑器001
原生API编写简单富文本编辑器002
原生API编写简单富文本编辑器003
原生API编写简单富文本编辑器004
原生API编写简单富文本编辑器005

在上一节,我们实现了一个简单的富文本编辑器,但是其中很多功能按钮点击还没有反应,因为这些功能需要参数,而我们并没有提供参数,这一节就来实现这些功能,它们包括:

  • 字体
  • 字号
  • 前景色
  • 背景色
  • 插入链接
  • 取消链接

字体设置

要设置字体,首先我们需要一个字体列表,为了能够在大多数电脑上显示正确的字体,我们目前就挑选一些系统自带的常用字体组成一个字体列表。

// index.js
……

const fontFamilys = [

 '仿宋',

 '黑体',

 '楷体',

 '宋体',

 '微软雅黑',

 '新宋体',

 'Calibri',

 'Consolas',

 'Droid Sans',

 ];

然后当我们点击字体设置按钮时,动态生成一个字体选择组件,插入到字体选择按钮下方。


btn.onclick = function(e) {

 if (command === 'fontName') {

 showFontName(e);

 } else {

 document.execCommand(command, true, '');

 }

 };


function showFontName(e) {
// 定义字体列表
 const fontFamilys = [

 '仿宋',

 '黑体',

 '楷体',

 '宋体',

 '微软雅黑',

 '新宋体',

 'Calibri',

 'Consolas',

 'Droid Sans',

 ];
 // 生成字体选择列表模板

 let tpl = '<div class="editor-dialog-fontfamily">';

 tpl += '<ul>';

 for (let i = 0; i < fontFamilys.length; i++) {

 tpl += `<li οnclick="selectFontFamily('${fontFamilys[i]}')">${fontFamilys[i]}</li>`;

 }

 tpl+ '</ul>';

 tpl += '</div>';

// 将字体选择列表填充到通用弹窗内

 let dialog = document.getElementById('editorDialog');

 dialog.innerHTML = tpl;
	
// 获取按钮的相对位置坐标

 let target = e.target;

 let left = target.offsetLeft;

 let top = target.offsetTop;
	
	
// 将弹窗放置在按钮下方位置并显示出来

 dialog.style.left = left + 'px';

 dialog.style.top = top + target.offsetHeight + 20 + 'px';

 dialog.style.display = 'block';

}


在HTML中,我们需要一个专门放弹出框的元素:

<div class="editor">

	 <div id="editorBar" class="editor-toolbar">
		//...

	 </div>

	 <div id="editorContent" class="editor-content" contenteditable="true"></div>

	 <div id="editorDialog"></div>  <!-- 专门放置弹出框 -->

 </div>

在CSS中,给弹出框和字体选择列表一些简单的样式:


.editor {

// ...

 position: relative;

}

#editorDialog {

 position: absolute;

 display: none;

 border: 1px solid #e9e9e9;

 z-index: 100;

}

  

.editor-dialog-fontfamily ul {

 list-style: none;

 padding: 0;

 padding-right: 15px;

}

  

.editor-dialog-fontfamily ul li {

 height: 30px;

 line-height: 30px;

 padding-left: 10px;

 cursor: pointer;

}

  

.editor-dialog-fontfamily ul li:hover {

 background-color: #cecece;

}

看看效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fTPJXf66-1670512639517)(https://gitee.com/hjb2722404/tuchuang/raw/master/img/202204291654159.png)]

然后我们实现选择具体字体后的方法:

function selectFontFamily(fontName) {

 const rs = document.execCommand('fontName', true, fontName);

 console.log(rs);

}

我们发现,当我们点击选择某个字体后,编辑区中所选的文字字体并没有改变,并且控制台输出执行结果,发现是false

原因是,当我们点击某个字体的时候,浏览器就会取消编辑区内的选区,所以当我们执行命令时,并没有选区,所以会执行失败。

现在我们对上面的代码进行改造,将字体列表改造为一个下拉选框,当选择值变化时,设置字体

let tpl = '<div class="editor-dialog-fontfamily">';

 tpl += '<select id="fontNameSelect" οnchange="selectFontFamily()">';

 for (let i = 0; i < fontFamilys.length; i++) {

 tpl += `<option value="${fontFamilys[i]}" style="font-family: '${fontFamilys[i]}'">${fontFamilys[i]}</option>`;

 }

 tpl+ '</select>';

 tpl += '</div>';

//...


function selectFontFamily() {

 const target = document.getElementById('fontNameSelect');

 const rs = document.execCommand('fontName', true, target.value);

 console.log(rs);

}



这时我们再看:
在这里插入图片描述

到此,终于完成了字体的设置功能,接下来我们先对字体设置功能进行一定的优化——与其点击某个按钮才弹出下拉框选择,不如一开始就用下拉框替代按钮

window.onload= function() {

 const btns = document.getElementById('editorBar').getElementsByTagName('button');

 for (let i=0; i<btns.length; i++) {

 const btn = btns[i];

 const command = btn.getAttribute('command');

 if (command === 'fontName') {

 showFontName(btn);

 }

 btn.onclick = function(e) {

 document.execCommand(command, 'true', '');

 };

 }

};


function showFontName(btn) {

 let tpl = getFontNameTpl();

 const $li = btn.parentElement;

 $li.innerHTML = tpl;

}


function getFontNameTpl() {

 const fontFamilys = [

 '仿宋',

 '黑体',

 '楷体',

 '宋体',

 '微软雅黑',

 '新宋体',

 'Calibri',

 'Consolas',

 'Droid Sans',

 'Microsoft YaHei',

 ];

 let tpl = '';

 tpl += '<select id="fontNameSelect" οnchange="selectFontFamily()">';

 for (let i = 0; i < fontFamilys.length; i++) {

 tpl += `<option value="${fontFamilys[i]}" style="font-family: '${fontFamilys[i]}'">${fontFamilys[i]}</option>`;

 }

 tpl+ '</select>';

 return tpl;

}

还需要改一下CSS,取消按钮li的宽度限制,并给一个下边距:

.editor-toolbar ul li {

 height: 20px;

 line-height: 20px;

 display: inline-block;

 cursor: pointer;

 margin-left: 10px;

 margin-bottom: 10px;

}

看下最终效果:

在这里插入图片描述

字号

字号与字体是相同的作用机制,我们补充下代码:


const command = btn.getAttribute('command');

 if (command === 'fontName') {

 showFontName(btn);

 }

 if (command === 'fontSize') {

 showFontSize(btn);

 }


// ...

function getFontSizeTpl() {

 const fontSizes = [

 '12',

 '14',

 '16',

 '18',

 '20',

 '24',

 '28',

 '36',

 '48',

 '72',

 ];

 let tpl = '';

 tpl += '<select id="fontSizeSelect" οnchange="selectFontSize()">';

 for (let i = 0; i < fontSizes.length; i++) {

 tpl += `<option value="${fontSizes[i]}" style="font-size: '${fontSizes[i]}'">${fontSizes[i]}</option>`;

 }

 tpl+ '</select>';

 return tpl;

}

  

function showFontSize(btn) {

 let tpl = getFontSizeTpl();

 const $li = btn.parentElement;

 $li.innerHTML = tpl;

}

  

function selectFontSize() {

 const target = document.getElementById('fontSizeSelect');

 const rs = document.execCommand('fontSize', true, parseInt(target.value) + 'px');

}

在这里插入图片描述

我们会发现,在下拉框中,我们未字体提供了若干以像素为单位的值,并且当用户选择时,我们会将这个像素值作为execCommand命令的参数,但是实际操作时,无论我们选择多少的字号,最后的字号始终是一样大的。

通过查看控制台元素面板,我们发现无论我们传入什么值,最终浏览器给文字加的控制字号的属性size 都是 7

再仔细翻看文档,我们看到对fontSize 命令的说明:

fontSize : 在插入点或者选中文字部分修改字体大小. 需要提供一个HTML字体尺寸 (1-7) 作为参数

所以当我们传入超出范围的不合法参数时,它会始终将字体设置为最大尺寸7

修改一下:

const fontSizes = [

 '1',

 '2',

 '3',

 '4',

 '5',

 '6',

 '7',

 ];

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jrKdDMwi-1670512639523)(https://gitee.com/hjb2722404/tuchuang/raw/master/img/202205050959011.png)]

这回就正常了,可是用户并不知道1-7代表的具体大小对应的是常见的文档软件(例如word)中的大小(通常以像素表示),我们需要继续改造:

function getFontSizeTpl() {

 const fontSizes = [

 {

 key: 1,

 value: '12',

 },

 {

 key: 2,

 value: '13',

 },

 {

 key: 3,

 value: '16',

 },

 {

 key: 4,

 value: '18',

 },

 {

 key: 5,

 value: '24',

 },

 {

 key: 6,

 value: '32',

 },

 {

 key: 7,

 value: '48',

 },

 ];

 let tpl = '';

 tpl += '<select id="fontSizeSelect" οnchange="selectFontSize()">';

 for (let i = 0; i < fontSizes.length; i++) {

 tpl += `<option value="${fontSizes[i].key}" style="font-size: '${fontSizes[i].value}px'">${fontSizes[i].value}</option>`;

 }

 tpl+ '</select>';

 return tpl;

}

在这里插入图片描述

总算是正常了。

前景色

众所周知,要想随心所欲地设置富文本的颜色,必须提供一个颜色选择器,我们这里暂不考虑浏览器兼容性,直接使用HTML5中 typecolorinput 组件。

function showColorPicker(btn, type) {

 let tpl = getColorPickerTpl();

 const $dialog = document.getElementById('editorDialog');

 $dialog.innerHTML = tpl;

 $dialog.style.top = (btn.offsetTop + btn.offsetHeight + 15) + 'px';

 $dialog.style.left = btn.offsetLeft + 'px';

 $dialog.style.display = 'block';

 const colorPicker = document.getElementById('colorPicker');

 colorPicker.addEventListener("input", setColor.bind(this, type), false);

 colorPicker.addEventListener("change", setColor.bind(this, type), false);
	
document.addEventListener("click", function(e) {

 e.stopPropagation();

 const $i = btn.firstChild;

 if (e.target !== $i && e.target !==colorPicker) {

 $dialog.style.display = 'none';

 }

 });

}

  

function getColorPickerTpl(type) {

 const tpl = `

 <input type="color" id="colorPicker" />

 `;

 return tpl;

}

  

function setColor(type, event) {

 const $dialog = document.getElementById('editorDialog');

 document.execCommand(type, 'false', event.target.value);

 $dialog.style.display = 'none';

  

}

在这里插入图片描述

背景色

背景色的逻辑与前景色一样,所以其逻辑可以复用,只需要传入不同参数即可:

 btn.onclick = function(e) {

 document.execCommand(command, 'true', '');

 if (command === 'fontColor') {

 showColorPicker(btn, 'foreColor');

 }

 if (command === 'backColor') {

 showColorPicker(btn, 'backColor');

 }

 };

在这里插入图片描述

但是,这里有一个问题,我们为文档对象和调色板绑定了事件监听,但却没有在任何地方进行解绑。

为了完善解绑功能,我们需要定义两个全局变量来分别存储设置富文本内容字体的监听函数和解绑监听隐藏调色板的函数。因为两个监听函数使用了bind 方法已便传参,但bind的问题是,它会产生一个新的函数,所以我们需要将这个新产生的函数缓存下来,以便在移除监听时传入与绑定时相同的函数。

var hideColorPickerFun;

var setColorFun;

function showColorPicker(btn, type) {

	//...
	hideColorPickerFun = hideColorPicker.bind(document, colorPicker, $dialog, btn, type);

	 setColorFun = setColor.bind(this, type, $dialog, colorPicker, btn);

	 colorPicker.addEventListener("input", setColorFun, false);

	 colorPicker.addEventListener("change", setColorFun, false);

	 document.addEventListener("click", hideColorPickerFun, false);
}

function hideColorPicker(colorPicker, $dialog, btn, type, e) {

 e.stopPropagation();

 const $i = btn.firstChild;

 if (e.target !== $i && e.target !==colorPicker) {

 $dialog.style.display = 'none';

 document.removeEventListener('click', hideColorPickerFun, false);

 colorPicker.removeEventListener('input', setColorFun, false);

 colorPicker.removeEventListener('change', setColorFun, false);

 $dialog.innerHTML = '';

 }

}

  

function setColor(type, $dialog, colorPicker, btn, event) {

 document.execCommand(type, 'false', event.target.value);

 $dialog.style.display = 'none';

 document.removeEventListener('click', hideColorPickerFun, false);

 colorPicker.removeEventListener('input', setColorFun, false);

 colorPicker.removeEventListener('change', setColorFun, false);

 $dialog.innerHTML = '';

}

另外,在给功能按钮绑定事件时,我们将条件判断改为分支判断,方便扩展:

 btn.onclick = function(e) {

 switch (command) {

 case 'fontColor':

 showColorPicker(btn, 'foreColor');

 break;

 case 'backColor':

 showColorPicker(btn, 'backColor');

 break;

 default:

 document.execCommand(command, 'true', '');

 }

 };

插入链接

插入链接的流程是当点击插入链接按钮的时候,弹出一个填写链接的输入框和一个确定按钮,点击确定按钮,为选中的文字增加链接。

直接上代码:

switch (command) {

 case 'fontColor':

 showColorPicker(btn, 'foreColor');

 break;

 case 'backColor':

 showColorPicker(btn, 'backColor');

 break;

 case 'createLink':

 showLinkDialog(btn);

 break;

 default:

 document.execCommand(command, 'true', '');

 }



function showLinkDialog(btn) {

 const tpl = getLinkDialogTpl();

 const $dialog = document.getElementById('editorDialog');

 $dialog.innerHTML = tpl;

 $dialog.style.top = (btn.offsetTop + btn.offsetHeight + 15) + 'px';

 $dialog.style.left = btn.offsetLeft + 'px';

 $dialog.style.display = 'block';

 const linkDialog = document.getElementById('linkDialog');

 linkDialog.focus();

 const createLinkBtn = document.getElementById('createLinkBtn');

 createLinkBtn.addEventListener('click', createLink, false);

}

  

function getLinkDialogTpl() {

 const tpl = `

 <input type="text" id="linkDialog" />

 <button id="createLinkBtn">确定</button>

 `;

 return tpl;

}

  

function createLink() {

 const linkDialog = document.getElementById('linkDialog');

 document.execCommand('createLink', 'false', linkDialog.value);
const createLinkBtn = document.getElementById('createLinkBtn');

 createLinkBtn.removeEventListener('click', createLink, false);

 const $dialog = document.getElementById('editorDialog');

 $dialog.innerHTML = '';

 $dialog.style.display = 'none';

}


在这里插入图片描述

但是这里还有一个问题,当我们的焦点转移到链接输入框时,编辑器中的选区就被取消了,需要我们重新选择文本后再点击确定按钮,才能添加成功。

这个问题的原因和解决方法我们先暂时搁置,后面再细讲。

取消链接

之前插入链接不起作用是因为我们的编辑器没有插入链接功能,无法插入链接,就无法验证取消链接功能,现在我们给编辑器加上了插入链接功能,插入链接后,选中链接,直接点击取消链接按钮,链接就能被取消了。

插入图片

插入图片的原理与插入链接一样。

直接上代码:

switch (command) {

 case 'fontColor':

 showColorPicker(btn, 'foreColor');

 break;

 case 'backColor':

 showColorPicker(btn, 'backColor');

 break;

 case 'createLink':

 showLinkDialog(btn);

 break;

 case 'insertImage':

 showImageDialog(btn);

 break;

 default:

 document.execCommand(command, 'true', '');

 }


function showImageDialog(btn) {

 const tpl = getImageDialogTpl();

 const $dialog = document.getElementById('editorDialog');

 $dialog.innerHTML = tpl;

 $dialog.style.top = (btn.offsetTop + btn.offsetHeight + 15) + 'px';

 $dialog.style.left = btn.offsetLeft + 'px';

 $dialog.style.display = 'block';

 const imageDialog = document.getElementById('imageDialog');

 imageDialog.focus();

 const createIamgeBtn = document.getElementById('createIamgeBtn');

 createIamgeBtn.addEventListener('click', createImage, false);

}

  

function getImageDialogTpl() {

 const tpl = `

 <input type="text" id="imageDialog" />

 <button id="createIamgeBtn">确定</button>

 `;

 return tpl;

}

  

function createImage() {

 const imageDialog = document.getElementById('imageDialog');

 document.execCommand('insertImage', 'false', imageDialog.value);

 const createLinkBtn = document.getElementById('createIamgeBtn');

 createIamgeBtn.removeEventListener('click', createImage, false);

 const $dialog = document.getElementById('editorDialog');

 $dialog.innerHTML = '';

 $dialog.style.display = 'none';

}

在这里插入图片描述

在这里插入图片描述

至此,我们已经实现了所有浏览器API提供的编辑功能。

当然,现在看代码,有很多冗余,不够优雅,而且功能上也还有很多问题,比如:

  1. 设置的字体是使用 font属性,而非CSS
  2. 设置的字号只接受1-7, 并且是以 size 属性而非 CSS控制,超出大小无法设置。
  3. color使用HTML的input时,始终有一个input框在那里,并且如果手动触发click显示调色板,则调色板的位置无法自动跟随
  4. link 只能创建或取消,无法修改,无法指定是以何种方式打开
  5. link和image填写框聚焦时编辑器选区会被取消

当然,这只是肉眼可见的一些问题,还有更多问题,我们下一讲统一再将。

本系列文章代码可从gitee获取

本节所有代码可在分支 1.0.4 上找到。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/74781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

思科设备中STP生成树协议及其配置

目录 一、网络冗余存在的问题 1.广播风暴 2.MAC地址表震荡 二、STP简介 1.BPDU简介与字段含义 2.网桥ID 3.路径开销 4.端口ID 5.BPDU计时器 &#xff08;1&#xff09;Hello Time &#xff08;2&#xff09;Forward Delay转发延迟 &#xff08;3&#xff09;Max Ag…

[附源码]JAVA毕业设计小区物业管理系统录像展示.mp4(系统+LW)

[附源码]JAVA毕业设计小区物业管理系统录像展示.mp4&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09…

【Golang】切片的底层实现(关于slice调用append函数后分配新数组的问题)

问题描述 今天在写代码的时候遇到一个很奇怪的现象&#xff0c;先看下面两段代码 func push(a []int, v int) {a[1] 2a append(a, v) } func main() {a : []int{0, 1, 2}push(a, 3)fmt.Println(a) }结果&#xff1a;[0 2 2] func push(a []int, v int) {a append(a, v)a[…

宝塔下 nginx 支持图片放缩

要想通过nginx实现图片的放缩功能&#xff0c;首先需要对nginx添加http_image_filter_module模块的支持&#xff0c;首先查看安装的nginx是否已经支持了对应的模块 nginx -V 如图&#xff0c;如果返回的代码中没有包含 http_image_filter_module&#xff0c;则代表安装的nginx…

Docker学习笔记3(狂神)

可视化 这样我们就已经安装成功了。 我们一般选择本地的。 然后我们就可以看到这样的面板。 不过这个我们平时不会去使用&#xff0c;只是作为了解即可。 镜像分层的理解&#xff1a; 如何提交一个自己的镜像。 Commit镜像 实战测试 我们现在启动了tomcat。 我们进入了tomc…

Spring RestTemplate请求过程

文章目录前言1. RestTemplate请求整体过程2. httpRequest 创建3. doWithRequest4. execute5. http响应解析前言 目前Spring RestTemplate是常用的http请求工具类&#xff0c;本文简单Spring RestTemplate的请求过程。 1. RestTemplate请求整体过程 接下来以ResponseEntity e…

paddleOCR识别问题和解决方案

常见问题解答&#xff1a; 文本检测相关FAQ paddle ocr 常见问答 https://aistudio.baidu.com/aistudio/projectdetail/4491412 参数介绍 import argparse import os import sys import cv2 import numpy as np import paddle from PIL import Image, ImageDraw, ImageFont …

如何基于 APISIX 迭代数字智联平台

分享嘉宾&#xff1a;沈巍&#xff0c;浙大网新研发总监。 网新电气成立于 2011 年&#xff0c;属于浙大网新旗下&#xff0c;为绿色智慧交通系统解决方案的提供商。业务范围包括铁路、隧道、城市智能交通、航空、高速公路等行业。整个高铁信息化的业务分布占到了全国市场的 20…

Electron 麒麟 Linux 系统 root 账户报错

使用Electron打包成客户端在麒麟Linux 操作系统上运行&#xff0c;普通用户启动程序正常 使用root用户出现各种问题。总结问题如下&#xff1a; 1. Running as root without --no-sandbox is not supported。 解决方案&#xff1a; 在启动命令后面加入 --no-sandbox sudo …

为SSH远程配置固定的公网TCP端口地址【内网穿透】

由于使用免费的cpolar生成的公网地址&#xff0c;为随机临时地址&#xff0c;24小时内会发生变化&#xff0c;并且带宽较小&#xff0c;只有1M。对于需要长期SSH远程的用户来说&#xff0c;配置固定的公网TCP端口地址&#xff0c;提高带宽就很有必要。 1. 保留一个固定TCP端口地…

信息收集道道之外网信息收集

#信息收集道道之外网信息收集 从个人的角度去简单整理下打点前的信息收集那些事。从信息收集本质上来说多数内容都是大同小异&#xff0c;遇到坚壁时&#xff0c;不用死磕&#xff0c;毕竟条条大路通罗马&#xff08;大佬们也可以说说看法~向各位大佬学习&#xff01; 红队知…

业务数据LEFT JOIN 多表查询慢--优化操作

首先你会想到&#xff0c;给表加索引&#xff0c;那么mysql会给主键自动建立索引吗? 会的&#xff0c;当然会。 在我们查询的业务表操作的时候&#xff0c;表业务数据庞大起来的时候&#xff0c;以及left join多的时候&#xff0c;甚至多表关联到几十张表的时候&#xff0c;查…

【云原生】二进制部署k8s集群(中)搭建node节点

内容预知 连接上文 1. 部署 Worker Node 组件 1.1 work node 组件部署前需了解的节点注册机制 1.2 Worker Node 组件部署步骤 2. k8s的CNI网络插件模式 2.1 k8s的三种网络模式 K8S 中 Pod 网络通信&#xff1a; &#xff08;1&#xff09;Pod 内容器与容器之间的通信 &am…

2022年四川建筑八大员(标准员)考试试题及答案

百分百题库提供建筑八大员&#xff08;标准员&#xff09;考试试题、建筑八大员&#xff08;标准员&#xff09;考试真题、建筑八大员&#xff08;标准员&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 1.施工项目管理目标…

[附源码]Python计算机毕业设计SSM基于框架的校园爱心公益平台的设计与实现(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

java基于Springboot的学生毕业离校系统-计算机毕业设计

项目介绍 学生毕业离校系统的开发过程中&#xff0c;采用B / S架构&#xff0c;主要使用Java技术进行开发&#xff0c;结合最新流行的springboot框架。中间件服务器是Tomcat服务器&#xff0c;使用Mysql数据库和Eclipse开发环境。该学生毕业离校系统包括管理员、学生和教师。其…

【SpringBoot应用篇】SpringBoot+JasperReport导出PDF

【SpringBoot应用篇】SpringBootJasperReport导出PDFJasperReport简介JasperReport的开发步骤生命周期执行流程模板工具Jaspersoft Studio概述安装配置面板介绍基本使用模板制作编译模板入门案例环境准备导出一个最基本的PDF文件导出用户列表需求数据直接从数据库中获取数据从后…

Lombok实现原理解析

文章目录前言一、Lombok注解分析二、编译期的注解处理期三、Lombok使用方法四、自定义注解处理器1、自定义注解2、实现Processor接口3、注册注解处理器五、实战MyGetter注解1、新建Maven工程myLombok2、新建子模块myget3、新建子模块person4、编译并查看结果总结前言 相信做ja…

171-有趣的OpenAI的chatGPT小实验

最近玩了一下chatGPT 问了他很多问题 然后我问了一个问题 帮我想10个帮女朋友过生日的办法 然后AI就回复了我10种 然后我继续问了我说再来10个 他又想了10种&#xff0c; 所以我特别想看看他到底有没有极限 10个 20个 30个 40个 50个 60个 70个 80个 90个 100个 接下去…

秋招---SQL学习

文章目录SQL的执行顺序一般是怎样的SQL如何性能优化1.select尽量不要查询全部*&#xff0c;而是查具体字段2.避免在where子句中使用 or 来连接条件3.尽量使用数值替代字符串类型tinyint,int,bigint,smallint类型4.用varchar代替char那什么时候要用char不用varchar呢链接&#x…