feat: generate id for heading in the default editor (#4909)

#### What type of PR is this?

/area console
/kind improvement
/milestone 2.11.x

#### What this PR does / why we need it:

在默认编辑器中,自动为 heading 类型元素生成有意义的 id,而不是原来的 `heading-x`。

<img width="1602" alt="图片" src="https://github.com/halo-dev/halo/assets/21301288/665e0acc-cb22-44d3-a4d5-c5b913687f32">

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/4838

#### Special notes for your reviewer:

需要测试在默认编辑器中编写若干 heading 类型的元素,发布之后查看元素的 id。

#### Does this PR introduce a user-facing change?

```release-note
为默认编辑器中 heading 类型元素生成有意义的 id。
```
pull/4898/head^2
Ryan Wang 2023-11-27 17:52:10 +08:00 committed by GitHub
parent f37fcb18fa
commit 70889e3cdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 1 deletions

View File

@ -87,6 +87,7 @@ import { usePluginModuleStore } from "@/stores/plugin";
import type { PluginModule } from "@halo-dev/console-shared";
import { useDebounceFn } from "@vueuse/core";
import { onBeforeUnmount } from "vue";
import { generateAnchor } from "@/utils/anchor";
const { t } = useI18n();
@ -436,7 +437,7 @@ const handleGenerateTableOfContent = () => {
editor.value.state.doc.descendants((node, pos) => {
if (node.type.name === "heading") {
const id = `heading-${headings.length + 1}`;
const id = generateAnchor(node.textContent);
if (node.attrs.id !== id) {
transaction?.setNodeMarkup(pos, undefined, {

View File

@ -0,0 +1,28 @@
import { describe, it, expect } from "vitest";
import { generateAnchor } from "../anchor";
describe("generateAnchor", () => {
it("should handle basic text", () => {
expect(generateAnchor("Hello World")).toBe("hello-world");
});
it("should trim whitespace", () => {
expect(generateAnchor(" Hello World ")).toBe("hello-world");
});
it("should replace multiple spaces with a single dash", () => {
expect(generateAnchor("Hello World")).toBe("hello-world");
});
it("should handle Chinese characters", () => {
expect(generateAnchor("你好")).toBe("%E4%BD%A0%E5%A5%BD");
});
it("should handle special characters", () => {
expect(generateAnchor("Hello@#World$")).toBe("hello%40%23world%24");
});
it("should handle empty string", () => {
expect(generateAnchor("")).toBe("");
});
});

View File

@ -0,0 +1,5 @@
export function generateAnchor(text: string) {
return encodeURIComponent(
String(text).trim().toLowerCase().replace(/\s+/g, "-")
);
}