support search file
This commit is contained in:
parent
1da8fbd02a
commit
5f116222fd
@ -24,6 +24,7 @@ StackEdit中文版的docker镜像地址:[mafgwo/stackedit](https://hub.docker.
|
|||||||
- 支持GitHub图床粘贴/拖拽图片自动上传(2022-07-31)
|
- 支持GitHub图床粘贴/拖拽图片自动上传(2022-07-31)
|
||||||
- 支持了右上角一键切换主题,补全了深色主题的样式(2022-08-07)
|
- 支持了右上角一键切换主题,补全了深色主题的样式(2022-08-07)
|
||||||
- 编辑与预览区域样式优化(2022-08-10)
|
- 编辑与预览区域样式优化(2022-08-10)
|
||||||
|
- 左边栏文件资源管理支持搜索文件(2022-08-17)
|
||||||
|
|
||||||
## 国外开源版本弊端:
|
## 国外开源版本弊端:
|
||||||
- 作者已经不维护了
|
- 作者已经不维护了
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="explorer flex flex--column">
|
<div class="explorer flex flex--column">
|
||||||
<div class="side-title flex flex--row flex--space-between">
|
<div class="side-title flex flex--row flex--space-between">
|
||||||
<div class="flex flex--row">
|
<div class="flex flex--row" v-if="!showSearch">
|
||||||
<button class="side-title__button side-title__button--new-file button" @click="newItem()" v-title="'创建文件'">
|
<button class="side-title__button side-title__button--new-file button" @click="newItem()" v-title="'创建文件'">
|
||||||
<icon-file-plus></icon-file-plus>
|
<icon-file-plus></icon-file-plus>
|
||||||
</button>
|
</button>
|
||||||
@ -14,14 +14,36 @@
|
|||||||
<button class="side-title__button side-title__button--rename button" @click="editItem()" v-title="'重命名'">
|
<button class="side-title__button side-title__button--rename button" @click="editItem()" v-title="'重命名'">
|
||||||
<icon-pen></icon-pen>
|
<icon-pen></icon-pen>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="side-title__button side-title__button--search button" @click="toSearch()" v-title="'搜索文件'">
|
||||||
|
<icon-search></icon-search>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex--row" v-else>
|
||||||
|
<button class="side-title__button button" @click="back()" v-title="'返回资源管理器'">
|
||||||
|
<icon-dots-horizontal></icon-dots-horizontal>
|
||||||
|
</button>
|
||||||
|
<div class="side-title__title">
|
||||||
|
搜索文件
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="side-title__button side-title__button--close button" @click="toggleExplorer(false)" v-title="'关闭资源管理器'">
|
<button class="side-title__button side-title__button--close button" @click="toggleExplorer(false)" v-title="'关闭资源管理器'">
|
||||||
<icon-close></icon-close>
|
<icon-close></icon-close>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="explorer__tree" :class="{'explorer__tree--new-item': !newChildNode.isNil}" v-if="!light" tabindex="0" @keydown.delete="deleteItem()">
|
<div class="explorer__tree" :class="{'explorer__tree--new-item': !newChildNode.isNil}" v-if="!light" v-show="!showSearch" tabindex="0" @keydown.delete="deleteItem()">
|
||||||
<explorer-node :node="rootNode" :depth="0"></explorer-node>
|
<explorer-node :node="rootNode" :depth="0"></explorer-node>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="explorer__search" tabindex="0" v-if="!light && showSearch">
|
||||||
|
<input type="text" v-model="searchText" class="text-input" placeholder="请输入关键字回车" @keyup.enter="search"></input>
|
||||||
|
<div class="explorer__search-list">
|
||||||
|
<div class="search-tips" v-if="searching">正在查询中...</div>
|
||||||
|
<a class="menu-entry button flex flex--row flex--align-center" :class="{'search-node--selected': currentFileId === fileItem.id}"
|
||||||
|
v-for="fileItem in searchItems" :key="fileItem.id" @click="clickSearch(fileItem)" href="javascript:void(0)">
|
||||||
|
{{ fileItem.name }}
|
||||||
|
</a>
|
||||||
|
<div class="search-tips">最多返回匹配的50个文档</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -30,11 +52,21 @@ import { mapState, mapGetters, mapActions } from 'vuex';
|
|||||||
import ExplorerNode from './ExplorerNode';
|
import ExplorerNode from './ExplorerNode';
|
||||||
import explorerSvc from '../services/explorerSvc';
|
import explorerSvc from '../services/explorerSvc';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
import MenuEntry from './menus/common/MenuEntry';
|
||||||
|
import localDbSvc from '../services/localDbSvc';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ExplorerNode,
|
ExplorerNode,
|
||||||
|
MenuEntry,
|
||||||
},
|
},
|
||||||
|
data: () => ({
|
||||||
|
currentFileId: '',
|
||||||
|
showSearch: false,
|
||||||
|
searching: false,
|
||||||
|
searchText: '',
|
||||||
|
searchItems: [],
|
||||||
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState([
|
||||||
'light',
|
'light',
|
||||||
@ -46,6 +78,7 @@ export default {
|
|||||||
'rootNode',
|
'rootNode',
|
||||||
'selectedNode',
|
'selectedNode',
|
||||||
]),
|
]),
|
||||||
|
workspaceId: () => store.getters['workspace/currentWorkspace'].id,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions('data', [
|
...mapActions('data', [
|
||||||
@ -59,11 +92,62 @@ export default {
|
|||||||
store.commit('explorer/setEditingId', node.item.id);
|
store.commit('explorer/setEditingId', node.item.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
back() {
|
||||||
|
this.showSearch = false;
|
||||||
|
},
|
||||||
|
toSearch() {
|
||||||
|
this.showSearch = true;
|
||||||
|
},
|
||||||
|
search() {
|
||||||
|
this.searchItems = [];
|
||||||
|
if (!this.searchText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.searching = true;
|
||||||
|
const allFileById = {};
|
||||||
|
const filterIds = [];
|
||||||
|
localDbSvc.getWorkspaceItems(this.workspaceId, (item) => {
|
||||||
|
if (item.type !== 'file' && item.type !== 'content') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (item.type === 'file') {
|
||||||
|
allFileById[item.id] = item;
|
||||||
|
}
|
||||||
|
if (filterIds.length >= 50) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fileId = item.id.split('/')[0];
|
||||||
|
// 包含了直接跳过
|
||||||
|
if (filterIds.indexOf(fileId) > -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (item.name && item.name.indexOf(this.searchText) > -1) {
|
||||||
|
filterIds.push(fileId);
|
||||||
|
}
|
||||||
|
if (item.text && item.text.indexOf(this.searchText) > -1) {
|
||||||
|
filterIds.push(fileId);
|
||||||
|
}
|
||||||
|
}, () => {
|
||||||
|
filterIds.forEach((it) => {
|
||||||
|
const file = allFileById[it];
|
||||||
|
if (file) {
|
||||||
|
this.searchItems.push(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.searching = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clickSearch(item) {
|
||||||
|
store.commit('explorer/setSelectedId', item.id);
|
||||||
|
store.commit('file/setCurrentId', item.id);
|
||||||
|
this.showSearch = false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.$watch(
|
this.$watch(
|
||||||
() => store.getters['file/current'].id,
|
() => store.getters['file/current'].id,
|
||||||
(currentFileId) => {
|
(currentFileId) => {
|
||||||
|
this.currentFileId = currentFileId;
|
||||||
store.commit('explorer/setSelectedId', currentFileId);
|
store.commit('explorer/setSelectedId', currentFileId);
|
||||||
store.dispatch('explorer/openNode', currentFileId);
|
store.dispatch('explorer/openNode', currentFileId);
|
||||||
}, {
|
}, {
|
||||||
@ -75,6 +159,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@import '../styles/variables.scss';
|
||||||
|
|
||||||
.explorer,
|
.explorer,
|
||||||
.explorer__tree {
|
.explorer__tree {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -89,4 +175,38 @@ export default {
|
|||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.explorer__search {
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.explorer__search-list {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-entry {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-entry__icon {
|
||||||
|
width: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
border-bottom: 1px solid $hr-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-tips {
|
||||||
|
font-size: 10px;
|
||||||
|
background-color: rgba(255, 173, 51, 0.14902);
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-node--selected {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
|
.app--dark & {
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -215,6 +215,10 @@ $item-font-size: 14px;
|
|||||||
.explorer-node--selected > & {
|
.explorer-node--selected > & {
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
|
.app--dark & {
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
.explorer__tree:focus & {
|
.explorer__tree:focus & {
|
||||||
background-color: #39f;
|
background-color: #39f;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
3
src/icons/Search.vue
Normal file
3
src/icons/Search.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<svg t="1660710445280" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2419" width="32" height="32"><path d="M576 672a192 192 0 0 1 192-192 182.72 182.72 0 0 1 48 6.4v-106.56a48 48 0 0 0-13.12-32L565.76 96a48 48 0 0 0-34.88-15.04H128A48 48 0 0 0 80 128v768A48 48 0 0 0 128 944h640a48 48 0 0 0 48-48v-38.4a184.64 184.64 0 0 1-48 6.4 192 192 0 0 1-192-192z m-64-326.72V112l272 272h-233.28A38.72 38.72 0 0 1 512 345.28z" p-id="2420"></path><path d="M957.76 774.4l-71.36-53.76A128 128 0 1 0 768 800a125.76 125.76 0 0 0 79.36-28.48l72 54.08zM768 736a64 64 0 1 1 64-64 64 64 0 0 1-64 64z" p-id="2421"></path></svg>
|
||||||
|
</template>
|
@ -56,6 +56,7 @@ import Key from './Key';
|
|||||||
import DotsHorizontal from './DotsHorizontal';
|
import DotsHorizontal from './DotsHorizontal';
|
||||||
import Seal from './Seal';
|
import Seal from './Seal';
|
||||||
import SwitchTheme from './SwitchTheme';
|
import SwitchTheme from './SwitchTheme';
|
||||||
|
import Search from './Search';
|
||||||
|
|
||||||
Vue.component('iconProvider', Provider);
|
Vue.component('iconProvider', Provider);
|
||||||
Vue.component('iconFormatBold', FormatBold);
|
Vue.component('iconFormatBold', FormatBold);
|
||||||
@ -114,3 +115,4 @@ Vue.component('iconKey', Key);
|
|||||||
Vue.component('iconDotsHorizontal', DotsHorizontal);
|
Vue.component('iconDotsHorizontal', DotsHorizontal);
|
||||||
Vue.component('iconSeal', Seal);
|
Vue.component('iconSeal', Seal);
|
||||||
Vue.component('iconSwitchTheme', SwitchTheme);
|
Vue.component('iconSwitchTheme', SwitchTheme);
|
||||||
|
Vue.component('iconSearch', Search);
|
||||||
|
@ -87,7 +87,7 @@ textarea {
|
|||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
background-color: #fff;
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
background-image: none;
|
background-image: none;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: $border-radius-base;
|
border-radius: $border-radius-base;
|
||||||
|
Loading…
Reference in New Issue
Block a user