Compare commits
10 Commits
68b831e155
...
a7a6c52e18
Author | SHA1 | Date |
---|---|---|
|
a7a6c52e18 | |
|
e612aafb9c | |
|
5d83324f50 | |
|
72310d3278 | |
|
3f97149a73 | |
|
0e466c8e44 | |
|
676cbb7468 | |
|
35f80a4ed4 | |
|
ebbb784f21 | |
|
1247134435 |
|
@ -2,4 +2,5 @@ node_modules/
|
|||
rpm-4.18.0
|
||||
out/
|
||||
ueditor-1.4.3.3/
|
||||
ueditor/
|
||||
ueditor/
|
||||
asset-manifest.json
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
|
@ -1,5 +1,7 @@
|
|||
const CracoLessPlugin = require('craco-less');
|
||||
const HtmlWebpackPlugin=require('html-webpack-plugin')
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, dir);
|
||||
}
|
||||
const path = require('path');
|
||||
module.exports = {
|
||||
plugins: [
|
||||
|
@ -19,10 +21,25 @@ module.exports = {
|
|||
configure: (webpackConfig, { env, paths }) => {
|
||||
// 修改output.publicPath为'./'
|
||||
// webpackConfig.output.publicPath = './';
|
||||
webpackConfig.output.path = path.join(__dirname,);
|
||||
webpackConfig.output.publicPath = "";
|
||||
webpackConfig.output.path = path.join(__dirname,"/build");
|
||||
webpackConfig.output.publicPath = "./";
|
||||
// webpackConfig.output.module
|
||||
// .rule("icons")
|
||||
// .test(/\.svg$/)
|
||||
// .include.add(resolve("src/icons"))
|
||||
// .end()
|
||||
// .use("svg-sprite-loader")
|
||||
// .loader("svg-sprite-loader")
|
||||
// .options({
|
||||
// symbolId: "icon-[name]",
|
||||
// })
|
||||
// .end()
|
||||
return webpackConfig;
|
||||
},
|
||||
alias: {
|
||||
// 将@/*映射为src目录
|
||||
'@': path.resolve(__dirname, 'src/'),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,16 +14,17 @@ class UploadUtils {
|
|||
});
|
||||
static Bucket = 'note-1324909903'
|
||||
static Region = 'ap-beijing'
|
||||
|
||||
static staticStore
|
||||
constructor(store) {
|
||||
this.store = store;
|
||||
UploadUtils.staticStore = store;
|
||||
}
|
||||
getActiveFile() {
|
||||
let tableBarItem = JSON.parse(this.store.get("persist:tableBarItem"));
|
||||
if (!tableBarItem) {
|
||||
return;
|
||||
}
|
||||
return tableBarItem.activeKey ? tableBarItem.activeKey.replaceAll('"', "") : undefined;
|
||||
return tableBarItem.activeKey ? JSON.parse(tableBarItem.activeKey) : undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -33,7 +34,6 @@ class UploadUtils {
|
|||
let dirMessage = JSON.parse(this.store.get("persist:dirMessage"));
|
||||
let fileMd5
|
||||
if (activeFile && dirMessage && dirMessage.data) {
|
||||
console.log("dirMessage.data", JSON.parse(dirMessage.data, []))
|
||||
let find = JSON.parse(dirMessage.data, []).find(file => file.fileId === activeFile || file.filePath === activeFile);
|
||||
if (find) {
|
||||
fileMd5 = find.fileMd5;
|
||||
|
@ -53,8 +53,9 @@ class UploadUtils {
|
|||
Bucket: UploadUtils.Bucket,
|
||||
Region: UploadUtils.Region,
|
||||
// 不能以 / 开头
|
||||
Key: activeFile,
|
||||
Key: process.platform==='win32'?activeFile.replaceAll("\\","/"):activeFile
|
||||
}, function (err, data) {
|
||||
console.log('upLoadFileUtil:',err || data);
|
||||
if (data && data.ETag) {
|
||||
// 文件存在,比较MD5值
|
||||
let onlineMd5 = data.ETag.replaceAll('"', "")
|
||||
|
@ -65,10 +66,11 @@ class UploadUtils {
|
|||
"buttons": ["确认"],
|
||||
"defaultId": 0
|
||||
});
|
||||
return
|
||||
}
|
||||
}else {
|
||||
UploadUtils.selfUploadFile(activeFile)
|
||||
}
|
||||
UploadUtils.selfUploadFile(activeFile,fileMd5)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -79,8 +81,9 @@ class UploadUtils {
|
|||
Bucket: UploadUtils.Bucket,
|
||||
Region: UploadUtils.Region,
|
||||
// 不能以 / 开头
|
||||
Key: activeFile,
|
||||
Key: process.platform==='win32'?activeFile.replaceAll("\\","/"):activeFile,
|
||||
}, function (err, data) {
|
||||
console.log('downLoadFileUtil:',err || data);
|
||||
if (data && data.ETag) {
|
||||
// 文件存在,比较MD5值
|
||||
let onlineMd5 = data.ETag.replaceAll('"', "")
|
||||
|
@ -91,19 +94,19 @@ class UploadUtils {
|
|||
"buttons": ["确认"],
|
||||
"defaultId": 0
|
||||
});
|
||||
return
|
||||
}
|
||||
}else {
|
||||
UploadUtils.selfDownLoadFile(activeFile)
|
||||
}
|
||||
UploadUtils.selfDownLoadFile(activeFile)
|
||||
})
|
||||
}
|
||||
|
||||
static selfUploadFile(activeFile) {
|
||||
console.log("cos.uploadFile")
|
||||
static selfUploadFile(activeFile,fileMd5) {
|
||||
console.log("cos.uploadFile",activeFile,fileMd5)
|
||||
UploadUtils.cos.uploadFile({
|
||||
Bucket: UploadUtils.Bucket, /* 填入您自己的存储桶,必须字段 */
|
||||
Region: UploadUtils.Region, /* 存储桶所在地域,例如 ap-beijing,必须字段 */
|
||||
Key: activeFile, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
|
||||
Key: process.platform==='win32'?activeFile.replaceAll("\\","/"):activeFile, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
|
||||
FilePath: activeFile, /* 必须 */
|
||||
SliceSize: 1024 * 1024 * 5, /* 触发分块上传的阈值,超过5MB使用分块上传,非必须 */
|
||||
onTaskReady: function (taskId) { /* 非必须 */
|
||||
|
@ -132,7 +135,7 @@ class UploadUtils {
|
|||
},
|
||||
// 支持自定义headers 非必须
|
||||
}, function (err, data) {
|
||||
console.log(err || data);
|
||||
console.log("uploadFile:callback:",err || data);
|
||||
|
||||
});
|
||||
}
|
||||
|
@ -142,7 +145,7 @@ class UploadUtils {
|
|||
UploadUtils.cos.getObject({
|
||||
Bucket: UploadUtils.Bucket, /* 填入您自己的存储桶,必须字段 */
|
||||
Region: UploadUtils.Region, /* 存储桶所在地域,例如 ap-beijing,必须字段 */
|
||||
Key: activeFile, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
|
||||
Key: process.platform==='win32'?activeFile.replaceAll("\\","/"):activeFile, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
|
||||
Output: activeFile
|
||||
// 支持自定义headers 非必须
|
||||
}, function (err, data) {
|
||||
|
@ -161,6 +164,20 @@ class UploadUtils {
|
|||
"buttons": ["确认"],
|
||||
"defaultId": 0
|
||||
})
|
||||
// 修改store中Md5值
|
||||
let persist = UploadUtils.staticStore.get("persist:dirMessage");
|
||||
if (persist){
|
||||
let persistObj = JSON.parse(persist)
|
||||
let data1 = persistObj.data;
|
||||
console.log("data1",data1,Array.isArray(data1))
|
||||
let parseArray = JSON.parse(data1, []);
|
||||
let find =parseArray.find(file => file.fileId === activeFile || file.filePath === activeFile);
|
||||
if (find) {
|
||||
find.fileMd5=data.ETag.replaceAll('"',"");
|
||||
}
|
||||
persistObj.data=JSON.stringify(parseArray)
|
||||
UploadUtils.staticStore.set("persist:dirMessage",JSON.stringify(persistObj))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -174,7 +191,7 @@ class UploadUtils {
|
|||
Bucket: UploadUtils.Bucket,
|
||||
Region: UploadUtils.Region,
|
||||
// 不能以 / 开头
|
||||
Key: activeFile,
|
||||
Key: process.platform==='win32'?activeFile.replaceAll("\\","/"):activeFile,
|
||||
}, function (err, data) {
|
||||
console.log("err || data.CommonPrefixes" + activeFile, err || data);
|
||||
if (data && data.ETag) {
|
||||
|
@ -184,26 +201,30 @@ class UploadUtils {
|
|||
return
|
||||
}
|
||||
console.log("fileList[0].ETag", onlineMd5)
|
||||
let number = dialog.showMessageBoxSync({
|
||||
dialog.showMessageBox({
|
||||
"message": "云文件已修改是否同步到本地",
|
||||
"type": "info",
|
||||
"buttons": ["是", "否"],
|
||||
"defaultId": 0
|
||||
});
|
||||
if (number === 0) {
|
||||
UploadUtils.selfDownLoadFile(activeFile)
|
||||
} else if (number === 1) {
|
||||
if (dialog.showMessageBoxSync({
|
||||
"message": "是否使用本地文件覆盖远程文件",
|
||||
"type": "info",
|
||||
"buttons": ["是", "否"],
|
||||
"defaultId": 0
|
||||
}) === 0) {
|
||||
UploadUtils.selfUploadFile(activeFile)
|
||||
}).then(result=>{
|
||||
let number = result.response
|
||||
if (number === 0) {
|
||||
UploadUtils.selfDownLoadFile(activeFile)
|
||||
} else if (number === 1) {
|
||||
dialog.showMessageBox({
|
||||
"message": "是否使用本地文件覆盖远程文件",
|
||||
"type": "info",
|
||||
"buttons": ["是", "否"],
|
||||
"defaultId": 0
|
||||
}).then(result=>{
|
||||
if (result.response===0){
|
||||
UploadUtils.selfUploadFile(activeFile,fileMd5)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
UploadUtils.selfUploadFile(activeFile)
|
||||
UploadUtils.selfUploadFile(activeFile,fileMd5)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"compilerOptions":{
|
||||
"baseUrl":"./",
|
||||
"paths":{
|
||||
"@/*":[
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
25
package.json
|
@ -5,7 +5,8 @@
|
|||
"main": "main.js",
|
||||
"author": "hua",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.3.7",
|
||||
"@ant-design/pro-components": "^2.3.57",
|
||||
"@craco/craco": "^6.0.0",
|
||||
"@electron-forge/cli": "^6.0.4",
|
||||
|
@ -19,15 +20,23 @@
|
|||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"antd": "^4.24.8",
|
||||
"antd": "^5.17.0",
|
||||
"axios": "^1.3.3",
|
||||
"concurrently": "^4.1.1",
|
||||
"cos-nodejs-sdk-v5": "^2.13.3",
|
||||
"craco-less": "^2.0.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"electron": "^22.1.0",
|
||||
"electron-is-dev": "^1.1.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-store": "^8.1.0",
|
||||
"formik": "^2.2.9",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"lexical": "^0.12.6",
|
||||
"localStorage": "^1.0.4",
|
||||
"md5": "^2.3.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"nanoid": "^4.0.2",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
|
@ -36,6 +45,7 @@
|
|||
"react-router-dom": "^6.8.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"redux": "^4.2.1",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"umi-request": "^1.4.0",
|
||||
"wait-on": "^3.3.0",
|
||||
|
@ -44,23 +54,24 @@
|
|||
"yjs": ">=13.5.42"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "concurrently \"wait-on http://localhost:3000 && electron-forge start\" \"cross-env BROWSER=none npm start\"",
|
||||
"start": "craco start",
|
||||
"dev": "concurrently \"wait-on http://localhost:3000 && electron .\" \"cross-env BROWSER=none npm start\"",
|
||||
"start": "craco start --verbose",
|
||||
"build": "craco build",
|
||||
"startel": "electron .",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.3.7",
|
||||
"cos-nodejs-sdk-v5": "^2.13.3",
|
||||
"electron-is-dev": "^1.1.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-store": "^8.1.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"md5": "^2.3.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"node-sass": "^7.0.3",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-persist-electron-storage": "^2.1.0"
|
||||
"redux-persist": "^6.0.0"
|
||||
},
|
||||
"browser": {
|
||||
"fs": false
|
||||
|
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -14,7 +14,7 @@
|
|||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!-- 应用加壳 -->
|
||||
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
|
||||
<title>送给最亲爱的小宝贝</title>
|
||||
<title>Note</title>
|
||||
<!--[if IE]>
|
||||
<script src="js/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
|
|
@ -10,4 +10,11 @@ node version manage
|
|||
## npm
|
||||
node package manage
|
||||
## nrm
|
||||
node remote manage
|
||||
npm remote manage nrm
|
||||
npm install -g nrm
|
||||
|
||||
|
||||
jpg转icon:https://cn.pic2ico.com/
|
||||
1. npm build 将react应用打包为html
|
||||
2. 将打包出来的文件放到根目录下
|
||||
3. npm package 将项目打包
|
|
@ -33,8 +33,8 @@ function App() {
|
|||
return (
|
||||
<>
|
||||
{/* 注册路由 */}
|
||||
{element}
|
||||
{/*<Note/>*/}
|
||||
{/*{element}*/}
|
||||
<Note/>
|
||||
</>
|
||||
)
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import {Menu} from 'antd';
|
||||
import {useDispatch} from "react-redux";
|
||||
import {dirRemove} from "../../../redux/dirMessage_reducer";
|
||||
import {dirRemove} from "@/redux/dirMessage_reducer";
|
||||
|
||||
function CloseDir (prop) {
|
||||
console.log("prop",prop)
|
||||
|
@ -9,6 +8,6 @@ function CloseDir (prop) {
|
|||
const closeDir = ()=>{
|
||||
dispatch(dirRemove({selectDirKey: prop.filePath}))
|
||||
}
|
||||
return <span className="menuItemClick" onClick={closeDir}>关闭目录</span>
|
||||
return <a className="menuItemClick" onClick={closeDir}>关闭目录</a>
|
||||
}
|
||||
export default CloseDir;
|
||||
export default CloseDir;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, {useRef, useState} from 'react';
|
||||
import {Input, message, Modal} from 'antd';
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {newDir} from "../../../utils/File";
|
||||
import {dirDirAdd} from "../../../redux/dirMessage_reducer";
|
||||
import {isEmpty} from "../../../utils/ObjectUtils";
|
||||
import {fileDirFormat} from "../../../utils/PathOperate";
|
||||
import {newDir} from "@/utils/File";
|
||||
import {dirDirAdd} from "@/redux/dirMessage_reducer";
|
||||
import {isEmpty} from "@/utils/ObjectUtils";
|
||||
import {fileDirFormat} from "@/utils/PathOperate";
|
||||
|
||||
const DirAddDir = (prop) => {
|
||||
console.log("prop",prop)
|
||||
|
@ -40,10 +40,10 @@ const DirAddDir = (prop) => {
|
|||
};
|
||||
return <>
|
||||
{contextHolder}
|
||||
<span className="menuItemClick" onClick={showModal}>添加文件夹</span>
|
||||
<a className="menuItemClick" onClick={showModal}>添加文件夹</a>
|
||||
<Modal title="新建文件夹" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
|
||||
<Input ref={inputValue} placeholder="文件夹名不能为空" />
|
||||
</Modal>
|
||||
</>;
|
||||
};
|
||||
export default DirAddDir;
|
||||
export default DirAddDir;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React, {useRef, useState} from 'react';
|
||||
import {Input, message, Modal} from 'antd';
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {newFile} from "../../../utils/File";
|
||||
import {dirFileAdd} from "../../../redux/dirMessage_reducer";
|
||||
import {addTableBarItem} from "../../../redux/tableBarItem_reducer";
|
||||
import {isEmpty} from "../../../utils/ObjectUtils";
|
||||
import {fileNameFormat, fullFileNameFormat} from "../../../utils/PathOperate";
|
||||
import {newFile} from "@/utils/File";
|
||||
import {dirFileAdd} from "@/redux/dirMessage_reducer";
|
||||
import {addTableBarItem} from "@/redux/tableBarItem_reducer";
|
||||
import {isEmpty} from "@/utils/ObjectUtils";
|
||||
import {fileNameFormat, fullFileNameFormat} from "@/utils/PathOperate";
|
||||
|
||||
const DirAddFile = (prop) => {
|
||||
console.log("prop",prop)
|
||||
|
@ -47,10 +47,10 @@ const DirAddFile = (prop) => {
|
|||
};
|
||||
return <>
|
||||
{contextHolder}
|
||||
<span className="menuItemClick" onClick={showModal}>{prop.fileExt}</span>
|
||||
<a className="menuItemClick" onClick={showModal}>{prop.fileExt}</a>
|
||||
<Modal title="新建文件名" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
|
||||
<Input ref={inputValue} placeholder="文件名不能为空" />
|
||||
</Modal>
|
||||
</>;
|
||||
};
|
||||
export default DirAddFile;
|
||||
export default DirAddFile;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import {Popconfirm} from 'antd';
|
||||
import {deleteFileAndDir} from "../../../utils/File";
|
||||
import {deleteFileAndDir} from "@/utils/File";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {dirFileRemove} from "../../../redux/dirMessage_reducer";
|
||||
import {removeTableBarItem, setActiveKey} from "../../../redux/tableBarItem_reducer";
|
||||
import {dirFileRemove} from "@/redux/dirMessage_reducer";
|
||||
import {removeTableBarItem, setActiveKey} from "@/redux/tableBarItem_reducer";
|
||||
const DirDeleteFile = (prop) => {
|
||||
console.log("prop",prop)
|
||||
const dispatch= useDispatch()
|
||||
|
@ -47,7 +47,7 @@ const DirDeleteFile = (prop) => {
|
|||
okText="确认"
|
||||
cancelText="取消"
|
||||
>
|
||||
<span className="menuItemClick">删除文件</span>
|
||||
<a className="menuItemClick">删除文件</a>
|
||||
</Popconfirm>;
|
||||
};
|
||||
export default DirDeleteFile;
|
||||
export default DirDeleteFile;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import React from 'react';
|
||||
import {openInDir} from "@/utils/File"
|
||||
const OpenInDir = (prop) => {
|
||||
return <a className="menuItemClick" onClick={()=>openInDir(prop)}>文件夹中打开</a>
|
||||
};
|
||||
export default OpenInDir;
|
|
@ -6,7 +6,7 @@ const RefreshDir = (prop) => {
|
|||
prop.refreshDir(prop.filePath)
|
||||
};
|
||||
return (
|
||||
<span className="menuItemClick" onClick={refreshDir}>更新目录</span>
|
||||
<a className="menuItemClick" onClick={refreshDir}>更新目录</a>
|
||||
);
|
||||
};
|
||||
export default RefreshDir;
|
||||
export default RefreshDir;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, {useRef, useState} from 'react';
|
||||
import {Input, Menu, Modal} from 'antd';
|
||||
import {updateFileName} from "../../../utils/File";
|
||||
import {updateFileName} from "@/utils/File";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {updateFileName as updateFileNameRedux} from "../../../redux/dirMessage_reducer";
|
||||
import {updateFileName as updateFileNameBar} from "../../../redux/tableBarItem_reducer";
|
||||
import {replaceFileNameByFilePath} from "../../../utils/PathOperate";
|
||||
import {replaceFileNameByFilePath} from "@/utils/PathOperate";
|
||||
|
||||
const UpdateFileName = (prop) => {
|
||||
console.log("prop",prop)
|
||||
|
@ -36,11 +36,11 @@ const UpdateFileName = (prop) => {
|
|||
};
|
||||
return (
|
||||
<>
|
||||
<span className="menuItemClick" onClick={showModal}>修改文件名</span>
|
||||
<a className="menuItemClick" onClick={showModal}>修改文件名</a>
|
||||
<Modal title="修改文件名" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
|
||||
<Input ref={inputValue} placeholder={prop.fileName} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default UpdateFileName;
|
||||
export default UpdateFileName;
|
||||
|
|
|
@ -2,13 +2,13 @@ import React, {Fragment, useEffect, useMemo, useState} from 'react';
|
|||
import {Input, Menu, Tree} from 'antd';
|
||||
import {FolderOutlined, FileMarkdownOutlined, FileOutlined} from '@ant-design/icons';
|
||||
import "./index.less"
|
||||
import {getFileDirByPath, getFileFullNameByPath, getFileNameByPath} from "../../utils/PathOperate";
|
||||
import {getFileDirByPath, getFileFullNameByPath, getFileNameByPath} from "@/utils/PathOperate";
|
||||
const {Search} = Input;
|
||||
import {useSelector, useDispatch} from "react-redux";
|
||||
import {addExpandedKeys, addTableBarItem, setExpandedKeys} from "../../redux/tableBarItem_reducer";
|
||||
import {readDir} from "../../utils/File";
|
||||
import {addExpandedKeys, addTableBarItem, setExpandedKeys} from "@/redux/tableBarItem_reducer";
|
||||
import {readDir} from "@/utils/File";
|
||||
import {nextDirAdd,refreshDir as refreshDirReducer} from "../../redux/dirMessage_reducer";
|
||||
import {isEmpty} from "../../utils/ObjectUtils";
|
||||
import {isEmpty} from "@/utils/ObjectUtils";
|
||||
import UpdateFileName from "./UpdateFileName";
|
||||
import RefreshDir from "./RefreshDir";
|
||||
import CloseDir from "./CloseDir";
|
||||
|
@ -17,7 +17,7 @@ import DirDeleteFile from "./DirDeleteFile";
|
|||
import DirAddDir from "./DirAddDir";
|
||||
import {createPortal} from "react-dom";
|
||||
import {isArray} from "@craco/craco/lib/utils";
|
||||
// const defaultData = [];
|
||||
import OpenInDir from "@/components/ItemTree/OpenInDir";
|
||||
// 将树平铺用于查找
|
||||
const dataList = [];
|
||||
const generateList = (data) => {
|
||||
|
@ -26,7 +26,7 @@ const generateList = (data) => {
|
|||
const {key, title, icon} = node;
|
||||
dataList.push({
|
||||
key,
|
||||
title,
|
||||
"title":title.props.children,
|
||||
icon
|
||||
});
|
||||
if (node.children) {
|
||||
|
@ -34,7 +34,7 @@ const generateList = (data) => {
|
|||
}
|
||||
}
|
||||
};
|
||||
// generateList(defaultData);
|
||||
|
||||
const getParentKey = (key, tree) => {
|
||||
let parentKey;
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
|
@ -50,12 +50,6 @@ const getParentKey = (key, tree) => {
|
|||
return parentKey;
|
||||
};
|
||||
|
||||
const menuItemClickMap = new Map();
|
||||
menuItemClickMap.set('1', <DirAddDir/>);
|
||||
menuItemClickMap.set('b', 2);
|
||||
menuItemClickMap.set('c', 3);
|
||||
|
||||
|
||||
function generateChildList(fileList) {
|
||||
const result = []
|
||||
for (let i = 0; i < fileList.length; i++) {
|
||||
|
@ -170,6 +164,7 @@ const ItemTree = (prop) => {
|
|||
const [autoExpandParent, setAutoExpandParent] = useState(false);
|
||||
// let filePath = useSelector(state => state.dirMessage.data);
|
||||
const [defaultValueState, setDefaultValueState] = useState(flushTree(prop.filePath));
|
||||
generateList(defaultValueState);
|
||||
const [state, setState] = useState();
|
||||
useEffect(() => {
|
||||
setDefaultValueState(flushTree(prop.filePath))
|
||||
|
@ -190,36 +185,6 @@ const ItemTree = (prop) => {
|
|||
}
|
||||
})
|
||||
}
|
||||
const itemTreeAddFile = (dirMessage)=>{
|
||||
setDefaultValueState(flushTree(dirMessage))
|
||||
// return
|
||||
// let newFile = {
|
||||
// "fileName": getFileFullNameByPath(filePath),
|
||||
// "filePath": filePath,
|
||||
// "dirFlag": false,
|
||||
// "children": []
|
||||
// }
|
||||
// // 查找对应的目录并添加信息
|
||||
// let fileDir = getFileDirByPath(filePath)
|
||||
// let updateValueState = [...prop.filePath]
|
||||
// updateValueState.forEach(file=>{
|
||||
// if (fileDir===file.filePath && file.dirFlag){
|
||||
// file.children=[...file.children,newFile]
|
||||
// } else if (fileDir.startsWith(file.filePath) && file.dirFlag ){
|
||||
// treeItemChildAddFile(file.children,newFile,fileDir)
|
||||
// }
|
||||
// })
|
||||
// setDefaultValueState(flushTree(updateValueState))
|
||||
}
|
||||
function treeItemChildAddFile(treeItemList,newFile,fileDir){
|
||||
treeItemList.forEach(file=>{
|
||||
if (fileDir===file.filePath && file.dirFlag){
|
||||
file.children=[...file.children,newFile]
|
||||
} else if (fileDir.startsWith(file.filePath) && file.dirFlag){
|
||||
treeItemChildAddFile(file.children,newFile,fileDir)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const onChange = (e) => {
|
||||
const {value} = e.target;
|
||||
|
@ -344,7 +309,7 @@ const ItemTree = (prop) => {
|
|||
const menuItem = []
|
||||
if (dirFlag){
|
||||
menuItem.push(getMenuItem('1',<DirAddDir fileDir={key} />))
|
||||
menuItem.push(getMenuItem('2',"添加文件",[
|
||||
menuItem.push(getMenuItem('2',<a>添加文件</a>,[
|
||||
getMenuItem("2-1",<DirAddFile fileDir={key} fileExt=".md"/>),
|
||||
getMenuItem("2-2",<DirAddFile fileDir={key} fileExt=".lexical"/>)]
|
||||
),null, 'group'
|
||||
|
@ -355,6 +320,7 @@ const ItemTree = (prop) => {
|
|||
menuItem.push(getMenuItem('5',<UpdateFileName fileName={title} filePath={key}/>))
|
||||
menuItem.push(getMenuItem('6',<DirDeleteFile filePath={key} />))
|
||||
}
|
||||
menuItem.push(getMenuItem('7',<OpenInDir filePath={key} />))
|
||||
return <Fragment>
|
||||
{createPortal(
|
||||
<Menu style={tmpStyle} onClick={e =>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import '~antd/dist/antd.less';
|
||||
@import '~antd/dist/reset.css';
|
||||
.ant-tree{
|
||||
overflow: auto scroll;
|
||||
height: 95.7%;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import '~antd/dist/antd.less';
|
||||
@import '~antd/dist/reset.css';
|
||||
|
||||
#components-grid-demo-playground [class~='ant-col'] {
|
||||
background: transparent;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {useEffect, useLayoutEffect} from 'react';
|
||||
import {CAN_USE_DOM} from './canUseDOM';
|
||||
import {CAN_USE_DOM} from '@/pages/Note/Hlexical/context/shared/canUseDOM';
|
||||
|
||||
const useLayoutEffectImpl = CAN_USE_DOM
|
||||
? useLayoutEffect
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {useCallback, useMemo, useState} from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
import Modal from '../plugins/Input/Modal';
|
||||
import Modal from '@/pages/Note/Hlexical/plugins/Input/Modal';
|
||||
|
||||
export default function useModal(){
|
||||
const [modalContent, setModalContent] = useState(null);
|
||||
|
|
Before Width: | Height: | Size: 613 B After Width: | Height: | Size: 613 B |
Before Width: | Height: | Size: 708 B After Width: | Height: | Size: 708 B |
Before Width: | Height: | Size: 469 B After Width: | Height: | Size: 469 B |
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 400 B After Width: | Height: | Size: 400 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 742 B After Width: | Height: | Size: 742 B |
Before Width: | Height: | Size: 729 B After Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 376 B |
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 423 B After Width: | Height: | Size: 423 B |
Before Width: | Height: | Size: 565 B After Width: | Height: | Size: 565 B |
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 446 B |
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B |
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 622 B After Width: | Height: | Size: 622 B |
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 595 B After Width: | Height: | Size: 595 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 327 B |
Before Width: | Height: | Size: 555 B After Width: | Height: | Size: 555 B |
Before Width: | Height: | Size: 310 B After Width: | Height: | Size: 310 B |
|
@ -1,40 +1,41 @@
|
|||
import FirstTheme from "./themes/FirstTheme";
|
||||
import FirstTheme from "@/pages/Note/Hlexical/themes/FirstTheme";
|
||||
import {LexicalComposer} from "@lexical/react/LexicalComposer";
|
||||
import {RichTextPlugin} from "@lexical/react/LexicalRichTextPlugin";
|
||||
import {ContentEditable} from "@lexical/react/LexicalContentEditable";
|
||||
import {HistoryPlugin} from "@lexical/react/LexicalHistoryPlugin";
|
||||
import {AutoFocusPlugin} from "@lexical/react/LexicalAutoFocusPlugin";
|
||||
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
|
||||
import ToolbarPlugin from "./plugins/ToolbarPlugin";
|
||||
import ToolbarPlugin from "@/pages/Note/Hlexical/plugins/ToolbarPlugin";
|
||||
import {MarkdownShortcutPlugin} from "@lexical/react/LexicalMarkdownShortcutPlugin";
|
||||
import {
|
||||
TRANSFORMERS, $convertFromMarkdownString,
|
||||
} from "@lexical/markdown";
|
||||
import "./index.less"
|
||||
import {importFile} from "../../../utils/File"
|
||||
import "@/pages/Note/Hlexical/index.less"
|
||||
import {importFile} from "@/utils/File"
|
||||
|
||||
import {isEmpty} from "../../../utils/ObjectUtils";
|
||||
import {isEmpty} from "@/utils/ObjectUtils";
|
||||
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 ImportFilePlugin from "./plugins/ImportFilePlugin";
|
||||
|
||||
import SaveFilePlugin from "./plugins/SaveFilePlugin";
|
||||
import AutoLinkPlugin from "@/pages/Note/Hlexical/plugins/AutoLinkPlugin";
|
||||
import ListMaxIndentLevelPlugin from "@/pages/Note/Hlexical/plugins/ListMaxIndentLevelPlugin";
|
||||
import CodeHighlightPlugin from "@/pages/Note/Hlexical/plugins/CodeHighlightPlugin";
|
||||
import ImportFilePlugin from "@/pages/Note/Hlexical/plugins/ImportFilePlugin";
|
||||
import SaveFilePlugin from "@/pages/Note/Hlexical/plugins/SaveFilePlugin";
|
||||
import {TabIndentationPlugin} from "@lexical/react/LexicalTabIndentationPlugin";
|
||||
import UsefulNodes from "./nodes/UsefulNodes";
|
||||
import ImagesPlugin from "./plugins/ImagesPlugin";
|
||||
|
||||
import UsefulNodes from "@/pages/Note/Hlexical/nodes/UsefulNodes";
|
||||
import ImagesPlugin from "@/pages/Note/Hlexical/plugins/ImagesPlugin";
|
||||
import {HorizontalRulePlugin} from "@lexical/react/LexicalHorizontalRulePlugin"
|
||||
import InlineImagePlugin from "./plugins/InlineImagePlugin";
|
||||
import InlineImagePlugin from "@/pages/Note/Hlexical/plugins/InlineImagePlugin";
|
||||
import {TablePlugin} from "@lexical/react/LexicalTablePlugin";
|
||||
import TableCellActionMenuPlugin from './plugins/TableActionMenuPlugin';
|
||||
import ExcalidrawPlugin from "./plugins/ExcalidrawPlugin";
|
||||
import TableOfContentsPlugin from "./plugins/TableOfContentsPlugin";
|
||||
import ContextMenuPlugin from "./plugins/ContextMenuPlugin"
|
||||
import {Spin} from "antd";
|
||||
import TableCellActionMenuPlugin from '@/pages/Note/Hlexical/plugins/TableActionMenuPlugin';
|
||||
import ExcalidrawPlugin from "@/pages/Note/Hlexical/plugins/ExcalidrawPlugin";
|
||||
import TableOfContentsPlugin from "@/pages/Note/Hlexical/plugins/TableOfContentsPlugin";
|
||||
import ContextMenuPlugin from "@/pages/Note/Hlexical/plugins/ContextMenuPlugin"
|
||||
import {Spin,FloatButton } from "antd";
|
||||
import { CommentOutlined, CustomerServiceOutlined } from '@ant-design/icons';
|
||||
import {useState} from "react";
|
||||
import DragDropPaste from "@/pages/Note/Hlexical/plugins/DragDropPastePlugin";
|
||||
import TreeViewPlugin from "@/pages/Note/Hlexical/plugins/TreeViewPlugin";
|
||||
function Placeholder() {
|
||||
return <div className="editor-placeholder">记录一些灵感吧</div>;
|
||||
}
|
||||
|
@ -95,7 +96,7 @@ export default function Hlexical(props) {
|
|||
{/*markdown 快捷键*/}
|
||||
<MarkdownShortcutPlugin transformers={TRANSFORMERS}/>
|
||||
{/*图片加载*/}
|
||||
<ImagesPlugin/>
|
||||
<ImagesPlugin captionsEnabled={true}/>
|
||||
<InlineImagePlugin/>
|
||||
|
||||
{/*分割线 */}
|
||||
|
@ -108,11 +109,23 @@ export default function Hlexical(props) {
|
|||
<ContextMenuPlugin/>
|
||||
{/* 画图 */}
|
||||
<ExcalidrawPlugin/>
|
||||
|
||||
{/* 拖拽复制*/}
|
||||
<DragDropPaste />
|
||||
<ImportFilePlugin filePath={props.filePath} setSpinningState={setSpinningState}/>
|
||||
<SaveFilePlugin filePath={props.filePath}/>
|
||||
{/*文件操作导入文件*/}
|
||||
{/*<ActionPlugin/>*/}
|
||||
<FloatButton.Group
|
||||
trigger="hover"
|
||||
type="primary"
|
||||
style={{
|
||||
right: 94,
|
||||
}}
|
||||
icon={<CustomerServiceOutlined />}
|
||||
>
|
||||
<FloatButton />
|
||||
<FloatButton icon={<CommentOutlined />} />
|
||||
</FloatButton.Group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -225,22 +225,60 @@ h1 {
|
|||
}
|
||||
|
||||
.editor-heading-h1 {
|
||||
font-size: 24px;
|
||||
font-size: 50px;
|
||||
color: rgb(5, 5, 5);
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 25px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.editor-heading-h2 {
|
||||
font-size: 15px;
|
||||
color: rgb(101, 103, 107);
|
||||
font-weight: 700;
|
||||
font-size: 45px;
|
||||
//color: rgb(101, 103, 107);
|
||||
//font-weight: 400;
|
||||
color: rgb(5, 5, 5);
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
margin-top: 10px;
|
||||
margin-top: 25px;
|
||||
padding: 0;
|
||||
text-transform: uppercase;
|
||||
//text-transform: uppercase;
|
||||
}
|
||||
.editor-heading-h3 {
|
||||
font-size: 40px;
|
||||
color: rgb(5, 5, 5);
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
margin-top: 20px;
|
||||
padding: 0;
|
||||
//text-transform: uppercase;
|
||||
}
|
||||
.editor-heading-h4 {
|
||||
font-size: 35px;
|
||||
color: rgb(5, 5, 5);
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
margin-top: 20px;
|
||||
padding: 0;
|
||||
//text-transform: uppercase;
|
||||
}
|
||||
.editor-heading-h5 {
|
||||
font-size: 30px;
|
||||
color: rgb(5, 5, 5);
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
margin-top: 15px;
|
||||
padding: 0;
|
||||
//text-transform: uppercase;
|
||||
}
|
||||
.editor-heading-h6 {
|
||||
font-size: 25px;
|
||||
color: rgb(5, 5, 5);
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
margin-top: 15px;
|
||||
padding: 0;
|
||||
//text-transform: uppercase;
|
||||
}
|
||||
|
||||
.editor-quote {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import '../index.less';
|
||||
import '@/pages/Note/Hlexical/nodes/ImageNode/index.less';
|
||||
import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
|
||||
import {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';
|
||||
import {CollaborationPlugin} from '@lexical/react/LexicalCollaborationPlugin';
|
||||
|
@ -30,20 +30,25 @@ import {Suspense, useCallback, useEffect, useRef, useState} from 'react';
|
|||
// import {createWebsocketProvider} from '../../../createWebsocketProvider';
|
||||
|
||||
import {useSharedHistoryContext} from '../../../context/SharedHistoryContext';
|
||||
import EmojisPlugin from '../../../plugins/EmojisPlugin';
|
||||
import KeywordsPlugin from '../../../plugins/KeywordsPlugin';
|
||||
import LinkPlugin from '../../../plugins/LinkPlugin';
|
||||
import EmojisPlugin from '@/pages/Note/Hlexical/plugins/EmojisPlugin';
|
||||
import KeywordsPlugin from '@/pages/Note/Hlexical/plugins/KeywordsPlugin';
|
||||
import LinkPlugin from '@/pages/Note/Hlexical/plugins/LinkPlugin';
|
||||
|
||||
// import MentionsPlugin from '../../../plugins/MentionsPlugin';
|
||||
import TreeViewPlugin from '../../../plugins/TreeViewPlugin';
|
||||
import ContentEditable from '../../../plugins/Input/ContentEditable';
|
||||
import ImageResizer from '../../../plugins/Input/ImageResizer';
|
||||
import Placeholder from '../../../plugins/Input/Placeholder';
|
||||
|
||||
import {$isImageNode} from '../index';
|
||||
import TreeViewPlugin from '@/pages/Note/Hlexical/plugins/TreeViewPlugin';
|
||||
import ContentEditable from '@/pages/Note/Hlexical/plugins/Input/ContentEditable';
|
||||
import ImageResizer from '@/pages/Note/Hlexical/plugins/Input/ImageResizer';
|
||||
import Placeholder from '@/pages/Note/Hlexical/plugins/Input/Placeholder';
|
||||
|
||||
import {$isImageNode} from '@/pages/Note/Hlexical/nodes/ImageNode';
|
||||
import {Image as AntImage} from "antd";
|
||||
/*
|
||||
* 缓存图片,后期使用预览所有图片。
|
||||
*/
|
||||
const imageCache = new Set();
|
||||
|
||||
/*
|
||||
* 添加图片到缓存列表
|
||||
*/
|
||||
function useSuspenseImage(src) {
|
||||
if (!imageCache.has(src)) {
|
||||
throw new Promise((resolve) => {
|
||||
|
@ -59,16 +64,23 @@ function useSuspenseImage(src) {
|
|||
|
||||
function LazyImage({altText, className, imageRef, src, width, height, maxWidth,}) {
|
||||
useSuspenseImage(src);
|
||||
return (<img
|
||||
className={className || undefined}
|
||||
const [editor] = useLexicalComposerContext();
|
||||
return editor.isEditable()?(<img
|
||||
className={className || undefined}
|
||||
src={src}
|
||||
alt={altText}
|
||||
ref={imageRef}
|
||||
style={{
|
||||
height, maxWidth, width,
|
||||
}}
|
||||
draggable="false"
|
||||
/>):(<AntImage
|
||||
src={src}
|
||||
alt={altText}
|
||||
height={height}
|
||||
width={width}
|
||||
ref={imageRef}
|
||||
style={{
|
||||
height, maxWidth, width,
|
||||
}}
|
||||
draggable="false"
|
||||
/>);
|
||||
/>)
|
||||
}
|
||||
|
||||
export default function ImageComponent({
|
||||
|
@ -87,6 +99,7 @@ export default function ImageComponent({
|
|||
const buttonRef = useRef(null);
|
||||
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
// 协作
|
||||
const {isCollabActive} = useCollaborationContext();
|
||||
const [editor] = useLexicalComposerContext();
|
||||
const [selection, setSelection] = useState(null);
|
||||
|
@ -139,6 +152,7 @@ export default function ImageComponent({
|
|||
}, [caption, editor, setSelected],);
|
||||
|
||||
useEffect(() => {
|
||||
// 增强
|
||||
let isMounted = true;
|
||||
const unregister = mergeRegister(
|
||||
editor.registerUpdateListener(({editorState}) => {
|
||||
|
@ -152,12 +166,15 @@ export default function ImageComponent({
|
|||
}, COMMAND_PRIORITY_LOW,),
|
||||
editor.registerCommand(CLICK_COMMAND, (payload) => {
|
||||
const event = payload;
|
||||
|
||||
console.log("点击图片:",event,isResizing)
|
||||
if (isResizing) {
|
||||
return true;
|
||||
}
|
||||
if (event.target === imageRef.current) {
|
||||
if (event.shiftKey) {
|
||||
if (event.ctrlKey){
|
||||
// ctrl 预览图片
|
||||
}else if (event.shiftKey) {
|
||||
// shift反选
|
||||
setSelected(!isSelected);
|
||||
} else {
|
||||
clearSelection();
|
||||
|
@ -168,6 +185,7 @@ export default function ImageComponent({
|
|||
|
||||
return false;
|
||||
}, COMMAND_PRIORITY_LOW,),
|
||||
// 拖拽
|
||||
editor.registerCommand(DRAGSTART_COMMAND, (event) => {
|
||||
if (event.target === imageRef.current) {
|
||||
// TODO This is just a temporary workaround for FF to behave like other browsers.
|
||||
|
@ -191,6 +209,7 @@ export default function ImageComponent({
|
|||
editor.update(() => {
|
||||
const node = $getNodeByKey(nodeKey);
|
||||
if ($isImageNode(node)) {
|
||||
// 展示说明文字
|
||||
node.setShowCaption(true);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
|||
import {Suspense} from 'react';
|
||||
|
||||
const ImageComponent = React.lazy(
|
||||
() => import('./ImageComponent'),
|
||||
() => import('@/pages/Note/Hlexical/nodes/ImageNode/ImageComponent'),
|
||||
);
|
||||
|
||||
function convertImageElement(domNode) {
|
||||
|
@ -15,17 +15,18 @@ function convertImageElement(domNode) {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
export class ImageNode extends DecoratorNode {
|
||||
// 路径
|
||||
__src;
|
||||
__altText;
|
||||
// 大小
|
||||
__width;
|
||||
__height;
|
||||
__maxWidth;
|
||||
// 图片文字说明
|
||||
__showCaption;
|
||||
__caption;
|
||||
__captionsEnabled;
|
||||
|
||||
static getType() {
|
||||
return 'image';
|
||||
}
|
||||
|
@ -154,6 +155,7 @@ export class ImageNode extends DecoratorNode {
|
|||
}
|
||||
|
||||
decorate() {
|
||||
console.log('ImageNode.decorate()')
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<ImageComponent
|
||||
|
@ -176,7 +178,7 @@ export class ImageNode extends DecoratorNode {
|
|||
export function $createImageNode({
|
||||
altText,
|
||||
height,
|
||||
maxWidth = 500,
|
||||
maxWidth = 600,
|
||||
captionsEnabled,
|
||||
src,
|
||||
width,
|
||||
|
|
|
@ -41,3 +41,236 @@
|
|||
.image-control-wrapper--resizing {
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.editor-container span.editor-image {
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editor-container .editor-image img {
|
||||
max-width: 200%;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.editor-container .editor-image img.focused {
|
||||
outline: 2px solid rgb(60, 132, 244);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editor-container .editor-image img.focused.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.editor-container .editor-image img.focused.draggable:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-caption-container .tree-view-output {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-caption-container {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-top: 1px solid #fff;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
min-width: 100px;
|
||||
color: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-caption-button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 30%;
|
||||
padding: 10px;
|
||||
margin: 0 auto;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
min-width: 100px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-caption-button:hover {
|
||||
background-color: rgba(60, 132, 244, 0.5);
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-edit-button {
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
border-radius: 5px;
|
||||
background-image: url(../../images/icons/pencil-fill.svg);
|
||||
background-size: 16px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
vertical-align: -0.25em;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-edit-button:hover {
|
||||
background-color: rgba(60, 132, 244, 0.1);
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer {
|
||||
display: block;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
position: absolute;
|
||||
background-color: rgb(60, 132, 244);
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-n {
|
||||
top: -6px;
|
||||
left: 48%;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-ne {
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
cursor: ne-resize;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-e {
|
||||
bottom: 48%;
|
||||
right: -6px;
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-se {
|
||||
bottom: -2px;
|
||||
right: -6px;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-s {
|
||||
bottom: -2px;
|
||||
left: 48%;
|
||||
cursor: s-resize;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-sw {
|
||||
bottom: -2px;
|
||||
left: -6px;
|
||||
cursor: sw-resize;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-w {
|
||||
bottom: 48%;
|
||||
left: -6px;
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.editor-container .editor-image .image-resizer.image-resizer-nw {
|
||||
top: -6px;
|
||||
left: -6px;
|
||||
cursor: nw-resize;
|
||||
}
|
||||
|
||||
.editor-container span.inline-editor-image {
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image img {
|
||||
max-width: 200%;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image img.focused {
|
||||
outline: 2px solid rgb(60, 132, 244);
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image img.focused.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image img.focused.draggable:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image .image-caption-container .tree-view-output {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image.position-full {
|
||||
margin: 1em 0 1em 0;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image.position-left {
|
||||
float: left;
|
||||
width: 50%;
|
||||
margin: 1em 1em 0 0;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image.position-right {
|
||||
float: right;
|
||||
width: 50%;
|
||||
margin: 1em 0 0 1em;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image .image-edit-button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
padding: 6px 8px;
|
||||
margin: 0 auto;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
min-width: 60px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image .image-edit-button:hover {
|
||||
background-color: rgba(60, 132, 244, 0.5);
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image .image-caption-container {
|
||||
display: block;
|
||||
background-color: #f4f4f4;
|
||||
min-width: 100%;
|
||||
color: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.editor-container .editor-image img.focused.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.editor-container .editor-image img.focused.draggable:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
.editor-container .inline-editor-image img.focused.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.editor-container .inline-editor-image img.focused.draggable:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ export default function ContextMenuPlugin(){
|
|||
|
||||
const items = await navigator.clipboard.read();
|
||||
const item = items[0];
|
||||
|
||||
console.log("navigator.clipboard.read:item",item)
|
||||
const permission = await navigator.permissions.query({
|
||||
// @ts-expect-error These types are incorrect.
|
||||
name: 'clipboard-read',
|
||||
|
@ -109,7 +109,7 @@ export default function ContextMenuPlugin(){
|
|||
alert('Not allowed to paste from clipboard.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 图片 image/png
|
||||
for (const type of item.types) {
|
||||
const dataString = await (await item.getType(type)).text();
|
||||
data.setData(type, dataString);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
||||
import {DRAG_DROP_PASTE} from '@lexical/rich-text';
|
||||
import {isMimeType, mediaFileReader} from '@lexical/utils';
|
||||
import {COMMAND_PRIORITY_LOW} from 'lexical';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
import {INSERT_IMAGE_COMMAND} from '../ImagesPlugin';
|
||||
|
||||
const ACCEPTABLE_IMAGE_TYPES = [
|
||||
'image/',
|
||||
'image/heic',
|
||||
'image/heif',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
];
|
||||
|
||||
export default function DragDropPaste() {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
useEffect(() => {
|
||||
return editor.registerCommand(
|
||||
DRAG_DROP_PASTE,
|
||||
(files) => {
|
||||
(async () => {
|
||||
const filesResult = await mediaFileReader(
|
||||
files,
|
||||
[ACCEPTABLE_IMAGE_TYPES].flatMap((x) => x),
|
||||
);
|
||||
for (const {file, result} of filesResult) {
|
||||
if (isMimeType(file, ACCEPTABLE_IMAGE_TYPES)) {
|
||||
editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
|
||||
altText: file.name,
|
||||
src: result,
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
return true;
|
||||
},
|
||||
COMMAND_PRIORITY_LOW,
|
||||
);
|
||||
}, [editor]);
|
||||
return null;
|
||||
}
|
|
@ -30,33 +30,34 @@ import {DialogActions, DialogButtonsList} from '../Input/Dialog';
|
|||
import FileInput from '../Input/FileInput';
|
||||
import TextInput from '../Input/TextInput';
|
||||
import {Button} from "antd";
|
||||
|
||||
|
||||
|
||||
|
||||
export const INSERT_IMAGE_COMMAND =
|
||||
createCommand('INSERT_IMAGE_COMMAND');
|
||||
|
||||
/**
|
||||
* 插入路径图片对话框
|
||||
* @param onClick
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
export function InsertImageUriDialogBody({
|
||||
onClick,
|
||||
}) {
|
||||
const [src, setSrc] = useState('');
|
||||
const [altText, setAltText] = useState('');
|
||||
|
||||
const [resizable,setResizable] = useState(true);
|
||||
const isDisabled = src === '';
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
label="Image URL"
|
||||
placeholder="i.e. https://source.unsplash.com/random"
|
||||
label="文件url"
|
||||
placeholder="例如:https://tenfei03.cfp.cn/creative/vcg/800/new/VCG41175510742.jpg"
|
||||
onChange={setSrc}
|
||||
value={src}
|
||||
data-test-id="image-modal-url-input"
|
||||
/>
|
||||
<TextInput
|
||||
label="Alt Text"
|
||||
placeholder="Random unsplash image"
|
||||
label="图片描述"
|
||||
placeholder="例如:风景"
|
||||
onChange={setAltText}
|
||||
value={altText}
|
||||
data-test-id="image-modal-alt-text-input"
|
||||
|
@ -65,7 +66,7 @@ export function InsertImageUriDialogBody({
|
|||
<Button
|
||||
data-test-id="image-modal-confirm-btn"
|
||||
disabled={isDisabled}
|
||||
onClick={() => onClick({altText, src})}>
|
||||
onClick={() => onClick({altText, src,resizable})}>
|
||||
确认
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
@ -73,14 +74,18 @@ export function InsertImageUriDialogBody({
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入上传文件对话框
|
||||
* @param onClick
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
export function InsertImageUploadedDialogBody({
|
||||
onClick,
|
||||
}) {
|
||||
const [src, setSrc] = useState('');
|
||||
const [altText, setAltText] = useState('');
|
||||
|
||||
const isDisabled = src === '';
|
||||
|
||||
const loadImage = (files) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
|
@ -121,6 +126,13 @@ export function InsertImageUploadedDialogBody({
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入文件对话框
|
||||
* @param activeEditor
|
||||
* @param onClose
|
||||
* @returns {Element}
|
||||
* @constructor
|
||||
*/
|
||||
export function InsertImageDialog({
|
||||
activeEditor,
|
||||
onClose,
|
||||
|
@ -166,6 +178,12 @@ export function InsertImageDialog({
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片插件
|
||||
* @param captionsEnabled
|
||||
* @returns {null}
|
||||
* @constructor
|
||||
*/
|
||||
export default function ImagesPlugin({captionsEnabled,}){
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
|
@ -214,7 +232,10 @@ export default function ImagesPlugin({captionsEnabled,}){
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽图标
|
||||
* @type {string}
|
||||
*/
|
||||
const TRANSPARENT_IMAGE =
|
||||
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
||||
const img = document.createElement('img');
|
||||
|
|
|
@ -86,15 +86,18 @@ const SaveFilePlugin=(props)=> {
|
|||
|
||||
// 后期不再读取文件,直接读取文件MD5
|
||||
importFile(filePath).then(value => {
|
||||
let save
|
||||
let newFileMd5
|
||||
if (props.filePath.endsWith(".md")){
|
||||
// editorState
|
||||
newFileMd5 = md5(value.toString());
|
||||
save = (isEmpty(value)) || md5(resultSave) !== newFileMd5;
|
||||
}else {
|
||||
newFileMd5 = md5(JSON.stringify(JSON.parse(value.toString())));
|
||||
save = (isEmpty(value)) || md5(resultSave) !== newFileMd5;
|
||||
let save =true;
|
||||
let oldFileMd5
|
||||
let newFileMd5 =md5(resultSave)
|
||||
if (!isEmpty(value)){
|
||||
if (props.filePath.endsWith(".md")){
|
||||
// editorState
|
||||
oldFileMd5 = md5(value.toString());
|
||||
save = (isEmpty(value)) || newFileMd5 !== oldFileMd5;
|
||||
}else {
|
||||
oldFileMd5 = md5(JSON.stringify(JSON.parse(value.toString())));
|
||||
save = (isEmpty(value)) || newFileMd5 !== oldFileMd5;
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
overWriteFile(filePath, resultSave).then(()=>{
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
z-index: 1;
|
||||
height: 300px;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.first-heading {
|
||||
|
@ -70,7 +70,7 @@
|
|||
padding: 0;
|
||||
overflow: scroll;
|
||||
width: 200px;
|
||||
height: 220px;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
|
@ -85,7 +85,7 @@
|
|||
.headings::before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
height: 220px;
|
||||
height: 100vh;
|
||||
width: 4px;
|
||||
right: 240px;
|
||||
margin-top: 5px;
|
||||
|
|
|
@ -39,49 +39,41 @@ import {
|
|||
getDefaultCodeLanguage,
|
||||
getCodeLanguages
|
||||
} from "@lexical/code";
|
||||
import DropDown, {DropDownItem} from "./Input/DropDown";
|
||||
import DropDown, {DropDownItem} from "@/pages/Note/Hlexical/plugins/Input/DropDown";
|
||||
|
||||
import {InsertImageDialog} from "./ImagesPlugin";
|
||||
import useModal from "../hook/userModal";
|
||||
import {InsertTableDialog} from "./TablePlugin";
|
||||
import {InsertImageDialog} from "@/pages/Note/Hlexical/plugins/ImagesPlugin";
|
||||
import useModal from "@/pages/Note/Hlexical/hook/userModal";
|
||||
import {InsertTableDialog} from "@/pages/Note/Hlexical/plugins/TablePlugin";
|
||||
import {INSERT_HORIZONTAL_RULE_COMMAND} from "@lexical/react/LexicalHorizontalRuleNode";
|
||||
import {InsertInlineImageDialog} from "./InlineImagePlugin";
|
||||
import {INSERT_EXCALIDRAW_COMMAND} from "./ExcalidrawPlugin";
|
||||
import {InsertInlineImageDialog} from "@/pages/Note/Hlexical/plugins/InlineImagePlugin";
|
||||
import {INSERT_EXCALIDRAW_COMMAND} from "@/pages/Note/Hlexical/plugins/ExcalidrawPlugin";
|
||||
|
||||
const LowPriority = 1;
|
||||
|
||||
const supportedBlockTypes = new Set([
|
||||
"paragraph",
|
||||
"quote",
|
||||
"code",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"ul",
|
||||
"ol"
|
||||
// 支持的块类型
|
||||
const supportedBlockTypes = new Set(["paragraph", "quote", "code", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol"
|
||||
]);
|
||||
|
||||
const blockTypeToBlockName = {
|
||||
code: "代码块",
|
||||
h1: "一级标题",
|
||||
h2: "二级标题",
|
||||
h3: "三级标题",
|
||||
h4: "四级标题",
|
||||
h5: "五级标题",
|
||||
h6: "六级标题",
|
||||
ol: "有序序列",
|
||||
// 块类型对应的名称
|
||||
const blockTypeToBlockName = {code: "代码块",
|
||||
h1: "一级标题", h2: "二级标题", h3: "三级标题", h4: "四级标题", h5: "五级标题", h6: "六级标题",
|
||||
ol: "有序序列", ul: "无序序列",
|
||||
paragraph: "普通文本",
|
||||
quote: "引用",
|
||||
ul: "无序序列"
|
||||
};
|
||||
|
||||
/**
|
||||
* 工具栏分割符
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
function Divider() {
|
||||
return <div className="divider"/>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 位置计算
|
||||
* @param editor
|
||||
* @param rect
|
||||
*/
|
||||
function positionEditorElement(editor, rect) {
|
||||
if (rect === null) {
|
||||
editor.style.opacity = "0";
|
||||
|
@ -96,6 +88,12 @@ function positionEditorElement(editor, rect) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 浮动连接设置
|
||||
* @param editor
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
function FloatingLinkEditor({editor}) {
|
||||
const editorRef = useRef(null);
|
||||
const inputRef = useRef(null);
|
||||
|
@ -236,6 +234,15 @@ function FloatingLinkEditor({editor}) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉选择
|
||||
* @param onChange
|
||||
* @param className
|
||||
* @param options
|
||||
* @param value
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
function Select({onChange, className, options, value}) {
|
||||
return (
|
||||
<select className={className} onChange={onChange} value={value}>
|
||||
|
@ -252,6 +259,7 @@ function Select({onChange, className, options, value}) {
|
|||
function getSelectedNode(selection) {
|
||||
const anchor = selection.anchor;
|
||||
const focus = selection.focus;
|
||||
console.log('selection',anchor,focus)
|
||||
const anchorNode = selection.anchor.getNode();
|
||||
const focusNode = selection.focus.getNode();
|
||||
if (anchorNode === focusNode) {
|
||||
|
@ -375,6 +383,7 @@ function BlockOptionsDropdownList({
|
|||
};
|
||||
|
||||
const formatCode = () => {
|
||||
console.log('blockType !== "code"',blockType)
|
||||
if (blockType !== "code") {
|
||||
editor.update(() => {
|
||||
const selection = $getSelection();
|
||||
|
@ -448,6 +457,7 @@ export default function ToolbarPlugin() {
|
|||
const [isUnderline, setIsUnderline] = useState(false);
|
||||
const [isStrikethrough, setIsStrikethrough] = useState(false);
|
||||
const [isCode, setIsCode] = useState(false);
|
||||
const [isEditable, setIsEditable] = useState(() => editor.isEditable());
|
||||
|
||||
const updateToolbar = useCallback(() => {
|
||||
const selection = $getSelection();
|
||||
|
@ -528,7 +538,7 @@ export default function ToolbarPlugin() {
|
|||
);
|
||||
}, [editor, updateToolbar]);
|
||||
|
||||
const codeLanguges = useMemo(() => getCodeLanguages(), []);
|
||||
const codeLanguages = useMemo(() => getCodeLanguages(), []);
|
||||
const onCodeLanguageSelect = useCallback(
|
||||
(e) => {
|
||||
editor.update(() => {
|
||||
|
@ -554,7 +564,18 @@ export default function ToolbarPlugin() {
|
|||
return (
|
||||
<div className="toolbar" ref={toolbarRef}>
|
||||
<button
|
||||
disabled={!canUndo}
|
||||
onClick={() => {
|
||||
editor.setEditable(!isEditable)
|
||||
setIsEditable(!isEditable)
|
||||
}}
|
||||
className={"toolbar-item spaced " + (isBold ? "active" : "")}
|
||||
aria-label="Format Bold"
|
||||
>
|
||||
{isEditable?'阅读':'编辑'}
|
||||
</button>
|
||||
<Divider/>
|
||||
<button
|
||||
disabled={!canUndo||!isEditable}
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(UNDO_COMMAND);
|
||||
}}
|
||||
|
@ -564,7 +585,7 @@ export default function ToolbarPlugin() {
|
|||
<i className="format undo"/>
|
||||
</button>
|
||||
<button
|
||||
disabled={!canRedo}
|
||||
disabled={!canRedo||!isEditable}
|
||||
onClick={() => {
|
||||
editor.dispatchCommand(REDO_COMMAND);
|
||||
}}
|
||||
|
@ -577,6 +598,7 @@ export default function ToolbarPlugin() {
|
|||
{supportedBlockTypes.has(blockType) && (
|
||||
<>
|
||||
<button
|
||||
disabled={!isEditable}
|
||||
className="toolbar-item block-controls"
|
||||
onClick={() =>
|
||||
setShowBlockOptionsDropDown(!showBlockOptionsDropDown)
|
||||
|
@ -605,7 +627,7 @@ export default function ToolbarPlugin() {
|
|||
<Select
|
||||
className="toolbar-item code-language"
|
||||
onChange={onCodeLanguageSelect}
|
||||
options={codeLanguges}
|
||||
options={codeLanguages}
|
||||
value={codeLanguage}
|
||||
/>
|
||||
<i className="chevron-down inside"/>
|
||||
|
|
|
@ -9,7 +9,8 @@ const firstTheme = {
|
|||
h2: "editor-heading-h2",
|
||||
h3: "editor-heading-h3",
|
||||
h4: "editor-heading-h4",
|
||||
h5: "editor-heading-h5"
|
||||
h5: "editor-heading-h5",
|
||||
h6: "editor-heading-h6"
|
||||
},
|
||||
list: {
|
||||
nested: {
|
||||
|
|
|
@ -2,12 +2,12 @@ import React, {useRef, useState} from 'react';
|
|||
import {
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {Layout, Avatar, Tabs, Button} from 'antd';
|
||||
import Hlexical from './Hlexical';
|
||||
import ItemTree from "../../components/ItemTree";
|
||||
import './index.less'
|
||||
import {Layout, Avatar, Tabs} from 'antd';
|
||||
import Hlexical from '@/pages/Note/Hlexical';
|
||||
import ItemTree from "@/components/ItemTree";
|
||||
import '@/pages/Note/index.less'
|
||||
import {useSelector, useDispatch} from "react-redux";
|
||||
import {addTableBarItem, removeTableBarItem, setActiveKey,editLeftTableOfContents} from "../../redux/tableBarItem_reducer"
|
||||
import {addTableBarItem, removeTableBarItem, setActiveKey,editLeftTableOfContents} from "@/redux/tableBarItem_reducer"
|
||||
|
||||
const {Sider} = Layout;
|
||||
const Note = () => {
|
||||
|
|
|
@ -27,4 +27,13 @@
|
|||
}
|
||||
#itemTreeTabs .ant-tabs-tab-btn{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.ant-tabs-nav .ant-tabs-nav-add,
|
||||
/* 显示新增标签按钮 */
|
||||
.ant-tabs-nav-operations {
|
||||
display: inline-block;
|
||||
}
|
||||
/* 隐藏触发隐藏操作按钮的样式 */
|
||||
.ant-tabs-nav-operations-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import { Button,InputNumber,message } from 'antd'
|
||||
import React, { Component } from 'react'
|
||||
import axios from 'axios'
|
||||
import HistoryRecord from '../../components/HistoryRecord'
|
||||
import { connect } from 'react-redux';
|
||||
import { historyRecordSync } from '../../redux/historyRecord_reducer';
|
||||
class SyncContext extends Component {
|
||||
state={size:1}
|
||||
eidtSyncContextSize=(event)=>{
|
||||
this.setState({size:event})
|
||||
}
|
||||
historySync=(data)=>{
|
||||
console.info("historySync(data)",data)
|
||||
this.props.historyRecordSync(data)
|
||||
}
|
||||
syncContext = () => {
|
||||
const key = 'SYNC_CONTEXT';
|
||||
message.loading({content:'Loading...',key,duration:0});
|
||||
axios.get(`/ticai/sync/context/${this.state.size}`)
|
||||
.then(response => {
|
||||
const result = response.data;
|
||||
if (result.status.code === 200) {
|
||||
console.log(result.data.length)
|
||||
message.destroy(key)
|
||||
message.success({
|
||||
content: "成功同步"+result.data.length+"条",
|
||||
duration: 2,
|
||||
})
|
||||
this.historySync(result.data)
|
||||
} else {
|
||||
message.destroy(key)
|
||||
message.error(result.status.message)
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
message.destroy(key)
|
||||
message.error(error)
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<span>同步最近</span><InputNumber min={1} max={10000} defaultValue={this.state.size} onChange={this.eidtSyncContextSize}/>条数据,
|
||||
<Button onClick={this.syncContext}>确认</Button>.
|
||||
<hr/>
|
||||
<HistoryRecord/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
// state => ({
|
||||
// }),//映射状态
|
||||
{historyRecordSync}//映射操作状态的方法
|
||||
)(SyncContext)
|
|
@ -3,7 +3,7 @@ import historyReducer from './historyRecord_reducer'
|
|||
import dirMessageReducer from './dirMessage_reducer'
|
||||
import pushHotkeysReducer from "./pushHotkeys_reducer";
|
||||
import tableBarItemReducer from "./tableBarItem_reducer";
|
||||
import {electronStorage} from "../utils/LocalStorage";
|
||||
import {electronStorage} from "@/utils/LocalStorage";
|
||||
import { persistStore, persistReducer } from 'redux-persist'
|
||||
// 持久化配置
|
||||
const historyRecordPersistConfig = {
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
const {createProxyMiddleware} = require('http-proxy-middleware')
|
||||
module.exports=function(app){
|
||||
app.use(
|
||||
createProxyMiddleware('/ticai',{
|
||||
target:'http://localhost:8090/',
|
||||
changeOrigin:false,
|
||||
pathRewrite:{'^/ticai':''}
|
||||
}),
|
||||
createProxyMiddleware('/oauth',{
|
||||
target:'http://localhost:1112',
|
||||
changeOrigin:true,
|
||||
pathRewrite:{'^/oauth':''}
|
||||
})
|
||||
)
|
||||
}
|
||||
// const {createProxyMiddleware} = require('http-proxy-middleware')
|
||||
// module.exports=function(app){
|
||||
// app.use(
|
||||
// createProxyMiddleware('/ticai',{
|
||||
// target:'http://localhost:8090/',
|
||||
// changeOrigin:false,
|
||||
// pathRewrite:{'^/ticai':''}
|
||||
// }),
|
||||
// createProxyMiddleware('/oauth',{
|
||||
// target:'http://localhost:1112',
|
||||
// changeOrigin:true,
|
||||
// pathRewrite:{'^/oauth':''}
|
||||
// })
|
||||
// )
|
||||
// }
|
|
@ -1,5 +1,5 @@
|
|||
const fs = window.require("fs").promises
|
||||
const {ipcRenderer} = window.require('electron')
|
||||
const {ipcRenderer,dialog} = window.require('electron')
|
||||
const pathOp = window.require("path")
|
||||
export async function readDir(filePath){
|
||||
const files = await fs.readdir(filePath);
|
||||
|
@ -51,4 +51,8 @@ export async function updateFileName(oldFileName,newFileName){
|
|||
|
||||
export async function saveFileWithName(){
|
||||
return ipcRenderer.invoke("saveFileWithName" )
|
||||
}
|
||||
|
||||
export async function openInDir(filePath){
|
||||
return dialog.showOpenDialog({defaultPath:"filePath",properties: ['openDirectory']} )
|
||||
}
|