UEditor 基础用法:快速上手富文本编辑器

在网页开发中,富文本编辑器是内容管理系统的核心组件之一。UEditor 是百度推出的一款开源编辑器,功能强大且高度可定制,非常适合文章发布、后台管理等场景。下面通过一段实际代码,简单说明如何快速集成 UEditor 并配置基础功能。

第一步:引入 UEditor 文件

首先需要下载 UEditor 的源码(可从官网获取),将相关文件放到项目目录下。假设你的编辑器文件存放在 /ueditor 路径中,在 HTML 头部引入配置文件和核心 JS:

<!-- 引入 UEditor 配置文件和核心脚本 -->
<script src="/ueditor/ueditor.config.js" asp-append-version="true"></script>
<script src="/ueditor/ueditor.all.min.js"></script>

第二步:初始化编辑器

在页面底部或 <body> 结束前,通过 JS 初始化编辑器。以下代码创建了一个高度自适应的编辑器,并自定义了工具栏按钮:

<script>
    var ue = UE.getEditor('container', {
        initialFrameHeight: 515,       // 初始高度
        initialFrameWidth: "100%",     // 宽度占满容器
        autoHeightEnabled: true,       // 自动撑高(根据内容)
        enableAutoSave: false,         // 关闭自动保存(避免频繁触发)

        // 自定义工具栏(按需删减按钮)
        toolbars: [[
            'fullscreen', 'source', '|', 'undo', 'redo', '|',
            'bold', 'italic', 'underline', 'forecolor', 'backcolor', '|',
            'insertorderedlist', 'insertunorderedlist', '|',
            'link', 'unlink', 'anchor', '|', 'image', 'inserttable', '|',
            'preview', 'searchreplace'
        ]]
    });
</script>

第三步:HTML 容器

在页面中需要放置编辑器的位置添加一个 <script> 标签作为容器,并指定 id 与初始化代码中的参数一致:

<script id="container" name="content" type="text/plain">
    这里写初始化内容(用户打开页面时会默认显示)
</script>

可能遇到的问题

  1. 路径错误:如果编辑器未加载,检查 JS 文件的路径是否正确。
  2. 工具栏不显示:确保 toolbars 配置的按钮名称没有拼写错误。
  3. 自适应高度失效:检查是否同时设置了 initialFrameHeight 和 autoHeightEnabled,两者需配合使用。
  4. 移动端兼容:UEditor 对移动端支持较弱,复杂场景建议搭配移动端专用编辑器。

实用小技巧

  • 精简工具栏:示例中只保留了常用按钮,可通过增减 toolbars 中的按钮名称灵活调整。
  • 内容获取与赋值:通过 ue.getContent() 获取编辑后的内容,ue.setContent('文本') 动态修改内容。
  • 主题自定义:修改 ueditor.config.js 中的 themePath 可更换编辑器皮肤。

如果只是需要基础的图文编辑功能,这段代码已经足够用了。更复杂的需求(比如上传文件到自己的服务器)可以查阅官方文档进一步配置。

Laravel 修改 API 请求前缀

本文所使用的 Laravel 大版本为 Laravel 10。

本来 Laravel 的 API 路由会默认自动带上api前缀,即类似于http://127.0.0.1:8000/api/get这样的形式,本来这种形式就挺美观的,也没有什么问题。但是遇到一个需求,要求在api前面加上一串固定的字符串,类似于http://127.0.0.1:8000/e10adc3949ba59abbe56e057f20f883e/api/get的形式。行吧,既然是客户的需求,那就改吧。

首先我们找到app/Providers/RouteServiceProvider.php这个文件,在boot函数里面修改api前缀,代码如下:

// language: php
/**
 * Define your route model bindings, pattern filters, and other route configuration.
 */
public function boot(): void
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });

    $this->routes(function () {
        Route::middleware('api')
            ->prefix('e10adc3949ba59abbe56e057f20f883e/api') // 👈 修改这行
            ->group(base_path('routes/api.php'));

        Route::middleware('web')
            ->group(base_path('routes/web.php'));
    });
}

这样,所有API路由的前缀就会自动变成e10adc3949ba59abbe56e057f20f883e/api。怎么样,Laravel 是不是很简单?!

后记:

时间来到2025年的2月份,这是这个月的第一篇文章,本来没什么想写的,但是再不写,这个月就要过去了,文章归档就要少一个月,就随便水一篇吧。

