开发中

This commit is contained in:
wanglin2 2023-05-04 22:40:44 +08:00
parent 26e3158bd8
commit ec9517c491
12 changed files with 535 additions and 221 deletions

File diff suppressed because one or more lines are too long

View File

@ -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?");
/***/ }),

2
web/package-lock.json generated
View File

@ -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"
}

View File

@ -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()
}
// 关闭所有窗口后退出

View File

@ -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
})
}

View File

@ -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)
})
}

View File

@ -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'),
})

View File

@ -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()
}
})
})
}

View File

@ -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

View File

@ -1,6 +1,9 @@
<template>
<div class="workbencheFileListContainer">
<div class="title">最近</div>
<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%">
@ -8,25 +11,11 @@
<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"></el-button>
<el-button
icon="el-icon-document-copy"
circle
size="mini"
></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>
<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"></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>
@ -48,28 +37,54 @@ export default {
},
created() {
this.getRecentFileList()
window.electronAPI.onRefreshRecentFileList((_event, value) => {
console.log(1);
window.electronAPI.onRefreshRecentFileList(() => {
this.getRecentFileList()
})
},
methods: {
async getRecentFileList() {
this.list = await window.electronAPI.getRecentFileList()
console.log(this.list)
let list = await window.electronAPI.getRecentFileList()
this.list = list.reverse()
},
openFileInDir(file) {
window.electronAPI.openFileInDir(file)
},
async deleteFile(file, index) {
let res = await window.electronAPI.deleteFile(file)
console.log(res)
if (res) {
} else {
this.list.splice(index, 1)
}
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(() => { });
}
}
}
@ -91,8 +106,16 @@ export default {
font-size: 18px;
border-bottom: 1px solid #e4e7ed;
height: 65px;
line-height: 65px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
.clearBtn {
cursor: pointer;
font-size: 14px;
color: #409eff;
}
}
.fileListBox {

View File

@ -2,7 +2,7 @@
<div class="workbencheSidebarContainer">
<div class="createBtn" @click="create">开始新建</div>
<div class="line"></div>
<div class="btn">
<div class="btn" @click="openLocalFile">
<span class="icon iconfont icondakai"></span>
<span class="text">打开本地文件</span>
</div>
@ -18,7 +18,11 @@ import { create } from '../utils'
export default {
methods: {
create
create,
openLocalFile() {
window.electronAPI.selectOpenFile()
}
}
}
</script>

View File

@ -26,27 +26,54 @@ export default {
MacControl,
WinControl
},
data () {
data() {
return {
name: ''
}
},
computed: {
...mapState(['fileName', 'isUnSave'])
},
},
watch: {
fileName(val) {
this.name = val
},
name(val) {
if (!val.trim()) return
this.setFileName(val)
let id = this.$route.params.id
window.electronAPI.rename(id, val.trim())
this.setFileName(val.trim())
// let id = this.$route.params.id
// window.electronAPI.rename(id, val.trim())
}
},
created() {
// window.onbeforeunload = async (e) => {
// e.returnValue = false
// if (!this.isUnSave) {
// window.electronAPI.close()
// } else {
// try {
// await this.checkIsClose()
// window.electronAPI.close()
// } catch (error) {}
// }
// }
},
methods: {
...mapMutations(['setFileName'])
...mapMutations(['setFileName']),
checkIsClose() {
return new Promise((resolve, reject) => {
this.$confirm('有操作尚未保存,是否确认关闭?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
resolve()
}).catch(() => {
reject()
});
})
}
}
}
</script>