From ec9517c491b25f4165d80259b7d0883646e100b9 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Thu, 4 May 2023 22:40:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=8F=91=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/dist_electron/index.js | 106 +++++++++- web/dist_electron/preload.js | 4 +- web/package-lock.json | 2 + web/src/background.js | 176 +--------------- web/src/electron/fileHandle.js | 196 ++++++++++++++++++ web/src/electron/otherHandle.js | 21 ++ web/src/electron/preload.js | 6 +- web/src/electron/storage.js | 86 ++++++++ web/src/pages/Edit/components/Edit.vue | 25 ++- .../pages/Workbenche/components/FileList.vue | 87 +++++--- .../pages/Workbenche/components/Sidebar.vue | 8 +- web/src/pages/Workbenche/views/Edit.vue | 39 +++- 12 files changed, 535 insertions(+), 221 deletions(-) create mode 100644 web/src/electron/fileHandle.js create mode 100644 web/src/electron/otherHandle.js create mode 100644 web/src/electron/storage.js diff --git a/web/dist_electron/index.js b/web/dist_electron/index.js index 68749a4c..dc14860d 100644 --- a/web/dist_electron/index.js +++ b/web/dist_electron/index.js @@ -1615,6 +1615,61 @@ eval("\n/**\n * For Node.js, simply re-export the core `util.deprecate` function /***/ }), +/***/ "./node_modules/uuid/index.js": +/*!************************************!*\ + !*** ./node_modules/uuid/index.js ***! + \************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var v1 = __webpack_require__(/*! ./v1 */ \"./node_modules/uuid/v1.js\");\nvar v4 = __webpack_require__(/*! ./v4 */ \"./node_modules/uuid/v4.js\");\n\nvar uuid = v4;\nuuid.v1 = v1;\nuuid.v4 = v4;\n\nmodule.exports = uuid;\n\n\n//# sourceURL=webpack:///./node_modules/uuid/index.js?"); + +/***/ }), + +/***/ "./node_modules/uuid/lib/bytesToUuid.js": +/*!**********************************************!*\ + !*** ./node_modules/uuid/lib/bytesToUuid.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\nvar byteToHex = [];\nfor (var i = 0; i < 256; ++i) {\n byteToHex[i] = (i + 0x100).toString(16).substr(1);\n}\n\nfunction bytesToUuid(buf, offset) {\n var i = offset || 0;\n var bth = byteToHex;\n // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4\n return ([\n bth[buf[i++]], bth[buf[i++]],\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]],\n bth[buf[i++]], bth[buf[i++]],\n bth[buf[i++]], bth[buf[i++]]\n ]).join('');\n}\n\nmodule.exports = bytesToUuid;\n\n\n//# sourceURL=webpack:///./node_modules/uuid/lib/bytesToUuid.js?"); + +/***/ }), + +/***/ "./node_modules/uuid/lib/rng.js": +/*!**************************************!*\ + !*** ./node_modules/uuid/lib/rng.js ***! + \**************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("// Unique ID creation requires a high quality random # generator. In node.js\n// this is pretty straight-forward - we use the crypto API.\n\nvar crypto = __webpack_require__(/*! crypto */ \"crypto\");\n\nmodule.exports = function nodeRNG() {\n return crypto.randomBytes(16);\n};\n\n\n//# sourceURL=webpack:///./node_modules/uuid/lib/rng.js?"); + +/***/ }), + +/***/ "./node_modules/uuid/v1.js": +/*!*********************************!*\ + !*** ./node_modules/uuid/v1.js ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var rng = __webpack_require__(/*! ./lib/rng */ \"./node_modules/uuid/lib/rng.js\");\nvar bytesToUuid = __webpack_require__(/*! ./lib/bytesToUuid */ \"./node_modules/uuid/lib/bytesToUuid.js\");\n\n// **`v1()` - Generate time-based UUID**\n//\n// Inspired by https://github.com/LiosK/UUID.js\n// and http://docs.python.org/library/uuid.html\n\nvar _nodeId;\nvar _clockseq;\n\n// Previous uuid creation time\nvar _lastMSecs = 0;\nvar _lastNSecs = 0;\n\n// See https://github.com/uuidjs/uuid for API details\nfunction v1(options, buf, offset) {\n var i = buf && offset || 0;\n var b = buf || [];\n\n options = options || {};\n var node = options.node || _nodeId;\n var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;\n\n // node and clockseq need to be initialized to random values if they're not\n // specified. We do this lazily to minimize issues related to insufficient\n // system entropy. See #189\n if (node == null || clockseq == null) {\n var seedBytes = rng();\n if (node == null) {\n // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)\n node = _nodeId = [\n seedBytes[0] | 0x01,\n seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]\n ];\n }\n if (clockseq == null) {\n // Per 4.2.2, randomize (14 bit) clockseq\n clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;\n }\n }\n\n // UUID timestamps are 100 nano-second units since the Gregorian epoch,\n // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so\n // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'\n // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.\n var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();\n\n // Per 4.2.1.2, use count of uuid's generated during the current clock\n // cycle to simulate higher resolution clock\n var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;\n\n // Time since last uuid creation (in msecs)\n var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;\n\n // Per 4.2.1.2, Bump clockseq on clock regression\n if (dt < 0 && options.clockseq === undefined) {\n clockseq = clockseq + 1 & 0x3fff;\n }\n\n // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new\n // time interval\n if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {\n nsecs = 0;\n }\n\n // Per 4.2.1.2 Throw error if too many uuids are requested\n if (nsecs >= 10000) {\n throw new Error('uuid.v1(): Can\\'t create more than 10M uuids/sec');\n }\n\n _lastMSecs = msecs;\n _lastNSecs = nsecs;\n _clockseq = clockseq;\n\n // Per 4.1.4 - Convert from unix epoch to Gregorian epoch\n msecs += 12219292800000;\n\n // `time_low`\n var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;\n b[i++] = tl >>> 24 & 0xff;\n b[i++] = tl >>> 16 & 0xff;\n b[i++] = tl >>> 8 & 0xff;\n b[i++] = tl & 0xff;\n\n // `time_mid`\n var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;\n b[i++] = tmh >>> 8 & 0xff;\n b[i++] = tmh & 0xff;\n\n // `time_high_and_version`\n b[i++] = tmh >>> 24 & 0xf | 0x10; // include version\n b[i++] = tmh >>> 16 & 0xff;\n\n // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)\n b[i++] = clockseq >>> 8 | 0x80;\n\n // `clock_seq_low`\n b[i++] = clockseq & 0xff;\n\n // `node`\n for (var n = 0; n < 6; ++n) {\n b[i + n] = node[n];\n }\n\n return buf ? buf : bytesToUuid(b);\n}\n\nmodule.exports = v1;\n\n\n//# sourceURL=webpack:///./node_modules/uuid/v1.js?"); + +/***/ }), + +/***/ "./node_modules/uuid/v4.js": +/*!*********************************!*\ + !*** ./node_modules/uuid/v4.js ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var rng = __webpack_require__(/*! ./lib/rng */ \"./node_modules/uuid/lib/rng.js\");\nvar bytesToUuid = __webpack_require__(/*! ./lib/bytesToUuid */ \"./node_modules/uuid/lib/bytesToUuid.js\");\n\nfunction v4(options, buf, offset) {\n var i = buf && offset || 0;\n\n if (typeof(options) == 'string') {\n buf = options === 'binary' ? new Array(16) : null;\n options = null;\n }\n options = options || {};\n\n var rnds = options.random || (options.rng || rng)();\n\n // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n\n // Copy bytes to buffer, if provided\n if (buf) {\n for (var ii = 0; ii < 16; ++ii) {\n buf[i + ii] = rnds[ii];\n }\n }\n\n return buf || bytesToUuid(rnds);\n}\n\nmodule.exports = v4;\n\n\n//# sourceURL=webpack:///./node_modules/uuid/v4.js?"); + +/***/ }), + /***/ "./node_modules/vue-cli-plugin-electron-builder/lib/createProtocol.js": /*!****************************************************************************!*\ !*** ./node_modules/vue-cli-plugin-electron-builder/lib/createProtocol.js ***! @@ -1749,7 +1804,43 @@ eval("/*\n Yaku v0.16.7\n (c) 2015 Yad Smood. http://ysmood.org\n License MIT\n* /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! electron */ \"electron\");\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(electron__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var vue_cli_plugin_electron_builder_lib__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vue-cli-plugin-electron-builder/lib */ \"./node_modules/vue-cli-plugin-electron-builder/lib/index.js\");\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! path */ \"path\");\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var open__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! open */ \"open\");\n/* harmony import */ var open__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(open__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var electron_json_storage__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! electron-json-storage */ \"./node_modules/electron-json-storage/lib/storage.js\");\n/* harmony import */ var electron_json_storage__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(electron_json_storage__WEBPACK_IMPORTED_MODULE_4__);\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nconst fs = __webpack_require__(/*! fs-extra */ \"./node_modules/fs-extra/lib/index.js\")\r\nconst isDevelopment = \"development\" !== 'production'\r\n\r\n// 保存到最近文件\r\nconst RECENT_FILE_LIST = 'recentFileList'\r\nconst saveToRecent = file => {\r\n let list = removeFileInRecent()\r\n list.push(file)\r\n electron_json_storage__WEBPACK_IMPORTED_MODULE_4___default.a.set(RECENT_FILE_LIST, list)\r\n}\r\nconst getRecent = () => {\r\n let res = electron_json_storage__WEBPACK_IMPORTED_MODULE_4___default.a.getSync(RECENT_FILE_LIST)\r\n return Array.isArray(res) ? res : []\r\n}\r\nconst clearRecent = () => {\r\n electron_json_storage__WEBPACK_IMPORTED_MODULE_4___default.a.remove(RECENT_FILE_LIST)\r\n}\r\nconst removeFileInRecent = (file) => {\r\n let list = getRecent()\r\n let index = list.find(item => {\r\n return item === file\r\n })\r\n if (index !== -1) {\r\n list.splice(index, 1)\r\n }\r\n electron_json_storage__WEBPACK_IMPORTED_MODULE_4___default.a.set(RECENT_FILE_LIST, list)\r\n return list\r\n}\r\n// clearRecent()\r\n\r\n// Scheme must be registered before the app is ready\r\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"protocol\"].registerSchemesAsPrivileged([\r\n { scheme: 'app', privileges: { secure: true, standard: true } }\r\n])\r\n\r\n// 创建主页面\r\nlet mainWindow = null\r\nasync function createMainWindow() {\r\n mainWindow = new electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"]({\r\n width: 1200,\r\n height: 800,\r\n frame: false,\r\n titleBarStyle: 'hiddenInset',\r\n webPreferences: {\r\n webSecurity: false,\r\n nodeIntegration: true,\r\n enableRemoteModule: true,\r\n contextIsolation: true,\r\n preload: path__WEBPACK_IMPORTED_MODULE_2___default.a.join(__dirname, 'preload.js')\r\n }\r\n })\r\n\r\n if (true) {\r\n // Load the url of the dev server if in development mode\r\n await mainWindow.loadURL(\"http://localhost:8080\" + '/#/workbenche')\r\n if (!process.env.IS_TEST) win.webContents.openDevTools()\r\n } else {}\r\n}\r\n\r\n// 通知主页面刷新最近文件列表\r\nconst notifyMainWindowRefreshRecentFileList = () => {\r\n mainWindow.webContents.send('refreshRecentFileList')\r\n}\r\n\r\n// 监听事件\r\nconst bindEvent = () => {\r\n // 新建编辑页面\r\n const openIds = []\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('create', async (event, id) => {\r\n openIds.push(id)\r\n const win = new electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"]({\r\n width: 1200,\r\n height: 800,\r\n frame: false,\r\n titleBarStyle: 'hiddenInset',\r\n webPreferences: {\r\n webSecurity: false,\r\n nodeIntegration: true,\r\n enableRemoteModule: true,\r\n contextIsolation: true,\r\n preload: path__WEBPACK_IMPORTED_MODULE_2___default.a.join(__dirname, 'preload.js')\r\n }\r\n })\r\n win.on('closed', () => {\r\n let index = openIds.find(item => {\r\n return item === id\r\n })\r\n if (index !== -1) {\r\n openIds.splice(index, 1)\r\n }\r\n })\r\n if (true) {\r\n // Load the url of the dev server if in development mode\r\n win.loadURL(\r\n \"http://localhost:8080\" + '/#/workbenche/edit/' + id\r\n )\r\n if (!process.env.IS_TEST) win.webContents.openDevTools()\r\n } else {}\r\n })\r\n\r\n // 保存文件\r\n const idToFilePath = {}\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('save', async (event, id, data) => {\r\n if (!idToFilePath[id]) {\r\n const webContents = event.sender\r\n const win = electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"].fromWebContents(webContents)\r\n const res = electron__WEBPACK_IMPORTED_MODULE_0__[\"dialog\"].showSaveDialogSync(win, {\r\n title: '保存',\r\n defaultPath: '未命名.smm',\r\n filters: [{ name: '思维导图', extensions: ['smm'] }]\r\n })\r\n if (res) {\r\n idToFilePath[id] = res\r\n fs.writeFile(res, data)\r\n saveToRecent(res)\r\n notifyMainWindowRefreshRecentFileList()\r\n return path__WEBPACK_IMPORTED_MODULE_2___default.a.parse(idToFilePath[id]).name\r\n }\r\n } else {\r\n fs.writeFile(idToFilePath[id], data)\r\n }\r\n })\r\n\r\n // 重命名文件\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('rename', async (event, id, name) => {\r\n if (!idToFilePath[id]) {\r\n return\r\n }\r\n let oldPath = idToFilePath[id]\r\n let { base, ...oldPathData } = path__WEBPACK_IMPORTED_MODULE_2___default.a.parse(oldPath)\r\n oldPathData.name = name\r\n let newPath = path__WEBPACK_IMPORTED_MODULE_2___default.a.format(oldPathData)\r\n idToFilePath[id] = newPath\r\n await fs.rename(oldPath, newPath)\r\n notifyMainWindowRefreshRecentFileList()\r\n })\r\n\r\n // 处理缩放事件\r\n ;['minimize', 'maximize', 'unmaximize', 'close'].forEach(item => {\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on(item, event => {\r\n const webContents = event.sender\r\n const win = electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"].fromWebContents(webContents)\r\n win[item]()\r\n })\r\n })\r\n\r\n // 使用默认浏览器打开指定url\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('openUrl', (event, url) => {\r\n open__WEBPACK_IMPORTED_MODULE_3___default()(url)\r\n })\r\n\r\n // 获取最近文件列表\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('getRecentFileList', () => {\r\n return getRecent().map(item => {\r\n let data = path__WEBPACK_IMPORTED_MODULE_2___default.a.parse(item)\r\n return {\r\n url: item,\r\n dir: data.dir,\r\n name: data.name\r\n }\r\n })\r\n })\r\n\r\n // 打开指定目录\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('openFileInDir', (event, file) => {\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"shell\"].showItemInFolder(file)\r\n })\r\n\r\n // 删除指定文件\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('deleteFile', (event, file) => {\r\n let res = ''\r\n let id = Object.keys(idToFilePath).find(item => {\r\n console.log(item, idToFilePath[item])\r\n return idToFilePath[item] === file\r\n })\r\n let index = -1\r\n if (id) {\r\n index = openIds.findIndex(item => {\r\n return item === id\r\n })\r\n }\r\n console.log(file, id, index)\r\n if (index === -1) {\r\n res = fs.rmSync(file)\r\n if (res) {\r\n removeFileInRecent(file)\r\n }\r\n } else {\r\n res = '该文件正在编辑,请关闭后再试'\r\n }\r\n return res\r\n })\r\n}\r\n\r\n// 关闭所有窗口后退出\r\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].on('window-all-closed', () => {\r\n // On macOS it is common for applications and their menu bar\r\n // to stay active until the user quits explicitly with Cmd + Q\r\n if (process.platform !== 'darwin') {\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].quit()\r\n }\r\n})\r\n\r\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].on('activate', () => {\r\n // On macOS it's common to re-create a window in the app when the\r\n // dock icon is clicked and there are no other windows open.\r\n if (electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"].getAllWindows().length === 0) {\r\n createMainWindow()\r\n bindEvent()\r\n }\r\n})\r\n\r\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].on('ready', async () => {\r\n createMainWindow()\r\n bindEvent()\r\n})\r\n\r\n// Exit cleanly on request from parent process in development mode.\r\nif (isDevelopment) {\r\n if (process.platform === 'win32') {\r\n process.on('message', data => {\r\n if (data === 'graceful-exit') {\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].quit()\r\n }\r\n })\r\n } else {\r\n process.on('SIGTERM', () => {\r\n electron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].quit()\r\n })\r\n }\r\n}\r\n\n\n//# sourceURL=webpack:///./src/background.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! electron */ \"electron\");\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(electron__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var vue_cli_plugin_electron_builder_lib__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vue-cli-plugin-electron-builder/lib */ \"./node_modules/vue-cli-plugin-electron-builder/lib/index.js\");\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! path */ \"path\");\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _electron_fileHandle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./electron/fileHandle */ \"./src/electron/fileHandle.js\");\n/* harmony import */ var _electron_otherHandle__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./electron/otherHandle */ \"./src/electron/otherHandle.js\");\n\n\n\n\n\n\n\n\nconst isDevelopment = \"development\" !== 'production'\n\n// Scheme must be registered before the app is ready\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"protocol\"].registerSchemesAsPrivileged([\n { scheme: 'app', privileges: { secure: true, standard: true } }\n])\n\n// 创建主页面\nlet mainWindow = null\nasync function createMainWindow() {\n mainWindow = new electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"]({\n width: 1200,\n height: 800,\n frame: false,\n titleBarStyle: 'hiddenInset',\n webPreferences: {\n webSecurity: false,\n nodeIntegration: true,\n enableRemoteModule: true,\n contextIsolation: true,\n preload: path__WEBPACK_IMPORTED_MODULE_2___default.a.join(__dirname, 'preload.js')\n }\n })\n\n if (true) {\n // Load the url of the dev server if in development mode\n await mainWindow.loadURL(\"http://localhost:8082\" + '/#/workbenche')\n // if (!process.env.IS_TEST) mainWindow.webContents.openDevTools()\n } else {}\n}\n\n// 绑定事件\nconst bindEvent = () => {\n Object(_electron_fileHandle__WEBPACK_IMPORTED_MODULE_3__[\"bindFileHandleEvent\"])({ mainWindow })\n Object(_electron_otherHandle__WEBPACK_IMPORTED_MODULE_4__[\"bindOtherHandleEvent\"])()\n}\n\n// 关闭所有窗口后退出\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].on('window-all-closed', () => {\n // On macOS it is common for applications and their menu bar\n // to stay active until the user quits explicitly with Cmd + Q\n if (process.platform !== 'darwin') {\n electron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].quit()\n }\n})\n\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].on('activate', () => {\n // On macOS it's common to re-create a window in the app when the\n // dock icon is clicked and there are no other windows open.\n if (electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"].getAllWindows().length === 0) {\n createMainWindow()\n bindEvent()\n }\n})\n\nelectron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].on('ready', async () => {\n createMainWindow()\n bindEvent()\n})\n\n// Exit cleanly on request from parent process in development mode.\nif (isDevelopment) {\n if (process.platform === 'win32') {\n process.on('message', data => {\n if (data === 'graceful-exit') {\n electron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].quit()\n }\n })\n } else {\n process.on('SIGTERM', () => {\n electron__WEBPACK_IMPORTED_MODULE_0__[\"app\"].quit()\n })\n }\n}\n\n\n//# sourceURL=webpack:///./src/background.js?"); + +/***/ }), + +/***/ "./src/electron/fileHandle.js": +/*!************************************!*\ + !*** ./src/electron/fileHandle.js ***! + \************************************/ +/*! exports provided: bindFileHandleEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bindFileHandleEvent\", function() { return bindFileHandleEvent; });\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! electron */ \"electron\");\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(electron__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var fs_extra__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs-extra */ \"./node_modules/fs-extra/lib/index.js\");\n/* harmony import */ var fs_extra__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs_extra__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! path */ \"path\");\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./storage */ \"./src/electron/storage.js\");\n/* harmony import */ var uuid__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! uuid */ \"./node_modules/uuid/index.js\");\n/* harmony import */ var uuid__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(uuid__WEBPACK_IMPORTED_MODULE_4__);\n\n\n\n\n\n\nconst bindFileHandleEvent = ({ mainWindow }) => {\n // 通知主页面刷新最近文件列表\n const notifyMainWindowRefreshRecentFileList = () => {\n mainWindow.webContents.send('refreshRecentFileList')\n }\n\n // 新建编辑页面\n const openIds = []\n const createEditWindow = async (event, id) => {\n openIds.push(id)\n const win = new electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"]({\n width: 1200,\n height: 800,\n frame: false,\n titleBarStyle: 'hiddenInset',\n webPreferences: {\n webSecurity: false,\n nodeIntegration: true,\n enableRemoteModule: true,\n contextIsolation: true,\n preload: path__WEBPACK_IMPORTED_MODULE_2___default.a.join(__dirname, 'preload.js')\n }\n })\n win.on('closed', () => {\n let index = openIds.find(item => {\n return item === id\n })\n if (index !== -1) {\n openIds.splice(index, 1)\n }\n })\n if (true) {\n // Load the url of the dev server if in development mode\n win.loadURL(\n \"http://localhost:8082\" + '/#/workbenche/edit/' + id\n )\n if (!process.env.IS_TEST) win.webContents.openDevTools()\n } else {}\n }\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('create', createEditWindow)\n\n // 保存文件\n const idToFilePath = {}\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('save', async (event, id, data) => {\n if (!idToFilePath[id]) {\n const webContents = event.sender\n const win = electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"].fromWebContents(webContents)\n const res = electron__WEBPACK_IMPORTED_MODULE_0__[\"dialog\"].showSaveDialogSync(win, {\n title: '保存',\n defaultPath: '未命名.smm',\n filters: [{ name: '思维导图', extensions: ['smm'] }]\n })\n if (res) {\n idToFilePath[id] = res\n fs_extra__WEBPACK_IMPORTED_MODULE_1___default.a.writeFile(res, data)\n Object(_storage__WEBPACK_IMPORTED_MODULE_3__[\"saveToRecent\"])(res)\n .then(() => {\n notifyMainWindowRefreshRecentFileList()\n })\n return path__WEBPACK_IMPORTED_MODULE_2___default.a.parse(idToFilePath[id]).name\n }\n } else {\n fs_extra__WEBPACK_IMPORTED_MODULE_1___default.a.writeFile(idToFilePath[id], data)\n }\n })\n\n // 打开文件\n const openFile = (event, file) => {\n let id = Object(uuid__WEBPACK_IMPORTED_MODULE_4__[\"v4\"])()\n idToFilePath[id] = file\n Object(_storage__WEBPACK_IMPORTED_MODULE_3__[\"saveToRecent\"])(file)\n .then(() => {\n notifyMainWindowRefreshRecentFileList()\n })\n createEditWindow(null, id)\n }\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('openFile', openFile)\n\n // 选择打开本地文件\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('selectOpenFile', (event) => {\n const res = electron__WEBPACK_IMPORTED_MODULE_0__[\"dialog\"].showOpenDialogSync({\n title: '选择',\n filters: [{ name: '思维导图', extensions: ['smm'] }],\n })\n if (res && res[0]) {\n openFile(null, res[0])\n }\n })\n\n // 获取文件内容\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('getFileContent', (event, id) => {\n return new Promise((resolve) => {\n let file = idToFilePath[id]\n if (!file) {\n resolve(null)\n return\n }\n fs_extra__WEBPACK_IMPORTED_MODULE_1___default.a.readFile(file, { encoding: 'utf-8' }, (err, data) => {\n resolve({\n name: path__WEBPACK_IMPORTED_MODULE_2___default.a.parse(file).name,\n content: JSON.parse(data)\n })\n })\n })\n })\n\n // 重命名文件\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('rename', (event, id, name) => {\n return new Promise((resolve) => {\n if (!idToFilePath[id]) {\n resolve('文件不存在')\n return\n }\n let oldPath = idToFilePath[id]\n let { base, ...oldPathData } = path__WEBPACK_IMPORTED_MODULE_2___default.a.parse(oldPath)\n oldPathData.name = name\n let newPath = path__WEBPACK_IMPORTED_MODULE_2___default.a.format(oldPathData)\n idToFilePath[id] = newPath\n fs_extra__WEBPACK_IMPORTED_MODULE_1___default.a.rename(oldPath, newPath, (err) => {\n if (err) {\n resolve('重命名失败')\n } else {\n Object(_storage__WEBPACK_IMPORTED_MODULE_3__[\"replaceFileInRecent\"])(oldPath, newPath).then(() => {\n notifyMainWindowRefreshRecentFileList()\n resolve()\n })\n }\n })\n })\n })\n\n // 获取最近文件列表\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('getRecentFileList', () => {\n return Object(_storage__WEBPACK_IMPORTED_MODULE_3__[\"getRecent\"])().map(item => {\n console.log(item);\n let data = path__WEBPACK_IMPORTED_MODULE_2___default.a.parse(item)\n return {\n url: item,\n dir: data.dir,\n name: data.name\n }\n })\n })\n\n // 清空最近文件列表\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('clearRecentFileList', async () => {\n try {\n Object(_storage__WEBPACK_IMPORTED_MODULE_3__[\"clearRecent\"])()\n return ''\n } catch (error) {\n return '清空失败'\n }\n })\n\n // 打开指定目录\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('openFileInDir', (event, file) => {\n electron__WEBPACK_IMPORTED_MODULE_0__[\"shell\"].showItemInFolder(file)\n })\n\n // 删除指定文件\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].handle('deleteFile', (event, file) => {\n let res = ''\n let id = Object.keys(idToFilePath).find(item => {\n return idToFilePath[item] === file\n })\n let index = -1\n if (id) {\n index = openIds.findIndex(item => {\n return item === id\n })\n }\n if (index === -1) {\n res = fs_extra__WEBPACK_IMPORTED_MODULE_1___default.a.rmSync(file)\n if (!res) {\n Object(_storage__WEBPACK_IMPORTED_MODULE_3__[\"removeFileInRecent\"])(file)\n }\n } else {\n res = '该文件正在编辑,请关闭后再试'\n }\n return res\n })\n}\n\n//# sourceURL=webpack:///./src/electron/fileHandle.js?"); + +/***/ }), + +/***/ "./src/electron/otherHandle.js": +/*!*************************************!*\ + !*** ./src/electron/otherHandle.js ***! + \*************************************/ +/*! exports provided: bindOtherHandleEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bindOtherHandleEvent\", function() { return bindOtherHandleEvent; });\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! electron */ \"electron\");\n/* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(electron__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var open__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! open */ \"open\");\n/* harmony import */ var open__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(open__WEBPACK_IMPORTED_MODULE_1__);\n\n\n\nconst bindOtherHandleEvent = () => {\n // 处理缩放事件\n ;['minimize', 'maximize', 'unmaximize', 'close'].forEach(item => {\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on(item, event => {\n const webContents = event.sender\n const win = electron__WEBPACK_IMPORTED_MODULE_0__[\"BrowserWindow\"].fromWebContents(webContents)\n win[item]()\n })\n })\n\n // 使用默认浏览器打开指定url\n electron__WEBPACK_IMPORTED_MODULE_0__[\"ipcMain\"].on('openUrl', (event, url) => {\n open__WEBPACK_IMPORTED_MODULE_1___default()(url)\n })\n}\n\n//# sourceURL=webpack:///./src/electron/otherHandle.js?"); + +/***/ }), + +/***/ "./src/electron/storage.js": +/*!*********************************!*\ + !*** ./src/electron/storage.js ***! + \*********************************/ +/*! exports provided: RECENT_FILE_LIST, saveToRecent, getRecent, clearRecent, removeFileInRecent, replaceFileInRecent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RECENT_FILE_LIST\", function() { return RECENT_FILE_LIST; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saveToRecent\", function() { return saveToRecent; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getRecent\", function() { return getRecent; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"clearRecent\", function() { return clearRecent; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"removeFileInRecent\", function() { return removeFileInRecent; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"replaceFileInRecent\", function() { return replaceFileInRecent; });\n/* harmony import */ var electron_json_storage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! electron-json-storage */ \"./node_modules/electron-json-storage/lib/storage.js\");\n/* harmony import */ var electron_json_storage__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(electron_json_storage__WEBPACK_IMPORTED_MODULE_0__);\n\n\nconst RECENT_FILE_LIST = 'recentFileList'\n\n// 保存到最近文件\nconst saveToRecent = file => {\n return new Promise((resolve, reject) => {\n let list = getRecent()\n let index = list.findIndex(item => {\n return item === file\n })\n if (index !== -1) {\n list.splice(index, 1)\n }\n list.push(file)\n electron_json_storage__WEBPACK_IMPORTED_MODULE_0___default.a.set(RECENT_FILE_LIST, list, (err) => {\n if (err) {\n reject(err)\n } else {\n resolve()\n }\n })\n })\n}\n\n// 获取最近文件列表\nconst getRecent = () => {\n let res = electron_json_storage__WEBPACK_IMPORTED_MODULE_0___default.a.getSync(RECENT_FILE_LIST)\n return (Array.isArray(res) ? res : []).filter((item) => {\n return !!item\n })\n}\n\n// 清除最近文件列表\nconst clearRecent = () => {\n return new Promise((resolve, reject) => {\n electron_json_storage__WEBPACK_IMPORTED_MODULE_0___default.a.remove(RECENT_FILE_LIST, (err) => {\n if (err) {\n reject(err)\n } else {\n resolve()\n }\n })\n })\n}\n\n// 从最近文件列表中移除指定文件\nconst removeFileInRecent = (file) => {\n return new Promise((resolve, reject) => {\n let list = getRecent()\n let index = list.findIndex(item => {\n return item === file\n })\n if (index !== -1) {\n list.splice(index, 1)\n }\n electron_json_storage__WEBPACK_IMPORTED_MODULE_0___default.a.set(RECENT_FILE_LIST, list, (err) => {\n if (err) {\n reject(err)\n } else {\n resolve()\n }\n })\n })\n}\n\n// 替换指定文件\nconst replaceFileInRecent = (oldFile, newFile) => {\n return new Promise((resolve, reject) => {\n let list = getRecent()\n let index = list.findIndex(item => {\n return item === oldFile\n })\n if (index !== -1) {\n list.splice(index, 1)\n }\n list.push(newFile)\n electron_json_storage__WEBPACK_IMPORTED_MODULE_0___default.a.set(RECENT_FILE_LIST, list, (err) => {\n if (err) {\n reject(err)\n } else {\n resolve()\n }\n })\n })\n}\n\n//# sourceURL=webpack:///./src/electron/storage.js?"); /***/ }), @@ -1760,7 +1851,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var elec /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("module.exports = __webpack_require__(/*! E:\\wanglin\\mind-map\\web\\src\\background.js */\"./src/background.js\");\n\n\n//# sourceURL=webpack:///multi_./src/background.js?"); +eval("module.exports = __webpack_require__(/*! /Users/lisa/wanglin/github/mind-map/web/src/background.js */\"./src/background.js\");\n\n\n//# sourceURL=webpack:///multi_./src/background.js?"); /***/ }), @@ -1797,6 +1888,17 @@ eval("module.exports = require(\"constants\");\n\n//# sourceURL=webpack:///exter /***/ }), +/***/ "crypto": +/*!*************************!*\ + !*** external "crypto" ***! + \*************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = require(\"crypto\");\n\n//# sourceURL=webpack:///external_%22crypto%22?"); + +/***/ }), + /***/ "electron": /*!***************************!*\ !*** external "electron" ***! diff --git a/web/dist_electron/preload.js b/web/dist_electron/preload.js index 71272763..53575a6b 100644 --- a/web/dist_electron/preload.js +++ b/web/dist_electron/preload.js @@ -93,7 +93,7 @@ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("const { contextBridge, ipcRenderer } = __webpack_require__(/*! electron */ \"electron\")\r\n\r\ncontextBridge.exposeInMainWorld('platform', process.platform)\r\ncontextBridge.exposeInMainWorld('IS_ELECTRON', true)\r\n\r\ncontextBridge.exposeInMainWorld('electronAPI', {\r\n minimize: () => ipcRenderer.send('minimize'),\r\n maximize: () => ipcRenderer.send('maximize'),\r\n unmaximize: () => ipcRenderer.send('unmaximize'),\r\n close: () => ipcRenderer.send('close'),\r\n create: (id) => ipcRenderer.send('create', id),\r\n save: (id, data) => ipcRenderer.invoke('save', id, data),\r\n rename: (id, name) => ipcRenderer.invoke('rename', id, name),\r\n openUrl: (url) => ipcRenderer.send('openUrl', url),\r\n getRecentFileList: () => ipcRenderer.invoke('getRecentFileList'),\r\n openFileInDir: (file) => ipcRenderer.send('openFileInDir', file),\r\n deleteFile: (file) => ipcRenderer.invoke('deleteFile', file),\r\n onRefreshRecentFileList: (callback) => ipcRenderer.on('refreshRecentFileList', callback)\r\n})\n\n//# sourceURL=webpack:///./src/electron/preload.js?"); +eval("const { contextBridge, ipcRenderer } = __webpack_require__(/*! electron */ \"electron\")\n\ncontextBridge.exposeInMainWorld('platform', process.platform)\ncontextBridge.exposeInMainWorld('IS_ELECTRON', true)\n\ncontextBridge.exposeInMainWorld('electronAPI', {\n minimize: () => ipcRenderer.send('minimize'),\n maximize: () => ipcRenderer.send('maximize'),\n unmaximize: () => ipcRenderer.send('unmaximize'),\n close: () => ipcRenderer.send('close'),\n create: (id) => ipcRenderer.send('create', id),\n getFileContent: (id) => ipcRenderer.invoke('getFileContent', id),\n save: (id, data) => ipcRenderer.invoke('save', id, data),\n rename: (id, name) => ipcRenderer.invoke('rename', id, name),\n openUrl: (url) => ipcRenderer.send('openUrl', url),\n getRecentFileList: () => ipcRenderer.invoke('getRecentFileList'),\n clearRecentFileList: () => ipcRenderer.invoke('clearRecentFileList'),\n openFileInDir: (file) => ipcRenderer.send('openFileInDir', file),\n deleteFile: (file) => ipcRenderer.invoke('deleteFile', file),\n onRefreshRecentFileList: (callback) => ipcRenderer.on('refreshRecentFileList', callback),\n openFile: (file) => ipcRenderer.send('openFile', file),\n selectOpenFile: () => ipcRenderer.send('selectOpenFile'),\n})\n\n//# sourceURL=webpack:///./src/electron/preload.js?"); /***/ }), @@ -104,7 +104,7 @@ eval("const { contextBridge, ipcRenderer } = __webpack_require__(/*! electron */ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("module.exports = __webpack_require__(/*! E:\\wanglin\\mind-map\\web\\src\\electron\\preload.js */\"./src/electron/preload.js\");\n\n\n//# sourceURL=webpack:///multi_./src/electron/preload.js?"); +eval("module.exports = __webpack_require__(/*! /Users/lisa/wanglin/github/mind-map/web/src/electron/preload.js */\"./src/electron/preload.js\");\n\n\n//# sourceURL=webpack:///multi_./src/electron/preload.js?"); /***/ }), diff --git a/web/package-lock.json b/web/package-lock.json index 44eec9dd..d5ed6629 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -20799,6 +20799,7 @@ "version": "4.5.19", "dev": true, "requires": { + "@babel/core": "^7.11.0", "@babel/helper-compilation-targets": "^7.9.6", "@babel/helper-module-imports": "^7.8.3", "@babel/plugin-proposal-class-properties": "^7.8.3", @@ -20811,6 +20812,7 @@ "@vue/babel-plugin-jsx": "^1.0.3", "@vue/babel-preset-jsx": "^1.2.4", "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.6.5", "core-js-compat": "^3.6.5", "semver": "^6.1.0" } diff --git a/web/src/background.js b/web/src/background.js index eb670f4a..bfa792ed 100644 --- a/web/src/background.js +++ b/web/src/background.js @@ -4,46 +4,14 @@ import { app, protocol, BrowserWindow, - ipcMain, - BrowserView, - dialog, - shell } from 'electron' import { createProtocol } from 'vue-cli-plugin-electron-builder/lib' import path from 'path' -import open from 'open' -import storage from 'electron-json-storage' +import { bindFileHandleEvent } from './electron/fileHandle' +import { bindOtherHandleEvent } from './electron/otherHandle'; -const fs = require('fs-extra') const isDevelopment = process.env.NODE_ENV !== 'production' -// 保存到最近文件 -const RECENT_FILE_LIST = 'recentFileList' -const saveToRecent = file => { - let list = removeFileInRecent() - list.push(file) - storage.set(RECENT_FILE_LIST, list) -} -const getRecent = () => { - let res = storage.getSync(RECENT_FILE_LIST) - return Array.isArray(res) ? res : [] -} -const clearRecent = () => { - storage.remove(RECENT_FILE_LIST) -} -const removeFileInRecent = (file) => { - let list = getRecent() - let index = list.find(item => { - return item === file - }) - if (index !== -1) { - list.splice(index, 1) - } - storage.set(RECENT_FILE_LIST, list) - return list -} -// clearRecent() - // Scheme must be registered before the app is ready protocol.registerSchemesAsPrivileged([ { scheme: 'app', privileges: { secure: true, standard: true } } @@ -69,7 +37,7 @@ async function createMainWindow() { if (process.env.WEBPACK_DEV_SERVER_URL) { // Load the url of the dev server if in development mode await mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL + '/#/workbenche') - if (!process.env.IS_TEST) win.webContents.openDevTools() + // if (!process.env.IS_TEST) mainWindow.webContents.openDevTools() } else { createProtocol('app') // Load the index.html when not in development @@ -77,142 +45,10 @@ async function createMainWindow() { } } -// 通知主页面刷新最近文件列表 -const notifyMainWindowRefreshRecentFileList = () => { - mainWindow.webContents.send('refreshRecentFileList') -} - -// 监听事件 +// 绑定事件 const bindEvent = () => { - // 新建编辑页面 - const openIds = [] - ipcMain.on('create', async (event, id) => { - openIds.push(id) - const win = new BrowserWindow({ - width: 1200, - height: 800, - frame: false, - titleBarStyle: 'hiddenInset', - webPreferences: { - webSecurity: false, - nodeIntegration: true, - enableRemoteModule: true, - contextIsolation: true, - preload: path.join(__dirname, 'preload.js') - } - }) - win.on('closed', () => { - let index = openIds.find(item => { - return item === id - }) - if (index !== -1) { - openIds.splice(index, 1) - } - }) - if (process.env.WEBPACK_DEV_SERVER_URL) { - // Load the url of the dev server if in development mode - win.loadURL( - process.env.WEBPACK_DEV_SERVER_URL + '/#/workbenche/edit/' + id - ) - if (!process.env.IS_TEST) win.webContents.openDevTools() - } else { - // Load the index.html when not in development - win.loadURL('app://./index.html/#/workbenche/edit/' + id) - } - }) - - // 保存文件 - const idToFilePath = {} - ipcMain.handle('save', async (event, id, data) => { - if (!idToFilePath[id]) { - const webContents = event.sender - const win = BrowserWindow.fromWebContents(webContents) - const res = dialog.showSaveDialogSync(win, { - title: '保存', - defaultPath: '未命名.smm', - filters: [{ name: '思维导图', extensions: ['smm'] }] - }) - if (res) { - idToFilePath[id] = res - fs.writeFile(res, data) - saveToRecent(res) - notifyMainWindowRefreshRecentFileList() - return path.parse(idToFilePath[id]).name - } - } else { - fs.writeFile(idToFilePath[id], data) - } - }) - - // 重命名文件 - ipcMain.handle('rename', async (event, id, name) => { - if (!idToFilePath[id]) { - return - } - let oldPath = idToFilePath[id] - let { base, ...oldPathData } = path.parse(oldPath) - oldPathData.name = name - let newPath = path.format(oldPathData) - idToFilePath[id] = newPath - await fs.rename(oldPath, newPath) - notifyMainWindowRefreshRecentFileList() - }) - - // 处理缩放事件 - ;['minimize', 'maximize', 'unmaximize', 'close'].forEach(item => { - ipcMain.on(item, event => { - const webContents = event.sender - const win = BrowserWindow.fromWebContents(webContents) - win[item]() - }) - }) - - // 使用默认浏览器打开指定url - ipcMain.on('openUrl', (event, url) => { - open(url) - }) - - // 获取最近文件列表 - ipcMain.handle('getRecentFileList', () => { - return getRecent().map(item => { - let data = path.parse(item) - return { - url: item, - dir: data.dir, - name: data.name - } - }) - }) - - // 打开指定目录 - ipcMain.on('openFileInDir', (event, file) => { - shell.showItemInFolder(file) - }) - - // 删除指定文件 - ipcMain.handle('deleteFile', (event, file) => { - let res = '' - let id = Object.keys(idToFilePath).find(item => { - console.log(item, idToFilePath[item]) - return idToFilePath[item] === file - }) - let index = -1 - if (id) { - index = openIds.findIndex(item => { - return item === id - }) - } - console.log(file, id, index) - if (index === -1) { - res = fs.rmSync(file) - if (res) { - removeFileInRecent(file) - } - } else { - res = '该文件正在编辑,请关闭后再试' - } - return res - }) + bindFileHandleEvent({ mainWindow }) + bindOtherHandleEvent() } // 关闭所有窗口后退出 diff --git a/web/src/electron/fileHandle.js b/web/src/electron/fileHandle.js new file mode 100644 index 00000000..01528d48 --- /dev/null +++ b/web/src/electron/fileHandle.js @@ -0,0 +1,196 @@ +import { + BrowserWindow, + ipcMain, + dialog, + shell +} from 'electron' +import fs from 'fs-extra' +import path from 'path' +import { saveToRecent, clearRecent, removeFileInRecent, replaceFileInRecent, getRecent } from './storage' +import { v4 as uuid } from 'uuid' + +export const bindFileHandleEvent = ({ mainWindow }) => { + // 通知主页面刷新最近文件列表 + const notifyMainWindowRefreshRecentFileList = () => { + mainWindow.webContents.send('refreshRecentFileList') + } + + // 新建编辑页面 + const openIds = [] + const createEditWindow = async (event, id) => { + openIds.push(id) + const win = new BrowserWindow({ + width: 1200, + height: 800, + frame: false, + titleBarStyle: 'hiddenInset', + webPreferences: { + webSecurity: false, + nodeIntegration: true, + enableRemoteModule: true, + contextIsolation: true, + preload: path.join(__dirname, 'preload.js') + } + }) + win.on('closed', () => { + let index = openIds.find(item => { + return item === id + }) + if (index !== -1) { + openIds.splice(index, 1) + } + }) + if (process.env.WEBPACK_DEV_SERVER_URL) { + // Load the url of the dev server if in development mode + win.loadURL( + process.env.WEBPACK_DEV_SERVER_URL + '/#/workbenche/edit/' + id + ) + if (!process.env.IS_TEST) win.webContents.openDevTools() + } else { + // Load the index.html when not in development + win.loadURL('app://./index.html/#/workbenche/edit/' + id) + } + } + ipcMain.on('create', createEditWindow) + + // 保存文件 + const idToFilePath = {} + ipcMain.handle('save', async (event, id, data) => { + if (!idToFilePath[id]) { + const webContents = event.sender + const win = BrowserWindow.fromWebContents(webContents) + const res = dialog.showSaveDialogSync(win, { + title: '保存', + defaultPath: '未命名.smm', + filters: [{ name: '思维导图', extensions: ['smm'] }] + }) + if (res) { + idToFilePath[id] = res + fs.writeFile(res, data) + saveToRecent(res) + .then(() => { + notifyMainWindowRefreshRecentFileList() + }) + return path.parse(idToFilePath[id]).name + } + } else { + fs.writeFile(idToFilePath[id], data) + } + }) + + // 打开文件 + const openFile = (event, file) => { + let id = uuid() + idToFilePath[id] = file + saveToRecent(file) + .then(() => { + notifyMainWindowRefreshRecentFileList() + }) + createEditWindow(null, id) + } + ipcMain.on('openFile', openFile) + + // 选择打开本地文件 + ipcMain.on('selectOpenFile', (event) => { + const res = dialog.showOpenDialogSync({ + title: '选择', + filters: [{ name: '思维导图', extensions: ['smm'] }], + }) + if (res && res[0]) { + openFile(null, res[0]) + } + }) + + // 获取文件内容 + ipcMain.handle('getFileContent', (event, id) => { + return new Promise((resolve) => { + let file = idToFilePath[id] + if (!file) { + resolve(null) + return + } + fs.readFile(file, { encoding: 'utf-8' }, (err, data) => { + resolve({ + name: path.parse(file).name, + content: JSON.parse(data) + }) + }) + }) + }) + + // 重命名文件 + ipcMain.handle('rename', (event, id, name) => { + return new Promise((resolve) => { + if (!idToFilePath[id]) { + resolve('文件不存在') + return + } + let oldPath = idToFilePath[id] + let { base, ...oldPathData } = path.parse(oldPath) + oldPathData.name = name + let newPath = path.format(oldPathData) + idToFilePath[id] = newPath + fs.rename(oldPath, newPath, (err) => { + if (err) { + resolve('重命名失败') + } else { + replaceFileInRecent(oldPath, newPath).then(() => { + notifyMainWindowRefreshRecentFileList() + resolve() + }) + } + }) + }) + }) + + // 获取最近文件列表 + ipcMain.handle('getRecentFileList', () => { + return getRecent().map(item => { + console.log(item); + let data = path.parse(item) + return { + url: item, + dir: data.dir, + name: data.name + } + }) + }) + + // 清空最近文件列表 + ipcMain.handle('clearRecentFileList', async () => { + try { + clearRecent() + return '' + } catch (error) { + return '清空失败' + } + }) + + // 打开指定目录 + ipcMain.on('openFileInDir', (event, file) => { + shell.showItemInFolder(file) + }) + + // 删除指定文件 + ipcMain.handle('deleteFile', (event, file) => { + let res = '' + let id = Object.keys(idToFilePath).find(item => { + return idToFilePath[item] === file + }) + let index = -1 + if (id) { + index = openIds.findIndex(item => { + return item === id + }) + } + if (index === -1) { + res = fs.rmSync(file) + if (!res) { + removeFileInRecent(file) + } + } else { + res = '该文件正在编辑,请关闭后再试' + } + return res + }) +} \ No newline at end of file diff --git a/web/src/electron/otherHandle.js b/web/src/electron/otherHandle.js new file mode 100644 index 00000000..be87bc16 --- /dev/null +++ b/web/src/electron/otherHandle.js @@ -0,0 +1,21 @@ +import { + BrowserWindow, + ipcMain, +} from 'electron' +import open from 'open' + +export const bindOtherHandleEvent = () => { + // 处理缩放事件 + ;['minimize', 'maximize', 'unmaximize', 'close'].forEach(item => { + ipcMain.on(item, event => { + const webContents = event.sender + const win = BrowserWindow.fromWebContents(webContents) + win[item]() + }) + }) + + // 使用默认浏览器打开指定url + ipcMain.on('openUrl', (event, url) => { + open(url) + }) +} \ No newline at end of file diff --git a/web/src/electron/preload.js b/web/src/electron/preload.js index 53242898..ae058c3e 100644 --- a/web/src/electron/preload.js +++ b/web/src/electron/preload.js @@ -9,11 +9,15 @@ contextBridge.exposeInMainWorld('electronAPI', { unmaximize: () => ipcRenderer.send('unmaximize'), close: () => ipcRenderer.send('close'), create: (id) => ipcRenderer.send('create', id), + getFileContent: (id) => ipcRenderer.invoke('getFileContent', id), save: (id, data) => ipcRenderer.invoke('save', id, data), rename: (id, name) => ipcRenderer.invoke('rename', id, name), openUrl: (url) => ipcRenderer.send('openUrl', url), getRecentFileList: () => ipcRenderer.invoke('getRecentFileList'), + clearRecentFileList: () => ipcRenderer.invoke('clearRecentFileList'), openFileInDir: (file) => ipcRenderer.send('openFileInDir', file), deleteFile: (file) => ipcRenderer.invoke('deleteFile', file), - onRefreshRecentFileList: (callback) => ipcRenderer.on('refreshRecentFileList', callback) + onRefreshRecentFileList: (callback) => ipcRenderer.on('refreshRecentFileList', callback), + openFile: (file) => ipcRenderer.send('openFile', file), + selectOpenFile: () => ipcRenderer.send('selectOpenFile'), }) \ No newline at end of file diff --git a/web/src/electron/storage.js b/web/src/electron/storage.js new file mode 100644 index 00000000..67eba367 --- /dev/null +++ b/web/src/electron/storage.js @@ -0,0 +1,86 @@ +import storage from 'electron-json-storage' + +export const RECENT_FILE_LIST = 'recentFileList' + +// 保存到最近文件 +export const saveToRecent = file => { + return new Promise((resolve, reject) => { + let list = getRecent() + let index = list.findIndex(item => { + return item === file + }) + if (index !== -1) { + list.splice(index, 1) + } + list.push(file) + storage.set(RECENT_FILE_LIST, list, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} + +// 获取最近文件列表 +export const getRecent = () => { + let res = storage.getSync(RECENT_FILE_LIST) + return (Array.isArray(res) ? res : []).filter((item) => { + return !!item + }) +} + +// 清除最近文件列表 +export const clearRecent = () => { + return new Promise((resolve, reject) => { + storage.remove(RECENT_FILE_LIST, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} + +// 从最近文件列表中移除指定文件 +export const removeFileInRecent = (file) => { + return new Promise((resolve, reject) => { + let list = getRecent() + let index = list.findIndex(item => { + return item === file + }) + if (index !== -1) { + list.splice(index, 1) + } + storage.set(RECENT_FILE_LIST, list, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} + +// 替换指定文件 +export const replaceFileInRecent = (oldFile, newFile) => { + return new Promise((resolve, reject) => { + let list = getRecent() + let index = list.findIndex(item => { + return item === oldFile + }) + if (index !== -1) { + list.splice(index, 1) + } + list.push(newFile) + storage.set(RECENT_FILE_LIST, list, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} \ No newline at end of file diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 1cb23115..aa559551 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -93,7 +93,8 @@ export default { mindMap: null, mindMapData: null, prevImg: '', - openTest: false + openTest: false, + isFirst: true } }, computed: { @@ -112,9 +113,9 @@ export default { } } }, - mounted() { + async mounted() { // this.showNewFeatureInfo() - this.getData() + await this.getData() this.init() this.$bus.$on('execCommand', this.execCommand) this.$bus.$on('paddingChange', this.onPaddingChange) @@ -233,8 +234,15 @@ export default { * @Date: 2021-07-03 22:11:37 * @Desc: 获取思维导图数据,实际应该调接口获取 */ - getData() { - let storeData = getData() + async getData() { + let data = await window.electronAPI.getFileContent(this.$route.params.id) + let storeData = null + if (data) { + this.setFileName(data.name) + storeData = data.content + } else { + storeData = getData() + } this.mindMapData = storeData }, @@ -248,10 +256,15 @@ export default { return } this.$bus.$on('data_change', data => { - this.setIsUnSave(true) + if (!this.isFirst) { + this.setIsUnSave(true) + } else { + this.isFirst = false + } storeData(data) }) this.$bus.$on('view_data_change', data => { + console.log(2); this.setIsUnSave(true) storeConfig({ view: data diff --git a/web/src/pages/Workbenche/components/FileList.vue b/web/src/pages/Workbenche/components/FileList.vue index 982db8c3..6da3c927 100644 --- a/web/src/pages/Workbenche/components/FileList.vue +++ b/web/src/pages/Workbenche/components/FileList.vue @@ -1,6 +1,9 @@