import React, { useState, useEffect } from "react"; import { Alert, Button, Row, Col, Space, Input, Tooltip, } from "antd"; import "@wangeditor/editor/dist/css/style.css"; // 引入 css import { Editor, Toolbar } from "@wangeditor/editor-for-react"; import { SlateNode } from "@wangeditor/editor"; import { generateNewId } from "oak-domain/lib/utils/uuid"; import classNames from "classnames"; import Prompt from "../../../components/common/prompt"; import Style from "./web.module.less"; import { CloseOutlined, EyeOutlined, MenuOutlined, CaretDownOutlined } from "@ant-design/icons"; // 工具栏配置 const toolbarConfig = { excludeKeys: ["fullScreen"], }; // TS 语法 // 自定义校验图片 function customCheckImageFn(src, alt, url) { // TS 语法 if (!src) { return; } if (src.indexOf("http") !== 0) { return "图片网址必须以 http/https 开头"; } return true; // 返回值有三种选择: // 1. 返回 true ,说明检查通过,编辑器将正常插入图片 // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串) // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息 } function TocView(props) { const { toc, showToc, tocPosition, setShowToc, highlightBgColor, scrollId } = props; useEffect(() => { document.documentElement.style.setProperty('--highlight-bg-color', highlightBgColor); }, [highlightBgColor]); const generateTocList = (items, currentLevel = 1, parentId = null) => { //递归生成嵌套列表 const result = []; let lastId = parentId; while (items.length > 0) { const item = items[0]; //有无子级标题,有则显示展开图标 const hasChildren = items.length > 1 && items[1].level > item.level; if (item.level > currentLevel) { // 递归生成子列表 const sublist = generateTocList(items, item.level, item.id); result.push(
  • ); } else if (item.level < currentLevel) { // 结束当前层级 break; } else { // 添加当前层级的
  • result.push(
  • { const iconElem = document.getElementById(`icon-${item.id}`); const ulElem = document.getElementById(`ul-${item.id}`); const isFolded = iconElem?.className.includes('iconFold') || ulElem?.className.includes('fold'); if (isFolded) { iconElem?.classList.remove(Style.iconFold); ulElem?.classList.remove(Style.fold); } else { iconElem?.classList.add(Style.iconFold); ulElem?.classList.add(Style.fold); } }}/>
    { // editor.scrollToElem(item.id); //编辑器滚动到对应元素 const elem = document.getElementById(item.id); const elemTop = elem?.getBoundingClientRect().top; const scrollContainer = document.getElementById(scrollId || 'article-upsert-editorContainer'); const containerTop = scrollContainer?.getBoundingClientRect().top; scrollContainer?.scrollBy({ top: elemTop - containerTop, behavior: 'smooth', }); //添加背景色 elem?.classList.add(Style.highlight); //移除背景色类名 setTimeout(function () { elem?.classList.remove(Style.highlight); }, 1000); event.preventDefault(); event.stopPropagation(); }}> {item.text}
  • ); items.shift(); // 移除已处理的项 lastId = item.id; } } return result; }; return (
    {showToc ? (<>
    大纲
    setShowToc(false)}/>
    {(toc && toc.length > 0) ? () : (
    对文档内容应用“标题”样式,即可生成大纲
    )} ) : (
    )}
    ); } export default function Render(props) { const { methods, data } = props; const { t, setEditor, check, uploadFile, update, setHtml, gotoPreview, clearContentTip, } = methods; const { id, content, editor, origin = 'qiniu', oakFullpath, html, tocPosition = 'none', highlightBgColor = 'none', scrollId } = data; const [articleId, setArticleId] = useState(''); const [toc, setToc] = useState([]); const [showToc, setShowToc] = useState(false); useEffect(() => { if (id) { setArticleId(id); } }, [id]); useEffect(() => { if (tocPosition !== 'none') { setShowToc(true); } }, [tocPosition]); return (
    {tocPosition === 'left' ? ( ) : null}
    {data.contentTip && ( clearContentTip()}/>)}
    update({ name: e.target.value })} value={data.name} placeholder={'请输入文章标题'} size="large" maxLength={32} suffix={`${[...(data.name || '')].length}/32`} className={Style.titleInput}/>
    { setHtml(editor.getHtml()); const headers = editor.getElemsByTypePrefix('header'); const tocItems = headers.map((header) => { const text = SlateNode.string(header); const { id, type } = header; return { text, level: parseInt(type.substring(6)), id, }; }); setToc([...tocItems]); }} style={{ minHeight: 440, }} mode="default"/>
    {tocPosition === 'right' ? ( ) : null}
    ); }