blog-image replace

This commit is contained in:
lianglianglee 2023-12-05 13:42:32 +08:00
parent 89717d2622
commit f197d95fe7
11 changed files with 42 additions and 42 deletions

View File

@ -27,13 +27,13 @@ style:
# Your blog website logo image # Your blog website logo image
# You can use local image, image external link or don't fill # You can use local image, image external link or don't fill
logo: https://blog-image.lianglianglee.com/images/favicon.png logo: https://static.lianglianglee.com/images/favicon.png
# Favicon (You can use local image or image external link) # Favicon (You can use local image or image external link)
favicon: https://blog-image.lianglianglee.com/images/favicon.png favicon: https://static.lianglianglee.com/images/favicon.png
# Avatar (You can use local image or image external link) # Avatar (You can use local image or image external link)
avatar: https://blog-image.lianglianglee.com/images/favicon.png avatar: https://static.lianglianglee.com/images/favicon.png
# Font size, customize font size. (you don't usually have to fill) # Font size, customize font size. (you don't usually have to fill)
# e.g. font_size: 18px # e.g. font_size: 18px

View File

@ -11,7 +11,7 @@ date: 2023-06-29 01:12:23
查看阵列情况发现一个U盘已经挂了。 查看阵列情况发现一个U盘已经挂了。
![](https://blog-image.lianglianglee.com/assets/20230629_112301.png) ![](https://static.lianglianglee.com/assets/20230629_112301.png)
才3个月已经挂了有点尴尬。哪怕更换了启动U盘也不敢存放了。想着换系统。搞来搞去没有满意的系统。所以干脆裸装算了。就使用debian作为系统组软RAID,应用就万物皆docker。 才3个月已经挂了有点尴尬。哪怕更换了启动U盘也不敢存放了。想着换系统。搞来搞去没有满意的系统。所以干脆裸装算了。就使用debian作为系统组软RAID,应用就万物皆docker。

View File

@ -42,7 +42,7 @@ tree .git
使用git status查看当前仓库的状态 使用git status查看当前仓库的状态
![image](https://blog-image.lianglianglee.com/assets/image-20231023162908-77wvd7b.png) ![image](https://static.lianglianglee.com/assets/image-20231023162908-77wvd7b.png)
可以看到一个新增文件,这个时候.git目录没什么变化 可以看到一个新增文件,这个时候.git目录没什么变化
@ -50,7 +50,7 @@ tree .git
接下来,我们需要使用 git add 命令将文件加入到暂存区再看git状态 接下来,我们需要使用 git add 命令将文件加入到暂存区再看git状态
![image](https://blog-image.lianglianglee.com/assets/image-20231023163048-1qbqrle.png) ![image](https://static.lianglianglee.com/assets/image-20231023163048-1qbqrle.png)
使用tree命令看一下目录 使用tree命令看一下目录
@ -68,15 +68,15 @@ tree .git
多出来一个文件夹,去看看这个文件夹是什么东西 多出来一个文件夹,去看看这个文件夹是什么东西
![image](https://blog-image.lianglianglee.com/assets/image-20231023163209-iri6tvb.png) ![image](https://static.lianglianglee.com/assets/image-20231023163209-iri6tvb.png)
一个无后缀的文件,打开后,是一个乱码 一个无后缀的文件,打开后,是一个乱码
![image](https://blog-image.lianglianglee.com/assets/image-20231023163309-3bjxq3y.png) ![image](https://static.lianglianglee.com/assets/image-20231023163309-3bjxq3y.png)
这类文件可以使用 git cat-file 打开,打开这个文件,需要将两个字符的文件夹也给加上,这里就是`e1cfd5c071a3098ba096bc37300a003f0f3f036e` 这类文件可以使用 git cat-file 打开,打开这个文件,需要将两个字符的文件夹也给加上,这里就是`e1cfd5c071a3098ba096bc37300a003f0f3f036e`
![image](https://blog-image.lianglianglee.com/assets/image-20231023163527-bo6ez6n.png) ![image](https://static.lianglianglee.com/assets/image-20231023163527-bo6ez6n.png)
可以看到这里是就是文本的内容 可以看到这里是就是文本的内容
@ -113,11 +113,11 @@ git commit -m"第一次提交"
第一个文件类似一个目录介绍里面是我们add时的那个文件和文件名 第一个文件类似一个目录介绍里面是我们add时的那个文件和文件名
![image](https://blog-image.lianglianglee.com/assets/image-20231023164251-hjinoiu.png) ![image](https://static.lianglianglee.com/assets/image-20231023164251-hjinoiu.png)
另一个文件是commit的一些信息提交人邮箱时间等其中 tree这一行就是刚刚的文件。 另一个文件是commit的一些信息提交人邮箱时间等其中 tree这一行就是刚刚的文件。
![image](https://blog-image.lianglianglee.com/assets/image-20231023164404-thcflji.png) ![image](https://static.lianglianglee.com/assets/image-20231023164404-thcflji.png)
这里就可以把整个提交流程串起来了 这里就可以把整个提交流程串起来了
@ -129,7 +129,7 @@ git commit 为什么会生成两个文件,还不知道为什么,我们等会
这时通过这一串ID可以把整个仓库串起来 这时通过这一串ID可以把整个仓库串起来
![image](https://blog-image.lianglianglee.com/assets/image-20231023170406-ghlsodf.png) ![image](https://static.lianglianglee.com/assets/image-20231023170406-ghlsodf.png)
我们再加一个文件test_bak.txt这里同样的add和commit 我们再加一个文件test_bak.txt这里同样的add和commit
@ -183,7 +183,7 @@ committer 李亮亮 <wb-lll538609@cainiao.com> 1698050920 +0800
这个时候,关系链变成了这样: 这个时候,关系链变成了这样:
![image](https://blog-image.lianglianglee.com/assets/image-20231023170924-eeck5wr.png) ![image](https://static.lianglianglee.com/assets/image-20231023170924-eeck5wr.png)
到这里基本确定了objects的存储方式。既然是一个树那怎么知道根节点是哪个呢 到这里基本确定了objects的存储方式。既然是一个树那怎么知道根节点是哪个呢

View File

@ -109,12 +109,12 @@ NODE_VERSION 14.3
确定后,即开始构建,构建完成后,可以在右上角看到临时域名 确定后,即开始构建,构建完成后,可以在右上角看到临时域名
![](https://blog-image.lianglianglee.com/assets/depoly_detail.png) ![](https://static.lianglianglee.com/assets/depoly_detail.png)
返回你的项目 返回你的项目
![](https://blog-image.lianglianglee.com/assets/cloudflare_page.png) ![](https://static.lianglianglee.com/assets/cloudflare_page.png)
可以看到项目的永久域名,后续都可以使用这个域名访问 可以看到项目的永久域名,后续都可以使用这个域名访问
@ -124,7 +124,7 @@ NODE_VERSION 14.3
添加CNAME 添加CNAME
![](https://blog-image.lianglianglee.com/assets/dns_config.png) ![](https://static.lianglianglee.com/assets/dns_config.png)
等待10-30分钟即可访问 等待10-30分钟即可访问

View File

@ -13,13 +13,13 @@ MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这
show engines; show engines;
``` ```
![](https://blog-image.lianglianglee.com/assets/tih4p92cmigv3qr02q3avl3src.png) ![](https://static.lianglianglee.com/assets/tih4p92cmigv3qr02q3avl3src.png)
## 查看已有表信息 ## 查看已有表信息
show table status like 'user' \G show table status like 'user' \G
![](https://blog-image.lianglianglee.com/assets/0aa6rm8psaif1pjt86nud1ealp.png) ![](https://static.lianglianglee.com/assets/0aa6rm8psaif1pjt86nud1ealp.png)
**Name**:表名 **Name**:表名

View File

@ -6,7 +6,7 @@ date: '2023-04-04T13:00:07.258Z'
--- ---
# 1. 简介 # 1. 简介
MySQL 的基本架构示意图,从中你可以清楚地看到 SQL 语句在 MySQL 的各个功能模块中的执行过程![image.png](https://blog-image.lianglianglee.com/assets/image-20221008195359-0baltm2.png) MySQL 的基本架构示意图,从中你可以清楚地看到 SQL 语句在 MySQL 的各个功能模块中的执行过程![image.png](https://static.lianglianglee.com/assets/image-20221008195359-0baltm2.png)
大体来说MySQL 可以分为 Server 层和存储引擎层两部分。 大体来说MySQL 可以分为 Server 层和存储引擎层两部分。
1. Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。 1. Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
@ -24,7 +24,7 @@ MySQL 的基本架构示意图,从中你可以清楚地看到 SQL 语句在 My
MyISAM 引擎是 MySQL5.5 版本(不含)之前的数据库所默认的数据表引擎。每一个采用 MyISAM 引擎的数据表在实际存储中都是由三个文件组成,分别是 frm 文件MYD 文件和 MYI 文件,文件后缀为上述三个,文件名与数据表名相同。一个典型的 MyISAM 类型的数据表如下图(红框部分)所示: MyISAM 引擎是 MySQL5.5 版本(不含)之前的数据库所默认的数据表引擎。每一个采用 MyISAM 引擎的数据表在实际存储中都是由三个文件组成,分别是 frm 文件MYD 文件和 MYI 文件,文件后缀为上述三个,文件名与数据表名相同。一个典型的 MyISAM 类型的数据表如下图(红框部分)所示:
![image](https://blog-image.lianglianglee.com/assets/image-20221013142251-odk822t.png) ![image](https://static.lianglianglee.com/assets/image-20221013142251-odk822t.png)
frm 文件保存表的结构 frm 文件保存表的结构
@ -78,7 +78,7 @@ MEMORY 存储引擎拥有极高的插入,更新和查询效率
聚簇索引是索引的叶子节点上存储的数据。一张表只能有一个。因为 Innodb 引擎必须有主键因为数据在聚簇索引上及时不设置主键Innodb 也会生成一个主键【DB_ROW_ID】。Innodb 中的除主键外的索引都是非聚簇索引(二级索引)。非聚簇索引上存放的是主键的数据,从而根据主键查找实际的数据(回表)。 聚簇索引是索引的叶子节点上存储的数据。一张表只能有一个。因为 Innodb 引擎必须有主键因为数据在聚簇索引上及时不设置主键Innodb 也会生成一个主键【DB_ROW_ID】。Innodb 中的除主键外的索引都是非聚簇索引(二级索引)。非聚簇索引上存放的是主键的数据,从而根据主键查找实际的数据(回表)。
![image.png](https://blog-image.lianglianglee.com/assets/image-20221008195619-a1o1jlv.png) ![image.png](https://static.lianglianglee.com/assets/image-20221008195619-a1o1jlv.png)
## 2.2 非聚簇索引 ## 2.2 非聚簇索引
@ -468,7 +468,7 @@ MySQL 的行锁是在引擎层由各个引擎自己实现的。但并不是所
顾名思义,行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。 顾名思义,行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。
![image.png](https://blog-image.lianglianglee.com/assets/image-20221008194341-ol5iakt.png) ![image.png](https://static.lianglianglee.com/assets/image-20221008194341-ol5iakt.png)
**在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。** **在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。**
@ -484,7 +484,7 @@ innodb_lock_wait_timeout死锁等待的超时时间默认为 50s意味
当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。这里我用数据库中的行锁举个例子 当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。这里我用数据库中的行锁举个例子
![image.png](https://blog-image.lianglianglee.com/assets/image-20221008194533-hogldh7.png) ![image.png](https://static.lianglianglee.com/assets/image-20221008194533-hogldh7.png)
这时候,事务 A 在等待事务 B 释放 id=2 的行锁,而事务 B 在等待事务 A 释放 id=1 的行锁。 事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。当出现死锁以后,有两种策略: 这时候,事务 A 在等待事务 B 释放 id=2 的行锁,而事务 B 在等待事务 A 释放 id=1 的行锁。 事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。当出现死锁以后,有两种策略:
@ -502,7 +502,7 @@ innodb_lock_wait_timeout死锁等待的超时时间默认为 50s意味
* **共享锁S 锁):**共享锁允许一个事务读数据,不允许修改数据,如果其他事务要再对该行加锁,只能加共享锁; * **共享锁S 锁):**共享锁允许一个事务读数据,不允许修改数据,如果其他事务要再对该行加锁,只能加共享锁;
* **排它锁X 锁):**排他锁是修改数据时加的锁,可以读取和修改数据,一旦一个事务对该行数据加锁,其他事务将不能再对该数据加任务锁。 * **排它锁X 锁):**排他锁是修改数据时加的锁,可以读取和修改数据,一旦一个事务对该行数据加锁,其他事务将不能再对该数据加任务锁。
![image.png](https://blog-image.lianglianglee.com/assets/image-20221008201113-yjyu78x.png) ![image.png](https://static.lianglianglee.com/assets/image-20221008201113-yjyu78x.png)
### 8.1.6 相关文档 ### 8.1.6 相关文档
@ -524,9 +524,9 @@ alter table sbtest1 add column h int(11) default null,algorithm=inplace;
* inplace无需拷贝全表数据到新表但可能还是需要 IN-PLACE 方式(原地,无需生成新的临时表)重建整表。这种情况下,在 DDL 的初始准备和最后结束两个阶段时通常需要加排他 MDL 锁metadata lock元数据锁除此外DDL 期间不会阻塞 DML * inplace无需拷贝全表数据到新表但可能还是需要 IN-PLACE 方式(原地,无需生成新的临时表)重建整表。这种情况下,在 DDL 的初始准备和最后结束两个阶段时通常需要加排他 MDL 锁metadata lock元数据锁除此外DDL 期间不会阻塞 DML
* instant只需修改数据字典中的元数据无需拷贝数据也无需重建整表同样也无需加排他 MDL 锁,原表数据也不受影响。整个 DDL 过程几乎是瞬间完成的,也不会阻塞 DML。这个新特性是 8.0.12 引入的(腾讯提交的补丁)。 * instant只需修改数据字典中的元数据无需拷贝数据也无需重建整表同样也无需加排他 MDL 锁,原表数据也不受影响。整个 DDL 过程几乎是瞬间完成的,也不会阻塞 DML。这个新特性是 8.0.12 引入的(腾讯提交的补丁)。
![image.png](https://blog-image.lianglianglee.com/assets/image-20221008192941-c13f3bz.png) ![image.png](https://static.lianglianglee.com/assets/image-20221008192941-c13f3bz.png)
![image.png](https://blog-image.lianglianglee.com/assets/image-20221008192924-dkgljj2.png) ![image.png](https://static.lianglianglee.com/assets/image-20221008192924-dkgljj2.png)
> MySQL 会自己决策使用哪种方式,一般不需要指定 > MySQL 会自己决策使用哪种方式,一般不需要指定

View File

@ -46,13 +46,13 @@ docker run -d -v /srv/dev-disk-by-uuid-XXXXXX-XXX-XXX-XX/disk1:/var/webdav -e US
4. 开始手动同步 4. 开始手动同步
速度: 速度:
![](https://blog-image.lianglianglee.com/assets/20230406_155750.png) ![](https://static.lianglianglee.com/assets/20230406_155750.png)
基本能跑到850Mbps以上 基本能跑到850Mbps以上
老NAS的CPU占用 老NAS的CPU占用
![](https://blog-image.lianglianglee.com/assets/old_nas_cpu_use.png) ![](https://static.lianglianglee.com/assets/old_nas_cpu_use.png)
## 应用迁移 ## 应用迁移
@ -64,11 +64,11 @@ docker run -d -v /srv/dev-disk-by-uuid-XXXXXX-XXX-XXX-XX/disk1:/var/webdav -e US
需要注意在portainer中的CMD 对应TrueNAS的Container Args而且有空格就要隔开比如`-mode prod -workspace /siyuanworkspace -accessAuthCode password`,就要按照空格一个一个拆开 需要注意在portainer中的CMD 对应TrueNAS的Container Args而且有空格就要隔开比如`-mode prod -workspace /siyuanworkspace -accessAuthCode password`,就要按照空格一个一个拆开
![](https://blog-image.lianglianglee.com/assets/20230406_162245.png) ![](https://static.lianglianglee.com/assets/20230406_162245.png)
因为我需要独立的IP所以在网络使用了独立的静态IP 因为我需要独立的IP所以在网络使用了独立的静态IP
![](https://blog-image.lianglianglee.com/assets/20230406_161755.png) ![](https://static.lianglianglee.com/assets/20230406_161755.png)
映射一下文件夹 映射一下文件夹
@ -78,7 +78,7 @@ docker run -d -v /srv/dev-disk-by-uuid-XXXXXX-XXX-XXX-XX/disk1:/var/webdav -e US
**关于文件夹权限** **关于文件夹权限**
我习惯将Dock挂载的目录也SMB共享出来,方便修改配置之类的TrueNAS默认不允许这样干则需要进入`应用`-> `设置`-> `高级设置` 关闭`Enable Host Path Safety Checks` 我习惯将Dock挂载的目录也SMB共享出来,方便修改配置之类的TrueNAS默认不允许这样干则需要进入`应用`-> `设置`-> `高级设置` 关闭`Enable Host Path Safety Checks`
![](https://blog-image.lianglianglee.com/assets/20230406_162603.png) ![](https://static.lianglianglee.com/assets/20230406_162603.png)
同意将docker挂载的目录分到一个docker文件夹下大概目录是这样 同意将docker挂载的目录分到一个docker文件夹下大概目录是这样

View File

@ -20,7 +20,7 @@ date: 2023-11-30 14:42:03
查看日志:`journalctl -xe` 查看日志:`journalctl -xe`
![image](https://blog-image.lianglianglee.com/assets/image-20231129104735-a92h5s7.png) ![image](https://static.lianglianglee.com/assets/image-20231129104735-a92h5s7.png)
核心报错: 核心报错:
@ -34,7 +34,7 @@ connection activation failed no suitable device found for this connection
查看配置 查看配置
![image](https://blog-image.lianglianglee.com/assets/image-20231129105007-obzvhzr.png) ![image](https://static.lianglianglee.com/assets/image-20231129105007-obzvhzr.png)
发现网卡物理地址有配置而且复制虚拟机基于虚拟磁盘复制此文件肯定不会变。故这个网卡地址是一样的。PVE复制的时候又会生成新的网卡地址。 发现网卡物理地址有配置而且复制虚拟机基于虚拟磁盘复制此文件肯定不会变。故这个网卡地址是一样的。PVE复制的时候又会生成新的网卡地址。
@ -42,6 +42,6 @@ connection activation failed no suitable device found for this connection
变更网卡地址即可寻找PVE生成的网卡地址 变更网卡地址即可寻找PVE生成的网卡地址
![image](https://blog-image.lianglianglee.com/assets/image-20231129105152-ina6aoi.png) ![image](https://static.lianglianglee.com/assets/image-20231129105152-ina6aoi.png)
然后更新到centos配置文件中重启网络即可解决。 然后更新到centos配置文件中重启网络即可解决。

View File

@ -12,28 +12,28 @@ date: 2023-04-13 13:58:25
服务以Java jar的形式在服务器上直接运行外层套一个nginx作端口及域名中转发 服务以Java jar的形式在服务器上直接运行外层套一个nginx作端口及域名中转发
域名直接解析到云服务器用户可以通过DNS轻而易举拿到云服务器IP然后攻击。 域名直接解析到云服务器用户可以通过DNS轻而易举拿到云服务器IP然后攻击。
当时碰到了SSH暴力破解网站被爬虫刷流量 当时碰到了SSH暴力破解网站被爬虫刷流量
![](https://blog-image.lianglianglee.com/assets/20230413_140305.png) ![](https://static.lianglianglee.com/assets/20230413_140305.png)
## CDN模式 ## CDN模式
![](https://blog-image.lianglianglee.com/assets/20230413_140212.png) ![](https://static.lianglianglee.com/assets/20230413_140212.png)
目前常用的隐藏源站IP的方式就是在外层套一个CDN。比如阿里云的DCDN加速。其原理就是在外层再套一个nginx类服务。因为云服务厂商有更充足的带宽和计算资源还有更充足的异常请求检查算法比自己建设要好不少。 目前常用的隐藏源站IP的方式就是在外层套一个CDN。比如阿里云的DCDN加速。其原理就是在外层再套一个nginx类服务。因为云服务厂商有更充足的带宽和计算资源还有更充足的异常请求检查算法比自己建设要好不少。
引入cloudflare主要原因是其有免费的基础服务基本够我使用。引入DCDN还有一个好处就是可以查看请求量的简要信息。 引入cloudflare主要原因是其有免费的基础服务基本够我使用。引入DCDN还有一个好处就是可以查看请求量的简要信息。
接入及其简单只需要在购买域名的平台将域名服务器切换到cloudflare提供的即可。 接入及其简单只需要在购买域名的平台将域名服务器切换到cloudflare提供的即可。
![](https://blog-image.lianglianglee.com/assets/20230413_140508.png) ![](https://static.lianglianglee.com/assets/20230413_140508.png)
## 引入分析 ## 引入分析
网站部署起来后就要想办法分析服务相关信息比如PVUV等等。第一反应引入第三方网站。比如google analytics。该服务国内可用。引入也很简单只需要引入一个JS文件即可。 网站部署起来后就要想办法分析服务相关信息比如PVUV等等。第一反应引入第三方网站。比如google analytics。该服务国内可用。引入也很简单只需要引入一个JS文件即可。
引入后发现其数据比较离谱,后来发现是因为浏览器自带的防跟踪功能,会屏蔽掉这些数据分析的服务商 引入后发现其数据比较离谱,后来发现是因为浏览器自带的防跟踪功能,会屏蔽掉这些数据分析的服务商
![](https://blog-image.lianglianglee.com/assets/20230413_140810.png) ![](https://static.lianglianglee.com/assets/20230413_140810.png)
如果真的要分析PVUV则需要自建服务。经过一番调研采用了umami自建服务。 如果真的要分析PVUV则需要自建服务。经过一番调研采用了umami自建服务。
使用docker-compose部署一个umami + postgreSQL。然后域名定义成 analyze.***.com同样交给cloudflare作CDN。 使用docker-compose部署一个umami + postgreSQL。然后域名定义成 analyze.***.com同样交给cloudflare作CDN。
使用同样简单在网站中引入一个JS文件即可。 使用同样简单在网站中引入一个JS文件即可。
采集后的分析 采集后的分析
![](https://blog-image.lianglianglee.com/assets/20230413_140617.png) ![](https://static.lianglianglee.com/assets/20230413_140617.png)
## 自恢复 ## 自恢复
算算服务器上部署了几个程序: 算算服务器上部署了几个程序:

View File

@ -48,7 +48,7 @@ console.log(JSON.stringify(value))
可以看到抓取到了url及标题。 可以看到抓取到了url及标题。
![image](https://blog-image.lianglianglee.com/assets/20231130144903.png) ![image](https://static.lianglianglee.com/assets/20231130144903.png)
怎么解决存储的问题呢因为每次刷新页面都会丢失这些数据。最简单的方案直接用localStorage所以需要提供一个获取和存储的方法 怎么解决存储的问题呢因为每次刷新页面都会丢失这些数据。最简单的方案直接用localStorage所以需要提供一个获取和存储的方法

View File

@ -383,11 +383,11 @@ markdown转换使用CopyDown
查看执行效果: 查看执行效果:
目录: 目录:
![](https://blog-image.lianglianglee.com/assets/20230803_183347.png) ![](https://static.lianglianglee.com/assets/20230803_183347.png)
图片: 图片:
![](https://blog-image.lianglianglee.com/assets/20230803_183713.png) ![](https://static.lianglianglee.com/assets/20230803_183713.png)