diff --git a/web/src/assets/icon-font/iconfont.css b/web/src/assets/icon-font/iconfont.css
index 2f25af7d..de977d30 100644
--- a/web/src/assets/icon-font/iconfont.css
+++ b/web/src/assets/icon-font/iconfont.css
@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
- src: url('iconfont.woff2?t=1697073602349') format('woff2'),
- url('iconfont.woff?t=1697073602349') format('woff'),
- url('iconfont.ttf?t=1697073602349') format('truetype');
+ src: url('iconfont.woff2?t=1709091401707') format('woff2'),
+ url('iconfont.woff?t=1709091401707') format('woff'),
+ url('iconfont.ttf?t=1709091401707') format('truetype');
}
.iconfont {
@@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
+.iconwenjian1:before {
+ content: "\e69f";
+}
+
.icondodeparent:before {
content: "\e70f";
}
diff --git a/web/src/assets/icon-font/iconfont.ttf b/web/src/assets/icon-font/iconfont.ttf
index f24f96b8..e65ff59a 100644
Binary files a/web/src/assets/icon-font/iconfont.ttf and b/web/src/assets/icon-font/iconfont.ttf differ
diff --git a/web/src/assets/icon-font/iconfont.woff b/web/src/assets/icon-font/iconfont.woff
index 013d113c..577f3102 100644
Binary files a/web/src/assets/icon-font/iconfont.woff and b/web/src/assets/icon-font/iconfont.woff differ
diff --git a/web/src/assets/icon-font/iconfont.woff2 b/web/src/assets/icon-font/iconfont.woff2
index 04194e88..b4a7f5da 100644
Binary files a/web/src/assets/icon-font/iconfont.woff2 and b/web/src/assets/icon-font/iconfont.woff2 differ
diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js
index 09c3df7a..28b40d6c 100644
--- a/web/src/lang/en_us.js
+++ b/web/src/lang/en_us.js
@@ -264,7 +264,8 @@ export default {
fileContentError: 'File content error',
fileOpenFailed: 'File open failed',
defaultFileName: 'Mind map',
- creatingTip: 'Creating file'
+ creatingTip: 'Creating file',
+ directory: 'Directory'
},
edit: {
newFeatureNoticeTitle: 'New feature reminder',
diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js
index 7896a1e2..fa7458c6 100644
--- a/web/src/lang/zh_cn.js
+++ b/web/src/lang/zh_cn.js
@@ -260,7 +260,8 @@ export default {
fileContentError: '文件内容有误',
fileOpenFailed: '文件打开失败',
defaultFileName: '思维导图',
- creatingTip: '正在创建文件'
+ creatingTip: '正在创建文件',
+ directory: '目录'
},
edit: {
newFeatureNoticeTitle: '新特性提醒',
diff --git a/web/src/pages/Edit/components/Toolbar.vue b/web/src/pages/Edit/components/Toolbar.vue
index 0ea11401..4c126971 100644
--- a/web/src/pages/Edit/components/Toolbar.vue
+++ b/web/src/pages/Edit/components/Toolbar.vue
@@ -26,12 +26,16 @@
@@ -54,7 +124,7 @@
-
+
@@ -112,7 +182,15 @@ export default {
horizontalList: [],
verticalList: [],
showMoreBtn: true,
- popoverShow: false
+ popoverShow: false,
+ fileTreeProps: {
+ label: 'name',
+ children: 'children',
+ isLeaf: 'leaf'
+ },
+ fileTreeVisible: false,
+ rootDirName: '',
+ fileTreeExpand: true
}
},
computed: {
@@ -178,6 +256,76 @@ export default {
}, 1000)
},
+ // 加载本地文件树
+ async loadFileTreeNode(node, resolve) {
+ try {
+ let dirHandle
+ if (node.level === 0) {
+ dirHandle = await window.showDirectoryPicker()
+ this.rootDirName = dirHandle.name
+ } else {
+ dirHandle = node.data.handle
+ }
+ const list = []
+ for await (const [key, value] of dirHandle.entries()) {
+ const isFile = value.kind === 'file'
+ if (isFile && !/\.(smm|xmind|md|json)$/.test(value.name)) {
+ continue
+ }
+ const enableEdit = isFile && /\.smm$/.test(value.name)
+ list.push({
+ id: key,
+ name: value.name,
+ type: value.kind,
+ handle: value,
+ leaf: isFile,
+ enableEdit
+ })
+ }
+ resolve(list)
+ } catch (error) {
+ console.log(error)
+ this.fileTreeVisible = false
+ resolve([])
+ if (error.toString().includes('aborted')) {
+ return
+ }
+ this.$message.warning(this.$t('toolbar.notSupportTip'))
+ }
+ },
+
+ // 扫描本地文件夹
+ openDirectory() {
+ this.fileTreeVisible = false
+ this.fileTreeExpand = true
+ this.rootDirName = ''
+ this.$nextTick(() => {
+ this.fileTreeVisible = true
+ })
+ },
+
+ // 编辑指定文件
+ editLocalFile(data) {
+ if (data.handle) {
+ fileHandle = data.handle
+ this.readFile()
+ }
+ },
+
+ // 导入指定文件
+ async importLocalFile(data) {
+ try {
+ const file = await data.handle.getFile()
+ this.$refs.ImportRef.onChange({
+ raw: file,
+ name: file.name
+ })
+ this.$refs.ImportRef.confirm()
+ } catch (error) {
+ console.log(error)
+ }
+ },
+
// 打开本地文件
async openLocalFile() {
try {
@@ -325,6 +473,43 @@ export default {
color: hsla(0, 0%, 100%, 0.9);
.toolbarBlock {
background-color: #262a2e;
+
+ .fileTreeBox {
+ background-color: #262a2e;
+
+ /deep/ .el-tree {
+ background-color: #262a2e;
+
+ &.el-tree--highlight-current {
+ .el-tree-node.is-current > .el-tree-node__content {
+ background-color: hsla(0, 0%, 100%, 0.05) !important;
+ }
+ }
+
+ .el-tree-node:focus > .el-tree-node__content {
+ background-color: hsla(0, 0%, 100%, 0.05) !important;
+ }
+
+ .el-tree-node__content:hover,
+ .el-upload-list__item:hover {
+ background-color: hsla(0, 0%, 100%, 0.02) !important;
+ }
+ }
+
+ .fileTreeWrap {
+ .customTreeNode {
+ .treeNodeInfo {
+ color: #fff;
+ }
+
+ .treeNodeBtnList {
+ .el-button {
+ padding: 7px 5px;
+ }
+ }
+ }
+ }
+ }
}
.toolbarBtn {
@@ -369,10 +554,96 @@ export default {
border: 1px solid rgba(0, 0, 0, 0.06);
margin-right: 20px;
flex-shrink: 0;
+ position: relative;
&:last-of-type {
margin-right: 0;
}
+
+ .fileTreeBox {
+ position: absolute;
+ left: 0;
+ top: 68px;
+ width: 100%;
+ height: 30px;
+ background-color: #fff;
+ padding: 12px 5px;
+ padding-top: 0;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ border-radius: 5px;
+ min-width: 200px;
+
+ &.expand {
+ height: 300px;
+
+ .fileTreeWrap {
+ visibility: visible;
+ }
+ }
+
+ .fileTreeToolbar {
+ width: 100%;
+ height: 30px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ border-bottom: 1px solid #e9e9e9;
+ margin-bottom: 12px;
+ padding-left: 12px;
+
+ .fileTreeName {
+ }
+
+ .fileTreeActionList {
+ .btn {
+ font-size: 18px;
+ margin-left: 12px;
+ cursor: pointer;
+ }
+ }
+ }
+
+ .fileTreeWrap {
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ visibility: hidden;
+
+ .customTreeNode {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 13px;
+ padding-right: 5px;
+
+ .treeNodeInfo {
+ display: flex;
+ align-items: center;
+
+ .treeNodeIcon {
+ margin-right: 5px;
+ opacity: 0.7;
+ }
+
+ .treeNodeName {
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ }
+
+ .treeNodeBtnList {
+ display: flex;
+ align-items: center;
+ }
+ }
+ }
+ }
}
.toolbarBtn {