feat: 添加商务知识库功能,包括文件夹树和文件管理
This commit is contained in:
parent
9741192bee
commit
a4876971a4
|
@ -0,0 +1,62 @@
|
|||
import http from '@/utils/http'
|
||||
|
||||
const { request } = http
|
||||
|
||||
// 文件信息
|
||||
export interface KnowledgeFile {
|
||||
id: string
|
||||
name: string
|
||||
size: string
|
||||
type: string
|
||||
uploadTime: string
|
||||
}
|
||||
|
||||
// 文件夹信息
|
||||
export interface KnowledgeFolder {
|
||||
id: string
|
||||
name: string
|
||||
children?: KnowledgeFolder[]
|
||||
}
|
||||
|
||||
// 获取文件夹树
|
||||
export function getFolderTreeApi() {
|
||||
return request<KnowledgeFolder[]>({
|
||||
url: '/knowledge/folders',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 获取文件列表(按文件夹)
|
||||
export function getFilesApi(folderId: string) {
|
||||
return request<KnowledgeFile[]>({
|
||||
url: '/knowledge/files',
|
||||
method: 'get',
|
||||
params: { folderId },
|
||||
})
|
||||
}
|
||||
|
||||
// 创建文件夹
|
||||
export function createFolderApi(data: { name: string; parentId?: string }) {
|
||||
return request({
|
||||
url: '/knowledge/create-folder',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
export function deleteFileApi(fileId: string) {
|
||||
return request({
|
||||
url: `/knowledge/delete-file/${fileId}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
export function downloadFileApi(fileId: string) {
|
||||
return request<Blob>({
|
||||
url: `/knowledge/download/${fileId}`,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
})
|
||||
}
|
53
src/main.ts
53
src/main.ts
|
@ -1,43 +1,46 @@
|
|||
import { createApp } from 'vue'
|
||||
import ArcoVue, { Card, Drawer, Modal } from '@arco-design/web-vue'
|
||||
import '@/styles/arco-ui/index.less'
|
||||
// import '@arco-themes/vue-gi-demo/index.less'
|
||||
// import '@arco-design/web-vue/dist/arco.css'
|
||||
|
||||
// 额外引入 Arco Design Icon图标库
|
||||
import ArcoVueIcon from '@arco-design/web-vue/es/icon'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import pinia from '@/stores'
|
||||
|
||||
// 使用动画库
|
||||
import 'animate.css/animate.min.css'
|
||||
// UI框架 - Arco Design
|
||||
import ArcoVue, { Card, Drawer, Modal } from '@arco-design/web-vue'
|
||||
import ArcoVueIcon from '@arco-design/web-vue/es/icon'
|
||||
import '@/styles/arco-ui/index.less' // 自定义Arco样式
|
||||
|
||||
// 自定义过渡动画
|
||||
import '@/styles/css/transition.css'
|
||||
// UI框架 - Element Plus
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
// 导入全局scss主文件
|
||||
// 动画相关
|
||||
import 'animate.css/animate.min.css' // 第三方动画库
|
||||
import '@/styles/css/transition.css' // 自定义过渡动画
|
||||
|
||||
// 样式主文件
|
||||
import '@/styles/index.scss'
|
||||
|
||||
// 支持SVG
|
||||
// SVG支持
|
||||
import 'virtual:svg-icons-register'
|
||||
|
||||
// 自定义指令
|
||||
import directives from './directives'
|
||||
|
||||
// 状态管理
|
||||
import pinia from '@/stores'
|
||||
|
||||
// 对特定组件进行默认配置
|
||||
Card.props.bordered = false
|
||||
|
||||
// 初始化应用
|
||||
const app = createApp(App)
|
||||
Modal._context = app._context
|
||||
Drawer._context = app._context
|
||||
|
||||
// 配置Arco组件全局属性
|
||||
Card.props.bordered = false // 设置Card组件默认不显示边框
|
||||
Modal._context = app._context // 修复Modal组件上下文问题
|
||||
Drawer._context = app._context // 修复Drawer组件上下文问题
|
||||
|
||||
// 安装插件
|
||||
app.use(router)
|
||||
app.use(pinia)
|
||||
app.use(ArcoVue)
|
||||
app.use(ArcoVueIcon)
|
||||
app.use(directives)
|
||||
.use(pinia)
|
||||
.use(ArcoVue)
|
||||
.use(ArcoVueIcon)
|
||||
.use(ElementPlus)
|
||||
.use(directives)
|
||||
|
||||
app.mount('#app')
|
||||
// 挂载应用
|
||||
app.mount('#app')
|
|
@ -911,14 +911,32 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
// ],
|
||||
// },
|
||||
// 添加商务知识库
|
||||
{
|
||||
path: '/bussiness-knowledge',
|
||||
name: 'bussinesskonwledge',
|
||||
component: Layout,
|
||||
redirect: '/bussiness-knowledge/data',
|
||||
meta: { title: '商务资料知识库', icon: 'message', hidden: false, sort: 6 },
|
||||
children: [
|
||||
{
|
||||
path: '/bussiness-konwledge/data',
|
||||
name: 'bussiness-knowledge',
|
||||
component: () => import('@/views/bussiness-data/bussiness.vue'),
|
||||
meta: {
|
||||
title: '商务数据库信息',
|
||||
icon: 'info-circle',
|
||||
hidden: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/chat-platform',
|
||||
name: 'ChatPlatform',
|
||||
component: Layout,
|
||||
redirect: '/chat-platform/options',
|
||||
meta: { title: '聊天平台', icon: 'message', hidden: false, sort: 6 },
|
||||
meta: { title: '聊天平台', icon: 'message', hidden: false, sort: 7 },
|
||||
children: [
|
||||
// {
|
||||
// path: '/chat-platform/options',
|
||||
|
@ -937,7 +955,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||
name: 'EnterpriseSettings',
|
||||
component: Layout,
|
||||
redirect: '/enterprise-settings/company-info',
|
||||
meta: { title: '企业设置', icon: 'setting', hidden: false, sort: 7 },
|
||||
meta: { title: '企业设置', icon: 'setting', hidden: false, sort: 8 },
|
||||
children: [
|
||||
{
|
||||
path: '/enterprise-settings/company-info',
|
||||
|
@ -986,7 +1004,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||
name: 'EnterpriseDashboard',
|
||||
component: Layout,
|
||||
redirect: '/enterprise-dashboard/overview',
|
||||
meta: { title: '企业看板', icon: 'dashboard', hidden: false, sort: 8 },
|
||||
meta: { title: '企业看板', icon: 'dashboard', hidden: false, sort: 9},
|
||||
children: [
|
||||
{
|
||||
path: '/enterprise-dashboard/overview',
|
||||
|
@ -1035,7 +1053,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||
name: 'SystemResource',
|
||||
component: Layout,
|
||||
redirect: '/system-resource/device-management/warehouse',
|
||||
meta: { title: '关于平台', icon: 'server', hidden: false, sort: 9 },
|
||||
meta: { title: '关于平台', icon: 'server', hidden: false, sort: 10 },
|
||||
children: [
|
||||
{
|
||||
path: '/system-resource/device-management/warehouse',
|
||||
|
|
|
@ -70,6 +70,6 @@ declare global {
|
|||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
|
|
@ -0,0 +1,467 @@
|
|||
<template>
|
||||
<div class="business-knowledge-container">
|
||||
<el-container>
|
||||
<!-- 左侧文件夹树 -->
|
||||
<el-aside width="300px" class="folder-tree-container">
|
||||
<div class="header">
|
||||
<h3>文件夹结构</h3>
|
||||
<el-button type="primary" size="small" @click="handleCreateFolder">
|
||||
新建文件夹
|
||||
</el-button>
|
||||
</div>
|
||||
<el-tree
|
||||
:data="folderTree"
|
||||
node-key="id"
|
||||
:props="defaultProps"
|
||||
@node-click="handleFolderClick"
|
||||
empty-text="暂无文件夹"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="folder-item">
|
||||
<el-icon><Folder /></el-icon>
|
||||
<span class="folder-name">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
|
||||
<!-- 当没有文件夹时显示 -->
|
||||
<div v-if="!folderTree || folderTree.length === 0" class="empty-folder">
|
||||
<el-empty description="暂无文件夹" :image-size="80" />
|
||||
</div>
|
||||
</el-aside>
|
||||
|
||||
<!-- 右侧文件列表 -->
|
||||
<el-main class="file-list-container">
|
||||
<div class="header">
|
||||
<h3>{{ currentFolderName }}</h3>
|
||||
<el-button type="primary" @click="handleUploadFile">
|
||||
<el-icon><Upload /></el-icon>
|
||||
上传文件
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<el-table
|
||||
:data="fileList"
|
||||
style="width: 100%"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-table-column prop="name" label="文件名">
|
||||
<template #default="{ row }">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span style="margin-left: 10px">{{ row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="文件类型" width="120" />
|
||||
<el-table-column prop="size" label="文件大小" width="120">
|
||||
<template #default="{ row }">
|
||||
{{ formatFileSize(row.size) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="uploadTime" label="上传时间" width="180" />
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="handlePreview(row)">
|
||||
预览
|
||||
</el-button>
|
||||
<el-button size="small" type="primary" @click="handleDownload(row)">
|
||||
下载
|
||||
</el-button>
|
||||
<el-button size="small" type="danger" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<el-empty v-if="!loading && fileList.length === 0" description="暂时没有文件" />
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<!-- 新建文件夹对话框 -->
|
||||
<el-dialog
|
||||
v-model="folderDialogVisible"
|
||||
title="新建文件夹"
|
||||
width="400px"
|
||||
>
|
||||
<el-form :model="folderForm" ref="folderFormRef" label-width="80px" :rules="folderRules">
|
||||
<el-form-item label="文件夹名" prop="name">
|
||||
<el-input v-model="folderForm.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父级目录" prop="parentId">
|
||||
<el-select v-model="folderForm.parentId" placeholder="请选择父级目录">
|
||||
<el-option label="根目录" :value="''" />
|
||||
<el-option
|
||||
v-for="folder in allFolders"
|
||||
:key="folder.id"
|
||||
:label="folder.name"
|
||||
:value="folder.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="folderDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitFolderForm">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 上传文件对话框 -->
|
||||
<el-dialog
|
||||
v-model="uploadDialogVisible"
|
||||
title="上传文件"
|
||||
width="500px"
|
||||
>
|
||||
<el-form :model="uploadForm" ref="uploadFormRef" label-width="80px">
|
||||
<el-form-item label="选择文件" prop="file">
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
drag
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:file-list="fileListTemp"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或<em>点击上传</em>
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="resetUpload">取消</el-button>
|
||||
<el-button type="primary" @click="submitUploadForm">上传</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox, ElForm } from 'element-plus'
|
||||
import { Document, Folder, Upload, UploadFilled } from '@element-plus/icons-vue'
|
||||
import http from '@/utils/http'
|
||||
// 导入API
|
||||
import {
|
||||
createFolderApi,
|
||||
deleteFileApi,
|
||||
downloadFileApi,
|
||||
getFilesApi,
|
||||
getFolderTreeApi
|
||||
} from '@/apis/bussiness/bussiness'
|
||||
|
||||
// 文件夹树数据
|
||||
const folderTree = ref([])
|
||||
// 文件列表数据
|
||||
const fileList = ref([])
|
||||
// 临时文件列表(用于上传组件)
|
||||
const fileListTemp = ref([])
|
||||
|
||||
// 树形结构配置
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
}
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false)
|
||||
// 当前选中的文件夹ID
|
||||
const currentFolderId = ref('')
|
||||
// 当前选中的文件夹名称
|
||||
const currentFolderName = ref('根目录')
|
||||
|
||||
// 对话框显示状态
|
||||
const folderDialogVisible = ref(false)
|
||||
const uploadDialogVisible = ref(false)
|
||||
|
||||
// 文件夹表单数据
|
||||
const folderForm = reactive({
|
||||
name: '',
|
||||
parentId: ''
|
||||
})
|
||||
|
||||
// 文件夹表单验证规则
|
||||
const folderRules = {
|
||||
name: [
|
||||
{ required: true, message: '请输入文件夹名称', trigger: 'blur' },
|
||||
{ max: 50, message: '文件夹名称不能超过50个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 上传表单数据
|
||||
const uploadForm = reactive({
|
||||
file: null
|
||||
})
|
||||
|
||||
// 所有文件夹(用于新建文件夹时选择父目录)
|
||||
const allFolders = ref([])
|
||||
// 表单引用
|
||||
const folderFormRef = ref(null)
|
||||
const uploadFormRef = ref(null)
|
||||
|
||||
// 获取所有文件夹(用于选择父目录)
|
||||
const getAllFolders = async () => {
|
||||
try {
|
||||
const response = await getFolderTreeApi()
|
||||
// @ts-ignore
|
||||
folderTree.value = response.data
|
||||
// 扁平化文件夹结构,用于父级目录选择
|
||||
flattenFolders(response.data)
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
ElMessage.error('获取文件夹失败:' + (error.response?.data?.message || error.message))
|
||||
}
|
||||
}
|
||||
|
||||
// 扁平化文件夹结构
|
||||
const flattenFolders = (folders, parent = null) => {
|
||||
folders.forEach(folder => {
|
||||
// @ts-ignore
|
||||
allFolders.value.push(folder)
|
||||
if (folder.children && folder.children.length > 0) {
|
||||
flattenFolders(folder.children, folder)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化文件大小
|
||||
const formatFileSize = (size) => {
|
||||
if (!size) return '0 B'
|
||||
|
||||
// 转换为数字(API返回可能是字符串)
|
||||
const fileSize = Number(size)
|
||||
if (isNaN(fileSize)) return '未知'
|
||||
|
||||
if (fileSize < 1024) {
|
||||
return fileSize + ' B'
|
||||
} else if (fileSize < 1024 * 1024) {
|
||||
return (fileSize / 1024).toFixed(2) + ' KB'
|
||||
} else if (fileSize < 1024 * 1024 * 1024) {
|
||||
return (fileSize / (1024 * 1024)).toFixed(2) + ' MB'
|
||||
} else {
|
||||
return (fileSize / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文件夹点击
|
||||
const handleFolderClick = (data) => {
|
||||
currentFolderId.value = data.id
|
||||
currentFolderName.value = data.name
|
||||
loadFiles(data.id)
|
||||
}
|
||||
|
||||
// 加载文件列表
|
||||
const loadFiles = async (folderId) => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await getFilesApi(folderId)
|
||||
// @ts-ignore
|
||||
fileList.value = response.data || []
|
||||
} catch (error) {
|
||||
ElMessage.error('获取文件列表失败:' + (error.response?.data?.message || error.message))
|
||||
fileList.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 新建文件夹
|
||||
const handleCreateFolder = () => {
|
||||
folderForm.name = ''
|
||||
folderForm.parentId = currentFolderId.value
|
||||
folderDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 提交文件夹表单
|
||||
const submitFolderForm = async () => {
|
||||
// 表单验证
|
||||
const formRef = folderFormRef.value
|
||||
if (!formRef) return
|
||||
|
||||
try {
|
||||
// @ts-ignore
|
||||
await formRef.validate()
|
||||
// 调用创建文件夹API
|
||||
await createFolderApi(folderForm)
|
||||
ElMessage.success('文件夹创建成功')
|
||||
folderDialogVisible.value = false
|
||||
// 重新加载文件夹树
|
||||
getAllFolders()
|
||||
} catch (error) {
|
||||
if (error.name === 'ValidationError') return
|
||||
ElMessage.error('创建文件夹失败:' + (error.response?.data?.message || error.message))
|
||||
}
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
const handleUploadFile = () => {
|
||||
resetUpload()
|
||||
uploadDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 处理文件选择
|
||||
const handleFileChange = (file, fileList) => {
|
||||
uploadForm.file = file.raw
|
||||
fileListTemp.value = fileList
|
||||
}
|
||||
|
||||
// 重置上传表单
|
||||
const resetUpload = () => {
|
||||
uploadForm.file = null
|
||||
fileListTemp.value = []
|
||||
if (uploadFormRef.value) {
|
||||
uploadFormRef.value.resetFields()
|
||||
}
|
||||
}
|
||||
|
||||
// 提交上传表单
|
||||
const submitUploadForm = async () => {
|
||||
if (!uploadForm.file) {
|
||||
ElMessage.warning('请选择文件')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建FormData对象
|
||||
const formData = new FormData()
|
||||
formData.append('file', uploadForm.file)
|
||||
formData.append('folderId', currentFolderId.value)
|
||||
|
||||
// 使用http工具的post方法上传文件
|
||||
await http.post('/knowledge/upload', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
|
||||
ElMessage.success('文件上传成功')
|
||||
uploadDialogVisible.value = false
|
||||
// 重新加载文件列表
|
||||
loadFiles(currentFolderId.value)
|
||||
} catch (error) {
|
||||
// 错误处理已经在http拦截器中统一处理,这里可以不重复处理
|
||||
console.error('文件上传失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 预览文件
|
||||
const handlePreview = (row) => {
|
||||
// 实际应用中应打开预览窗口
|
||||
ElMessage.info(`预览文件: ${row.name}`)
|
||||
// 示例:打开新窗口预览
|
||||
// window.open(`/knowledge/preview/${row.id}`)
|
||||
}
|
||||
// 下载文件
|
||||
const handleDownload = async (row) => {
|
||||
try {
|
||||
// 使用http工具专门的download方法
|
||||
const response = await http.download(`/knowledge/download/${row.id}`)
|
||||
|
||||
// 处理二进制流
|
||||
const blob = new Blob([response.data])
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = row.name
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
document.body.removeChild(a)
|
||||
ElMessage.success('文件下载成功')
|
||||
} catch (error) {
|
||||
console.error('文件下载失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除文件 "${row.name}" 吗?`,
|
||||
'确认删除',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
},
|
||||
)
|
||||
|
||||
// 调用删除文件API
|
||||
await deleteFileApi(row.id)
|
||||
ElMessage.success('文件删除成功')
|
||||
// 重新加载文件列表
|
||||
loadFiles(currentFolderId.value)
|
||||
} catch (error) {
|
||||
if (error === 'cancel') return
|
||||
// eslint-disable-next-line ts/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
ElMessage.error(`文件删除失败:${error.response?.data?.message || error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getAllFolders()
|
||||
loadFiles(currentFolderId.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.business-knowledge-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.folder-tree-container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.folder-tree-container .header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.folder-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.folder-name {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.empty-folder {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.file-list-container {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.file-list-container .header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue