website-01/components/admin/RichEditor.tsx
2026-06-22 14:43:46 +08:00

93 lines
2.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import dynamic from 'next/dynamic';
import { useMemo } from 'react';
import { cn } from '@/lib/utils';
// quill 主题样式client 组件可直接 import
import 'react-quill/dist/quill.snow.css';
// react-quill 仅支持客户端渲染
const ReactQuill = dynamic(() => import('react-quill'), {
ssr: false,
loading: () => (
<div className="flex h-64 items-center justify-center rounded bg-gray-50 text-xs text-gray-400">
</div>
),
});
export interface RichEditorProps {
value?: string;
onChange: (html: string) => void;
placeholder?: string;
className?: string;
}
const TOOLBAR = [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ color: [] }, { background: [] }],
[{ align: [] }, { list: 'ordered' }, { list: 'bullet' }],
['blockquote', 'code-block'],
['link', 'image'],
['clean'],
];
// 图片插入:转 base64 内联(如需对接后端上传,可在此处替换实现)
// quill 的 toolbar handler this 指向 toolbar 实例,需要 any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function imageHandler(this: any): void {
const quill = this?.quill;
if (!quill) return;
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.addEventListener('change', () => {
const file = input.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
const range = quill.getSelection();
const idx = range ? range.index : 0;
quill.insertEmbed(idx, 'image', String(reader.result));
};
reader.readAsDataURL(file);
});
input.click();
}
export function RichEditor({
value,
onChange,
placeholder = '请输入内容…',
className,
}: RichEditorProps) {
const modules = useMemo(
() => ({
toolbar: {
container: TOOLBAR,
handlers: { image: imageHandler },
},
}),
[],
);
return (
<div
className={cn(
'overflow-hidden rounded-md border border-gray-300 bg-white',
className,
)}
>
<ReactQuill
theme="snow"
value={value ?? ''}
onChange={onChange}
modules={modules}
placeholder={placeholder}
/>
</div>
);
}