feat:保存文件成功
This commit is contained in:
parent
f36bdfc262
commit
7bb24704cf
|
@ -15,31 +15,28 @@ import {AutoLinkNode, LinkNode} from "@lexical/link";
|
||||||
import {MarkdownShortcutPlugin} from "@lexical/react/LexicalMarkdownShortcutPlugin";
|
import {MarkdownShortcutPlugin} from "@lexical/react/LexicalMarkdownShortcutPlugin";
|
||||||
import {
|
import {
|
||||||
TRANSFORMERS, $convertFromMarkdownString,
|
TRANSFORMERS, $convertFromMarkdownString,
|
||||||
$convertToMarkdownString,
|
|
||||||
} from "@lexical/markdown";
|
} from "@lexical/markdown";
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
import {useEffect, useId, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
||||||
import {importFile, overWriteFile, saveFileWithName} from "../../../utils/File"
|
import {importFile} from "../../../utils/File"
|
||||||
import {$getRoot, CLEAR_HISTORY_COMMAND, $createTextNode,} from "lexical";
|
|
||||||
import md5 from "md5"
|
|
||||||
import {isEmpty} from "../../../utils/ObjectUtils";
|
import {isEmpty} from "../../../utils/ObjectUtils";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
|
||||||
import {updatedSavedFile} from "../../../redux/tableBarItem_reducer";
|
|
||||||
import {ListPlugin} from "@lexical/react/LexicalListPlugin";
|
import {ListPlugin} from "@lexical/react/LexicalListPlugin";
|
||||||
import {LinkPlugin} from "@lexical/react/LexicalLinkPlugin";
|
import {LinkPlugin} from "@lexical/react/LexicalLinkPlugin";
|
||||||
import AutoLinkPlugin from "./plugins/AutoLinkPlugin";
|
import AutoLinkPlugin from "./plugins/AutoLinkPlugin";
|
||||||
import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin";
|
import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin";
|
||||||
import CodeHighlightPlugin from "./plugins/CodeHighlightPlugin";
|
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() {
|
function Placeholder() {
|
||||||
return <div className="editor-placeholder">Enter some rich text...</div>;
|
return <div className="editor-placeholder">Enter some rich text...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const editorConfig = {
|
let editorConfig = {
|
||||||
// The editor theme
|
// The editor theme
|
||||||
theme: ExampleTheme,
|
theme: ExampleTheme,
|
||||||
// Handling of errors during update
|
// Handling of errors during update
|
||||||
|
@ -61,133 +58,21 @@ const editorConfig = {
|
||||||
LinkNode
|
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 设置编辑器状态
|
// 从字符串化 JSON 设置编辑器状态
|
||||||
// const editorState = editor.parseEditorState(editorStateJSONString);
|
// const editorState = editor.parseEditorState(editorStateJSONString);
|
||||||
// editor.setEditorState(editorState);
|
// editor.setEditorState(editorState);
|
||||||
|
|
||||||
export default function Hlexical(props) {
|
export default function Hlexical(props) {
|
||||||
console.log("Hlexical(props):this.props.filePath:", props.filePath)
|
console.log("Hlexical(props):this.props.filePath:", props.filePath)
|
||||||
const [editorState, setEditorState] = useState();
|
if (props.filePath.endsWith(".md")){
|
||||||
const dispatch = useDispatch();
|
importFile(props.filePath).then(value => {
|
||||||
|
if (isEmpty(value)) {
|
||||||
function onChange(editorState) {
|
return
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
editorConfig={...editorConfig,editorState: () => $convertFromMarkdownString(value.toString(), TRANSFORMERS)}
|
||||||
}).catch(error =>
|
}).catch(error =>
|
||||||
console.error(error)
|
console.error(error)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
|
||||||
ipcRenderer.on("pushHotkeys", saveFile);
|
|
||||||
return () => {
|
|
||||||
console.log("销毁取消监听");
|
|
||||||
ipcRenderer.removeListener("pushHotkeys", saveFile)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LexicalComposer initialConfig={editorConfig}>
|
<LexicalComposer initialConfig={editorConfig}>
|
||||||
<div className="editor-container">
|
<div className="editor-container">
|
||||||
|
@ -202,17 +87,17 @@ export default function Hlexical(props) {
|
||||||
<HistoryPlugin/>
|
<HistoryPlugin/>
|
||||||
{/*黑窗口动态记录当前操作*/}
|
{/*黑窗口动态记录当前操作*/}
|
||||||
{/*<TreeViewPlugin/>*/}
|
{/*<TreeViewPlugin/>*/}
|
||||||
|
|
||||||
<AutoFocusPlugin/>
|
<AutoFocusPlugin/>
|
||||||
<CodeHighlightPlugin/>
|
<CodeHighlightPlugin/>
|
||||||
<ListPlugin/>
|
<ListPlugin/>
|
||||||
<LinkPlugin/>
|
<LinkPlugin/>
|
||||||
<AutoLinkPlugin/>
|
<AutoLinkPlugin/>
|
||||||
<ListMaxIndentLevelPlugin maxDepth={7}/>
|
<ListMaxIndentLevelPlugin maxDepth={7}/>
|
||||||
|
<TablePlugin/>
|
||||||
{/*markdown 快捷键*/}
|
{/*markdown 快捷键*/}
|
||||||
<MarkdownShortcutPlugin transformers={TRANSFORMERS}/>
|
<MarkdownShortcutPlugin transformers={TRANSFORMERS}/>
|
||||||
<OnChangePlugin onChange={onChange}/>
|
|
||||||
<ImportFilePlugin filePath={props.filePath}/>
|
<ImportFilePlugin filePath={props.filePath}/>
|
||||||
|
<SaveFilePlugin filePath={props.filePath}/>
|
||||||
{/*文件操作导入文件*/}
|
{/*文件操作导入文件*/}
|
||||||
{/*<ActionPlugin/>*/}
|
{/*<ActionPlugin/>*/}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -147,8 +147,8 @@ body {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
tab-size: 2;
|
tab-size: 2;
|
||||||
/* white-space: pre; */
|
white-space: pre;
|
||||||
overflow-x: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
}
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ export const dirMessageSlice = createSlice({
|
||||||
// selectDirKey:""
|
// selectDirKey:""
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
|
// 一级目录有相同的就要和并,从第一次展开处和并,不需要展示上一级。
|
||||||
dirAdd: (state, action) => {
|
dirAdd: (state, action) => {
|
||||||
console.log("dirMessage:dirAdd", state, action)
|
console.log("dirMessage:dirAdd", state, action)
|
||||||
if (action.payload) {
|
if (action.payload) {
|
||||||
|
|
Loading…
Reference in New Issue