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 { 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 App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
import pinia from '@/stores'
|
||||||
|
|
||||||
// 使用动画库
|
// UI框架 - Arco Design
|
||||||
import 'animate.css/animate.min.css'
|
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样式
|
||||||
|
|
||||||
// 自定义过渡动画
|
// UI框架 - Element Plus
|
||||||
import '@/styles/css/transition.css'
|
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'
|
import '@/styles/index.scss'
|
||||||
|
|
||||||
// 支持SVG
|
// SVG支持
|
||||||
import 'virtual:svg-icons-register'
|
import 'virtual:svg-icons-register'
|
||||||
|
|
||||||
// 自定义指令
|
// 自定义指令
|
||||||
import directives from './directives'
|
import directives from './directives'
|
||||||
|
|
||||||
// 状态管理
|
// 初始化应用
|
||||||
import pinia from '@/stores'
|
|
||||||
|
|
||||||
// 对特定组件进行默认配置
|
|
||||||
Card.props.bordered = false
|
|
||||||
|
|
||||||
const app = createApp(App)
|
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(router)
|
||||||
app.use(pinia)
|
.use(pinia)
|
||||||
app.use(ArcoVue)
|
.use(ArcoVue)
|
||||||
app.use(ArcoVueIcon)
|
.use(ArcoVueIcon)
|
||||||
app.use(directives)
|
.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',
|
path: '/chat-platform',
|
||||||
name: 'ChatPlatform',
|
name: 'ChatPlatform',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/chat-platform/options',
|
redirect: '/chat-platform/options',
|
||||||
meta: { title: '聊天平台', icon: 'message', hidden: false, sort: 6 },
|
meta: { title: '聊天平台', icon: 'message', hidden: false, sort: 7 },
|
||||||
children: [
|
children: [
|
||||||
// {
|
// {
|
||||||
// path: '/chat-platform/options',
|
// path: '/chat-platform/options',
|
||||||
|
@ -937,7 +955,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
name: 'EnterpriseSettings',
|
name: 'EnterpriseSettings',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/enterprise-settings/company-info',
|
redirect: '/enterprise-settings/company-info',
|
||||||
meta: { title: '企业设置', icon: 'setting', hidden: false, sort: 7 },
|
meta: { title: '企业设置', icon: 'setting', hidden: false, sort: 8 },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/enterprise-settings/company-info',
|
path: '/enterprise-settings/company-info',
|
||||||
|
@ -986,7 +1004,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
name: 'EnterpriseDashboard',
|
name: 'EnterpriseDashboard',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/enterprise-dashboard/overview',
|
redirect: '/enterprise-dashboard/overview',
|
||||||
meta: { title: '企业看板', icon: 'dashboard', hidden: false, sort: 8 },
|
meta: { title: '企业看板', icon: 'dashboard', hidden: false, sort: 9},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/enterprise-dashboard/overview',
|
path: '/enterprise-dashboard/overview',
|
||||||
|
@ -1035,7 +1053,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
name: 'SystemResource',
|
name: 'SystemResource',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/system-resource/device-management/warehouse',
|
redirect: '/system-resource/device-management/warehouse',
|
||||||
meta: { title: '关于平台', icon: 'server', hidden: false, sort: 9 },
|
meta: { title: '关于平台', icon: 'server', hidden: false, sort: 10 },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/system-resource/device-management/warehouse',
|
path: '/system-resource/device-management/warehouse',
|
||||||
|
|
|
@ -70,6 +70,6 @@ declare global {
|
||||||
// for type re-export
|
// for type re-export
|
||||||
declare global {
|
declare global {
|
||||||
// @ts-ignore
|
// @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')
|
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