提交 97f423c0 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 a1f837ea
......@@ -12,7 +12,7 @@ import { Boot } from '@wangeditor/editor'
import { SlateEditor, DomEditor, SlateElement, SlateTransforms } from '@wangeditor/editor'
import { Editor, Toolbar } from '@wangeditor/editor-for-react'
import { fontFamilyList, fontSizeList, lineHeightList } from './utils/setting'
import { fontFamilyList, lineHeightList } from './utils/setting'
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import timesave from '@/assets/images/timesave.png'
......@@ -415,7 +415,26 @@ const WangEditorCustomer = (props, ref) => {
}
},
MENU_CONF: {
fontSize: { fontSizeList },
fontSize: {
fontSizeList: [
{ name: '初号', value: '56px' },
{ name: '小初', value: '48px' },
{ name: '一号', value: '34px' },
{ name: '小一', value: '32px' },
{ name: '二号', value: '29px' },
{ name: '小二', value: '24px' },
{ name: '三号', value: '21px' },
{ name: '小三', value: '20px' },
{ name: '四号', value: '18px' },
{ name: '小四', value: '16px' },
{ name: '五号', value: '14px' },
{ name: '小五', value: '12px' },
{ name: '六号', value: '10px' },
{ name: '小六', value: '8px' },
{ name: '七号', value: '7px' },
{ name: '八号', value: '6px' }
]
},
fontFamily: { fontFamilyList },
lineHeight: { lineHeightList }
}
......
import { useEffect, useState } from 'react'
import { Modal, Input, Button, Flex, Spin } from 'antd'
import { useAIEdit } from '@/hooks/useBaiduAI'
import { SlateTransforms } from '@wangeditor/editor'
const { TextArea } = Input
import { SlateEditor, SlateElement, SlateTransforms } from '@wangeditor/editor'
const actionMap = {
rewrite: { name: '改写', prompt: '帮我改写以下文字内容:' },
expand: { name: '扩写', prompt: '帮我在以下文字内容基础上进行扩写:' },
abbreviate: { name: '润色', prompt: '帮我润色以下文字内容:' },
summary: { name: '总结', prompt: '帮我总结以下文字内容:' },
punctuation: { name: '标点校对', prompt: '帮我校对以下文字内容中的标点符号' },
contentInspect: { name: '内容检查', prompt: '帮我检查以下文字内容中的敏感词和错别字' }
punctuation: {
name: '标点校对',
prompt: `帮我校对标点符号,要求如下:
1、如果标点符号没有问题,则告诉我“该内容标点符号正常”。
2、如果标点符号存在问题,则按照如下规则进行返回:
(1)带有错误标点符号的句子:
(2)纠正标点符号之后正确的句子:
(3)更新符号之后的完整内容:
具体要校对的内容如下:`
},
contentInspect: {
name: '内容检查',
prompt: `帮我检查敏感词和错别字,要求如下:
1、如果没有敏感词或者错别字,则告诉我“该内容正常,无敏感词和错别字”。
2、如果存在敏感词或错别字问题,则按照如下规则进行返回:
(1)带有敏感词或错别字的句子:
(2)纠正敏感词或错别字之后正确的句子:
(3)更新之后的完整内容:
具体要校对的内容如下:`
}
}
export default function AIModal({ editor, docAction }) {
......@@ -38,14 +56,37 @@ export default function AIModal({ editor, docAction }) {
}
const handlePrimary = () => {
let result = content.trim()
// 标点校对结果判断
if (docAction === 'punctuation') {
if (result.includes('该内容标点符号正常')) {
setIsModalOpen(false)
return
}
const match = result.match(/更新符号之后的完整内容[::]?\s?([\s\S]*)/)
if (match) {
result = match[1].trim()
}
}
// 内容检查结果判断
if (docAction === 'contentInspect') {
if (result.includes('该内容正常,无敏感词和错别字')) {
setIsModalOpen(false)
return
}
const match = result.match(/更新之后的完整内容[::]?\s?([\s\S]*)/)
if (match) {
result = match[1].trim()
}
}
console.log(result)
editor.restoreSelection()
// 删除当前选中的节点
SlateTransforms.removeNodes(editor)
const contentArr = content.split('\n').filter(item => item)
const nodeList = contentArr.map(item => {
const resultArr = result.split('\n').filter(item => item)
const nodeList = resultArr.map(item => {
return { type: 'paragraph', indent: '2em', children: [{ text: item }] }
})
console.log(nodeList, editor.children)
// 插入节点
SlateTransforms.insertNodes(editor, nodeList)
setIsModalOpen(false)
......
import { DomEditor, SlateTransforms } from '@wangeditor/editor';
import iconClose from '@/assets/images/icon_chapter_close.png';
import { h } from 'snabbdom';
import { findNodeWithParent } from '../utils/setting';
import $ from 'jquery';
import { DomEditor, SlateTransforms } from '@wangeditor/editor'
import iconClose from '@/assets/images/icon_chapter_close.png'
import { h } from 'snabbdom'
import { findNodeWithParent } from '../utils/setting'
const withChapterSectionNode = (editor) => {
const { isInline, isVoid, normalizeNode } = editor;
const newEditor = editor;
const withChapterSectionNode = editor => {
const { isInline, isVoid, normalizeNode } = editor
const newEditor = editor
// newEditor.isInline = (elem) => {
// const type = DomEditor.getNodeType(elem);
......@@ -14,26 +13,26 @@ const withChapterSectionNode = (editor) => {
// return isInline(elem);
// };
newEditor.isVoid = (elem) => {
const type = DomEditor.getNodeType(elem);
if (type === 'chapterSection') return true; // 设置为 void
return isVoid(elem);
};
newEditor.isVoid = elem => {
const type = DomEditor.getNodeType(elem)
if (type === 'chapterSection') return true // 设置为 void
return isVoid(elem)
}
// 重新 normalize
newEditor.normalizeNode = ([node, path]) => {
const type = DomEditor.getNodeType(node);
const type = DomEditor.getNodeType(node)
if (type !== 'chapterSection') {
// 未命中 chapterSection ,执行默认的 normalizeNode
return normalizeNode([node, path]);
return normalizeNode([node, path])
}
// editor 顶级 node
const topLevelNodes = newEditor.children || [];
const topLevelNodes = newEditor.children || []
// 后面必须跟一个 p header blockquote(否则后面无法继续输入文字)
const nextNode = topLevelNodes[path[0] + 1] || {};
const nextNodeType = DomEditor.getNodeType(nextNode);
const nextNode = topLevelNodes[path[0] + 1] || {}
const nextNodeType = DomEditor.getNodeType(nextNode)
if (
nextNodeType !== 'paragraph' &&
nextNodeType !== 'blockquote' &&
......@@ -46,16 +45,16 @@ const withChapterSectionNode = (editor) => {
!nextNodeType.startsWith('header')
) {
// 后面不是 p 或 header ,则插入一个空 p
const p = { type: 'paragraph', children: [{ text: '' }] };
const insertPath = [path[0] + 1];
const p = { type: 'paragraph', children: [{ text: '' }] }
const insertPath = [path[0] + 1]
SlateTransforms.insertNodes(newEditor, p, {
at: insertPath, // 在 link-card 后面插入
});
at: insertPath // 在 link-card 后面插入
})
}
};
}
return newEditor; // 返回 newEditor ,重要!!!
};
return newEditor // 返回 newEditor ,重要!!!
}
// 在编辑器中渲染新元素
// 定义 renderElem 函数
......@@ -70,8 +69,8 @@ const renderChapterSection = (elem, children, editor) => {
height = 100,
size = 20,
family = '黑体',
callback,
} = elem;
callback
} = elem
// 附件元素 vnode
const attachVnode = h(
......@@ -83,7 +82,7 @@ const renderChapterSection = (elem, children, editor) => {
// HTML 属性,驼峰式写法
contentEditable: false,
className: 'chapter-item-section',
id: 'chapter-item-section',
id: 'chapter-item-section'
},
dataset: {
title,
......@@ -91,7 +90,7 @@ const renderChapterSection = (elem, children, editor) => {
bgColor,
textColor,
align,
family,
family
},
style: {
backgroundColor: bgColor,
......@@ -100,15 +99,14 @@ const renderChapterSection = (elem, children, editor) => {
display: 'flex',
alignItems: 'center',
padding: '0 20px',
fontFamily: family,
fontFamily: family
}, // style ,驼峰式写法
on: {
click(ev) {
ev.stopPropagation();
ev.preventDefault();
ev.stopPropagation()
ev.preventDefault()
const data = {
title,
title,
random: random,
i: Math.random(),
......@@ -117,12 +115,12 @@ const renderChapterSection = (elem, children, editor) => {
align: align,
height: height,
size: size,
family: family,
};
localStorage.setItem('chapterItemNum', JSON.stringify(data));
callback && callback(data);
} /* 其他... */,
},
family: family
}
localStorage.setItem('chapterItemNum', JSON.stringify(data))
callback && callback(data)
} /* 其他... */
}
},
// 子节点
[
......@@ -134,17 +132,17 @@ const renderChapterSection = (elem, children, editor) => {
textAlign: align,
flex: 1,
color: textColor,
fontFamily: family,
},
fontFamily: family
}
},
[title],
[title]
),
h(
'span',
{
props: {
contentEditable: false,
className: `chapter-close`,
className: `chapter-close`
},
style: {
position: 'absolute',
......@@ -152,43 +150,38 @@ const renderChapterSection = (elem, children, editor) => {
right: '15px',
display: 'inline',
width: '18px',
height: '18px',
height: '18px'
},
on: {
async click(ev) {
ev.stopPropagation();
ev.preventDefault();
ev.stopPropagation()
ev.preventDefault()
try {
const path = findNodeWithParent(
editor.children,
'chapterSection',
'random',
random,
);
SlateTransforms.removeNodes(editor, { at: path.reverse() });
const path = findNodeWithParent(editor.children, 'chapterSection', 'random', random)
SlateTransforms.removeNodes(editor, { at: path.reverse() })
} catch (e) {
console.log(e);
console.log(e)
}
},
},
}
}
},
[
h('img', {
props: { src: iconClose, width: 18, height: 18 },
style: { cursor: 'pointer' },
}),
],
),
],
);
style: { cursor: 'pointer' }
})
]
)
]
)
return attachVnode;
};
return attachVnode
}
const renderElemConf = {
type: 'chapterSection',
renderElem: renderChapterSection,
};
renderElem: renderChapterSection
}
// 把新元素转换为 HTML
const chapterSectionToHtml = (elem, childrenHtml) => {
......@@ -201,8 +194,8 @@ const chapterSectionToHtml = (elem, childrenHtml) => {
align = 'center',
height = 100,
size = 20,
family = '黑体',
} = elem;
family = '黑体'
} = elem
// 生成 HTML 代码
const html = `<h2
......@@ -218,26 +211,26 @@ const chapterSectionToHtml = (elem, childrenHtml) => {
data-size="${size}"
data-family="${family}"
style="background-color: ${bgColor}; height: ${height}px; box-sizing: border-box; padding: 0 20px; display: flex; align-items: center; font-family: '${family}'; "
><p style="flex: 1; color: ${textColor}; font-family: '${family}'; text-align: ${align}; font-size: ${size}px;">${title}</p></h2>`;
><p style="flex: 1; color: ${textColor}; font-family: '${family}'; text-align: ${align}; font-size: ${size}px;">${title}</p></h2>`
return html;
};
return html
}
const chapterSectionElemToHtmlConf = {
type: 'chapterSection', // 新元素的 type ,重要!!!
elemToHtml: chapterSectionToHtml,
};
elemToHtml: chapterSectionToHtml
}
// 解析新元素 HTML 到编辑器
const parseChapterSectionHtml = (domElem, children, editor) => {
// 从 DOM element 中获取“附件”的信息
const bgColor = domElem.getAttribute('data-bgColor') || '';
const textColor = domElem.getAttribute('data-textColor') || '#333333';
const title = domElem.getAttribute('data-title') || '';
const random = domElem.getAttribute('data-random') || '';
const align = domElem.getAttribute('data-align') || 'center';
const height = domElem.getAttribute('data-height') || 100;
const size = domElem.getAttribute('data-size') || 20;
const family = domElem.getAttribute('data-family') || '黑体';
const bgColor = domElem.getAttribute('data-bgColor') || ''
const textColor = domElem.getAttribute('data-textColor') || '#333333'
const title = domElem.getAttribute('data-title') || ''
const random = domElem.getAttribute('data-random') || ''
const align = domElem.getAttribute('data-align') || 'center'
const height = domElem.getAttribute('data-height') || 100
const size = domElem.getAttribute('data-size') || 20
const family = domElem.getAttribute('data-family') || '黑体'
// 生成“附件”元素(按照此前约定的数据结构)
const myResume = {
......@@ -250,27 +243,22 @@ const parseChapterSectionHtml = (domElem, children, editor) => {
height,
size,
family,
children: [{ text: '' }], // void node 必须有 children ,其中有一个空字符串,重要!!!
};
children: [{ text: '' }] // void node 必须有 children ,其中有一个空字符串,重要!!!
}
return myResume;
};
return myResume
}
const parseChapterSectionConf = {
selector: 'h2[data-w-e-type="chapterSection"]', // CSS 选择器,匹配特定的 HTML 标签
parseElemHtml: parseChapterSectionHtml,
};
parseElemHtml: parseChapterSectionHtml
}
const chapterSectionModule = {
editorPlugin: withChapterSectionNode,
renderElems: [renderElemConf],
elemsToHtml: [chapterSectionElemToHtmlConf],
parseElemsHtml: [parseChapterSectionConf],
};
parseElemsHtml: [parseChapterSectionConf]
}
export default chapterSectionModule;
export {
withChapterSectionNode,
renderElemConf,
chapterSectionElemToHtmlConf,
parseChapterSectionConf,
};
export default chapterSectionModule
export { withChapterSectionNode, renderElemConf, chapterSectionElemToHtmlConf, parseChapterSectionConf }
import { DomEditor, SlateTransforms } from '@wangeditor/editor';
import iconClose from '@/assets/images/icon_chapter_close.png';
import { h } from 'snabbdom';
import { findNodeWithParent } from '../utils/setting';
import $ from 'jquery';
import { DomEditor, SlateTransforms } from '@wangeditor/editor'
import iconClose from '@/assets/images/icon_chapter_close.png'
import { h } from 'snabbdom'
import { findNodeWithParent } from '../utils/setting'
const withChapterHeaderNode = (editor) => {
const { isInline, isVoid, normalizeNode } = editor;
const newEditor = editor;
const withChapterHeaderNode = editor => {
const { isInline, isVoid, normalizeNode } = editor
const newEditor = editor
// newEditor.isInline = (elem) => {
// const type = DomEditor.getNodeType(elem);
......@@ -14,25 +13,25 @@ const withChapterHeaderNode = (editor) => {
// return isInline(elem);
// };
newEditor.isVoid = (elem) => {
const type = DomEditor.getNodeType(elem);
if (type === 'chapterHeader') return true; // 设置为 void
return isVoid(elem);
};
newEditor.isVoid = elem => {
const type = DomEditor.getNodeType(elem)
if (type === 'chapterHeader') return true // 设置为 void
return isVoid(elem)
}
// 重新 normalize
newEditor.normalizeNode = ([node, path]) => {
const type = DomEditor.getNodeType(node);
const type = DomEditor.getNodeType(node)
if (type !== 'chapterHeader') {
// 未命中 chapterHeader ,执行默认的 normalizeNode
return normalizeNode([node, path]);
return normalizeNode([node, path])
}
// editor 顶级 node
const topLevelNodes = newEditor.children || [];
const topLevelNodes = newEditor.children || []
// 后面必须跟一个 p header blockquote (否则后面无法继续输入文字)
const nextNode = topLevelNodes[path[0] + 1] || {};
const nextNodeType = DomEditor.getNodeType(nextNode);
const nextNode = topLevelNodes[path[0] + 1] || {}
const nextNodeType = DomEditor.getNodeType(nextNode)
if (
nextNodeType !== 'paragraph' &&
nextNodeType !== 'blockquote' &&
......@@ -45,16 +44,16 @@ const withChapterHeaderNode = (editor) => {
!nextNodeType.startsWith('header')
) {
// 后面不是 p 或 header ,则插入一个空 p
const p = { type: 'paragraph', children: [{ text: '' }] };
const insertPath = [path[0] + 1];
const p = { type: 'paragraph', children: [{ text: '' }] }
const insertPath = [path[0] + 1]
SlateTransforms.insertNodes(newEditor, p, {
at: insertPath, // 在 link-card 后面插入
});
at: insertPath // 在 link-card 后面插入
})
}
};
}
return newEditor; // 返回 newEditor ,重要!!!
};
return newEditor // 返回 newEditor ,重要!!!
}
// 在编辑器中渲染新元素
// 定义 renderElem 函数
......@@ -69,8 +68,8 @@ const renderChapterHeader = (elem, children, editor) => {
height = 200,
size = 200,
family = '黑体',
callback,
} = elem;
callback
} = elem
// 附件元素 vnode
const attachVnode = h(
......@@ -82,7 +81,7 @@ const renderChapterHeader = (elem, children, editor) => {
// HTML 属性,驼峰式写法
contentEditable: false,
className: 'chapter-item-header',
id: 'chapter-item-header',
id: 'chapter-item-header'
},
dataset: {
title,
......@@ -92,7 +91,7 @@ const renderChapterHeader = (elem, children, editor) => {
align,
bgColor,
size,
family,
family
},
style: {
backgroundImage: `url(${bgImgUrl})`,
......@@ -105,12 +104,12 @@ const renderChapterHeader = (elem, children, editor) => {
display: 'flex',
alignItems: 'center',
padding: '0 20px',
fontFamily: family,
fontFamily: family
}, // style ,驼峰式写法
on: {
click(ev) {
ev.stopPropagation();
ev.preventDefault();
ev.stopPropagation()
ev.preventDefault()
const data = {
title,
......@@ -122,12 +121,12 @@ const renderChapterHeader = (elem, children, editor) => {
align: align,
height: height,
size: size,
family: family,
};
localStorage.setItem('chapterTitleNum', JSON.stringify(data));
callback && callback(data);
} /* 其他... */,
},
family: family
}
localStorage.setItem('chapterTitleNum', JSON.stringify(data))
callback && callback(data)
} /* 其他... */
}
},
// 子节点
[
......@@ -139,17 +138,17 @@ const renderChapterHeader = (elem, children, editor) => {
flex: 1,
color: textColor ? textColor : '#000',
textAlign: align,
fontFamily: family,
},
fontFamily: family
}
},
[title],
[title]
),
h(
'span',
{
props: {
contentEditable: false,
className: `chapter-close`,
className: `chapter-close`
},
style: {
position: 'absolute',
......@@ -157,39 +156,39 @@ const renderChapterHeader = (elem, children, editor) => {
right: '15px',
display: 'inline',
width: '18px',
height: '18px',
height: '18px'
},
on: {
async click(ev) {
ev.stopPropagation();
ev.preventDefault();
ev.stopPropagation()
ev.preventDefault()
try {
const path = findNodeWithParent(editor.children, 'chapterHeader', 'random', random);
SlateTransforms.removeNodes(editor, { at: path.reverse() });
const path = findNodeWithParent(editor.children, 'chapterHeader', 'random', random)
SlateTransforms.removeNodes(editor, { at: path.reverse() })
} catch (e) {
console.log(e);
console.log(e)
}
},
},
}
}
},
[
h('img', {
props: { src: iconClose, width: 18, height: 18 },
style: { cursor: 'pointer' },
}),
],
),
],
);
style: { cursor: 'pointer' }
})
]
)
]
)
return attachVnode;
};
return attachVnode
}
const renderElemConf = {
type: 'chapterHeader',
renderElem: renderChapterHeader,
};
renderElem: renderChapterHeader
}
// 把新元素转换为 HTML
const chapterHeaderToHtml = (elem, childrenHtml) => {
......@@ -203,12 +202,12 @@ const chapterHeaderToHtml = (elem, childrenHtml) => {
bgColor = '',
height = 200,
size = 26,
family = '黑体',
} = elem;
family = '黑体'
} = elem
let background = `background-image:url(${bgImgUrl}); background-repeat: no-repeat; background-position: center center; background-size: cover;`;
let background = `background-image:url(${bgImgUrl}); background-repeat: no-repeat; background-position: center center; background-size: cover;`
if (bgColor) {
background += ` background-color: ${bgColor};`;
background += ` background-color: ${bgColor};`
}
// 生成 HTML 代码
......@@ -226,26 +225,26 @@ const chapterHeaderToHtml = (elem, childrenHtml) => {
data-size="${size}"
data-family="${family}"
style="${background} height: ${height}px; display: flex; align-items: center; padding: 0 20px; box-sizing: border-box; font-family: '${family}'; "
><p style="flex: 1; font-family: '${family}'; font-size: ${size}px; color: ${textColor}; text-align: ${align};">${title}</p></h1>`;
><p style="flex: 1; font-family: '${family}'; font-size: ${size}px; color: ${textColor}; text-align: ${align};">${title}</p></h1>`
return html;
};
return html
}
const chapterHeaderElemToHtmlConf = {
type: 'chapterHeader', // 新元素的 type ,重要!!!
elemToHtml: chapterHeaderToHtml,
};
elemToHtml: chapterHeaderToHtml
}
// 解析新元素 HTML 到编辑器
const parseChapterHeaderHtml = (domElem, children, editor) => {
// 从 DOM element 中获取“附件”的信息
const bgImgUrl = domElem.getAttribute('data-bgImgUrl') || '';
const textColor = domElem.getAttribute('data-textColor') || '#000000';
const title = domElem.getAttribute('data-title') || '';
const random = domElem.getAttribute('data-random') || '';
const align = domElem.getAttribute('data-align') || 'left';
const bgColor = domElem.getAttribute('data-bgColor') || '';
const size = domElem.getAttribute('data-size') || 26;
const family = domElem.getAttribute('data-family') || '黑体';
const bgImgUrl = domElem.getAttribute('data-bgImgUrl') || ''
const textColor = domElem.getAttribute('data-textColor') || '#000000'
const title = domElem.getAttribute('data-title') || ''
const random = domElem.getAttribute('data-random') || ''
const align = domElem.getAttribute('data-align') || 'left'
const bgColor = domElem.getAttribute('data-bgColor') || ''
const size = domElem.getAttribute('data-size') || 26
const family = domElem.getAttribute('data-family') || '黑体'
// 生成“附件”元素(按照此前约定的数据结构)
const myResume = {
......@@ -258,27 +257,22 @@ const parseChapterHeaderHtml = (domElem, children, editor) => {
bgColor,
size,
family,
children: [{ text: '' }], // void node 必须有 children ,其中有一个空字符串,重要!!!
};
children: [{ text: '' }] // void node 必须有 children ,其中有一个空字符串,重要!!!
}
return myResume;
};
return myResume
}
const parseChapterHeaderConf = {
selector: 'h1[data-w-e-type="chapterHeader"]', // CSS 选择器,匹配特定的 HTML 标签
parseElemHtml: parseChapterHeaderHtml,
};
parseElemHtml: parseChapterHeaderHtml
}
const chapterHeaderModule = {
editorPlugin: withChapterHeaderNode,
renderElems: [renderElemConf],
elemsToHtml: [chapterHeaderElemToHtmlConf],
parseElemsHtml: [parseChapterHeaderConf],
};
parseElemsHtml: [parseChapterHeaderConf]
}
export default chapterHeaderModule;
export {
withChapterHeaderNode,
renderElemConf,
chapterHeaderElemToHtmlConf,
parseChapterHeaderConf,
};
export default chapterHeaderModule
export { withChapterHeaderNode, renderElemConf, chapterHeaderElemToHtmlConf, parseChapterHeaderConf }
......@@ -10,7 +10,7 @@ export function useAIEdit() {
const fetch = useCallback(async params => {
setIsLoading(true)
try {
const defaultParams = { ernie_name: 'ERNIE-Lite-8K-0922', stream: false }
const defaultParams = { ernie_name: 'ERNIE-4.0-8K-Preview', stream: false }
const res = await baiduAIChat({ ...defaultParams, params })
const message = { role: 'assistant', content: res.data.result }
setMessages(prevMessages => [...prevMessages, message])
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论