mirror of https://github.com/fantasticit/think.git
feature: 增加版本比对
parent
099c44cded
commit
732aa1d132
|
@ -63,6 +63,7 @@
|
|||
"dompurify": "^2.3.5",
|
||||
"downloadjs": "^1.4.7",
|
||||
"html-to-docx": "^1.4.0",
|
||||
"htmldiff-js": "^1.0.5",
|
||||
"interactjs": "^1.10.11",
|
||||
"katex": "^0.15.2",
|
||||
"kity": "^2.0.4",
|
||||
|
|
|
@ -39,6 +39,44 @@
|
|||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
:global {
|
||||
#diff-visual {
|
||||
del {
|
||||
color: #cb4000;
|
||||
text-decoration: none;
|
||||
list-style: none;
|
||||
background-color: #ffeaea;
|
||||
}
|
||||
|
||||
del::before {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
padding: 0 4px;
|
||||
font-weight: 400;
|
||||
color: #8a8f8d;
|
||||
text-decoration: none;
|
||||
content: '-';
|
||||
}
|
||||
|
||||
ins {
|
||||
color: green;
|
||||
text-decoration: none;
|
||||
list-style: none;
|
||||
background-color: #e9ffe9;
|
||||
}
|
||||
|
||||
ins::before {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
padding: 0 4px;
|
||||
font-weight: 400;
|
||||
color: #8a8f8d;
|
||||
text-decoration: none;
|
||||
content: '+';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { IconChevronLeft } from '@douyinfe/semi-icons';
|
||||
import { Button, Modal, Typography } from '@douyinfe/semi-ui';
|
||||
import { Button, Modal, Select, Space, Tag, Typography } from '@douyinfe/semi-ui';
|
||||
import { EditorContent, useEditor } from '@tiptap/react';
|
||||
import cls from 'classnames';
|
||||
import { DataRender } from 'components/data-render';
|
||||
import { LocaleTime } from 'components/locale-time';
|
||||
import { useDocumentVersion } from 'data/document';
|
||||
import { generateDiffHtml } from 'helpers/generate-html';
|
||||
import { safeJSONParse } from 'helpers/json';
|
||||
import { DocumentVersionControl } from 'hooks/use-document-version';
|
||||
import { IsOnMobile } from 'hooks/use-on-mobile';
|
||||
|
@ -28,6 +29,7 @@ export const DocumentVersion: React.FC<Partial<IProps>> = ({ documentId, onSelec
|
|||
const { visible, toggleVisible } = DocumentVersionControl.useHook();
|
||||
const { data, loading, error, refresh } = useDocumentVersion(documentId, { enabled: visible });
|
||||
const [selectedVersion, setSelectedVersion] = useState(null);
|
||||
const [diffVersion, setDiffVersion] = useState(null);
|
||||
|
||||
const editor = useEditor({
|
||||
editable: false,
|
||||
|
@ -42,10 +44,18 @@ export const DocumentVersion: React.FC<Partial<IProps>> = ({ documentId, onSelec
|
|||
|
||||
const select = useCallback(
|
||||
(version) => {
|
||||
setDiffVersion(null);
|
||||
setSelectedVersion(version);
|
||||
editor.commands.setContent(safeJSONParse(version.data, { default: {} }).default);
|
||||
},
|
||||
[editor]
|
||||
[editor, setDiffVersion]
|
||||
);
|
||||
|
||||
const changeDiffVision = useCallback(
|
||||
(version) => {
|
||||
setDiffVersion(version);
|
||||
},
|
||||
[setDiffVersion]
|
||||
);
|
||||
|
||||
const restore = useCallback(() => {
|
||||
|
@ -54,6 +64,20 @@ export const DocumentVersion: React.FC<Partial<IProps>> = ({ documentId, onSelec
|
|||
close();
|
||||
}, [selectedVersion, close, onSelect]);
|
||||
|
||||
useEffect(() => {
|
||||
if (diffVersion && selectedVersion) {
|
||||
const historyVersion = data.find((item) => item.version === diffVersion);
|
||||
|
||||
const diffHtml = generateDiffHtml(
|
||||
safeJSONParse(selectedVersion.data, { default: {} }).default,
|
||||
safeJSONParse(historyVersion.data, { default: {} }).default
|
||||
);
|
||||
|
||||
const element = document.getElementById('diff-visual');
|
||||
element.innerHTML = diffHtml;
|
||||
}
|
||||
}, [diffVersion, data, selectedVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return;
|
||||
if (!data.length) return;
|
||||
|
@ -77,11 +101,28 @@ export const DocumentVersion: React.FC<Partial<IProps>> = ({ documentId, onSelec
|
|||
版本记录
|
||||
</Title>
|
||||
</div>
|
||||
{selectedVersion && !isMobile && (
|
||||
<div>
|
||||
<Button type="primary" theme="solid" disabled={!onSelect || !selectedVersion} onClick={restore}>
|
||||
恢复此记录
|
||||
</Button>
|
||||
<Tag color="light-blue" style={{ padding: '12px 14px', fontSize: '14px' }}>
|
||||
{new Date(+selectedVersion.version).toLocaleString()}
|
||||
</Tag>
|
||||
<div style={{ padding: '0 8px' }}>与</div>
|
||||
<Select placeholder="请选择历史" size="small" onChange={changeDiffVision} value={diffVersion} showClear>
|
||||
{data.map(({ version }) => {
|
||||
return (
|
||||
<Select.Option value={version} key={version} disabled={version === selectedVersion.version}>
|
||||
{new Date(+version).toLocaleString()}
|
||||
</Select.Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<div style={{ paddingLeft: '8px' }}>对比</div>
|
||||
<Space style={{ position: 'absolute', right: '240px' }}>
|
||||
<Tag style={{ backgroundColor: '#e9ffe9' }}>增加的内容</Tag>
|
||||
<Tag style={{ backgroundColor: '#ffeaea' }}>删除的内容</Tag>
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
theme="light"
|
||||
|
@ -93,6 +134,9 @@ export const DocumentVersion: React.FC<Partial<IProps>> = ({ documentId, onSelec
|
|||
>
|
||||
刷新
|
||||
</Button>
|
||||
<Button type="primary" theme="solid" disabled={!onSelect || !selectedVersion} onClick={restore}>
|
||||
恢复此记录
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -106,7 +150,11 @@ export const DocumentVersion: React.FC<Partial<IProps>> = ({ documentId, onSelec
|
|||
<div className={styles.contentWrap}>
|
||||
<main className={cls(isMobile && styles.isMobile)}>
|
||||
<div className={'container'}>
|
||||
{diffVersion ? (
|
||||
<div id="diff-visual" className="ProseMirror"></div>
|
||||
) : (
|
||||
<EditorContent editor={editor} />
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
<aside className={cls(isMobile && styles.isMobile)}>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { generateHTML } from '@tiptap/core';
|
||||
import HtmlDiff from 'htmldiff-js';
|
||||
import { CollaborationKit } from 'tiptap/editor';
|
||||
|
||||
const json2html = (json) => generateHTML(json, CollaborationKit);
|
||||
export const generateDiffHtml = (selected, other) => {
|
||||
const selectedHtml = json2html(selected);
|
||||
const otherHtml = json2html(other);
|
||||
let diffHtml = HtmlDiff.execute(selectedHtml, otherHtml);
|
||||
diffHtml = diffHtml.replace(/<iframe\s*[^>]*>(.*?)<\/iframe>/gi, '');
|
||||
return diffHtml;
|
||||
};
|
|
@ -109,6 +109,7 @@ importers:
|
|||
eslint-plugin-simple-import-sort: ^7.0.0
|
||||
fs-extra: ^10.0.0
|
||||
html-to-docx: ^1.4.0
|
||||
htmldiff-js: ^1.0.5
|
||||
interactjs: ^1.10.11
|
||||
katex: ^0.15.2
|
||||
kity: ^2.0.4
|
||||
|
@ -207,6 +208,7 @@ importers:
|
|||
dompurify: 2.3.5
|
||||
downloadjs: 1.4.7
|
||||
html-to-docx: 1.4.0
|
||||
htmldiff-js: 1.0.5
|
||||
interactjs: 1.10.11
|
||||
katex: 0.15.2
|
||||
kity: 2.0.4
|
||||
|
@ -6708,6 +6710,10 @@ packages:
|
|||
htmlparser2: 3.10.1
|
||||
dev: false
|
||||
|
||||
/htmldiff-js/1.0.5:
|
||||
resolution: {integrity: sha512-rmow9353OK0elkub15Sbze8Nj7BYfduqoJJw4yEvHHjOcHeCazNPk0PoUbjE8SvxKgjymeRIFU/OnS8jtitRtA==}
|
||||
dev: false
|
||||
|
||||
/htmlparser2/3.10.1:
|
||||
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in New Issue