已经很长很长时间没有工作了,迫于经济压力,开年以来,在做网站,对,你没看错,AI时代做网站!我知道这条路大概率走不通,但是又没有什么可做的,权且当个自我安慰吧,以至于不太焦虑。前两天有一个网站通过了 Google AdSense,但看到收入后,更焦虑了,每天几只有几分钱,还是我自己浏览的。这个也没有办法,网站缺少竞争力,没有什么流量,那么没有收入也是正常的,唉,再继续完善继续优化吧。

纯 JS 动态插入 li 标签:告别 jQuery 的轻量级方案

在网页开发中,动态修改列表内容是很常见的需求。比如实时添加待办事项、展示动态加载的数据等。如果不想依赖 jQuery 这类第三方库,通过原生 JavaScript 也能轻松实现。下面通过基础代码和扩展案例,演示如何用纯 JS 操作 DOM 动态插入列表项。

基础实现代码

var li = document.createElement("li");
li.appendChild(document.createTextNode("li里的文字"));              
document.getElementById('ul_id').appendChild(li);

代码拆解

  1. createElement
    创建 <li> 元素,类似准备一个空容器。
  2. createTextNode + appendChild
    先创建文本节点再插入到 li,比直接写 innerHTML 更安全(避免 XSS 攻击风险)。
  3. getElementById 定位父元素
    通过 ul 的 id 找到目标列表,用 appendChild 将 li 添加为最后一个子元素。

实际应用场景

<!-- 示例:点击按钮动态添加列表项 -->
<ul id="todoList">
  <li>第一项任务</li>
</ul>
<button onclick="addItem()">添加新任务</button>

<script>
function addItem() {
  var li = document.createElement("li");
  li.textContent = "新增任务" + Date.now(); // 更简洁的文本写入方式
  document.getElementById('todoList').appendChild(li);
}
</script>

纯 JS 实现 AJAX

在现代网页开发中,AJAX(Asynchronous JavaScript and XML)技术被广泛应用于实现异步请求,以便无需重新加载页面即可更新内容。下面我们展示如何用纯 JavaScript 实现 AJAX 请求。

1. 基本的 AJAX 请求

//language:js
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
    document.getElementById("demo").innerHTML = this.responseText;
}
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send();

在这个例子中,我们创建了一个新的 XMLHttpRequest 对象,并通过 open 方法指定请求类型(GET)、请求的 URL(”ajax_info.txt”)以及是否异步(true)。当请求成功完成后,onload 事件会被触发,响应的内容会被显示在页面上的 demo 元素中。

2. 发送表单数据并响应

如果我们需要发送表单数据,可以使用 FormData 对象,它会自动处理表单中的所有数据。

//language:js
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
    let response = JSON.parse(this.responseText);
    if (response.code == 0) {
        alert('留言成功');
    } else {
        alert(response.msg);
    }                
}
xhttp.open("POST", "post-message.php", true);

var guestBookForm = document.getElementById("guestBookForm");
var formData = new FormData(guestBookForm);

xhttp.send(formData);

在这个例子中,我们发送一个 POST 请求到 post-message.php,并通过 FormData 对象封装了表单中的数据。当 PHP 处理完请求后,返回一个 JSON 格式的响应,JavaScript 会根据返回的 code 来判断留言是否成功。

3. PHP 处理代码

以下是对应的 PHP 处理代码:

//language:php
<?php
include 'db.php';

$guestName = $_POST['guestName'];
$message = $_POST['message'];

if (empty($guestName)) {
    echo json_encode(['code' => -1, 'msg' => '昵称不能为空']);
    exit();
}

if (empty($message)) {
    echo json_encode(['code' => -1, 'msg' => '留言内容不能为空']);
    exit();
}

// 进一步的处理,如存入数据库等
?>

在 PHP 端,我们检查了用户提交的 guestName 和 message 是否为空。如果为空,则返回相应的错误信息。

4. 自定义 FormData

如果需要手动构建 FormData,例如在动态生成数据时,可以这样做:

//language:js
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
    let response = JSON.parse(this.responseText);                                
}
xhttp.open("POST", "pay.php", true);

var formData = new FormData();
formData.append("id", id);
formData.append("unitPrice", unitPrice);
formData.append("count", P[i]);

xhttp.send(formData);

通过这种方式,你可以灵活地向服务器发送任何自定义的数据。

JS判断某个对象是否有某个属性

