You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
12 KiB
377 lines
12 KiB
|
4 months ago
|
<!--
|
||
|
|
@ 作者: han2015
|
||
|
|
@ 时间: 2025-05-12 15:39:13
|
||
|
|
@ 备注: 文档管理组件
|
||
|
|
-->
|
||
|
|
<script lang="ts" setup>
|
||
|
|
import { getFileIcon, readableSize,fileType} from "./tools"
|
||
|
|
import sharePermission from './sharePermission.vue';
|
||
|
|
import { matterPage,matterInfo} from "@/api/doc/type"
|
||
|
|
import { doAccessManage,getSpaceMatterList,doCreateSpaceDir,doDelSpaceMatter,doDelSpace} from "@/api/doc/space"
|
||
|
|
import { h, proxyRefs } from 'vue'
|
||
|
|
import {
|
||
|
|
Delete,
|
||
|
|
View,
|
||
|
|
Download,
|
||
|
|
Plus,
|
||
|
|
Edit,
|
||
|
|
Promotion,
|
||
|
|
Folder,
|
||
|
|
} from '@element-plus/icons-vue'
|
||
|
|
import {ElMessage,UploadFile,UploadFiles,ElPagination} from "element-plus";
|
||
|
|
import preview from './preview.vue';
|
||
|
|
import router from "@/router";
|
||
|
|
|
||
|
|
const matterList = ref<matterInfo[]>([])
|
||
|
|
const newdir=ref("") //创建新目录时的目录名
|
||
|
|
const currentHoverRow=ref("") //table 行的按钮控制
|
||
|
|
const breadcrumbList=ref<matterInfo[]>([{name:"根目录",uuid:"root", dir:true}]) //面包屑导航
|
||
|
|
|
||
|
|
const selectedValue = ref("sixhour") //分享弹窗的时间变量
|
||
|
|
|
||
|
|
const tabSelected=ref<matterInfo[]>([]) //table组件多选数据维护
|
||
|
|
const currentNode=ref<matterInfo>({}) //打开的路径层次
|
||
|
|
|
||
|
|
const dynamicVNode = ref<VNode | null>(null) //permission 组件的父组件
|
||
|
|
const paginInfo = ref({ page: 0, total: 0 })
|
||
|
|
|
||
|
|
const props = withDefaults(defineProps<{
|
||
|
|
uid:string, //当前用户的uuid,注意已经过base64编码
|
||
|
|
spaceid:string,
|
||
|
|
spacename:string,
|
||
|
|
officeHost:string,
|
||
|
|
siteHost:string,
|
||
|
|
owner:string,
|
||
|
|
apiURL:string,
|
||
|
|
roles:string
|
||
|
|
}>(),{})
|
||
|
|
|
||
|
|
watch(props,()=>{
|
||
|
|
onLoadMatterList()
|
||
|
|
})
|
||
|
|
|
||
|
|
const uploadFormData = computed(() => {
|
||
|
|
return {
|
||
|
|
space: props.spaceid,
|
||
|
|
puuid: currentNode.value.uuid, // 父目录的uuid,基目录为root
|
||
|
|
}
|
||
|
|
});
|
||
|
|
const fileList=ref([])//upload files
|
||
|
|
|
||
|
|
//--------------权限控制&添加空间成员-------------
|
||
|
|
function onAccessManage(){
|
||
|
|
dynamicVNode.value = h(sharePermission, {
|
||
|
|
uid: props.uid,
|
||
|
|
uuid: "",
|
||
|
|
spaceid:props.spaceid, //
|
||
|
|
confirmFunc: (_list: string[],_infos:string[]) => {
|
||
|
|
// 组织权限数据
|
||
|
|
//_len=_list.length
|
||
|
|
let permited = btoa(_list.join("|"))
|
||
|
|
doAccessManage(props.uid,{
|
||
|
|
"space":props.spaceid,
|
||
|
|
"roles":permited,
|
||
|
|
"owner":props.owner,
|
||
|
|
"len":_list.length
|
||
|
|
}).then(()=>{
|
||
|
|
|
||
|
|
})
|
||
|
|
},
|
||
|
|
closeFunc: () => {
|
||
|
|
dynamicVNode.value=null
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
//删除空间
|
||
|
|
function onDeleteSpace(){
|
||
|
|
ElMessageBox.confirm(`确认删除空间 ( ${props.spacename}) ? 空间内所有文件将不可恢复!取消则放弃删除操作。`, "警告", {
|
||
|
|
confirmButtonText: "确定删除",
|
||
|
|
cancelButtonText: "取消",
|
||
|
|
type: "warning",
|
||
|
|
}).then(()=>{
|
||
|
|
doDelSpace(props.uid,{
|
||
|
|
"space":props.spaceid,
|
||
|
|
}).then(()=>{
|
||
|
|
router.replace({ query: { t: Date.now() } })
|
||
|
|
})
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
//----------------------------------------
|
||
|
|
//删除
|
||
|
|
function onDelMatter(row:matterInfo){
|
||
|
|
if (row.uuid){
|
||
|
|
ElMessageBox.confirm(`确认删除( ${row.name}) ?删除后不可恢复!取消则放弃删除操作。`, "警告", {
|
||
|
|
confirmButtonText: "确定",
|
||
|
|
cancelButtonText: "取消",
|
||
|
|
type: "warning",
|
||
|
|
}).then(()=>{
|
||
|
|
doDelSpaceMatter(props.uid,{
|
||
|
|
"uuid":row.uuid,
|
||
|
|
"space":props.spaceid,
|
||
|
|
"roles":props.roles,
|
||
|
|
}).then(()=>{
|
||
|
|
currentNode.value.uuid = row.puuid ?? ""
|
||
|
|
currentNode.value.name = row.path ? row.path.replace(`/${row.name}`,'').match(/[^/]+$/g)?.pop() :"上级目录"
|
||
|
|
onLoadMatterList()
|
||
|
|
})
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function onDownload(row:matterInfo){
|
||
|
|
ElMessageBox.confirm("确认下载此数据项?", "提示", {
|
||
|
|
confirmButtonText: "确定",
|
||
|
|
cancelButtonText: "取消",
|
||
|
|
type: "warning",
|
||
|
|
}).then(()=>{
|
||
|
|
if (row.uuid){
|
||
|
|
let _url= props.apiURL+`/space/download/${row.uuid}/${row.name}?space=${props.spaceid}`
|
||
|
|
window.open(_url)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//加载目录文件列表
|
||
|
|
function onLoadMatterList(){
|
||
|
|
let _page: matterPage = {
|
||
|
|
page: paginInfo.value.page,
|
||
|
|
pageSize: 50,
|
||
|
|
orderCreateTime: "DESC",
|
||
|
|
orderDir: "DESC",
|
||
|
|
puuid:currentNode.value.uuid,
|
||
|
|
deleted:false,
|
||
|
|
space:props.spaceid,
|
||
|
|
roles:props.roles,
|
||
|
|
};
|
||
|
|
|
||
|
|
getSpaceMatterList(props.uid,_page).then((resp)=>{
|
||
|
|
//page+1 是由于分页的起始index是1,而后端api的分页index起始是0
|
||
|
|
paginInfo.value={total:resp.data.totalPages, page:resp.data.page}
|
||
|
|
matterList.value=resp.data.data
|
||
|
|
})
|
||
|
|
}
|
||
|
|
//----------for dir-----------
|
||
|
|
//该函数仅操作前端,为新文件夹命名
|
||
|
|
function createDir(){
|
||
|
|
matterList.value?.unshift({
|
||
|
|
name:"",
|
||
|
|
userUuid:props.spaceid,
|
||
|
|
puuid:"",
|
||
|
|
uuid:"",
|
||
|
|
dir:true,
|
||
|
|
size:0,
|
||
|
|
deleted:false,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
//该函数为实际创建文件夹的函数
|
||
|
|
function onCreateDir(){
|
||
|
|
doCreateSpaceDir(props.uid,{
|
||
|
|
puuid:currentNode.value.uuid,
|
||
|
|
name:newdir.value,
|
||
|
|
space:props.spaceid,
|
||
|
|
roles:props.roles,
|
||
|
|
}).then((resp)=> {
|
||
|
|
newdir.value=""
|
||
|
|
onLoadMatterList()
|
||
|
|
})
|
||
|
|
.catch((e)=>{
|
||
|
|
ElMessage.error(e.msg)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
//------------------------------------------
|
||
|
|
|
||
|
|
//打开一个目录
|
||
|
|
function handleDoubleClick(row:matterInfo,ind?:number){
|
||
|
|
if(row.dir){
|
||
|
|
//table的双击事件也在此方法处理
|
||
|
|
if(typeof(ind)==="number"){
|
||
|
|
//返回某一级
|
||
|
|
if(breadcrumbList.value.length>1){
|
||
|
|
breadcrumbList.value=breadcrumbList.value.slice(0,ind+1)
|
||
|
|
currentNode.value=breadcrumbList.value[breadcrumbList.value.length-1]
|
||
|
|
onLoadMatterList()
|
||
|
|
}
|
||
|
|
}else{
|
||
|
|
//进入下一级
|
||
|
|
currentNode.value=row
|
||
|
|
breadcrumbList.value.push(row)
|
||
|
|
onLoadMatterList()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function handleMouseEnter(row:any){
|
||
|
|
currentHoverRow.value=row.name
|
||
|
|
}
|
||
|
|
//上传成功
|
||
|
|
function handleSingleUpload(response:any){
|
||
|
|
fileList.value=[]
|
||
|
|
onLoadMatterList()
|
||
|
|
}
|
||
|
|
interface uploadError{
|
||
|
|
msg:string
|
||
|
|
}
|
||
|
|
//上传失败
|
||
|
|
function handleSigLoadErr(error: Error, uploadFile: UploadFile, uploadFiles:UploadFiles){
|
||
|
|
ElMessage.error(JSON.parse(error.message).msg)
|
||
|
|
}
|
||
|
|
//-------------------edit & preive file for space---------------------
|
||
|
|
//文件预览
|
||
|
|
function onPrivateView(row:matterInfo){
|
||
|
|
const _type=fileType(row.name!)
|
||
|
|
if(_type!==""){ //office file
|
||
|
|
const info =btoa(encodeURIComponent(`${row.name}`)) //预览模式不需要绝对路径,只核对一下文件名即可
|
||
|
|
const _url=`${props.siteHost}${props.apiURL}/space/download/${row.uuid}/${row.name}?space=${props.spaceid}`
|
||
|
|
//前半部分内容是为了校验信息,主要内容是fileurl
|
||
|
|
window.open(`/#/onlyoffice?name=${row.name}&dtype=${_type}&info=${info}&fileurl=`+encodeURIComponent(_url),"_blank")
|
||
|
|
}else{
|
||
|
|
alert("暂不支持该类型预览")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//onlyoffice在线编辑
|
||
|
|
async function onlyOfficeEdit(row:matterInfo){
|
||
|
|
const _type=fileType(row.name!)
|
||
|
|
if(_type===""){
|
||
|
|
alert("暂不支持该类型编辑")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ElMessageBox.confirm("线上资源有限,确定继续线上编辑吗", "提示", {
|
||
|
|
confirmButtonText: "确定",
|
||
|
|
cancelButtonText: "取消",
|
||
|
|
type: "warning",
|
||
|
|
}).then(()=>{
|
||
|
|
//office file
|
||
|
|
//base64 encode for MASK
|
||
|
|
const _verify = btoa(row.uuid.match(/(\w+-\w+)/)![0]+"true") //增加一个权限验证的标记
|
||
|
|
const info =btoa(encodeURIComponent(`${row.userUuid}/root${row.path}`)) //编辑模式必须要全路径
|
||
|
|
const _url=`${props.siteHost}${props.apiURL}/space/download/${row.uuid}/${row.name}?space=${props.spaceid}`
|
||
|
|
window.open(`/#/onlyoffice?name=${row.name}&dtype=${_type}&info=${info}&verify=${_verify}&fileurl=`+encodeURIComponent(_url),"_blank")
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
//------------------------------------------------------
|
||
|
|
|
||
|
|
//渲染完页面再执行
|
||
|
|
onMounted(() => {
|
||
|
|
currentNode.value.uuid="root"
|
||
|
|
onLoadMatterList()
|
||
|
|
});
|
||
|
|
|
||
|
|
const handleSelectionChange = (val:matterInfo[]) => {
|
||
|
|
tabSelected.value = val
|
||
|
|
}
|
||
|
|
|
||
|
|
//判断是不是空间的所有者
|
||
|
|
function isOwner(){
|
||
|
|
return props.uid===btoa(props.owner)
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div>
|
||
|
|
<el-row :gutter="24" style="margin: 12px 0px;">
|
||
|
|
<span class="el-breadcrumb" style="font-weight: bold;">[ {{ props.spacename }} ] : </span>
|
||
|
|
<el-breadcrumb separator="/">
|
||
|
|
<el-breadcrumb-item v-for="(item,index) in breadcrumbList"
|
||
|
|
:key="index" @click="index===breadcrumbList.length-1?'': handleDoubleClick(item,index)">
|
||
|
|
<span style="font-weight: bold;">{{ item.name }}</span>
|
||
|
|
</el-breadcrumb-item>
|
||
|
|
</el-breadcrumb>
|
||
|
|
</el-row>
|
||
|
|
|
||
|
|
<el-row :gutter="24">
|
||
|
|
<el-col :span="14">
|
||
|
|
<el-upload class="el-button el-button--default" :file-list="fileList"
|
||
|
|
:data="uploadFormData"
|
||
|
|
:on-success="handleSingleUpload"
|
||
|
|
:on-error="handleSigLoadErr"
|
||
|
|
:show-file-list="false"
|
||
|
|
:action="apiURL+'/space/upload'" :limit="1">
|
||
|
|
<span>上传文件</span>
|
||
|
|
</el-upload>
|
||
|
|
<el-button @click="createDir">新建目录</el-button>
|
||
|
|
<el-button type="danger" plain @click="onDelMatter({uuid:currentNode.uuid,name:currentNode.name,dir:true,
|
||
|
|
puuid:currentNode.puuid,path:currentNode.path})">删除目录</el-button>
|
||
|
|
</el-col>
|
||
|
|
<el-button-group class="control" v-if="isOwner()" style="margin-right: 10px;margin-left: auto;">
|
||
|
|
<el-button :icon="Plus" @click="onAccessManage">成员</el-button>
|
||
|
|
<el-button :icon="Delete" @click="onDeleteSpace">删除</el-button>
|
||
|
|
</el-button-group>
|
||
|
|
</el-row>
|
||
|
|
|
||
|
|
<el-row :gutter="24" style="height: 84%;overflow-y: auto;">
|
||
|
|
<el-table
|
||
|
|
stripe
|
||
|
|
:data="matterList"
|
||
|
|
:header-cell-style="{ background: '#f5f8fd' }"
|
||
|
|
style="width: 100%"
|
||
|
|
row-key="uuid"
|
||
|
|
:row-style ="() => ({ lineHeight: '36px' })"
|
||
|
|
@selection-change="handleSelectionChange"
|
||
|
|
@cell-dblclick="handleDoubleClick"
|
||
|
|
@cell-mouse-enter="handleMouseEnter">
|
||
|
|
<el-table-column type="selection" width="50" />
|
||
|
|
<el-table-column width="450" property="name" label="文件名">
|
||
|
|
<template #default="scope">
|
||
|
|
<input type="text" autofocus placeholder="文件夹名" style="border:groove;height:30px;" v-model="newdir" @change="onCreateDir" v-if="scope.row.name===''" />
|
||
|
|
<div v-if="scope.row.name" style="display: flex; align-items: center;">
|
||
|
|
<el-icon :size="26">
|
||
|
|
<component v-if="scope.row.dir" :is="Folder" />
|
||
|
|
<component v-else :is="getFileIcon(scope.row.name)" />
|
||
|
|
</el-icon>
|
||
|
|
<span style="margin-left: 10px">{{ scope.row.name }}</span>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column width="250" align="center">
|
||
|
|
<template #default="scope">
|
||
|
|
<div v-show="currentHoverRow === scope.row.name">
|
||
|
|
<el-button size="small" :icon="Promotion" circle ></el-button>
|
||
|
|
<el-button size="small" :icon="Edit" circle @click="onlyOfficeEdit(scope.row)"></el-button>
|
||
|
|
<el-button size="small" :icon="View" circle @click="onPrivateView(scope.row)"></el-button>
|
||
|
|
<el-button size="small" :icon="Download" circle @click="onDownload(scope.row)"></el-button>
|
||
|
|
<el-button size="small" :icon="Delete" circle @click="onDelMatter(scope.row)"></el-button>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column prop="size" width="100" :formatter="readableSize" label="大小" />
|
||
|
|
|
||
|
|
<el-table-column prop="updateTime" label="修改日期">
|
||
|
|
<template #default="scope">
|
||
|
|
<span v-if="scope.row.updateTime">{{ scope.row.updateTime.slice(0,16) }}</span>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
</el-table>
|
||
|
|
</el-row>
|
||
|
|
|
||
|
|
<el-row v-if="paginInfo.total>1" style="justify-content: center;">
|
||
|
|
<el-pagination size="small" background layout="prev, pager, next" :current-page="paginInfo.page+1" @current-change="(value:number)=>{paginInfo.page=value-1;onLoadMatterList();}" :page-count="paginInfo.total" class="mt-4"/>
|
||
|
|
</el-row>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div v-if="dynamicVNode">
|
||
|
|
<component :is="dynamicVNode" />
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
|
||
|
|
.shareDialog{
|
||
|
|
--el-messagebox-width:'800px';
|
||
|
|
padding:40px;
|
||
|
|
.el-text{
|
||
|
|
align-self: flex-start;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.dynamic-width-message-box-byme .el-message-box__message{
|
||
|
|
width: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
</style>
|