mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 22:08:25 +08:00
Compare commits
16 Commits
0.6.11-dem
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c789a95e4e | ||
|
|
b0532072c2 | ||
|
|
7d21ae4618 | ||
|
|
0e608de9da | ||
|
|
fbff68c635 | ||
|
|
ec9517c491 | ||
|
|
26e3158bd8 | ||
|
|
c2c9de1c03 | ||
|
|
27d2238d20 | ||
|
|
d6e625efa6 | ||
|
|
fba0ece8fd | ||
|
|
78ed038012 | ||
|
|
58f7be77ed | ||
|
|
362160c67b | ||
|
|
c8a3c569e1 | ||
|
|
6bc309e3fa |
BIN
web/build/icons/icon.icns
Normal file
BIN
web/build/icons/icon.icns
Normal file
Binary file not shown.
BIN
web/build/icons/icon.ico
Normal file
BIN
web/build/icons/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
web/build/icons/icon.png
Normal file
BIN
web/build/icons/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
2023
web/dist_electron/index.js
Normal file
2023
web/dist_electron/index.js
Normal file
File diff suppressed because one or more lines are too long
84
web/dist_electron/package.json
Normal file
84
web/dist_electron/package.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"name": "thoughts",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "一个简洁的思维导图",
|
||||
"author": "街角小林<1013335014@qq.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/wanglin2/mind-map"
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build && node ../copy.js",
|
||||
"lint": "vue-cli-service lint",
|
||||
"autoBuildDoc": "node ./scripts/autoBuildDoc.js",
|
||||
"buildDoc": "node ./scripts/buildDoc.js",
|
||||
"electron:build": "vue-cli-service electron:build",
|
||||
"electron:build-all": "vue-cli-service electron:build -p never -mwl",
|
||||
"electron:build-mac": "vue-cli-service electron:build -p never -m",
|
||||
"electron:build-win": "vue-cli-service electron:build -p never -w",
|
||||
"electron:build-linux": "vue-cli-service electron:build -p never -l",
|
||||
"electron:serve": "vue-cli-service electron:serve",
|
||||
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/full.js --dest ../simple-mind-map/dist && esbuild ../simple-mind-map/full.js --bundle --external:buffer --format=esm --outfile=../simple-mind-map/dist/simpleMindMap.esm.js",
|
||||
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"postuninstall": "electron-builder install-app-deps"
|
||||
},
|
||||
"main": "background.js",
|
||||
"dependencies": {
|
||||
"@toast-ui/editor": "^3.1.5",
|
||||
"core-js": "^3.6.5",
|
||||
"electron-json-storage": "^4.6.0",
|
||||
"element-ui": "^2.15.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"highlight.js": "^10.7.3",
|
||||
"open": "^6.4.0",
|
||||
"uuid": "^3.4.0",
|
||||
"v-viewer": "^1.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-i18n": "^8.27.2",
|
||||
"vue-router": "^3.5.1",
|
||||
"vuex": "^3.6.2",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.5.0",
|
||||
"@vue/cli-plugin-eslint": "^4.5.0",
|
||||
"@vue/cli-service": "^4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "^23.1.1",
|
||||
"electron-devtools-installer": "^3.1.0",
|
||||
"esbuild": "^0.17.15",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"less": "^3.12.2",
|
||||
"less-loader": "^7.1.0",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-checkbox": "^1.1.0",
|
||||
"prettier": "^1.19.1",
|
||||
"vue-cli-plugin-electron-builder": "~2.1.1",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.44.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
122
web/dist_electron/preload.js
Normal file
122
web/dist_electron/preload.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ "./src/electron/preload.js":
|
||||
/*!*********************************!*\
|
||||
!*** ./src/electron/preload.js ***!
|
||||
\*********************************/
|
||||
/*! 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 destroy: () => ipcRenderer.send('destroy'),\r\n create: id => ipcRenderer.send('create', id),\r\n getFileContent: id => ipcRenderer.invoke('getFileContent', id),\r\n save: (id, data, fileName) => ipcRenderer.invoke('save', id, data, fileName),\r\n rename: (id, name) => ipcRenderer.invoke('rename', id, name),\r\n openUrl: url => ipcRenderer.send('openUrl', url),\r\n addRecentFileList: (fileList) => ipcRenderer.invoke('addRecentFileList', fileList),\r\n getRecentFileList: () => ipcRenderer.invoke('getRecentFileList'),\r\n clearRecentFileList: () => ipcRenderer.invoke('clearRecentFileList'),\r\n openFileInDir: file => ipcRenderer.send('openFileInDir', file),\r\n deleteFile: file => ipcRenderer.invoke('deleteFile', file),\r\n onRefreshRecentFileList: callback =>\r\n ipcRenderer.on('refreshRecentFileList', callback),\r\n openFile: file => ipcRenderer.send('openFile', file),\r\n selectOpenFile: () => ipcRenderer.send('selectOpenFile'),\r\n copyFile: file => ipcRenderer.invoke('copyFile', file)\r\n})\r\n\n\n//# sourceURL=webpack:///./src/electron/preload.js?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 0:
|
||||
/*!***************************************!*\
|
||||
!*** multi ./src/electron/preload.js ***!
|
||||
\***************************************/
|
||||
/*! 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?");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "electron":
|
||||
/*!***************************!*\
|
||||
!*** external "electron" ***!
|
||||
\***************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
eval("module.exports = require(\"electron\");\n\n//# sourceURL=webpack:///external_%22electron%22?");
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
18423
web/package-lock.json
generated
18423
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,20 +2,39 @@
|
||||
"name": "thoughts",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "一个简洁的思维导图",
|
||||
"author": "街角小林<1013335014@qq.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/wanglin2/mind-map"
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build && node ../copy.js",
|
||||
"lint": "vue-cli-service lint",
|
||||
"autoBuildDoc": "node ./scripts/autoBuildDoc.js",
|
||||
"buildDoc": "node ./scripts/buildDoc.js",
|
||||
"electron:build": "vue-cli-service electron:build",
|
||||
"electron:build-all": "vue-cli-service electron:build -p never -mwl",
|
||||
"electron:build-mac": "vue-cli-service electron:build -p never -m",
|
||||
"electron:build-win": "vue-cli-service electron:build -p never -w",
|
||||
"electron:build-linux": "vue-cli-service electron:build -p never -l",
|
||||
"electron:serve": "vue-cli-service electron:serve",
|
||||
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/full.js --dest ../simple-mind-map/dist && esbuild ../simple-mind-map/full.js --bundle --external:buffer --format=esm --outfile=../simple-mind-map/dist/simpleMindMap.esm.js",
|
||||
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*",
|
||||
"buildDoc": "node ./scripts/buildDoc.js",
|
||||
"autoBuildDoc": "node ./scripts/autoBuildDoc.js"
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"postuninstall": "electron-builder install-app-deps"
|
||||
},
|
||||
"main": "background.js",
|
||||
"dependencies": {
|
||||
"@toast-ui/editor": "^3.1.5",
|
||||
"core-js": "^3.6.5",
|
||||
"electron-json-storage": "^4.6.0",
|
||||
"element-ui": "^2.15.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"highlight.js": "^10.7.3",
|
||||
"open": "^6.4.0",
|
||||
"uuid": "^3.4.0",
|
||||
"v-viewer": "^1.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-i18n": "^8.27.2",
|
||||
@@ -29,6 +48,8 @@
|
||||
"@vue/cli-service": "^4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "^23.1.1",
|
||||
"electron-devtools-installer": "^3.1.0",
|
||||
"esbuild": "^0.17.15",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
@@ -37,6 +58,7 @@
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-checkbox": "^1.1.0",
|
||||
"prettier": "^1.19.1",
|
||||
"vue-cli-plugin-electron-builder": "~2.1.1",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.44.2"
|
||||
},
|
||||
|
||||
BIN
web/src/.DS_Store
vendored
BIN
web/src/.DS_Store
vendored
Binary file not shown.
@@ -34,7 +34,17 @@ export const getData = () => {
|
||||
return simpleDeepClone(exampleData)
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(store)
|
||||
let parsedData = JSON.parse(store)
|
||||
if (window.IS_ELECTRON) {
|
||||
return simpleDeepClone(exampleData)
|
||||
let { root, ...rest } = parsedData
|
||||
return {
|
||||
...rest,
|
||||
root: simpleDeepClone(exampleData).root
|
||||
}
|
||||
} else {
|
||||
return parsedData
|
||||
}
|
||||
} catch (error) {
|
||||
return simpleDeepClone(exampleData)
|
||||
}
|
||||
|
||||
BIN
web/src/assets/.DS_Store
vendored
BIN
web/src/assets/.DS_Store
vendored
Binary file not shown.
BIN
web/src/assets/img/icon.png
Normal file
BIN
web/src/assets/img/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
114
web/src/background.js
Normal file
114
web/src/background.js
Normal file
@@ -0,0 +1,114 @@
|
||||
'use strict'
|
||||
|
||||
import { app, protocol, BrowserWindow } from 'electron'
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
|
||||
import path from 'path'
|
||||
import { bindFileHandleEvent } from './electron/fileHandle'
|
||||
import { bindOtherHandleEvent } from './electron/otherHandle'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
// 在应用程序准备就绪之前,必须注册方案
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'app', privileges: { secure: true, standard: true } }
|
||||
])
|
||||
|
||||
// 创建主页面
|
||||
let mainWindow = null
|
||||
async function createMainWindow() {
|
||||
mainWindow = 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')
|
||||
}
|
||||
})
|
||||
|
||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||
// 如果处于开发模式,则加载开发服务器的url
|
||||
await mainWindow.loadURL(
|
||||
process.env.WEBPACK_DEV_SERVER_URL + '/#/workbenche'
|
||||
)
|
||||
// if (!process.env.IS_TEST) mainWindow.webContents.openDevTools()
|
||||
} else {
|
||||
createProtocol('app')
|
||||
// 非开发环境时加载index.html
|
||||
mainWindow.loadURL('app://./index.html/#/workbenche')
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
const bindEvent = () => {
|
||||
bindFileHandleEvent({ mainWindow, initOpenFileQueue })
|
||||
bindOtherHandleEvent()
|
||||
}
|
||||
|
||||
// 关闭所有窗口后退出
|
||||
app.on('window-all-closed', () => {
|
||||
// 在macOS上,应用程序及其菜单栏通常保持活动状态,直到用户使用Cmd+Q明确退出
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
// 在macOS上,当点击dock图标且没有其他窗口打开时,通常会在应用程序中重新创建一个窗口。
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createMainWindow()
|
||||
// bindEvent()
|
||||
}
|
||||
})
|
||||
|
||||
// Attempt to bind file opening #2
|
||||
// https://stackoverflow.com/questions/62420427/how-do-i-make-my-electron-app-the-default-for-opening-files
|
||||
// https://github.com/rchrd2/example-electron-file-association
|
||||
// https://www.jianshu.com/p/a32542277b83
|
||||
const initOpenFileQueue = []
|
||||
app.on('will-finish-launching', () => {
|
||||
// Event fired When someone drags files onto the icon while your app is running
|
||||
if (process.platform == 'win32') {
|
||||
const argv = process.argv
|
||||
if (argv) {
|
||||
argv.forEach(filePath => {
|
||||
if (filePath.indexOf('.smm') >= 0) {
|
||||
initOpenFileQueue.push(filePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
app.on('open-file', (event, file) => {
|
||||
if (app.isReady() === false) {
|
||||
initOpenFileQueue.push(file)
|
||||
} else {
|
||||
console.log(file)
|
||||
}
|
||||
event.preventDefault()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.on('ready', async () => {
|
||||
createMainWindow()
|
||||
bindEvent()
|
||||
})
|
||||
|
||||
// 在开发模式下,应父进程的请求干净地退出。
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', data => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGTERM', () => {
|
||||
app.quit()
|
||||
})
|
||||
}
|
||||
}
|
||||
3
web/src/css/global.css
Normal file
3
web/src/css/global.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.noDrag {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
251
web/src/electron/fileHandle.js
Normal file
251
web/src/electron/fileHandle.js
Normal file
@@ -0,0 +1,251 @@
|
||||
import { BrowserWindow, ipcMain, dialog, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import {
|
||||
saveToRecent,
|
||||
clearRecent,
|
||||
removeFileInRecent,
|
||||
replaceFileInRecent,
|
||||
getRecent,
|
||||
saveFileListToRecent
|
||||
} from './storage'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
export const bindFileHandleEvent = ({ mainWindow, initOpenFileQueue }) => {
|
||||
// 通知主页面刷新最近文件列表
|
||||
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', () => {
|
||||
// 从openIds数组中删除
|
||||
let index = openIds.find(item => {
|
||||
return item === id
|
||||
})
|
||||
if (index !== -1) {
|
||||
openIds.splice(index, 1)
|
||||
}
|
||||
// 从idToFilePath中删除
|
||||
delete idToFilePath[id]
|
||||
})
|
||||
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, fileName = '未命名') => {
|
||||
if (!idToFilePath[id]) {
|
||||
const webContents = event.sender
|
||||
const win = BrowserWindow.fromWebContents(webContents)
|
||||
const res = dialog.showSaveDialogSync(win, {
|
||||
title: '保存',
|
||||
defaultPath: fileName + '.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 => {
|
||||
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.handle('addRecentFileList', async (event, fileList) => {
|
||||
try {
|
||||
console.log(fileList);
|
||||
await saveFileListToRecent(fileList)
|
||||
notifyMainWindowRefreshRecentFileList()
|
||||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
})
|
||||
|
||||
// 打开指定目录
|
||||
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) {
|
||||
try {
|
||||
fs.rmSync(file)
|
||||
} catch (error) {}
|
||||
removeFileInRecent(file)
|
||||
} else {
|
||||
res = '该文件正在编辑,请关闭后再试'
|
||||
}
|
||||
return res
|
||||
})
|
||||
|
||||
// 复制文件
|
||||
ipcMain.handle('copyFile', async (event, file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.pathExists(file, (err, exists) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
if (exists) {
|
||||
let { base, ...oldPathData } = path.parse(file)
|
||||
let newName = oldPathData.name + '-复制'
|
||||
let index = 1
|
||||
oldPathData.name = newName
|
||||
let newPath = path.format(oldPathData)
|
||||
// 检查新路径是否已存在
|
||||
while (fs.pathExistsSync(newPath)) {
|
||||
oldPathData.name = newName + index
|
||||
newPath = path.format(oldPathData)
|
||||
index++
|
||||
}
|
||||
fs.copy(file, newPath, err => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
saveToRecent(newPath).then(() => {
|
||||
notifyMainWindowRefreshRecentFileList()
|
||||
})
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject('文件不存在')
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// 直接双击文件打开应用时,需要直接打开该文件编辑
|
||||
initOpenFileQueue.forEach((file) => {
|
||||
openFile(null, file)
|
||||
})
|
||||
}
|
||||
18
web/src/electron/otherHandle.js
Normal file
18
web/src/electron/otherHandle.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { BrowserWindow, ipcMain } from 'electron'
|
||||
import open from 'open'
|
||||
|
||||
export const bindOtherHandleEvent = () => {
|
||||
// 处理缩放事件
|
||||
;['minimize', 'maximize', 'unmaximize', 'close', 'destroy'].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)
|
||||
})
|
||||
}
|
||||
27
web/src/electron/preload.js
Normal file
27
web/src/electron/preload.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('platform', process.platform)
|
||||
contextBridge.exposeInMainWorld('IS_ELECTRON', true)
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
minimize: () => ipcRenderer.send('minimize'),
|
||||
maximize: () => ipcRenderer.send('maximize'),
|
||||
unmaximize: () => ipcRenderer.send('unmaximize'),
|
||||
close: () => ipcRenderer.send('close'),
|
||||
destroy: () => ipcRenderer.send('destroy'),
|
||||
create: id => ipcRenderer.send('create', id),
|
||||
getFileContent: id => ipcRenderer.invoke('getFileContent', id),
|
||||
save: (id, data, fileName) => ipcRenderer.invoke('save', id, data, fileName),
|
||||
rename: (id, name) => ipcRenderer.invoke('rename', id, name),
|
||||
openUrl: url => ipcRenderer.send('openUrl', url),
|
||||
addRecentFileList: (fileList) => ipcRenderer.invoke('addRecentFileList', fileList),
|
||||
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),
|
||||
openFile: file => ipcRenderer.send('openFile', file),
|
||||
selectOpenFile: () => ipcRenderer.send('selectOpenFile'),
|
||||
copyFile: file => ipcRenderer.invoke('copyFile', file)
|
||||
})
|
||||
109
web/src/electron/storage.js
Normal file
109
web/src/electron/storage.js
Normal file
@@ -0,0 +1,109 @@
|
||||
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 saveFileListToRecent = fileList => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let list = getRecent()
|
||||
fileList.forEach(file => {
|
||||
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()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -8,11 +8,21 @@ import '@/assets/icon-font/iconfont.css'
|
||||
import 'viewerjs/dist/viewer.css'
|
||||
import VueViewer from 'v-viewer'
|
||||
import i18n from './i18n'
|
||||
import './css/global.css'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$bus = new Vue()
|
||||
Vue.use(ElementUI)
|
||||
Vue.use(VueViewer)
|
||||
Vue.mixin({
|
||||
data () {
|
||||
return {
|
||||
IS_ELECTRON: window.IS_ELECTRON,
|
||||
IS_MAC: window.platform === 'darwin',
|
||||
IS_WIN: window.platform === 'win32'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
|
||||
@@ -730,7 +730,7 @@ export default {
|
||||
* @Desc: 初始样式
|
||||
*/
|
||||
initStyle() {
|
||||
;[
|
||||
[
|
||||
'backgroundColor',
|
||||
'lineWidth',
|
||||
'lineStyle',
|
||||
@@ -786,7 +786,7 @@ export default {
|
||||
* @Desc: margin初始值
|
||||
*/
|
||||
initMarginStyle() {
|
||||
;['marginX', 'marginY'].forEach(key => {
|
||||
['marginX', 'marginY'].forEach(key => {
|
||||
this.style[key] = this.mindMap.getThemeConfig()[this.marginActiveTab][
|
||||
key
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="editContainer">
|
||||
<div class="editContainer" :style="{top: IS_ELECTRON ? '40px' : 0}">
|
||||
<div class="mindMapContainer" ref="mindMapContainer"></div>
|
||||
<Count v-if="!isZenMode"></Count>
|
||||
<Navigator :mindMap="mindMap"></Navigator>
|
||||
@@ -46,7 +46,7 @@ import { getData, storeData, storeConfig } from '@/api'
|
||||
import Navigator from './Navigator.vue'
|
||||
import NodeImgPreview from './NodeImgPreview.vue'
|
||||
import SidebarTrigger from './SidebarTrigger.vue'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import customThemeList from '@/customThemes'
|
||||
import icon from '@/config/icon'
|
||||
|
||||
@@ -93,11 +93,13 @@ export default {
|
||||
mindMap: null,
|
||||
mindMapData: null,
|
||||
prevImg: '',
|
||||
openTest: false
|
||||
openTest: false,
|
||||
isFirst: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
fileName: state => state.fileName,
|
||||
isZenMode: state => state.localConfig.isZenMode,
|
||||
openNodeRichText: state => state.localConfig.openNodeRichText,
|
||||
})
|
||||
@@ -111,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)
|
||||
@@ -136,8 +138,13 @@ export default {
|
||||
this.test()
|
||||
}, 5000)
|
||||
}
|
||||
if (window.IS_ELECTRON) {
|
||||
this.mindMap.keyCommand.addShortcut('Control+s', this.saveToLocal)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setFileName', 'setIsUnSave']),
|
||||
|
||||
/**
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-22 19:39:28
|
||||
@@ -227,8 +234,16 @@ 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 {
|
||||
this.setFileName('未命名')
|
||||
storeData = getData()
|
||||
}
|
||||
this.mindMapData = storeData
|
||||
},
|
||||
|
||||
@@ -242,9 +257,15 @@ export default {
|
||||
return
|
||||
}
|
||||
this.$bus.$on('data_change', data => {
|
||||
if (!this.isFirst) {
|
||||
this.setIsUnSave(true)
|
||||
} else {
|
||||
this.isFirst = false
|
||||
}
|
||||
storeData(data)
|
||||
})
|
||||
this.$bus.$on('view_data_change', data => {
|
||||
this.setIsUnSave(true)
|
||||
storeConfig({
|
||||
view: data
|
||||
})
|
||||
@@ -292,9 +313,9 @@ export default {
|
||||
iconList: icon
|
||||
})
|
||||
if (this.openNodeRichText) this.addRichTextPlugin()
|
||||
this.mindMap.keyCommand.addShortcut('Control+s', () => {
|
||||
this.manualSave()
|
||||
})
|
||||
// this.mindMap.keyCommand.addShortcut('Control+s', () => {
|
||||
// this.manualSave()
|
||||
// })
|
||||
// 转发事件
|
||||
;[
|
||||
'node_active',
|
||||
@@ -409,6 +430,18 @@ export default {
|
||||
// 移除节点富文本编辑插件
|
||||
removeRichTextPlugin() {
|
||||
this.mindMap.removePlugin(RichText)
|
||||
},
|
||||
|
||||
// 保存到本地文件
|
||||
async saveToLocal() {
|
||||
let id = this.$route.params.id
|
||||
let data = this.mindMap.getData(true)
|
||||
console.log('保存', id, data)
|
||||
let res = await window.electronAPI.save(id, JSON.stringify(data), this.fileName)
|
||||
if (res) {
|
||||
this.setFileName(res)
|
||||
}
|
||||
this.setIsUnSave(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,7 @@
|
||||
<Fullscreen :mindMap="mindMap"></Fullscreen>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a href="https://github.com/wanglin2/mind-map" target="_blank">
|
||||
<span class="iconfont icongithub"></span>
|
||||
</a>
|
||||
<span class="iconfont icongithub" @click="openGithub"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -89,6 +87,10 @@ export default {
|
||||
onLangChange(lang) {
|
||||
i18n.locale = lang
|
||||
storeLang(lang)
|
||||
},
|
||||
|
||||
openGithub() {
|
||||
window.electronAPI.openUrl('https://github.com/wanglin2/mind-map')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ export default {
|
||||
this.activeTab = 'normal'
|
||||
return
|
||||
}
|
||||
;[
|
||||
[
|
||||
'shape',
|
||||
'paddingX',
|
||||
'paddingY',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="toolbarContainer">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar" :style="{top: IS_ELECTRON ? '40px' : 0}">
|
||||
<!-- 节点操作 -->
|
||||
<div class="toolbarBlock">
|
||||
<div
|
||||
@@ -126,15 +126,15 @@
|
||||
</div>
|
||||
<!-- 导出 -->
|
||||
<div class="toolbarBlock">
|
||||
<div class="toolbarBtn" @click="createNewLocalFile">
|
||||
<div class="toolbarBtn" @click="createNewLocalFile" v-if="!IS_ELECTRON">
|
||||
<span class="icon iconfont iconxinjian"></span>
|
||||
<span class="text">{{ $t('toolbar.newFile') }}</span>
|
||||
</div>
|
||||
<div class="toolbarBtn" @click="openLocalFile">
|
||||
<div class="toolbarBtn" @click="openLocalFile" v-if="!IS_ELECTRON">
|
||||
<span class="icon iconfont icondakai"></span>
|
||||
<span class="text">{{ $t('toolbar.openFile') }}</span>
|
||||
</div>
|
||||
<div class="toolbarBtn" @click="saveLocalFile">
|
||||
<div class="toolbarBtn" @click="saveLocalFile" v-if="!IS_ELECTRON">
|
||||
<span class="icon iconfont iconlingcunwei"></span>
|
||||
<span class="text">{{ $t('toolbar.saveAs') }}</span>
|
||||
</div>
|
||||
|
||||
32
web/src/pages/Workbenche/Index.vue
Normal file
32
web/src/pages/Workbenche/Index.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="workbencheContainer">
|
||||
<div class="workbencheContent">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Workbenche',
|
||||
created () {
|
||||
document.title = '思绪思维导图'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheContainer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.workbencheContent {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
92
web/src/pages/Workbenche/components/AboutDialog.vue
Normal file
92
web/src/pages/Workbenche/components/AboutDialog.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="aboutDialog"
|
||||
title="关于"
|
||||
:visible.sync="dialogVisible"
|
||||
width="480px"
|
||||
@close="onClose"
|
||||
>
|
||||
<div class="aboutBox">
|
||||
<img src="../../../assets/img/icon.png" alt="" />
|
||||
<h2>思绪思维导图</h2>
|
||||
<p>版本:{{ version }}</p>
|
||||
<p>
|
||||
获取源码:<a href="https://github.com/wanglin2/mind-map/tree/electron"
|
||||
>mind-map</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
下载最新版本:<a href="https://github.com/wanglin2/mind-map/releases"
|
||||
>releases</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pkg from '../../../../package.json'
|
||||
|
||||
export default {
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
version: pkg.version
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val, oldVal) {
|
||||
this.dialogVisible = val
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClose() {
|
||||
this.$emit('change', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.aboutDialog {
|
||||
/deep/ .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.aboutBox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 30px;
|
||||
|
||||
img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
62
web/src/pages/Workbenche/components/Empty.vue
Normal file
62
web/src/pages/Workbenche/components/Empty.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="workbencheEmptyContainer">
|
||||
<div class="icon iconfont iconwushuju"></div>
|
||||
<div class="tip">暂时没有内容</div>
|
||||
<div class="desc">没有最近使用的文件记录</div>
|
||||
<div class="createBtn" @click="create">立即创建</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { create } from '../utils'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
create
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheEmptyContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.icon {
|
||||
font-size: 100px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.createBtn {
|
||||
width: 100px;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #409eff;
|
||||
color: #409eff;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
165
web/src/pages/Workbenche/components/FileList.vue
Normal file
165
web/src/pages/Workbenche/components/FileList.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<div class="workbencheFileListContainer">
|
||||
<div class="title">
|
||||
<span>最近</span>
|
||||
<span class="clearBtn" @click="clear">清空</span>
|
||||
</div>
|
||||
<div class="fileListBox">
|
||||
<Empty v-if="list.length <= 0"></Empty>
|
||||
<el-table v-else :data="list" style="width: 100%">
|
||||
<el-table-column prop="name" label="名称"> </el-table-column>
|
||||
<el-table-column prop="url" label="文件路径"> </el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
icon="el-icon-edit"
|
||||
circle
|
||||
size="mini"
|
||||
@click="openFile(scope.row.url)"
|
||||
></el-button>
|
||||
<el-button
|
||||
icon="el-icon-document-copy"
|
||||
circle
|
||||
size="mini"
|
||||
@click="copyFile(scope.row.url)"
|
||||
></el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
circle
|
||||
size="mini"
|
||||
@click="deleteFile(scope.row.url, scope.$index)"
|
||||
></el-button>
|
||||
<el-button
|
||||
icon="el-icon-folder-opened"
|
||||
circle
|
||||
size="mini"
|
||||
@click="openFileInDir(scope.row.url)"
|
||||
></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Empty from '../components/Empty.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Empty
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getRecentFileList()
|
||||
window.electronAPI.onRefreshRecentFileList(() => {
|
||||
this.getRecentFileList()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 获取最近文件列表
|
||||
async getRecentFileList() {
|
||||
let list = await window.electronAPI.getRecentFileList()
|
||||
this.list = list.reverse()
|
||||
},
|
||||
|
||||
// 在文件夹里打开文件
|
||||
openFileInDir(file) {
|
||||
window.electronAPI.openFileInDir(file)
|
||||
},
|
||||
|
||||
// 删除文件
|
||||
deleteFile(file, index) {
|
||||
this.$confirm('确定删除该文件?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
let res = await window.electronAPI.deleteFile(file)
|
||||
if (res) {
|
||||
this.$message.error('删除失败')
|
||||
} else {
|
||||
this.list.splice(index, 1)
|
||||
this.$message.success('删除成功')
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
|
||||
// 编辑文件
|
||||
openFile(file) {
|
||||
window.electronAPI.openFile(file)
|
||||
},
|
||||
|
||||
// 清空最近文件列表
|
||||
clear() {
|
||||
this.$confirm('确定清空最近文件?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
let res = await window.electronAPI.clearRecentFileList()
|
||||
if (res) {
|
||||
this.$message.error('清空失败')
|
||||
} else {
|
||||
this.list = []
|
||||
this.$message.success('清空成功')
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
|
||||
// 复制文件
|
||||
async copyFile(file) {
|
||||
try {
|
||||
window.electronAPI.copyFile(file)
|
||||
this.$message.success('复制成功')
|
||||
} catch (error) {
|
||||
this.$message.error('复制失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheFileListContainer {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
padding-top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
height: 65px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.clearBtn {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.fileListBox {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
15
web/src/pages/Workbenche/components/MacControl.vue
Normal file
15
web/src/pages/Workbenche/components/MacControl.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div class="macControl" v-if="IS_MAC"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.macControl {
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
90
web/src/pages/Workbenche/components/Sidebar.vue
Normal file
90
web/src/pages/Workbenche/components/Sidebar.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="workbencheSidebarContainer">
|
||||
<div class="createBtn" @click="create">开始新建</div>
|
||||
<div class="line"></div>
|
||||
<div class="btn" @click="openLocalFile">
|
||||
<span class="icon iconfont icondakai"></span>
|
||||
<span class="text">打开本地文件</span>
|
||||
</div>
|
||||
<div class="btn active">
|
||||
<span class="icon iconfont iconzuijinliulan"></span>
|
||||
<span class="text">最近文件</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { create } from '../utils'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
create,
|
||||
|
||||
openLocalFile() {
|
||||
window.electronAPI.selectOpenFile()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheSidebarContainer {
|
||||
flex-shrink: 0;
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
margin-right: 20px;
|
||||
padding: 15px 20px;
|
||||
|
||||
.createBtn {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background-color: #409eff;
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #e4e7ed;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
color: #000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
padding: 0 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
background-color: rgba(64, 158, 255, 0.3);
|
||||
color: rgba(64, 158, 255, 1);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
68
web/src/pages/Workbenche/components/WinControl.vue
Normal file
68
web/src/pages/Workbenche/components/WinControl.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="winControl noDrag" v-if="IS_WIN">
|
||||
<div class="winControlBtn iconfont iconzuixiaohua" @click="minimize"></div>
|
||||
<div
|
||||
class="winControlBtn iconfont"
|
||||
:class="[isMaximize ? 'icon3zuidahua-3' : 'iconzuidahua']"
|
||||
@click="toggleMaximize"
|
||||
></div>
|
||||
<div class="winControlBtn iconfont iconguanbi" @click="close"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isMaximize: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
minimize() {
|
||||
window.electronAPI.minimize()
|
||||
},
|
||||
|
||||
toggleMaximize() {
|
||||
if (this.isMaximize) {
|
||||
this.isMaximize = false
|
||||
window.electronAPI.unmaximize()
|
||||
} else {
|
||||
this.isMaximize = true
|
||||
window.electronAPI.maximize()
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
window.electronAPI.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.winControl {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
.winControlBtn {
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #ccced1;
|
||||
}
|
||||
|
||||
&.iconguanbi {
|
||||
&:hover {
|
||||
background-color: #e81123;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
6
web/src/pages/Workbenche/utils.js
Normal file
6
web/src/pages/Workbenche/utils.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
// 打开新的编辑窗口
|
||||
export const create = () => {
|
||||
window.electronAPI.create(uuid())
|
||||
}
|
||||
134
web/src/pages/Workbenche/views/Edit.vue
Normal file
134
web/src/pages/Workbenche/views/Edit.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="workbencheEditContainer">
|
||||
<div class="workbencheEditHeader">
|
||||
<MacControl></MacControl>
|
||||
<WinControl></WinControl>
|
||||
<div class="inputBox">
|
||||
<el-input
|
||||
v-model="name"
|
||||
size="mini"
|
||||
placeholder=""
|
||||
@blur="rename"
|
||||
@keyup.enter="rename"
|
||||
></el-input>
|
||||
<div class="modifyDotBox">
|
||||
<div class="modifyDot" v-show="isUnSave"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Edit></Edit>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Edit from '../../Edit/Index.vue'
|
||||
import WinControl from '../components/WinControl.vue'
|
||||
import MacControl from '../components/MacControl.vue'
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Edit,
|
||||
MacControl,
|
||||
WinControl
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['fileName', 'isUnSave'])
|
||||
},
|
||||
watch: {
|
||||
fileName(val) {
|
||||
this.name = val
|
||||
document.title = val
|
||||
},
|
||||
name(val) {
|
||||
if (!val.trim()) return
|
||||
this.setFileName(val.trim())
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.onbeforeunload = async e => {
|
||||
e.returnValue = false
|
||||
// 没有未保存内容直接关闭
|
||||
if (!this.isUnSave) {
|
||||
window.electronAPI.destroy()
|
||||
} else {
|
||||
try {
|
||||
// 否则询问用户是否关闭
|
||||
await this.checkIsClose()
|
||||
window.electronAPI.destroy()
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setFileName']),
|
||||
|
||||
// 重命名文件
|
||||
rename() {
|
||||
let id = this.$route.params.id
|
||||
window.electronAPI.rename(id, this.name.trim())
|
||||
},
|
||||
|
||||
// 询问是否关闭页面
|
||||
checkIsClose() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.$confirm('有操作尚未保存,是否确认关闭?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
resolve()
|
||||
})
|
||||
.catch(() => {
|
||||
reject()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheEditContainer {
|
||||
.workbencheEditHeader {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #ebeef1;
|
||||
-webkit-app-region: drag;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.inputBox {
|
||||
-webkit-app-region: no-drag;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.modifyDotBox {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-left: 10px;
|
||||
|
||||
.modifyDot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
150
web/src/pages/Workbenche/views/Home.vue
Normal file
150
web/src/pages/Workbenche/views/Home.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div
|
||||
class="workbencheHomeContainer"
|
||||
@drop="onDrop"
|
||||
@dragenter="onDragenter"
|
||||
@dragover="onDragover"
|
||||
@dragleave="onDragleave"
|
||||
>
|
||||
<div class="workbencheHomeHeader">
|
||||
<MacControl></MacControl>
|
||||
<WinControl></WinControl>
|
||||
<div class="rightBar">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<span class="settingBtn el-icon-setting"></span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="about">关于软件</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workbencheHomeContent">
|
||||
<Sidebar></Sidebar>
|
||||
<FileList></FileList>
|
||||
</div>
|
||||
<AboutDialog v-model="showAboutDialog"></AboutDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WinControl from '../components/WinControl.vue'
|
||||
import MacControl from '../components/MacControl.vue'
|
||||
import Sidebar from '../components/Sidebar.vue'
|
||||
import FileList from '../components/FileList.vue'
|
||||
import AboutDialog from '../components/AboutDialog.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
WinControl,
|
||||
MacControl,
|
||||
Sidebar,
|
||||
FileList,
|
||||
AboutDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAboutDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCommand(command) {
|
||||
switch (command) {
|
||||
case 'about':
|
||||
this.showAboutDialog = true
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
onDrop(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
let df = e.dataTransfer
|
||||
let dropFiles = []
|
||||
|
||||
if (df.items !== undefined) {
|
||||
for (let i = 0; i < df.items.length; i++) {
|
||||
let item = df.items[i]
|
||||
if (item.kind === 'file' && item.webkitGetAsEntry().isFile) {
|
||||
let file = item.getAsFile()
|
||||
if (/\.smm$/.test(file.name)) {
|
||||
dropFiles.push(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dropFiles.length === 1) {
|
||||
// 如果只有一个文件,直接打开编辑
|
||||
window.electronAPI.openFile(dropFiles[0].path)
|
||||
} else if (dropFiles.length > 1) {
|
||||
// 否则添加到最近文件列表
|
||||
window.electronAPI.addRecentFileList(
|
||||
dropFiles.map(file => {
|
||||
return file.path
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
onDragenter(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
},
|
||||
|
||||
onDragover(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
},
|
||||
|
||||
onDragleave(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheHomeContainer {
|
||||
background-color: #f6f8f9;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.workbencheHomeHeader {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #ebeef1;
|
||||
-webkit-app-region: drag;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.rightBar {
|
||||
-webkit-app-region: no-drag;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.settingBtn {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workbencheHomeContent {
|
||||
flex-grow: 1;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,6 +3,9 @@ import VueRouter from 'vue-router'
|
||||
import EditPage from '@/pages/Edit/Index'
|
||||
import DocPage from '@/pages/Doc/Index'
|
||||
import routerList from '@/pages/Doc/routerList'
|
||||
import WorkbenchePage from '@/pages/Workbenche/Index'
|
||||
import WorkbencheHomePage from '@/pages/Workbenche/views/Home'
|
||||
import WorkbencheEditPage from '@/pages/Workbenche/views/Edit'
|
||||
|
||||
// 处理没有翻译的章节路由
|
||||
const handleRouterList = () => {
|
||||
@@ -26,21 +29,39 @@ handleRouterList()
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Edit',
|
||||
component: EditPage
|
||||
{
|
||||
path: '/',
|
||||
name: 'Edit',
|
||||
component: EditPage
|
||||
},
|
||||
{
|
||||
path: '/workbenche',
|
||||
name: 'Workbenche',
|
||||
component: WorkbenchePage,
|
||||
redirect: '/workbenche/home',
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'WorkbencheHome',
|
||||
component: WorkbencheHomePage,
|
||||
},
|
||||
{
|
||||
path: 'edit/:id',
|
||||
name: 'WorkbencheEdit',
|
||||
component: WorkbencheEditPage,
|
||||
}
|
||||
]
|
||||
},
|
||||
...routerList.map((item) => {
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
redirect: `/doc/${item.lang}/introduction/`
|
||||
}
|
||||
}),
|
||||
...routerList.map((item) => {
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
component: DocPage,
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
component: DocPage,
|
||||
children: item.children.map((child) => {
|
||||
return {
|
||||
path: `${child.path}/:h?`,
|
||||
|
||||
@@ -7,6 +7,8 @@ Vue.use(Vuex)
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
fileName: '',// 本地的文件名
|
||||
isUnSave: false,// 当前操作是否未保存
|
||||
mindMapData: null, // 思维导图数据
|
||||
isHandleLocalFile: false, // 是否操作的是本地文件
|
||||
localConfig: {
|
||||
@@ -15,9 +17,20 @@ const store = new Vuex.Store({
|
||||
// 是否开启节点富文本
|
||||
openNodeRichText: true
|
||||
},
|
||||
activeSidebar: '' // 当前显示的侧边栏
|
||||
activeSidebar: '', // 当前显示的侧边栏
|
||||
localEditList: []// 客户端中正在编辑的思维导图列表
|
||||
},
|
||||
mutations: {
|
||||
// 设置本地文件名
|
||||
setFileName(state, data) {
|
||||
state.fileName = data
|
||||
},
|
||||
|
||||
// 设置当前操作是否未保存
|
||||
setIsUnSave(state, data) {
|
||||
state.isUnSave = data
|
||||
},
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-10 14:50:01
|
||||
@@ -59,6 +72,11 @@ const store = new Vuex.Store({
|
||||
*/
|
||||
setActiveSidebar(state, data) {
|
||||
state.activeSidebar = data
|
||||
},
|
||||
|
||||
// 设置客户端中当前正在编辑的思维导图列表
|
||||
setLocalEditList(state, list) {
|
||||
state.localEditList = list
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
@@ -1,16 +1,119 @@
|
||||
const path = require('path');
|
||||
const path = require('path')
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
module.exports = {
|
||||
publicPath: isDev ? '' : './dist',
|
||||
outputDir: '../dist',
|
||||
lintOnSave: false,
|
||||
productionSourceMap: false,
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src/')
|
||||
}
|
||||
}
|
||||
publicPath: isDev ? '' : './dist',
|
||||
outputDir: '../dist',
|
||||
lintOnSave: false,
|
||||
productionSourceMap: false,
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src/')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pluginOptions: {
|
||||
electronBuilder: {
|
||||
preload: 'src/electron/preload.js',
|
||||
builderOptions: {
|
||||
productName: '思绪思维导图',
|
||||
copyright: 'Copyright © 思绪思维导图',
|
||||
// compression: "maximum", // 机器好的可以打开,配置压缩,开启后会让 .AppImage 格式的客户端启动缓慢
|
||||
asar: true,
|
||||
fileAssociations: [
|
||||
{
|
||||
ext: 'smm',
|
||||
name: 'mind map file',
|
||||
role: 'Editor',
|
||||
icon: './build/icons/icon.ico'
|
||||
}
|
||||
],
|
||||
publish: [
|
||||
{
|
||||
provider: 'github',
|
||||
owner: 'wanglin2',
|
||||
repo: 'mind-map',
|
||||
vPrefixedTagName: true,
|
||||
releaseType: 'draft'
|
||||
}
|
||||
],
|
||||
directories: {
|
||||
output: 'dist_electron'
|
||||
},
|
||||
mac: {
|
||||
target: [
|
||||
{
|
||||
target: 'dmg',
|
||||
arch: ['x64', 'arm64', 'universal']
|
||||
}
|
||||
],
|
||||
artifactName: '${productName}-${os}-${version}-${arch}.${ext}',
|
||||
category: 'public.app-category.utilities',
|
||||
darkModeSupport: true
|
||||
},
|
||||
win: {
|
||||
target: [
|
||||
{
|
||||
target: 'portable',
|
||||
arch: ['x64']
|
||||
},
|
||||
{
|
||||
target: 'nsis',
|
||||
arch: ['x64']
|
||||
}
|
||||
],
|
||||
publisherName: '思绪思维导图',
|
||||
icon: 'build/icons/icon.ico',
|
||||
publish: ['github']
|
||||
},
|
||||
linux: {
|
||||
target: [
|
||||
{
|
||||
target: 'AppImage',
|
||||
arch: ['x64']
|
||||
},
|
||||
{
|
||||
target: 'tar.gz',
|
||||
arch: ['x64', 'arm64']
|
||||
},
|
||||
{
|
||||
target: 'deb',
|
||||
arch: ['x64', 'armv7l', 'arm64']
|
||||
},
|
||||
{
|
||||
target: 'rpm',
|
||||
arch: ['x64']
|
||||
},
|
||||
{
|
||||
target: 'snap',
|
||||
arch: ['x64']
|
||||
},
|
||||
{
|
||||
target: 'pacman',
|
||||
arch: ['x64']
|
||||
}
|
||||
],
|
||||
category: 'Utilities',
|
||||
icon: './build/icon.icns'
|
||||
},
|
||||
dmg: {
|
||||
icon: 'build/icons/icon.icns'
|
||||
},
|
||||
nsis: {
|
||||
oneClick: false,
|
||||
allowToChangeInstallationDirectory: true,
|
||||
perMachine: true,
|
||||
deleteAppDataOnUninstall: true
|
||||
}
|
||||
},
|
||||
// 渲染线程的配置文件
|
||||
chainWebpackRendererProcess: config => {
|
||||
config.plugin('define').tap(args => {
|
||||
args[0]['IS_ELECTRON'] = true
|
||||
return args
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user