文章
nextjs使用wangEditor
1、富文本编辑器
RichEditor.tsx
"use client";
import "@wangeditor-next/editor/dist/css/style.css";
import { Editor, Toolbar } from "@wangeditor-next/editor-for-react";
import { IDomEditor, IEditorConfig, IToolbarConfig } from "@wangeditor-next/editor";
import { useEffect, useState } from "react";
interface RichEditorProps {
content: string;
onChange: (html: string) => void;
}
export default function RichEditor({ content, onChange }: RichEditorProps) {
const [editor, setEditor] = useState<IDomEditor | null>(null);
const toolbarConfig: Partial<IToolbarConfig> = {};
const editorConfig: Partial<IEditorConfig> = {
placeholder: "请输入内容...",
autoFocus: false,
};
const handleCreated = (editorInstance: IDomEditor) => {
setEditor(editorInstance);
editorInstance.setHtml(content || "<p></p>");
};
const handleChange = (editorInstance: IDomEditor) => {
const html = editorInstance.getHtml();
onChange(html);
};
useEffect(() => {
return () => {
if (editor) {
editor.destroy();
setEditor(null);
}
};
}, [editor]);
return (
<div style={{ border: "1px solid #ccc", zIndex: 100 }}>
<Toolbar
editor={editor}
defaultConfig={toolbarConfig}
mode="default"
style={{ borderBottom: "1px solid #ccc" }}
/>
<Editor
value={content}
defaultConfig={editorConfig}
onCreated={handleCreated}
onChange={handleChange}
mode="default"
style={{ height: "500px", overflowY: "hidden" }}
/>
</div>
);
}2、封装进表单组件
"use client";
import { FormProvider, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
createDocumentDefaultValues,
createDocumentSchema,
CreateDocumentSchema,
} from "@/app/(protected)/dashboard/documents/_types/form-schema";
import { ControlledInput } from "@/components/controlled/controlled-input";
import dynamic from "next/dynamic";
import { Button } from "@/components/ui/button";
import { useEffect, useState } from "react";
const RichEditor = dynamic(() => import("@/components/rich-editor"), {
ssr: false,
loading: () => <p className="text-sm text-muted-foreground">加载编辑器...</p>,
});
interface DocumentFormProps {
mode: "create" | "edit";
initialData?: {
title: string;
content: string;
};
isPending?: boolean;
onSubmit: (data: { title: string; content: string }) => void;
}
export function DocumentForm({
mode,
initialData,
isPending,
onSubmit,
}: DocumentFormProps) {
const form = useForm<CreateDocumentSchema>({
defaultValues: createDocumentDefaultValues,
resolver: zodResolver(createDocumentSchema),
});
const [content, setContent] = useState("");
/** 编辑模式:回填 */
useEffect(() => {
if (mode === "edit" && initialData) {
form.reset({ title: initialData.title });
setTimeout(() => {
setContent(initialData.content)
}, 50);
}
}, [mode, initialData, form]);
const handleSubmit = (data: CreateDocumentSchema) => {
onSubmit({
title: data.title,
content,
});
};
return (
<FormProvider {...form}>
<form
onSubmit={form.handleSubmit(handleSubmit)}
className="space-y-6 max-w-4xl mx-auto"
>
{/* 标题 */}
<ControlledInput<CreateDocumentSchema>
name="title"
label="标题"
placeholder="请输入文档标题"
/>
{/* 内容 */}
<div className="space-y-2">
<label className="text-sm font-medium">内容</label>
<RichEditor content={content} onChange={setContent} />
</div>
<div className="flex justify-end">
<Button type="submit" disabled={isPending}>
{mode === "edit" ? "更新文档" : "创建文档"}
</Button>
</div>
</form>
</FormProvider>
);
}
这里的form-schema.ts
import { requiredStringSchema } from "@/lib/zod-schemas";
import z from "zod";
// 创建文档表单
export const createDocumentSchema = z.object({
title: requiredStringSchema,
});
export type CreateDocumentSchema = z.infer<typeof createDocumentSchema>;
export const createDocumentDefaultValues: CreateDocumentSchema = {
title: "",
};
// 更新文档表单
export const updateDocumentSchema = z.object({
id: z.string(),
title: requiredStringSchema,
});
export type UpdateDocumentSchema = z.infer<typeof updateDocumentSchema>;
export const updateDocumentDefaultValues: UpdateDocumentSchema = {
id: "",
title: "",
};