@@ -31,7 +22,7 @@
:allow-drop="allowDrop"
:default-expanded-keys="expandedKeys"
@node-click="handleClick"
- @node-contextmenu="handleContextMenu"
+ @node-contextmenu="trigger"
@node-expand="handleExpand"
@node-collapse="handleCollapse"
@node-drop="handleDrop"
@@ -59,27 +50,6 @@
在左侧栏选择要查看的文件
-
-
-
-
你真的要删除文件{{ removing?.endsWith('/') ? '夹' : '' }} {{ removing }} 吗?
@@ -97,29 +67,72 @@
import { ref, computed, watch, onActivated, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
-import { useElementSize, useEventListener } from '@vueuse/core'
-import { base64ToArrayBuffer, send, store, useColorMode } from '@koishijs/client'
+import { useElementSize } from '@vueuse/core'
+import { base64ToArrayBuffer, send, store, useColorMode, useContext, useMenu } from '@koishijs/client'
import { Entry } from '@koishijs/plugin-explorer'
-import { files, uploading, vFocus } from './store'
+import { files, TreeEntry, uploading, vFocus } from './store'
import { model } from './editor'
import * as monaco from 'monaco-editor'
-interface TreeEntry extends Entry {
- expanded?: boolean
-}
-
+const ctx = useContext()
const route = useRoute()
const router = useRouter()
const keyword = ref('')
const tree = ref(null)
-const menu = ref(null)
const root = ref<{ $el: HTMLElement }>(null)
const editor = ref(null)
-const menuTarget = ref(null)
const renaming = ref(null)
const data = ref([])
const removing = ref(null)
+const trigger = useMenu('explorer.tree')
+
+ctx.action('explorer.save', {
+ disabled: () => files[active.value]?.newValue === files[active.value]?.oldValue,
+ action: async () => {
+ const content = files[active.value].newValue
+ await send('explorer/write', active.value, content)
+ files[active.value].oldValue = content
+ },
+})
+
+ctx.action('explorer.refresh', {
+ action: () => send('explorer/refresh'),
+})
+
+ctx.action('explorer.tree.create-file', {
+ disabled: ({ explorer }) => explorer.tree.type !== 'directory',
+ action: ({ explorer }) => createEntry(explorer.tree, 'file'),
+})
+
+ctx.action('explorer.tree.create-directory', {
+ disabled: ({ explorer }) => explorer.tree.type !== 'directory',
+ action: ({ explorer }) => createEntry(explorer.tree, 'directory'),
+})
+
+ctx.action('explorer.tree.upload', {
+ disabled: ({ explorer }) => explorer.tree.type !== 'directory',
+ action: ({ explorer }) => uploading.value = explorer.tree.filename + '/',
+})
+
+ctx.action('explorer.tree.download', {
+ disabled: ({ explorer }) => explorer.tree.type !== 'file',
+ action: ({ explorer }) => downloadFile(explorer.tree.filename),
+})
+
+ctx.action('explorer.tree.remove', {
+ disabled: ({ explorer }) => !explorer.tree.filename,
+ action: ({ explorer }) => initRemove(explorer.tree),
+})
+
+ctx.action('explorer.tree.rename', {
+ disabled: ({ explorer }) => !explorer.tree.filename,
+ action: ({ explorer }) => {
+ cancelRename()
+ renaming.value = explorer.tree.filename
+ },
+})
+
const showRemoving = computed({
get: () => !!removing.value,
set: (v) => removing.value = null,
@@ -149,14 +162,6 @@ watch(() => store.explorer, (value) => {
data.value = merge(data.value, value) || []
}, { immediate: true })
-useEventListener('click', () => {
- menuTarget.value = null
-})
-
-useEventListener('contextmenu', () => {
- menuTarget.value = null
-})
-
let instance: monaco.editor.IStandaloneCodeEditor = null
watch(keyword, (val) => {
@@ -205,9 +210,9 @@ function filterNode(value: string, data: TreeEntry) {
return data.name.toLowerCase().includes(keyword.value.toLowerCase())
}
-function createEntry(type: 'file' | 'directory') {
+function createEntry(entry: TreeEntry, type: 'file' | 'directory') {
cancelRename()
- renaming.value = menuTarget.value.filename + '/'
+ renaming.value = entry.filename + '/'
files[renaming.value] = {
type,
name: '',
@@ -215,8 +220,8 @@ function createEntry(type: 'file' | 'directory') {
oldValue: '',
newValue: '',
}
- menuTarget.value.expanded = true
- menuTarget.value.children.push(files[renaming.value])
+ entry.expanded = true
+ entry.children.push(files[renaming.value])
}
function confirmRename(entry: TreeEntry) {
@@ -311,19 +316,12 @@ async function handleClick(data: TreeEntry) {
active.value = data.filename
}
-async function handleContextMenu(event: MouseEvent, entry?: TreeEntry) {
- event.preventDefault()
- menuTarget.value = entry || {
- name: '',
- filename: '',
- type: 'directory',
- children: data.value,
- }
- await nextTick()
- const { clientX, clientY } = event
- menu.value.style.left = clientX + 'px'
- menu.value.style.top = clientY + 'px'
-}
+const rootEntry = computed(() => ({
+ name: '',
+ filename: '',
+ type: 'directory',
+ children: data.value,
+}))
function handleExpand(entry: TreeEntry) {
entry.expanded = true
@@ -400,27 +398,4 @@ async function downloadFile(filename: string) {
margin: 0 0.75rem;
}
-.context-menu {
- position: fixed;
- z-index: 1000;
- min-width: 12rem;
- padding: 0.5rem 0;
- border-radius: 4px;
- background-color: var(--k-card-bg);
- box-shadow: var(--k-card-shadow);
- transition: var(--color-transition);
- font-size: 14px;
-
- .item {
- user-select: none;
- padding: 0.25rem 1.5rem;
- cursor: pointer;
- transition: var(--color-transition);
-
- &:hover {
- background-color: var(--k-hover-bg);
- }
- }
-}
-
diff --git a/plugins/explorer/client/store.ts b/plugins/explorer/client/store.ts
index 1c0bcc58..63bcbdb9 100644
--- a/plugins/explorer/client/store.ts
+++ b/plugins/explorer/client/store.ts
@@ -2,6 +2,16 @@ import { Dict, store } from '@koishijs/client'
import { Directive, reactive, ref, watch } from 'vue'
import { Entry } from '@koishijs/plugin-explorer'
+declare module '@koishijs/client' {
+ interface ActionContext {
+ 'explorer.tree': TreeEntry
+ }
+}
+
+export interface TreeEntry extends Entry {
+ expanded?: boolean
+}
+
export const files = reactive>({})
watch(() => store.explorer, () => {