Browse Source

个人空间文件夹上传

pull/1/head
han2015 4 months ago
parent
commit
1689019ff6
  1. 13
      src/api/doc/space.ts
  2. 17
      src/api/doc/type.ts
  3. 1
      src/views/doc/agent.vue
  4. 167
      src/views/doc/manage.vue
  5. 54
      src/views/doc/space.vue

13
src/api/doc/space.ts

@ -31,19 +31,6 @@ export function doCreateSpaceDir(uid:string,data?: createDir){
data: data data: data
}); });
} }
/**
*
*/
export function doFileUpload(params:FormData,_url:string): AxiosPromise<matterInfo> {
return request({
url: _url,
method: 'post',
data: params,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
export function doCreateAiagent(uid:string,data?: {space:string,matter:string}){ export function doCreateAiagent(uid:string,data?: {space:string,matter:string}){
return request({ return request({

17
src/api/doc/type.ts

@ -3,7 +3,8 @@
* @ 时间: 2025-05-12 15:39:13 * @ 时间: 2025-05-12 15:39:13
* @ 备注: 文档管理api * @ 备注: 文档管理api
*/ */
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
/** /**
* *
*/ */
@ -75,3 +76,17 @@ export interface respCreateShare{
name?:string; name?:string;
uuid?:string; uuid?:string;
} }
/**
*
*/
export function doFileUpload(params:FormData,_url:string): AxiosPromise<matterInfo> {
return request({
url: _url,
method: 'post',
data: params,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}

1
src/views/doc/agent.vue

@ -13,7 +13,6 @@ import {ElText,ElInput, ButtonInstance} from "element-plus";
import { VueMarkdown } from '@crazydos/vue-markdown' import { VueMarkdown } from '@crazydos/vue-markdown'
import rehypeRaw from 'rehype-raw' import rehypeRaw from 'rehype-raw'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
import { display } from 'html2canvas/dist/types/css/property-descriptors/display';
// //
const checkedModel = ref([]) const checkedModel = ref([])

167
src/views/doc/manage.vue

@ -8,7 +8,7 @@ import { getExpirTime, getFileIcon, readableSize} from "./tools"
import sharePermission from './sharePermission.vue'; import sharePermission from './sharePermission.vue';
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { getMatterList,postCreateDir,postDelMatter,postCreateShare,postMatterRename,postDelMatBatch,getMySpaces,doCreateSpace} from "@/api/doc/index" import { getMatterList,postCreateDir,postDelMatter,postCreateShare,postMatterRename,postDelMatBatch,getMySpaces,doCreateSpace} from "@/api/doc/index"
import { matterPage,matterInfo,respCreateShare,matterTree } from "@/api/doc/type" import { matterPage,matterInfo,respCreateShare,matterTree, doFileUpload} from "@/api/doc/type"
import { h } from 'vue' import { h } from 'vue'
import { import {
Delete, Delete,
@ -22,8 +22,7 @@ import {
Avatar, Avatar,
Plus Plus
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import {ElSelect,ElOption, ElText,ElInput,TableInstance,ElMessage,UploadFile,UploadFiles,ElPagination,ElTree,TreeInstance} from "element-plus"; import {ElSelect,ElOption, ElText,ElInput,TableInstance,ElMessage,UploadFile,UploadFiles,ElPagination,ElTree,TreeNode} from "element-plus";
import type { TreeNode } from 'element-plus/es/components/tree/src/tree.type'
import preview from './preview.vue'; import preview from './preview.vue';
import space from './space.vue'; import space from './space.vue';
@ -284,9 +283,11 @@ function onLoadMatterList(){
getMatterList(uid,_page).then((resp)=>{ getMatterList(uid,_page).then((resp)=>{
//page+1 index1apiindex0 //page+1 index1apiindex0
paginInfo.value={total:resp.data.totalPages,page:resp.data.page} paginInfo.value={total:resp.data.totalPages,page:resp.data.page}
matterList.value=resp.data.data.filter((item)=>{ matterList.value=resp.data.data
return !item.dir //2025-08-07: space
}) // .filter((item)=>{
// return !item.dir
// })
}) })
} }
//----------for dir----------- //----------for dir-----------
@ -338,14 +339,16 @@ function onNodeExpand(node: TreeNode, resolve: (data: matterTree[]) => void, rej
getMatterList(uid, _page).then((resp) => { getMatterList(uid, _page).then((resp) => {
paginInfo.value = { total: resp.data.totalPages, page: resp.data.page } paginInfo.value = { total: resp.data.totalPages, page: resp.data.page }
matterList.value = resp.data.data.filter((item)=>{ matterList.value = resp.data.data
return !item.dir // .filter((item)=>{
}) // return !item.dir
// })
let node_data = resp.data.data.filter((item) => { let node_data = resp.data.data.filter((item) => {
return item.dir return item.dir
}).map((item) => { }).map((val) => {
item.dir = !item.dir const copy = structuredClone(val)
return item copy.dir = !copy.dir
return copy
}) })
resolve(node_data) resolve(node_data)
@ -397,6 +400,10 @@ function onNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
// //
function onPrivateView(row:matterInfo){ function onPrivateView(row:matterInfo){
if(row.dir){
alert("目录不支持预览")
return
}
let a = `${row.uuid}${row.name}` let a = `${row.uuid}${row.name}`
let _token=document.cookie.match(/hxpan=([\w-]*)/) let _token=document.cookie.match(/hxpan=([\w-]*)/)
if (_token&&_token.length>1){ if (_token&&_token.length>1){
@ -411,6 +418,96 @@ function onPrivateView(row:matterInfo){
}) })
} }
//--------------UPGRADE: multy file upload section----------------
//, 使html
async function onCustomUpload(e:Event){
const files = e.target!.files??[]
for(let ff of files){
await handleSingleFile(ff)
}
onLoadMatterList() //
}
async function handleSingleFile(ff:File){
const fields=new FormData()
fields.append("userUuid",uploadFormData.value.userUuid)
fields.append("puuid",uploadFormData.value.puuid)
fields.append("file",ff)
const res = await doFileUpload(fields,'/hxpan/api/matter/upload')
if(res.code!=200){
console.log(ff.name+"上传失败! ")
alert(ff.name+"上传失败! ")
}
}
//input
//
async function uploadFolder(e:Event){
const files = e.target!.files??[]
for(let f of files){
await handleFolderFile(f)
}
onLoadMatterList() //
}
async function handleFolderFile(option:File){
//
const _path = option.webkitRelativePath
const _dir=_path.replace(/\/[^/]+\w+$/,"") //,[^/]
const node = matterList.value.filter((item)=>{
return item.dir && item.path?.endsWith(_dir)
})
let puuid=""
//
if(node.length==0){
const subs= await doCreateMultyDir(_dir,currentNode.value.uuid)
matterList.value.push(...subs) //
const newnodes=matterList.value.filter((item)=>{
return item.dir && item.path?.endsWith(_dir)
})
if(newnodes.length>0){
puuid=newnodes[0].uuid
}else{
console.log(_path+"上传失败! ")
alert(_path +" 上传失败! ")
return
}
}else{
puuid=node[0].uuid
}
const fields = new FormData()
fields.append('file', option)
fields.append("space",uploadFormData.value.space)
fields.append("puuid",puuid)
const res = await doFileUpload(fields,'/hxpan/api/matter/upload')
if(res.code!=200){
console.log(_path+"上传失败! ")
alert(_path +" 上传失败! ")
}
}
//matterinfos
async function doCreateMultyDir(path:string,uuid:string){
const list=[];
const dirs=path.split("/")
for(let i=0;i<dirs.length;i++){
await postCreateDir(uid,{
userUuid:uid,
puuid:uuid,
name:dirs[i],
}).then((resp)=> {
uuid=resp.data.uuid //uuid, puuid
list.push(resp.data)
})
}
return list
}
function handleMouseEnter(row:any){ function handleMouseEnter(row:any){
currentHoverRow.value=row.name currentHoverRow.value=row.name
} }
@ -419,6 +516,7 @@ function handleSingleUpload(response:any){
fileList.value=[] fileList.value=[]
onLoadMatterList() onLoadMatterList()
} }
interface uploadError{ interface uploadError{
msg:string msg:string
} }
@ -426,6 +524,9 @@ interface uploadError{
function handleSigLoadErr(error: Error, uploadFile: UploadFile, uploadFiles:UploadFiles){ function handleSigLoadErr(error: Error, uploadFile: UploadFile, uploadFiles:UploadFiles){
ElMessage.error(JSON.parse(error.message).msg) ElMessage.error(JSON.parse(error.message).msg)
} }
//-----------------------------------------------------------
//-------------------space feature--------------------- //-------------------space feature---------------------
function onNewSpace(){ function onNewSpace(){
const newname=ref("") const newname=ref("")
@ -451,6 +552,9 @@ function onNewSpace(){
} }
function onSpaceNodeClick(data:matterTree,node:TreeNode,self:any,env:any){ function onSpaceNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
if(PRIVATESPACE.value) { //
PRIVATESPACE.value=false
}
// //
if(spaceNodeUid==data.uuid) return; if(spaceNodeUid==data.uuid) return;
spaceNodeUid=data.uuid spaceNodeUid=data.uuid
@ -541,35 +645,39 @@ const handleSelectionChange = (val:matterInfo[]) => {
<el-link v-if="currentNode.name!=='root'" @click="onNodeClick(treeData[0], null as unknown as TreeNode, null, null)"> <el-link v-if="currentNode.name!=='root'" @click="onNodeClick(treeData[0], null as unknown as TreeNode, null, null)">
<span style="font-weight: bold;margin-right: 5px;">根目录</span>/ <span style="font-weight: bold;margin-right: 5px;">根目录</span>/
</el-link> </el-link>
<span style="font-weight: bold;margin:0 5px;">{{ currentNode.name }}</span> <span style="font-weight: bold;margin:0 5px;align-content:center;">{{ currentNode.name }}</span>
<el-col :span="6" class="search">
<el-input placeholder="搜索文件" v-model="searchname" @blur="searchname===''?onLoadMatterList():''"/>
<el-button :icon="Search" @click="onSearchFile(searchname)"></el-button>
</el-col>
</el-row> </el-row>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="14"> <el-col :span="14">
<el-upload class="el-button el-button--default" :file-list="fileList" <div class="el-button el-button--default" style="position: relative;">
:data="uploadFormData" <input type="file" style="position: absolute;opacity: 0;width: 50px;"
:on-success="handleSingleUpload" @change="onCustomUpload" multiple />
:on-error="handleSigLoadErr" 文件上传
:show-file-list="false" </div>
:action="apiURL+'/matter/upload'" :limit="1">
<span>上传</span> <div class="el-button el-button--default" style="position: relative;">
</el-upload> <input type="file" style="position: absolute;opacity: 0;width: 50px;"
<!-- <el-button>上传文件夹</el-button> --> @change="uploadFolder"
webkitdirectory
multiple
/>
文件夹上传
</div>
<el-button @click="createDir">新建目录</el-button> <el-button @click="createDir">新建目录</el-button>
<span v-if="tabSelected.length>1" style="margin:12px"> <span v-if="tabSelected.length>1" style="margin:12px">
<el-button @click="onShareMatter()">分享</el-button> <el-button @click="onShareMatter()">分享</el-button>
<el-button @click="onDelMatBatch">删除</el-button> <el-button @click="onDelMatBatch">删除</el-button>
</span> </span>
<el-button @click="onLoadMatterList()">刷新</el-button>
<el-button type="danger" plain @click="onShareMatter({uuid:currentNode.uuid,name:currentNode.name})">分享目录</el-button> <el-button type="danger" plain @click="onShareMatter({uuid:currentNode.uuid,name:currentNode.name})">分享目录</el-button>
<el-button type="danger" plain @click="onDelMatter({uuid:currentNode.uuid,name:currentNode.name,dir:true, <el-button type="danger" plain @click="onDelMatter({uuid:currentNode.uuid,name:currentNode.name,dir:true,
puuid:currentNode.puuid,path:currentNode.path})">删除目录</el-button> puuid:currentNode.puuid,path:currentNode.path})">删除目录</el-button>
</el-col> </el-col>
<el-col :span="8" class="search">
<el-input placeholder="搜索文件" v-model="searchname" @blur="searchname===''?onLoadMatterList():''"/>
<el-button :icon="Search" @click="onSearchFile(searchname)"></el-button>
</el-col>
</el-row> </el-row>
<el-row :gutter="24" style="height: 84%;overflow-y: auto;"> <el-row :gutter="24" style="height: 84%;overflow-y: auto;">
@ -599,7 +707,7 @@ const handleSelectionChange = (val:matterInfo[]) => {
<el-table-column width="250" align="center"> <el-table-column width="250" align="center">
<template #default="scope"> <template #default="scope">
<div v-show="currentHoverRow === scope.row.name"> <div v-show="currentHoverRow === scope.row.name">
<el-button size="small" :icon="Promotion" circle ></el-button> <!-- <el-button size="small" :icon="Promotion" circle ></el-button> -->
<el-button size="small" :icon="View" circle @click="onPrivateView(scope.row)"></el-button> <el-button size="small" :icon="View" circle @click="onPrivateView(scope.row)"></el-button>
<el-button size="small" :icon="Share" circle @click="onShareMatter(scope.row)"></el-button> <el-button size="small" :icon="Share" circle @click="onShareMatter(scope.row)"></el-button>
<el-button size="small" :icon="Download" circle @click="onDownload(scope.row)"></el-button> <el-button size="small" :icon="Download" circle @click="onDownload(scope.row)"></el-button>
@ -658,6 +766,7 @@ const handleSelectionChange = (val:matterInfo[]) => {
} }
.search{ .search{
margin-left: auto;
margin-right: 20px; margin-right: 20px;
display:inherit; display:inherit;
} }

54
src/views/doc/space.vue

@ -6,9 +6,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { getFileIcon, readableSize,fileType} from "./tools" import { getFileIcon, readableSize,fileType} from "./tools"
import sharePermission from './sharePermission.vue'; import sharePermission from './sharePermission.vue';
import { matterPage,matterInfo,matterTree} from "@/api/doc/type" import { matterPage,matterInfo,matterTree,doFileUpload} from "@/api/doc/type"
import { doAccessManage,getSpaceMatterList,doCreateSpaceDir,doDelSpaceMatter,doDelSpace, import { doAccessManage,getSpaceMatterList,doCreateSpaceDir,doDelSpaceMatter,doDelSpace,
doAiTraining ,doCreateAiagent,doFileUpload} from "@/api/doc/space" doAiTraining ,doCreateAiagent} from "@/api/doc/space"
import { h } from 'vue' import { h } from 'vue'
import { import {
Delete, Delete,
@ -34,10 +34,6 @@ let isNewNode=true //用来减少父组件树的重置
const dynamicVNode = ref<VNode | null>(null) //permission const dynamicVNode = ref<VNode | null>(null) //permission
const paginInfo = ref({ page: 0, total: 0 }) const paginInfo = ref({ page: 0, total: 0 })
//
const filesUpload=ref() //
const uploadDirRef = ref() //
//-----------AI--------------------- //-----------AI---------------------
//const agent=ref<{model:boolean,name:string}>({}) //const agent=ref<{model:boolean,name:string}>({})
const currentAgent=ref<{model:boolean,name:string,uuid:string}>({}) const currentAgent=ref<{model:boolean,name:string,uuid:string}>({})
@ -70,7 +66,6 @@ const uploadFormData = computed(() => {
puuid: currentNode.value.uuid, // uuidroot puuid: currentNode.value.uuid, // uuidroot
} }
}); });
const fileList=ref([])//upload files
//--------------&------------- //--------------&-------------
function onAccessManage(){ function onAccessManage(){
@ -256,7 +251,6 @@ function handleMouseEnter(row:any){
// //
function handleSingleUpload(response:any){ function handleSingleUpload(response:any){
handleAiUpload(response.data) handleAiUpload(response.data)
fileList.value=[]
onLoadMatterList() onLoadMatterList()
} }
interface uploadError{ interface uploadError{
@ -267,27 +261,33 @@ function handleSigLoadErr(error: Error, uploadFile: UploadFile, uploadFiles:Uplo
ElMessage.error(JSON.parse(error.message).msg) ElMessage.error(JSON.parse(error.message).msg)
} }
// //, 使
async function onCustomUpload(file:UploadFile){ async function onCustomUpload(e:Event){
const files = e.target!.files??[]
for(let ff of files){
await handleSingleFile(ff)
}
onLoadMatterList() //
}
async function handleSingleFile(ff:File){
const fields=new FormData() const fields=new FormData()
fields.append("space",uploadFormData.value.space) fields.append("space",uploadFormData.value.space)
fields.append("puuid",uploadFormData.value.puuid) fields.append("puuid",uploadFormData.value.puuid)
fields.append("file",file.raw) fields.append("file",ff)
const res = await doFileUpload(fields,'/hxpan/api/space/upload') const res = await doFileUpload(fields,'/hxpan/api/space/upload')
if(res.code!=200){ if(res.code!=200){
console.log(file.name+"上传失败! ") console.log(ff.name+"上传失败! ")
alert(file.name+"上传失败! ") alert(ff.name+"上传失败! ")
} }
//AI //AI
handleAiUpload(res.data) handleAiUpload(res.data)
//
fileList.value = []
filesUpload.value.clearFiles()
} }
// //input
//
async function uploadFolder(e:Event){ async function uploadFolder(e:Event){
const files = e.target!.files??[] const files = e.target!.files??[]
for(let f of files){ for(let f of files){
@ -466,26 +466,20 @@ function isOwner(){
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="14"> <el-col :span="14">
<el-upload class="el-button el-button--default"
ref="filesUpload"
:file-list="fileList"
:auto-upload="false"
:data="uploadFormData"
:on-change="onCustomUpload"
:show-file-list="true"
multiple>
<span>上传文件</span>
</el-upload>
<div class="el-button el-button--default" style="position: relative;"> <div class="el-button el-button--default" style="position: relative;">
<input type="file" class="el-button el-button--default" style="position: absolute;opacity: 0;width: 55px;" <input type="file" style="position: absolute;opacity: 0;width: 50px;"
ref="uploadDirRef" @change="onCustomUpload" multiple />
文件上传
</div>
<div class="el-button el-button--default" style="position: relative;">
<input type="file" style="position: absolute;opacity: 0;width: 50px;"
@change="uploadFolder" @change="uploadFolder"
webkitdirectory webkitdirectory
multiple multiple
/> />
文件夹上传 文件夹上传
</div> </div>
<el-button @click="onLoadMatterList()">刷新</el-button>
<el-button @click="createDir">新建目录</el-button> <el-button @click="createDir">新建目录</el-button>
<el-button type="danger" plain @click="onDelMatter({uuid:currentNode.uuid,name:currentNode.name,dir:true, <el-button type="danger" plain @click="onDelMatter({uuid:currentNode.uuid,name:currentNode.name,dir:true,
puuid:currentNode.puuid,path:currentNode.path})">删除目录</el-button> puuid:currentNode.puuid,path:currentNode.path})">删除目录</el-button>

Loading…
Cancel
Save