feat: folder
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run

This commit is contained in:
wangdan-fit2cloud 2025-06-18 02:10:36 +08:00
parent 449aa63f85
commit de22ffabc1
8 changed files with 135 additions and 26 deletions

View File

@ -1,6 +1,6 @@
<template>
<el-dialog
:title="$t('components.folder.addFolder')"
:title="title"
v-model="dialogVisible"
width="720"
append-to-body
@ -54,11 +54,19 @@ import { MsgSuccess, MsgAlert } from '@/utils/message'
import { t } from '@/locales'
const emit = defineEmits(['refresh'])
const props = defineProps({
title: {
type: String,
default: t('components.folder.addFolder'),
},
})
const FolderFormRef = ref()
const loading = ref(false)
const dialogVisible = ref<boolean>(false)
const sourceType = ref<any>('')
const isEdit = ref<boolean>(false)
const folderForm = ref<any>({
name: '',
@ -84,23 +92,37 @@ watch(dialogVisible, (bool) => {
desc: '',
parent_id: '',
}
isEdit.value = false
}
})
const open = (source: string, id: string) => {
const open = (source: string, id: string, data?: any) => {
sourceType.value = source
folderForm.value.parent_id = id
if (data) {
folderForm.value.name = data.name
folderForm.value.desc = data.desc
isEdit.value = true
}
dialogVisible.value = true
}
const submitHandle = async () => {
await FolderFormRef.value.validate((valid: any) => {
if (valid) {
folderApi.postFolder( sourceType.value, folderForm.value, loading).then((res) => {
MsgSuccess(t('common.createSuccess'))
emit('refresh')
dialogVisible.value = false
})
if (isEdit.value) {
folderApi.putFolder(sourceType.value, folderForm.value, loading).then((res) => {
MsgSuccess(t('common.editSuccess'))
emit('refresh')
dialogVisible.value = false
})
} else {
folderApi.postFolder(sourceType.value, folderForm.value, loading).then((res) => {
MsgSuccess(t('common.createSuccess'))
emit('refresh')
dialogVisible.value = false
})
}
}
})
}

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="folder-tree">
<el-input
v-model="filterText"
:placeholder="$t('common.search')"
@ -28,19 +28,54 @@
node-key="id"
>
<template #default="{ node, data }">
<div class="custom-tree-node flex align-center">
<AppIcon iconName="app-folder" style="font-size: 16px"></AppIcon>
<span class="ml-8">{{ node.label }}</span>
<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">{{ node.label }}</span>
</div>
<div
@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 class="rotate-90"><MoreFilled /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.stop="openCreateFolder(data)">
<el-icon><EditPen /></el-icon>
{{ '添加子文件夹' }}
</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)">
<el-icon><Delete /></el-icon>
{{ $t('common.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
</el-tree>
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" :title="title" />
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import type { TreeInstance } from 'element-plus'
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
import { t } from '@/locales'
import folderApi from '@/api/folder'
defineOptions({ name: 'FolderTree' })
const props = defineProps({
data: {
@ -51,6 +86,10 @@ const props = defineProps({
type: String,
default: 'root',
},
source: {
type: String,
default: 'APPLICATION',
},
isShared: {
type: Boolean,
default: false,
@ -68,6 +107,7 @@ interface Tree {
name: string
children?: Tree[]
id?: string
show?: boolean
}
const defaultProps = {
@ -75,15 +115,23 @@ const defaultProps = {
label: 'name',
}
const emit = defineEmits(['handleNodeClick'])
const emit = defineEmits(['handleNodeClick', 'refreshTree'])
const treeRef = ref<TreeInstance>()
const filterText = ref('')
const hoverNodeId = ref<string | undefined>('')
const title = ref('')
watch(filterText, (val) => {
treeRef.value!.filter(val)
})
function handleMouseEnter(data: Tree) {
hoverNodeId.value = data.id
}
function handleMouseleave() {
hoverNodeId.value = ''
}
const filterNode = (value: string, data: Tree) => {
if (!value) return true
return data.name.includes(value)
@ -97,6 +145,26 @@ const handleSharedNodeClick = () => {
treeRef.value?.setCurrentKey(undefined)
emit('handleNodeClick', { id: 'share', name: t(props.shareTitle) })
}
function deleteFolder(row: Tree) {
folderApi.delFolder(row.id as string, props.source).then(() => {
emit('refreshTree')
})
}
const CreateFolderDialogRef = ref()
function openCreateFolder(row: Tree) {
title.value = '添加子文件夹'
CreateFolderDialogRef.value.open(props.source, row.id)
}
function openEditFolder(row: Tree) {
title.value = '编辑文件夹'
CreateFolderDialogRef.value.open(props.source, row.id, row)
}
function refreshFolder() {
emit('refreshTree')
}
</script>
<style lang="scss" scoped>
.shared-knowledge {

View File

@ -1,16 +1,22 @@
export enum DeviceType {
Mobile = 'Mobile',
Desktop = 'Desktop'
Desktop = 'Desktop',
}
export enum ValidType {
Application = 'application',
Knowledge = 'knowledge',
User = 'user'
User = 'user',
}
export enum ValidCount {
Application = 5,
Knowledge = 50,
User = 2
User = 2,
}
export enum FolderSource {
KNOWLEDGE = 'KNOWLEDGE',
APPLICATION = 'APPLICATION',
TOOL = 'TOOL',
}

View File

@ -52,9 +52,9 @@
>
<template #default="{ row }">
<div class="flex-between">
<auto-tooltip :content="row.abstract">
<span :title="row.abstract">
{{ row.abstract }}
</auto-tooltip>
</span>
<div @click.stop v-show="mouseId === row.id && row.id !== 'new'">
<el-dropdown trigger="click" :teleported="false">
<el-icon class="rotate-90 mt-4"><MoreFilled /></el-icon>

View File

@ -52,7 +52,7 @@
ref="treeRef"
>
<template #default="{ node, data }">
<div class="custom-tree-node flex align-center lighter">
<div class="flex align-center lighter">
<img
src="@/assets/fileType/file-icon.svg"
alt=""

View File

@ -3,11 +3,13 @@
<template #left>
<h4 class="p-16 pb-0">{{ $t('views.knowledge.title') }}</h4>
<folder-tree
:source="FolderSource.KNOWLEDGE"
:data="folderList"
:currentNodeKey="currentFolder?.id"
@handleNodeClick="folderClickHandel"
class="p-8"
isShared
@refreshTree="refreshFolder"
/>
</template>
<SharedWorkspace v-if="currentFolder.id === 'share'"></SharedWorkspace>
@ -44,8 +46,13 @@
</el-select>
</div>
<el-dropdown trigger="click">
<el-button type="primary" class="ml-8"
v-hasPermission="[RoleConst.ADMIN.getWorkspaceRole,PermissionConst.KNOWLEDGE_CREATE.getWorkspacePermission]"
<el-button
type="primary"
class="ml-8"
v-hasPermission="[
RoleConst.ADMIN.getWorkspaceRole,
PermissionConst.KNOWLEDGE_CREATE.getWorkspacePermission,
]"
>
{{ $t('common.create') }}
<el-icon class="el-icon--right">
@ -217,8 +224,13 @@
<template #mouseEnter>
<div @click.stop>
<el-dropdown trigger="click">
<el-button text @click.stop
v-hasPermission="[RoleConst.ADMIN.getWorkspaceRole,PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermission]"
<el-button
text
@click.stop
v-hasPermission="[
RoleConst.ADMIN.getWorkspaceRole,
PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermission,
]"
>
<el-icon>
<MoreFilled />
@ -297,6 +309,7 @@ import useStore from '@/stores'
import { numberFormat } from '@/utils/common'
import { t } from '@/locales'
import { useRouter } from 'vue-router'
import { FolderSource } from '@/enums/common'
import { PermissionConst, RoleConst } from '@/utils/permission/data'
import { hasPermission } from '@/utils/permission/index'
@ -378,7 +391,7 @@ function getList() {
function getFolder() {
const params = {}
folder.asyncGetFolder('KNOWLEDGE', params, loading).then((res: any) => {
folder.asyncGetFolder(FolderSource.KNOWLEDGE, params, loading).then((res: any) => {
folderList.value = res.data
currentFolder.value = res.data?.[0] || {}
getList()
@ -401,7 +414,7 @@ function clickFolder(item: any) {
const CreateFolderDialogRef = ref()
function openCreateFolder() {
CreateFolderDialogRef.value.open('KNOWLEDGE', currentFolder.value.parent_id)
CreateFolderDialogRef.value.open(FolderSource.KNOWLEDGE, currentFolder.value.parent_id)
}
const GenerateRelatedDialogRef = ref<InstanceType<typeof GenerateRelatedDialog>>()

View File

@ -169,7 +169,7 @@ const searchType = ref('title')
const handleClick = (e: MouseEvent, ele: any) => {
e.preventDefault()
document.querySelector(`${ele}`).scrollIntoView({ behavior: 'smooth', block: 'start' })
document.querySelector(`${ele}`)?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
//

View File

@ -52,7 +52,7 @@
ref="treeRef"
>
<template #default="{ node, data }">
<div class="custom-tree-node flex align-center lighter">
<div class="flex align-center lighter">
<img
src="@/assets/fileType/file-icon.svg"
alt=""