feat:保存文件成功

This commit is contained in:
shixiaohua 2024-02-23 10:46:42 +08:00
parent f36bdfc262
commit 7bb24704cf
5 changed files with 165 additions and 131 deletions

View File

@ -15,31 +15,28 @@ import {AutoLinkNode, LinkNode} from "@lexical/link";
import {MarkdownShortcutPlugin} from "@lexical/react/LexicalMarkdownShortcutPlugin";
import {
TRANSFORMERS, $convertFromMarkdownString,
$convertToMarkdownString,
} from "@lexical/markdown";
import "./index.less"
import {useEffect, useId, useState} from 'react';
import {useEffect, useState} from 'react';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {importFile, overWriteFile, saveFileWithName} from "../../../utils/File"
import {$getRoot, CLEAR_HISTORY_COMMAND, $createTextNode,} from "lexical";
import md5 from "md5"
import {importFile} from "../../../utils/File"
import {isEmpty} from "../../../utils/ObjectUtils";
import {useDispatch, useSelector} from "react-redux";
import {updatedSavedFile} from "../../../redux/tableBarItem_reducer";
import {ListPlugin} from "@lexical/react/LexicalListPlugin";
import {LinkPlugin} from "@lexical/react/LexicalLinkPlugin";
import AutoLinkPlugin from "./plugins/AutoLinkPlugin";
import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin";
import CodeHighlightPlugin from "./plugins/CodeHighlightPlugin";
import {getFileNameByPath} from "../../../utils/PathOperate";
import ImportFilePlugin from "./plugins/ImportFilePlugin";
import {TablePlugin} from "@lexical/react/LexicalTablePlugin";
import SaveFilePlugin from "./plugins/SaveFilePlugin";
const {ipcRenderer} = window.require('electron')
function Placeholder() {
return <div className="editor-placeholder">Enter some rich text...</div>;
}
const editorConfig = {
let editorConfig = {
// The editor theme
theme: ExampleTheme,
// Handling of errors during update
@ -61,133 +58,21 @@ const editorConfig = {
LinkNode
]
};
function OnChangePlugin({onChange}) {
const [editor] = useLexicalComposerContext();
console.log('editor', editor)
useEffect(() => {
return editor.registerUpdateListener(({editorState}) => {
onChange(editorState);
});
}, [editor, onChange]);
}
function ImportFilePlugin(props) {
const [editor] = useLexicalComposerContext();
useEffect(() => {
if (props.filePath) {
importFile(props.filePath).then(value => {
if (isEmpty(value)) {
return
}
if (props.filePath.endsWith(".md")) {
const root = $getRoot();
const firstChild = root.getFirstChild();
if ($isCodeNode(firstChild) && firstChild.getLanguage() === 'markdown') {
$convertFromMarkdownString(
firstChild.getTextContent(),
PLAYGROUND_TRANSFORMERS,
);
} else {
const markdown = $convertToMarkdownString(PLAYGROUND_TRANSFORMERS);
root
.clear()
.append(
$createCodeNode('markdown').append($createTextNode(markdown)),
);
}
root.selectEnd();
} else {
const editorState = editor.parseEditorState(
JSON.stringify(JSON.parse(value.toString()).editorState)
);
editor.setEditorState(editorState);
editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined);
}
}).catch(error =>
console.error(error)
)
}
}, [])
}
// JSON
// const editorState = editor.parseEditorState(editorStateJSONString);
// editor.setEditorState(editorState);
export default function Hlexical(props) {
console.log("Hlexical(props):this.props.filePath:", props.filePath)
const [editorState, setEditorState] = useState();
const dispatch = useDispatch();
function onChange(editorState) {
// Call toJSON on the EditorState object, which produces a serialization safe string
const editorStateJSON = editorState.toJSON();
console.log('onChange-editorStateJSON')
// However, we still have a JavaScript object, so we need to convert it to an actual string with JSON.stringify
setEditorState(JSON.stringify(editorStateJSON));
}
let pushHotKey = useSelector(state => state.pushHotkeys.data);
let activeKey = useSelector(state => state.tableBarItem.activeKey);
const saveFile = (event, args) => {
console.log("event,args:", event, args)
console.log("触发保存pushHotKey:", pushHotKey)
if (pushHotKey !== "CTRL+S") {
return
}
let filePath = props.filePath;
if (filePath !== activeKey) {
console.log("文件不同", filePath, activeKey)
return;
}
console.log("触发保存filePath:", filePath)
if (isEmpty(editorState)) {
return
}
const editorStateSave = {"editorState": JSON.parse(editorState)};
let resultSave = JSON.stringify(editorStateSave);
//
if (!filePath) {
let saveDialogReturnValuePromise = saveFileWithName();
console.log("saveDialogReturnValuePromise", saveDialogReturnValuePromise)
saveDialogReturnValuePromise.then(result => {
if (!result.canceled) {
let fileKey = getFileNameByPath(result.filePath)
if (isEmpty(fileKey)){
fileKey = fileKey+".lexical"
}
overWriteFile(fileKey, resultSave)
//
dispatch(updatedSavedFile({filePath: result.filePath}))
//
dispatch()
}
})
return
}
importFile(filePath).then(value => {
let save = (isEmpty(value)) || md5(resultSave) !== md5(JSON.stringify(JSON.parse(value.toString())));
if (save) {
console.log("保存重写" + filePath)
overWriteFile(filePath, resultSave)
if (props.filePath.endsWith(".md")){
importFile(props.filePath).then(value => {
if (isEmpty(value)) {
return
}
editorConfig={...editorConfig,editorState: () => $convertFromMarkdownString(value.toString(), TRANSFORMERS)}
}).catch(error =>
console.error(error)
)
}
useEffect(() => {
ipcRenderer.on("pushHotkeys", saveFile);
return () => {
console.log("销毁取消监听");
ipcRenderer.removeListener("pushHotkeys", saveFile)
};
}
)
return (
<LexicalComposer initialConfig={editorConfig}>
<div className="editor-container">
@ -202,17 +87,17 @@ export default function Hlexical(props) {
<HistoryPlugin/>
{/*黑窗口动态记录当前操作*/}
{/*<TreeViewPlugin/>*/}
<AutoFocusPlugin/>
<CodeHighlightPlugin/>
<ListPlugin/>
<LinkPlugin/>
<AutoLinkPlugin/>
<ListMaxIndentLevelPlugin maxDepth={7}/>
<TablePlugin/>
{/*markdown 快捷键*/}
<MarkdownShortcutPlugin transformers={TRANSFORMERS}/>
<OnChangePlugin onChange={onChange}/>
<ImportFilePlugin filePath={props.filePath}/>
<SaveFilePlugin filePath={props.filePath}/>
{/*文件操作导入文件*/}
{/*<ActionPlugin/>*/}
</div>

View File

@ -147,8 +147,8 @@ body {
margin-top: 8px;
margin-bottom: 8px;
tab-size: 2;
/* white-space: pre; */
overflow-x: auto;
white-space: pre;
overflow: auto;
position: relative;
}

View File

@ -0,0 +1,37 @@
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import {useEffect} from "react";
import {importFile} from "../../../../utils/File";
import {isEmpty} from "../../../../utils/ObjectUtils";
import {CLEAR_HISTORY_COMMAND} from "lexical";
import {TRANSFORMERS, $convertFromMarkdownString, $convertToMarkdownString,} from "@lexical/markdown";
export default function ImportFilePlugin(props) {
const [editor] = useLexicalComposerContext();
useEffect(() => {
if (props.filePath) {
importFile(props.filePath).then(value => {
if (isEmpty(value)) {
return
}
if (props.filePath.endsWith(".md")) {
console.log("$convertFromMarkdownString(value.toString(), TRANSFORMERS)",editor)
// const editorState = editor.parseEditorState($convertFromMarkdownString(value.toString(), TRANSFORMERS))
// editor.setEditorState(editorState);
// editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined);
editor.update(() => {
$convertFromMarkdownString(value.toString(), TRANSFORMERS);
})
} else {
const editorState = editor.parseEditorState(
JSON.stringify(JSON.parse(value.toString()).editorState)
);
editor.setEditorState(editorState);
editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined);
}
}).catch(error =>
console.error(error)
)
}
}, [])
}

View File

@ -0,0 +1,111 @@
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import {Fragment, useEffect, useState} from "react";
import {importFile, overWriteFile, saveFileWithName} from "../../../../utils/File";
import {isEmpty} from "../../../../utils/ObjectUtils";
import {CLEAR_HISTORY_COMMAND} from "lexical";
import {TRANSFORMERS, $convertFromMarkdownString, $convertToMarkdownString,} from "@lexical/markdown";
import {getFileNameByPath} from "../../../../utils/PathOperate";
import {updatedSavedFile} from "../../../../redux/tableBarItem_reducer";
import {useDispatch, useSelector} from "react-redux";
import md5 from "md5"
import {message} from "antd";
const {ipcRenderer} = window.require('electron')
export default function SaveFilePlugin(props) {
let activeKey = useSelector(state => state.tableBarItem.activeKey);
const dispatch = useDispatch();
const [messageApi,contextHolder] = message.useMessage();
const [editor] = useLexicalComposerContext();
const [editorState,setEditorState]=useState();
function onChange(editorState) {
if (props.filePath.endsWith(".md")){
let read = editorState.read(() => $convertToMarkdownString(TRANSFORMERS));
setEditorState(read)
}else {
// Call toJSON on the EditorState object, which produces a serialization safe string
const editorStateJSON = editorState.toJSON();
console.log('onChange-editorStateJSON')
// However, we still have a JavaScript object, so we need to convert it to an actual string with JSON.stringify
setEditorState(JSON.stringify(editorStateJSON));
}
}
const saveFile = (event, args) => {
console.log("event,args:", event, args)
if (args !== "CTRL+S") {
return
}
let filePath = props.filePath;
if (filePath !== activeKey) {
console.log("文件不同", filePath, activeKey)
return;
}
console.log("触发保存filePath:", filePath)
if (isEmpty(editorState)) {
return
}
let resultSave;
if (props.filePath.endsWith(".md")){
resultSave=editorState
}else {
const editorStateSave = {"editorState": JSON.parse(editorState)};
resultSave = JSON.stringify(editorStateSave);
}
// 如果文件地址为空需要用户选择目录并设置文件。
if (!filePath) {
let saveDialogReturnValuePromise = saveFileWithName();
console.log("saveDialogReturnValuePromise", saveDialogReturnValuePromise)
saveDialogReturnValuePromise.then(result => {
if (!result.canceled) {
let fileKey = getFileNameByPath(result.filePath)
if (isEmpty(fileKey)){
fileKey = fileKey+".lexical"
}
overWriteFile(fileKey, resultSave)
// 修改当前文件名
dispatch(updatedSavedFile({filePath: result.filePath}))
// 文件目录更新
dispatch()
}
})
return
}
importFile(filePath).then(value => {
let save
if (props.filePath.endsWith(".md")){
// editorState
save = (isEmpty(value)) || md5(resultSave) !== md5(value.toString());
}else {
save = (isEmpty(value)) || md5(resultSave) !== md5(JSON.stringify(JSON.parse(value.toString())));
}
if (save) {
overWriteFile(filePath, resultSave)
console.log("保存成功"+ filePath)
messageApi.open({type:"success",content:"保存成功:" + filePath})
}
}).catch(error =>
console.error(error)
)
}
useEffect(() => {
ipcRenderer.on("pushHotkeys", saveFile);
let updateListener = editor.registerUpdateListener(({editorState}) => {
console.log("SaveFilePlugin:editorState",editorState)
onChange(editorState);
})
return () => {
console.log("销毁取消监听");
updateListener()
ipcRenderer.removeListener("pushHotkeys", saveFile)
};
},[editor,onChange]
)
return (
<Fragment>
{contextHolder}
</Fragment>
)
}

View File

@ -19,6 +19,7 @@ export const dirMessageSlice = createSlice({
// selectDirKey:""
},
reducers: {
// 一级目录有相同的就要和并,从第一次展开处和并,不需要展示上一级。
dirAdd: (state, action) => {
console.log("dirMessage:dirAdd", state, action)
if (action.payload) {