在 JavaScript 中,我们可以使用 hasOwnProperty 方法来判断某个对象是否拥有指定的属性。这个方法可以帮助我们区分对象本身的属性和从原型链继承的属性。

1. 使用 hasOwnProperty 方法

hasOwnProperty 是 Object.prototype 上的方法,它不会检查原型链,因此它是最可靠的方式来判断对象自身是否包含某个属性。

示例代码:

const obj = {
    name: "张三",
    age: 25
};

console.log(obj.hasOwnProperty("name")); // true
console.log(obj.hasOwnProperty("age"));  // true
console.log(obj.hasOwnProperty("gender")); // false

2. in 运算符

in 运算符可以用于检查对象及其原型链上是否存在某个属性。

示例代码:

const obj = {
    name: "李四"
};

console.log("name" in obj);  // true
console.log("toString" in obj); // true,因为 `toString` 是 Object.prototype 上的方法

3. Object.hasOwn(ES2022 新增)

在 ES2022 中,JavaScript 引入了 Object.hasOwn,它是 hasOwnProperty 的更安全替代方法。

示例代码:

const obj = {
    city: "北京"
};

console.log(Object.hasOwn(obj, "city")); // true
console.log(Object.hasOwn(obj, "country")); // false

4. 何时使用哪种方法?

  • hasOwnProperty:适用于检查对象自身是否拥有某个属性,避免检查原型链上的属性。
  • in 运算符:如果想要检查某个属性是否存在于对象或其原型链上,可以使用 in。
  • Object.hasOwn:如果环境支持 ES2022,建议使用 Object.hasOwn,因为它不会被 hasOwnProperty 可能被覆盖的问题影响。

5. 注意事项

如果某个对象的 hasOwnProperty 方法被覆盖,调用 Object.prototype.hasOwnProperty.call(obj, key) 仍然可以检查属性:

const obj = {
    hasOwnProperty: function() {
        return false;
    },
    id: 1001
};

console.log(obj.hasOwnProperty("id")); // false(被覆盖)
console.log(Object.prototype.hasOwnProperty.call(obj, "id")); // true

6. 结论

在 JavaScript 中,判断对象是否具有某个属性的方法有多种,通常推荐使用 hasOwnProperty 或 Object.hasOwn 进行安全检查。对于特殊情况,可以结合 in 运算符或者 Object.prototype.hasOwnProperty.call 来确保可靠性。

希望这篇文章对你有所帮助!

把JS数组赋值给表单里的字段

在实际开发中,我们经常需要将 JavaScript 数组的值提交到表单中。本文介绍如何将一个 JS 数组 selectedTextArray 赋值给表单中的隐藏字段,以便在提交时传输到服务器。

1. HTML 结构

我们使用一个 ID 为 form 的表单,并在 onclick 事件中调用 update() 方法来动态添加隐藏字段。

<form class="form-horizontal" id="form" action="/role-permission/update/{{ $role->id }}" method="post">
    @csrf
    <div class="box-body">                  
        <div class="form-group">
            <div class="col-sm-12">
                <div id="tree"></div>
            </div>
        </div>         
    </div>
    <div class="box-footer">
        <button type="submit" class="btn btn-primary pull-right" onclick="return update();">保存</button>
    </div>
</form>

2. JavaScript 代码

在 update() 方法中,我们遍历 selectedTextArray 数组,并动态创建 input 隐藏字段,将其添加到表单中。

function update() {
    // 假设 selectedTextArray 是一个已有的数组
    let selectedTextArray = ["read", "write", "execute"]; // 示例数据
    
    let form = document.getElementById("form");
    
    // 清除已有的隐藏字段,避免重复提交
    document.querySelectorAll("input[name='permissions_array[]']").forEach(el => el.remove());
    
    // 遍历数组并创建隐藏字段
    for (let i = 0; i < selectedTextArray.length; i++) {
        let item = document.createElement("input");
        item.type = "hidden";
        item.name = "permissions_array[]";
        item.value = selectedTextArray[i];  
        form.appendChild(item);
    }

    return true; // 允许表单提交
}

3. 关键点解析

  • item.name = "permissions_array[]"; 这一行代码的作用是确保数据以数组的形式提交到服务器。
  • document.querySelectorAll("input[name='permissions_array[]']").forEach(el => el.remove()); 这行代码确保每次点击提交按钮时都会清除先前的隐藏字段,避免重复数据。
  • 通过 appendChild(item); 将新创建的隐藏 input 字段添加到表单中。

