Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions simple-mind-map/src/core/render/TextEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,12 +489,16 @@ export default class TextEdit {
this.textEditNode.style.fontWeight = 'normal'
this.textEditNode.style.transform = 'translateY(0)'
this.setIsShowTextEdit(false)
this.mindMap.execCommand('SET_NODE_TEXT', currentNode, text)
// if (currentNode.isGeneralization) {
// // 概要节点
// currentNode.generalizationBelongNode.updateGeneralization()
// }
this.mindMap.render()
const lastText = currentNode.getData('text')
const hasChanged = text !== lastText
if (hasChanged) {
this.mindMap.execCommand('SET_NODE_TEXT', currentNode, text)
// if (currentNode.isGeneralization) {
// // 概要节点
// currentNode.generalizationBelongNode.updateGeneralization()
// }
this.mindMap.render()
}
this.mindMap.emit(
'hide_text_edit',
this.textEditNode,
Expand Down
6 changes: 3 additions & 3 deletions simple-mind-map/src/plugins/Export.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,9 @@ class Export {
async json(name, withConfig = true) {
const data = this.mindMap.getData(withConfig)
const str = JSON.stringify(data)
const blob = new Blob([str])
const res = await readBlob(blob)
return res
return new Blob([str], {
type: 'application/json;charset=utf-8'
})
}

// 专有文件,其实就是json文件
Expand Down
11 changes: 10 additions & 1 deletion simple-mind-map/src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,17 @@ export const parseDataUrl = data => {
// 下载文件
export const downloadFile = (file, fileName) => {
let a = document.createElement('a')
a.href = file
a.download = fileName
if (file instanceof Blob) {
const url = URL.createObjectURL(file)
a.href = url
a.click()
setTimeout(() => {
URL.revokeObjectURL(url)
}, 0)
return
}
a.href = file
a.click()
}

Expand Down
45 changes: 42 additions & 3 deletions web/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Vue from 'vue'
import vuexStore from '@/store'

const SIMPLE_MIND_MAP_DATA = 'SIMPLE_MIND_MAP_DATA'
const SIMPLE_MIND_MAP_VIEW = 'SIMPLE_MIND_MAP_VIEW'
const SIMPLE_MIND_MAP_CONFIG = 'SIMPLE_MIND_MAP_CONFIG'
const SIMPLE_MIND_MAP_LANG = 'SIMPLE_MIND_MAP_LANG'
const SIMPLE_MIND_MAP_LOCAL_CONFIG = 'SIMPLE_MIND_MAP_LOCAL_CONFIG'
Expand All @@ -22,14 +23,47 @@ export const getData = () => {
return Vue.prototype.getCurrentData()
}
let store = localStorage.getItem(SIMPLE_MIND_MAP_DATA)
let view = null
const viewStore = localStorage.getItem(SIMPLE_MIND_MAP_VIEW)
if (viewStore) {
try {
view = JSON.parse(viewStore)
} catch (error) {
view = null
}
}
if (store === null) {
return simpleDeepClone(exampleData)
const defaultData = simpleDeepClone(exampleData)
if (view) {
defaultData.view = view
}
return defaultData
} else {
try {
return JSON.parse(store)
const parsed = JSON.parse(store)
if (view) {
parsed.view = view
}
return parsed
} catch (error) {
return simpleDeepClone(exampleData)
const defaultData = simpleDeepClone(exampleData)
if (view) {
defaultData.view = view
}
return defaultData
}
}
}

// 存储视图数据
export const storeViewData = view => {
try {
if (window.takeOverApp || vuexStore.state.isHandleLocalFile) {
return
}
localStorage.setItem(SIMPLE_MIND_MAP_VIEW, JSON.stringify(view || null))
} catch (error) {
console.log(error)
}
}

Expand All @@ -49,6 +83,11 @@ export const storeData = data => {
...originData,
...data
}
const { view } = originData
if (view !== undefined) {
storeViewData(view)
delete originData.view
}
if (window.takeOverApp) {
mindMapData = originData
window.takeOverAppMethods.saveMindMapData(originData)
Expand Down
109 changes: 101 additions & 8 deletions web/src/pages/Edit/components/Edit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ import ShortcutKey from './ShortcutKey.vue'
import Contextmenu from './Contextmenu.vue'
import RichTextToolbar from './RichTextToolbar.vue'
import NodeNoteContentShow from './NodeNoteContentShow.vue'
import { getData, getConfig, storeData } from '@/api'
import { getData, getConfig, storeData, storeViewData } from '@/api'
import Navigator from './Navigator.vue'
import NodeImgPreview from './NodeImgPreview.vue'
import SidebarTrigger from './SidebarTrigger.vue'
Expand Down Expand Up @@ -195,6 +195,12 @@ export default {
mindMapConfig: {},
prevImg: '',
storeConfigTimer: null,
storeDataTimer: null,
storeViewTimer: null,
pendingStorePatch: null,
idleSaveTaskId: null,
isCanvasPointerDown: false,
pendingViewDataOnDrag: null,
showDragMask: false
}
},
Expand Down Expand Up @@ -239,6 +245,8 @@ export default {
this.$bus.$on('endTextEdit', this.handleEndTextEdit)
this.$bus.$on('createAssociativeLine', this.handleCreateLineFromActiveNode)
this.$bus.$on('startPainter', this.handleStartPainter)
this.$bus.$on('svg_mousedown', this.handleSvgMousedown)
this.$bus.$on('mouseup', this.handleCanvasMouseup)
this.$bus.$on('node_tree_render_end', this.handleHideLoading)
this.$bus.$on('showLoading', this.handleShowLoading)
this.$bus.$on('localStorageExceeded', this.onLocalStorageExceeded)
Expand All @@ -255,11 +263,17 @@ export default {
this.$bus.$off('endTextEdit', this.handleEndTextEdit)
this.$bus.$off('createAssociativeLine', this.handleCreateLineFromActiveNode)
this.$bus.$off('startPainter', this.handleStartPainter)
this.$bus.$off('svg_mousedown', this.handleSvgMousedown)
this.$bus.$off('mouseup', this.handleCanvasMouseup)
this.$bus.$off('node_tree_render_end', this.handleHideLoading)
this.$bus.$off('showLoading', this.handleShowLoading)
this.$bus.$off('localStorageExceeded', this.onLocalStorageExceeded)
window.removeEventListener('resize', this.handleResize)
this.$bus.$off('showDownloadTip', this.showDownloadTip)
clearTimeout(this.storeConfigTimer)
clearTimeout(this.storeDataTimer)
clearTimeout(this.storeViewTimer)
this.cancelIdleSaveTask()
this.mindMap.destroy()
},
methods: {
Expand Down Expand Up @@ -288,6 +302,19 @@ export default {
this.mindMap.painter.startPainter()
},

handleSvgMousedown() {
this.isCanvasPointerDown = true
},

handleCanvasMouseup() {
if (!this.isCanvasPointerDown && !this.pendingViewDataOnDrag) return
this.isCanvasPointerDown = false
if (!this.pendingViewDataOnDrag) return
const viewData = this.pendingViewDataOnDrag
this.pendingViewDataOnDrag = null
this.queueViewDataSave(viewData, true)
},

handleResize() {
this.mindMap.resize()
},
Expand All @@ -312,18 +339,84 @@ export default {
this.mindMapConfig = getConfig() || {}
},

// 在浏览器空闲时执行任务,减少对交互帧的抢占
runWhenIdle(fn) {
this.cancelIdleSaveTask()
if (typeof window.requestIdleCallback === 'function') {
this.idleSaveTaskId = window.requestIdleCallback(() => {
this.idleSaveTaskId = null
fn()
}, { timeout: 1000 })
} else {
this.idleSaveTaskId = window.setTimeout(() => {
this.idleSaveTaskId = null
fn()
}, 0)
}
},

cancelIdleSaveTask() {
if (!this.idleSaveTaskId) return
if (typeof window.cancelIdleCallback === 'function') {
window.cancelIdleCallback(this.idleSaveTaskId)
} else {
clearTimeout(this.idleSaveTaskId)
}
this.idleSaveTaskId = null
},

flushPendingStoreSave() {
if (!this.pendingStorePatch) return
this.runWhenIdle(() => {
if (!this.pendingStorePatch) return
const patch = this.pendingStorePatch
this.pendingStorePatch = null
if (patch.root !== undefined) {
storeData({ root: patch.root })
}
if (patch.view !== undefined) {
storeViewData(patch.view)
}
})
},

queueRootDataSave(data) {
this.pendingStorePatch = {
...(this.pendingStorePatch || {}),
root: data
}
clearTimeout(this.storeDataTimer)
this.storeDataTimer = setTimeout(() => {
this.flushPendingStoreSave()
}, 1200)
},

queueViewDataSave(data, immediate = false) {
this.pendingStorePatch = {
...(this.pendingStorePatch || {}),
view: data
}
clearTimeout(this.storeViewTimer)
if (immediate) {
this.flushPendingStoreSave()
return
}
this.storeViewTimer = setTimeout(() => {
this.flushPendingStoreSave()
}, 1200)
},

// 存储数据当数据有变时
bindSaveEvent() {
this.$bus.$on('data_change', data => {
storeData({ root: data })
this.queueRootDataSave(data)
})
this.$bus.$on('view_data_change', data => {
clearTimeout(this.storeConfigTimer)
this.storeConfigTimer = setTimeout(() => {
storeData({
view: data
})
}, 300)
if (this.isCanvasPointerDown) {
this.pendingViewDataOnDrag = data
return
}
this.queueViewDataSave(data)
})
},

Expand Down
57 changes: 54 additions & 3 deletions web/src/pages/Edit/components/Navigator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export default {
mindMapImg: '',
width: 0,
setSizeTimer: null,
withTransition: true
withTransition: true,
lastViewX: null,
lastViewY: null
}
},
computed: {
Expand All @@ -69,8 +71,9 @@ export default {
window.addEventListener('resize', this.setSize)
this.$bus.$on('toggle_mini_map', this.toggle_mini_map)
this.$bus.$on('data_change', this.data_change)
this.$bus.$on('view_data_change', this.data_change)
this.$bus.$on('node_tree_render_end', this.data_change)
this.$bus.$on('scale', this.data_change)
this.$bus.$on('translate', this.onTranslate)
window.addEventListener('mouseup', this.onMouseup)
this.mindMap.on(
'mini_map_view_box_position_change',
Expand All @@ -81,8 +84,9 @@ export default {
window.removeEventListener('resize', this.setSize)
this.$bus.$off('toggle_mini_map', this.toggle_mini_map)
this.$bus.$off('data_change', this.data_change)
this.$bus.$off('view_data_change', this.data_change)
this.$bus.$off('node_tree_render_end', this.data_change)
this.$bus.$off('scale', this.data_change)
this.$bus.$off('translate', this.onTranslate)
window.removeEventListener('mouseup', this.onMouseup)
this.mindMap.off(
'mini_map_view_box_position_change',
Expand All @@ -93,6 +97,10 @@ export default {
// 切换显示小地图
toggle_mini_map(show) {
this.showMiniMap = show
if (!show) {
this.lastViewX = null
this.lastViewY = null
}
this.$nextTick(() => {
if (this.$refs.navigatorBox) {
this.init()
Expand All @@ -114,6 +122,46 @@ export default {
}, 500)
},

// 画布平移时仅更新视口框,避免频繁重绘小地图图片
onTranslate(x, y) {
if (!this.showMiniMap) return
if (this.lastViewX === null || this.lastViewY === null) {
this.lastViewX = x
this.lastViewY = y
return
}
const dx = x - this.lastViewX
const dy = y - this.lastViewY
this.lastViewX = x
this.lastViewY = y
if (dx === 0 && dy === 0) return
const currentState = this.mindMap.miniMap.currentState
if (!currentState) return
const { miniMapBoxScale, miniMapBoxLeft, miniMapBoxTop } = currentState
const boxDx = -dx * miniMapBoxScale
const boxDy = -dy * miniMapBoxScale
const left = Math.max(
miniMapBoxLeft,
Number.parseFloat(this.viewBoxStyle.left) + boxDx
)
const right = Math.max(
miniMapBoxLeft,
Number.parseFloat(this.viewBoxStyle.right) - boxDx
)
const top = Math.max(
miniMapBoxTop,
Number.parseFloat(this.viewBoxStyle.top) + boxDy
)
const bottom = Math.max(
miniMapBoxTop,
Number.parseFloat(this.viewBoxStyle.bottom) - boxDy
)
this.viewBoxStyle.left = left + 'px'
this.viewBoxStyle.right = right + 'px'
this.viewBoxStyle.top = top + 'px'
this.viewBoxStyle.bottom = bottom + 'px'
},

// 计算容器宽度
setSize() {
clearTimeout(this.setSizeTimer)
Expand Down Expand Up @@ -152,6 +200,9 @@ export default {
this.svgBoxScale = miniMapBoxScale
this.svgBoxLeft = miniMapBoxLeft
this.svgBoxTop = miniMapBoxTop
const viewData = this.mindMap.view.getTransformData()
this.lastViewX = viewData.state.x
this.lastViewY = viewData.state.y
},

// 小地图鼠标按下事件
Expand Down