UnisKB/ui/src/components/folder-tree/index.vue

263 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="folder-tree">
<el-input
v-model.trim="filterText"
:placeholder="$t('common.search')"
prefix-icon="Search"
clearable
class="p-8"
/>
<div
@click="handleSharedNodeClick"
v-if="showShared && hasPermission(EditionConst.IS_EE, 'OR')"
class="shared-button flex cursor"
:class="currentNodeKey === 'share' && 'active'"
>
<AppIcon iconName="app-shared-active" style="font-size: 18px" class="color-primary"></AppIcon>
<span class="ml-8 lighter">{{ shareTitle }}</span>
</div>
<div class="tree-height border-t" :style="treeStyle">
<el-scrollbar>
<el-tree
ref="treeRef"
:data="data"
:props="defaultProps"
@node-click="handleNodeClick"
:filter-node-method="filterNode"
:default-expanded-keys="[currentNodeKey]"
:current-node-key="currentNodeKey"
highlight-current
class="overflow-inherit_node__children"
node-key="id"
v-loading="loading"
v-bind="$attrs"
>
<template #default="{ node, data }">
<div class="flex-between w-full" @mouseenter.stop="handleMouseEnter(data)">
<div class="flex align-center">
<AppIcon iconName="app-folder" style="font-size: 16px"></AppIcon>
<span class="ml-8 ellipsis" style="max-width: 110px" :title="node.label">{{
node.label
}}</span>
</div>
<div
v-if="canOperation"
@click.stop
v-show="hoverNodeId === data.id"
@mouseenter.stop="handleMouseEnter(data)"
@mouseleave.stop="handleMouseleave"
class="mr-16"
>
<el-dropdown trigger="click" :teleported="false">
<el-button text class="w-full">
<el-icon><MoreFilled /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@click.stop="openCreateFolder(data)"
v-if="node.level !== 3"
>
<AppIcon iconName="app-add-folder"></AppIcon>
{{ $t('components.folder.addChildFolder') }}
</el-dropdown-item>
<el-dropdown-item @click.stop="openEditFolder(data)">
<el-icon><EditPen /></el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item
divided
@click.stop="deleteFolder(data)"
:disabled="!data.parent_id"
>
<el-icon><Delete /></el-icon>
{{ $t('common.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
</el-tree>
</el-scrollbar>
</div>
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" :title="title" />
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
import type { TreeInstance } from 'element-plus'
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
import { t } from '@/locales'
import folderApi from '@/api/folder'
import { EditionConst } from '@/utils/permission/data'
import { hasPermission } from '@/utils/permission/index'
import useStore from '@/stores'
import { TreeToFlatten } from '@/utils/array'
import { MsgConfirm } from '@/utils/message'
defineOptions({ name: 'FolderTree' })
const props = defineProps({
data: {
type: Array,
default: () => [],
},
currentNodeKey: {
type: String,
default: 'default',
},
source: {
type: String,
default: 'APPLICATION',
},
showShared: {
type: Boolean,
default: false,
},
shareTitle: {
type: String,
default: '',
},
canOperation: {
type: Boolean,
default: true,
},
treeStyle: {
type: Object,
default: () => ({}),
},
})
const { folder } = useStore()
onBeforeRouteLeave((to, from) => {
folder.setCurrentFolder({})
})
interface Tree {
name: string
children?: Tree[]
id?: string
show?: boolean
parent_id?: string
}
const defaultProps = {
children: 'children',
label: 'name',
}
const emit = defineEmits(['handleNodeClick', 'refreshTree'])
const treeRef = ref<TreeInstance>()
const filterText = ref('')
const hoverNodeId = ref<string | undefined>('')
const title = ref('')
const loading = ref(false)
watch(filterText, (val) => {
treeRef.value!.filter(val)
})
const filterNode = (value: string, data: Tree) => {
if (!value) return true
return data.name.toLowerCase().includes(value.toLowerCase())
}
let time: any
function handleMouseEnter(data: Tree) {
clearTimeout(time)
hoverNodeId.value = data.id
}
function handleMouseleave() {
time = setTimeout(() => {
clearTimeout(time)
document.body.click()
}, 300)
}
const handleNodeClick = (data: Tree) => {
emit('handleNodeClick', data)
}
const handleSharedNodeClick = () => {
treeRef.value?.setCurrentKey(undefined)
emit('handleNodeClick', { id: 'share', name: props.shareTitle })
}
function deleteFolder(row: Tree) {
MsgConfirm(
`${t('common.deleteConfirm')}${row.name}`,
t('components.folder.deleteConfirmMessage'),
{
confirmButtonText: t('common.delete'),
confirmButtonClass: 'danger',
},
)
.then(() => {
folderApi.delFolder(row.id as string, props.source, loading).then(() => {
treeRef.value?.setCurrentKey(row.parent_id || 'default')
const prevFolder = TreeToFlatten(props.data).find((item: any) => item.id === row.parent_id)
folder.setCurrentFolder(prevFolder)
emit('refreshTree')
})
})
.catch(() => {})
}
const CreateFolderDialogRef = ref()
function openCreateFolder(row: Tree) {
title.value = t('components.folder.addChildFolder')
CreateFolderDialogRef.value.open(props.source, row.id)
}
function openEditFolder(row: Tree) {
title.value = t('components.folder.editFolder')
CreateFolderDialogRef.value.open(props.source, row.id, row)
}
function refreshFolder() {
emit('refreshTree')
}
</script>
<style lang="scss" scoped>
.folder-tree {
.shared-button {
padding: 10px 8px;
font-weight: 400;
font-size: 14px;
margin-bottom: 4px;
&.active {
background: var(--el-color-primary-light-9);
border-radius: var(--app-border-radius-base);
color: var(--el-color-primary);
font-weight: 500;
&:hover {
background: var(--el-color-primary-light-9);
}
}
&:hover {
border-radius: var(--app-border-radius-base);
background: var(--app-text-color-light-1);
}
&.is-active {
&:hover {
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
}
}
}
.tree-height {
padding-top: 4px;
height: calc(100vh - 175px);
}
}
:deep(.overflow-inherit_node__children) {
.el-tree-node__children {
overflow: inherit !important;
}
}
</style>