4. 提交到服务器的示例数据

假设 selectedTextArray 包含 ["read", "write", "execute"],提交后服务器接收到的数据格式如下:

{
    "permissions_array": ["read", "write", "execute"]
}

5. 结论

通过 JavaScript,我们可以动态地为表单添加隐藏字段,并将数组数据以正确的格式提交到服务器。这样可以方便地传输多选数据,如权限管理、标签选择等场景。

根据 MDF 文件查询数据库版本

在 SQL Server 中,MDF(主数据库文件)包含了数据库的版本信息。如果需要查询 MDF 文件对应的 SQL Server 版本,可以使用 PowerShell 读取文件中的特定字节数据。

1. 使用 PowerShell 查询 MDF 版本

PS > get-content -Encoding Byte "C:\path\to\database.mdf" | select-object -skip 0x12064 -first 2
PS > [int]($_[0] + $_[1] * 256)

2. 解析数据库版本

上述 PowerShell 命令的逻辑如下:

  1. get-content -Encoding Byte "C:\path\to\database.mdf" 以字节形式读取 MDF 文件。
  2. select-object -skip 0x12064 -first 2 跳过前 0x12064 个字节,并获取接下来的 2 个字节(这两个字节存储了 SQL Server 版本信息)。
  3. $_[0] + $_[1] * 256 计算版本号。

例如,我们执行下面的命令:

get-content -Encoding Byte "d:/B2B.mdf" | select-object -skip 0x12064 -first 2

命令返回:

149
2

那么,我们用149+2*256来计算版本号,得到结果是661,然后到下表里面去查找即可。

如果不想手动计算,也可以使用下面的三行命令,让程序自动计算版本号:

PS > $bytes = Get-Content -Encoding Byte "d:/B2B.mdf" | Select-Object -Skip 0x12064 -First 2
PS > $version = $bytes[0] + ($bytes[1] * 256)
PS > $version

3. 版本号对照表

计算出的整数版本号可以对照以下 SQL Server 版本表:

版本号SQL Server 版本
515SQL Server 7.0
539SQL Server 2000
611SQL Server 2005
655SQL Server 2008
661SQL Server 2008 R2
706SQL Server 2012
782SQL Server 2014
852SQL Server 2016
869SQL Server 2017
904SQL Server 2019
921SQL Server 2022

4. 结论

通过 PowerShell 读取 MDF 文件的特定字节,我们可以快速确定数据库版本,无需启动 SQL Server 实例。此方法适用于数据库管理员或运维人员排查数据库兼容性问题。

sqlserver 数据库锁

1、HOLDLOCK

其他事务可以读取,但不能更新删除

// language: sql
BEGIN TRAN
SELECT * FROM ceb_Area WITH(ROWLOCK,HOLDLOCK) where ID=1--行锁
SELECT * FROM ceb_Area WITH(TABLOCK,HOLDLOCK) where ID=1--表锁
WAITFOR DELAY '00:00:10'
COMMIT TRAN;  

2、TABLOCKX 排他表锁

其他事务不能读取表,更新和删除

// language: sql
BEGIN TRAN
SELECT * FROM CheweiCustoms WITH(TABLOCKX) WHERE id=313;
UPDATE CheweiCustoms SET Name='abccc' WHERE id = 313;
SELECT * FROM CheweiCustoms WHERE id=313;
WAITFOR DELAY '00:00:10';
COMMIT TRAN;  

同一个事物内,可以操作被锁住的表。其它事物不能操作,必须等锁表的事物完成后才行。

ASP.NET Core 记录请求日志

在 ASP.NET Core 应用程序中,记录请求日志可以帮助开发者分析请求情况、调试错误以及监控应用的运行状态。本文介绍如何通过配置 Logging 记录请求日志。

配置 appsettings.json 记录请求日志

在 appsettings.json 文件的 Logging 配置中,添加或修改以下内容:

"Logging": {
  "LogLevel": {
    "Default": "Information",
    "Microsoft": "Warning",
    "Microsoft.Hosting.Lifetime": "Information",
    // 下面两行用于记录请求日志
    "Microsoft.AspNetCore.Hosting.Diagnostics": "Information",
    "Microsoft.AspNetCore.Server.Kestrel": "Information"
  }
}

如果你的 appsettings.json 文件中已经有 Logging 配置,只需添加以下两行:

"Microsoft.AspNetCore.Hosting.Diagnostics": "Information",
"Microsoft.AspNetCore.Server.Kestrel": "Information"

说明

Microsoft.AspNetCore.Server.Kestrel:记录 Kestrel 服务器的详细请求信息,例如请求地址、请求头、响应状态等。

Microsoft.AspNetCore.Hosting.Diagnostics:记录 ASP.NET Core 请求处理的相关日志,包括中间件执行等。

运行应用并查看日志

如果你的应用使用 控制台日志 或 文件日志,可以在终端或日志文件中查看请求日志。例如,运行应用后,在控制台中可能会看到类似的日志输出:

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://localhost:5000/  
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 25ms 200 application/json  

如果你希望将日志输出到文件,可以在 appsettings.json 中进一步配置日志提供程序,例如使用 Serilog 或 NLog 进行日志存储和分析。

Debian使用服务单元部署asp.net core

创建 /etc/systemd/system/your_app.service 文件,替换为适合你的应用的名称:

[Unit]
Description=ASP.NET Core Application

[Service]
WorkingDirectory=/path/to/your_app/publish
ExecStart=/usr/bin/dotnet /path/to/your_app/publish/your_app.dll
Restart=always
RestartSec=10
SyslogIdentifier=dotnet-your_app
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target
  • WorkingDirectory:应用程序的工作目录(发布目录)。
  • ExecStart:启动应用的命令,这里指定 dotnet 和应用的 DLL 文件路径。
  • Restart:配置应用异常退出时自动重启。
  • SyslogIdentifier:指定日志标识符,可以方便地过滤日志。
  • User:指定运行服务的用户(例如 www-data,避免使用 root 用户)。
  • Environment:指定环境变量(如 Production)。

启动并启用服务:

sudo systemctl enable your_app.service
sudo systemctl start your_app.service

查看服务状态:

sudo systemctl status your_app.service

查看服务日志:

sudo journalctl -u your_app.service

nginx 反代模板:

server {
    listen 80;
    server_name your_domain.com;

    location / {
        proxy_pass https://notes.wujie.me:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

查看实时日志输出

要实时查看应用的日志输出,可以使用以下命令:

sudo journalctl -u your_app.service -f
  • -u your_app.service 指定服务名称,替换为你的服务文件名称。
  • -f 表示实时跟踪日志输出(类似 tail -f)。

查看全部日志

查看应用自启动以来的全部日志:

sudo journalctl -u your_app.service

这将显示该服务的全部日志记录。

按时间范围查看日志

可以按时间范围筛选日志,例如查看过去一小时的日志:

sudo journalctl -u your_app.service --since "1 hour ago"

或者查看特定日期之后的日志:

sudo journalctl -u your_app.service --since "2024-11-01"

按关键字过滤日志

如果你只想查看特定关键字的日志,例如包含错误信息的日志,可以使用 grep 命令进行过滤:

sudo journalctl -u your_app.service | grep "error"

 清除指定服务的日志

要清除特定服务(如 ASP.NET Core 应用)的日志,可以使用 journalctl 的 –vacuum 选项。虽然 journalctl 没有直接提供按服务清除的功能,但可以通过指定服务名来筛选日志,再结合清除命令:

sudo journalctl --vacuum-size=100M -u your_app.service

这将限制日志文件的总大小为 100MB,多余的日志会被删除。将 100M 替换为你期望的日志限制大小。

清除日志到指定时间点

你可以通过 –vacuum-time 参数清除指定时间之前的所有日志数据:

sudo journalctl --vacuum-time=7d

这将清除 7 天之前的所有日志数据,包括所有服务的日志(不是仅 ASP.NET Core 应用)。

清除所有 journald 日志

如果你希望彻底清除所有 journald 日志数据(适用于开发或测试环境,生产环境需谨慎),可以使用以下命令:

sudo journalctl --vacuum-size=1M

这会将日志数据限制为 1MB,基本相当于清空所有日志数据。

如果你希望彻底清除所有 journald 日志数据(适用于开发或测试环境,生产环境需谨慎),可以使用以下命令:

sudo journalctl –vacuum-size=1M

删除 journal 日志文件

可以手动删除存储在 /var/log/journal/ 目录下的日志文件。这样会清空所有服务的日志,建议谨慎使用。

sudo rm -rf /var/log/journal/*
sudo systemctl restart systemd-journald

定期清理

可以在 journald 配置文件 /etc/systemd/journald.conf 中设置日志的最大保留大小或时间,定期清理旧日志。