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.
1251 lines
39 KiB
1251 lines
39 KiB
<!--
|
|
@ 作者: han2015
|
|
@ 时间: 2025-05-12 15:39:13
|
|
@ 备注: 文档管理组件
|
|
-->
|
|
<script lang="ts" setup>
|
|
import { getExpirTime, getFileIcon, readableSize,fileType} from "./tools"
|
|
import sharePermission from './sharePermission.vue';
|
|
import { useUserStore } from "@/store/modules/user";
|
|
import { getMatterList,postCreateDir,postDelMatter,postCreateShare,postMatterRename,
|
|
postDelMatBatch,getMySpaces,doCreateSpace,getRecyclingList} from "@/api/doc/index"
|
|
import { matterPage,matterInfo,respCreateShare,matterTree, doFileUpload,matterPermit} from "@/api/doc/type"
|
|
import { h } from 'vue'
|
|
import router from "@/router";
|
|
import {
|
|
Delete,View,Download,Share,Search,Edit,
|
|
Avatar,Plus,Grid,List,
|
|
Setting,
|
|
Bell,
|
|
} from '@element-plus/icons-vue'
|
|
import {ElSelect,ElOption, ElText,ElInput,TableInstance,ElMessage,UploadFile,
|
|
UploadFiles,ElPagination,ElTree,TreeNode,ElDropdown,ElDropdownItem} from "element-plus";
|
|
import preview from './preview.vue';
|
|
import uploadlog from './uploadlog.vue';
|
|
import space from './space.vue';
|
|
import spacePermission from './spacePermission.vue';
|
|
import SvgIcon from "@/components/SvgIcon/index.vue";
|
|
import {doDelSpace,doAccessManage,doCreateAiagent} from "@/api/doc/space"
|
|
import Space from "./space.vue";
|
|
|
|
//TODO: add file icons done!
|
|
//TODO: click on table-item, 1)preview on file .....................
|
|
// 2) go into dir-item done!
|
|
// 3)search file done!
|
|
//TODO: rename matters done!
|
|
|
|
//TODO: get share list done!
|
|
//TODO: visit share link done!
|
|
|
|
//TODO: select on table items for batch opts 1)delete done!
|
|
// 2)share done!
|
|
//TODO: test on Pagination done!
|
|
|
|
const userStore = useUserStore();
|
|
const uid=btoa("p0"+userStore.userInfoCont.userId);
|
|
const rawUid="p0"+userStore.userInfoCont.userId
|
|
const siteHost=document.location.origin;
|
|
const apiURL=import.meta.env.VITE_APP_BASE_API+"/hxpan/api"
|
|
|
|
const matterList = ref<matterInfo[]>([])
|
|
const searchname=ref("")
|
|
const newdirName=ref("") //创建新目录时的目录名
|
|
const currentHoverRow=ref("") //table 行的按钮控制
|
|
const selectedValue = ref("sixhour") //分享弹窗的世间变量
|
|
const tabSelected=ref<matterInfo[]>([]) //table组件多选数据维护
|
|
//to support tree mode refactor
|
|
const treeData=ref<matterTree[]>([])//{name:'个人空间',uuid:'root',children:[]}
|
|
const treeRef = ref();
|
|
const currentNode=ref<matterTree>({}) //打开的路径层次
|
|
const officeHost=import.meta.env.VITE_OFFICE_HOST
|
|
const dynamicVNode = ref<VNode | null>(null) //permission 组件的父组件
|
|
|
|
|
|
const multipleTableRef = ref<TableInstance>()
|
|
const paginInfo = ref({ page: 0, total: 0 })
|
|
|
|
const PRIVATESPACE = ref(true) //是空间状态的控制 2种:私有云盘和共享空间
|
|
const SpaceID= ref<{name:string,uuid:string,userUuid:string,manager:boolean,permits:matterPermit}>({}) //当前space的id
|
|
const spaceEleRef = ref() //space组件的引用,它与spaceTreeRef没有父子关系,反而是为了处理spaceTree的操作而创建的该变量
|
|
const spaceTreeData=ref<matterTree[]>([])//{name:'个人空间',uuid:'root',children:[]}
|
|
const spaceTreeRef = ref(); //space的树树组件的引用
|
|
let spaceNodeUid="" //用来判断树组件的展开和关闭,如何只是展开和关闭的点击事件不在刷新,通currentNode的作用
|
|
|
|
const modListOrGrild=ref(true)
|
|
const modRecycling=ref(false)
|
|
//进度条
|
|
const percentage=ref(0)
|
|
const onprogress=ref(false)
|
|
const tabName = ref('logs')
|
|
|
|
const Departs = computed(() => {
|
|
return `${'p0'+userStore.userInfoCont.userId},${userStore.userInfoCont.company},${userStore.userInfoCont.department},${userStore.userInfoCont.organization}`
|
|
})
|
|
|
|
const uploadFormData = computed(() => {
|
|
return {
|
|
userUuid: uid, // 用户的uuid
|
|
puuid: currentNode.value.uuid, // 父目录的uuid,基目录为root
|
|
}
|
|
});
|
|
const fileList=ref([])//upload files
|
|
|
|
//--------------单文件分享-------------
|
|
function onShareMatter(row?:matterInfo){
|
|
const showSharePermission=ref(false)
|
|
let permited =""
|
|
let infos=""
|
|
let _len=0
|
|
ElMessageBox({
|
|
title: row?.name+' 请选择分享有效时间',
|
|
message: () => h('div',{style:{ width:'660px'}},[
|
|
h(ElSelect,
|
|
{
|
|
defaultFirstOption:true,
|
|
modelValue: selectedValue.value,
|
|
'onUpdate:modelValue': (value) => {
|
|
selectedValue.value = value
|
|
},
|
|
valueKey: "value",
|
|
fallbackPlacements:['bottom-start'],
|
|
style: { width:'360px' }
|
|
},() => [
|
|
h(ElOption, { label: '六小时',key: 'sixhour', value: 'sixhour' }),
|
|
h(ElOption, { label: '一 天', key: 'oneday', value: 'oneday' }),
|
|
h(ElOption, { label: '三 天', key: 'threeday', value: 'threeday' }),
|
|
h(ElOption, { label: '一 周', key: 'oneweek', value: 'oneweek' }),
|
|
h(ElOption, { label: '一 月', key: 'onemonth', value: 'onemonth' }),
|
|
h(ElOption, { label: '三 月', key: 'threemonth', value: 'threemonth' }),
|
|
h(ElOption, { label: '永 久', key: 'permanent', value: 'permanent' }),
|
|
]),
|
|
h(ElButton, {
|
|
style: "width:30px;margin:0 10px;",
|
|
icon: Avatar,
|
|
onClick: () => {
|
|
showSharePermission.value = true
|
|
}
|
|
}),
|
|
h(sharePermission, {
|
|
uid: uid,
|
|
uuid: row?.uuid ?? "",
|
|
modelValue: showSharePermission.value,
|
|
confirmFunc: (_list: string[],_infos:string[]) => {
|
|
// 组织权限数据
|
|
_len=_list.length
|
|
permited = btoa(_list.join("|"))
|
|
infos=_infos.join("|"),
|
|
showSharePermission.value = false
|
|
},
|
|
closeFunc: () => {
|
|
showSharePermission.value = false
|
|
}
|
|
})
|
|
]
|
|
),
|
|
showCancelButton: true
|
|
}).then(() => {
|
|
let param;
|
|
if (row){
|
|
param={matterUuids:row.uuid,expireInfinity:false,expireTime:"",
|
|
permitList:permited,len:_len,permitInfos:infos}
|
|
}else if (tabSelected.value.length>1){
|
|
param={matterUuids:tabSelected.value.map((item:matterInfo)=>item.uuid).join(","),expireInfinity:false,expireTime:"",
|
|
permitList:permited,len:_len,permitInfos:infos}
|
|
}
|
|
|
|
if(param){
|
|
if(selectedValue.value==='permanent'){
|
|
param.expireInfinity=true
|
|
}else{
|
|
param.expireTime=getExpirTime(selectedValue.value)
|
|
}
|
|
|
|
postCreateShare(uid,param).then((resp)=>{
|
|
showShareMessage(resp.data)
|
|
})
|
|
onLoadMatterList()
|
|
selectedValue.value="sixhour"
|
|
}
|
|
}).catch(() => {
|
|
selectedValue.value='sixhour'
|
|
return
|
|
})
|
|
}
|
|
|
|
function showShareMessage(row:respCreateShare){
|
|
let _shareURL=`${siteHost}/#/doc/share/?uuid=${row.uuid}&code=${row.code}`
|
|
ElMessageBox({
|
|
title: '分享详情',
|
|
customStyle: { '--el-messagebox-width':'800px',padding:'40px'},
|
|
message: () => h('div',{style:{display:'flex','flex-direction':'column','line-height':'34px', width:'600px'}},[
|
|
h(ElText,{style:{'align-self':'flex-start'}},()=>row.name),
|
|
h(ElText,{style:{'align-self':'flex-start'}},()=>"失效时间:"+row.expireTime),
|
|
h(ElText,{style:{'align-self':'flex-start'}},()=>"链接:"+_shareURL)
|
|
]),
|
|
confirmButtonText: '复制分享链接',
|
|
showCancelButton: true
|
|
}).then(()=>{
|
|
navigator.clipboard.writeText(_shareURL)
|
|
}).catch(() => {});
|
|
}
|
|
|
|
//----------------------------------------
|
|
|
|
function onDelNodeMatter(row:matterInfo){
|
|
onDelMatter(row,true)
|
|
}
|
|
//删除
|
|
function onDelMatter(row:matterInfo,dir?:boolean){
|
|
if (row.uuid){
|
|
ElMessageBox.confirm(`确认删除( ${row.name}) ?删除后不可恢复!取消则放弃删除操作。`, "警告", {
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
}).then(()=>{
|
|
postDelMatter(uid,{
|
|
"uuid":row.uuid
|
|
}).then(()=>{
|
|
if (row.dir || dir) {
|
|
const node = treeRef.value.getNode(row.uuid)
|
|
if (node) {
|
|
treeRef.value.remove(node)
|
|
}
|
|
currentNode.value.uuid = row.puuid ?? "root"
|
|
}
|
|
onLoadMatterList()
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
function onDelMatBatch(){
|
|
ElMessageBox.confirm("确认删除选择的内容?", "警告", {
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
}).then(()=>{
|
|
postDelMatBatch(uid,{
|
|
"uuids":tabSelected.value.map((item:matterInfo)=>item.uuid).join(",")
|
|
}).then(()=>{
|
|
router.replace({ query: { t: Date.now() } })
|
|
})
|
|
})
|
|
}
|
|
|
|
function onDownload(row:matterInfo){
|
|
ElMessageBox.confirm("确认下载此数据项?", "提示", {
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
}).then(()=>{
|
|
if (row.uuid){
|
|
let _url= apiURL+`/alien/download/${row.uuid}/${row.name}`
|
|
window.open(_url)
|
|
}
|
|
})
|
|
}
|
|
|
|
function getImagePreivewURL(_uuid:string,_name:string){
|
|
return `${apiURL}/alien/preview/${_uuid}/${_name}?ir=fill_100_100`
|
|
}
|
|
|
|
function getImageDownloadURL(_uuid:string,_name:string){
|
|
return `${apiURL}/alien/preview/${_uuid}/${_name}`
|
|
}
|
|
|
|
function onMatterRename(row:matterInfo){
|
|
const newname=ref(row.name)
|
|
ElMessageBox({
|
|
title:"请输入新的文件名",
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
message: () => h(ElInput, {
|
|
style: { width:'360px' },
|
|
modelValue: newname.value,
|
|
'onUpdate:modelValue': (val) => {
|
|
newname.value = val
|
|
},
|
|
}),
|
|
}).then(() => {
|
|
if(newname.value&&newname.value!=""){
|
|
postMatterRename(uid,{
|
|
uuid:row.uuid,
|
|
name:newname.value,
|
|
}).then(()=>onLoadMatterList())
|
|
}
|
|
})
|
|
}
|
|
|
|
//加载文件列表
|
|
function onSearchFile(name?:string){
|
|
let _page: matterPage = {
|
|
page: 0,
|
|
pageSize: 50,
|
|
orderCreateTime: "DESC",
|
|
orderDir: "DESC",
|
|
name:name
|
|
};
|
|
|
|
getMatterList(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.filter((item)=>{
|
|
return !item.dir
|
|
})
|
|
})
|
|
}
|
|
|
|
//获取回收站文件
|
|
function showRecycling(){
|
|
tabName.value="main"
|
|
modRecycling.value=true
|
|
currentNode.value={} //清空当前节点
|
|
if(!PRIVATESPACE.value) { //共享空间模式要切换到个人空间
|
|
PRIVATESPACE.value=true
|
|
}
|
|
|
|
getRecyclingList(uid,{}).then((resp)=>{
|
|
paginInfo.value={total:1,page:0}
|
|
matterList.value=resp.data
|
|
})
|
|
}
|
|
|
|
//获取上传日志
|
|
function showlogs(){
|
|
modRecycling.value=true
|
|
currentNode.value={} //清空当前节点
|
|
if(!PRIVATESPACE.value) { //共享空间模式要切换到个人空间
|
|
PRIVATESPACE.value=true
|
|
}
|
|
|
|
getRecyclingList(uid,{}).then((resp)=>{
|
|
paginInfo.value={total:1,page:0}
|
|
matterList.value=resp.data
|
|
})
|
|
}
|
|
|
|
//恢复删除文件
|
|
function restoreMatter(row:matterInfo){
|
|
if (row.uuid){
|
|
ElMessageBox.confirm(`确认要恢复( ${row.name}) ?`, "警告", {
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
}).then(()=>{
|
|
postDelMatter(uid,{
|
|
"uuid":row.uuid,
|
|
"restore":"true",
|
|
}).then(()=>{
|
|
ElMessage({
|
|
message: '已成功恢复文件,请在个人空间查看。',
|
|
type: 'success',
|
|
plain: true,
|
|
})
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
//加载目录文件列表
|
|
function onLoadMatterList(){
|
|
//重置进度条
|
|
percentage.value=0
|
|
onprogress.value=false
|
|
|
|
let _page: matterPage = {
|
|
page: paginInfo.value.page,
|
|
pageSize: 50,
|
|
orderCreateTime: "DESC",
|
|
orderDir: "DESC",
|
|
puuid:currentNode.value.uuid,
|
|
deleted:false
|
|
};
|
|
|
|
getMatterList(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
|
|
//2025-08-07: 保持space一样,文件夹一并显示
|
|
// .filter((item)=>{
|
|
// return !item.dir
|
|
// })
|
|
})
|
|
}
|
|
//----------for dir-----------
|
|
function createDir(){
|
|
matterList.value?.unshift({
|
|
name:"",
|
|
userUuid:uid,
|
|
puuid:"",
|
|
uuid:"",
|
|
dir:true,
|
|
size:0,
|
|
deleted:false,
|
|
})
|
|
}
|
|
|
|
function onCreateDir(){
|
|
postCreateDir(uid,{
|
|
userUuid:uid,
|
|
puuid:currentNode.value.uuid,
|
|
name:newdirName.value,
|
|
}).then((resp)=> {
|
|
newdirName.value=""
|
|
//调用tree的append方法
|
|
treeRef.value.append(
|
|
{uuid:resp.data.uuid,dir:false,name:resp.data.name,puuid:resp.data.puuid},
|
|
currentNode.value.uuid
|
|
)
|
|
matterList.value?.shift() //删除头部的空位
|
|
matterList.value?.push(resp.data) //把新建的添加到最后
|
|
})
|
|
.catch((e)=>{
|
|
ElMessage.error(e.msg)
|
|
})
|
|
}
|
|
|
|
function onDBclickMatter(row:matterInfo){
|
|
if(row.dir){
|
|
onNodeClick(row as matterTree,null,null,null)
|
|
}else{
|
|
onPrivateView(row)
|
|
}
|
|
}
|
|
|
|
//树节点展开
|
|
function onNodeExpand(node: TreeNode, resolve: (data: matterTree[]) => void, reject: () => void) {
|
|
modRecycling.value=false
|
|
let cuuid ="root"
|
|
if ((node.data as matterTree).uuid) {
|
|
cuuid= (node.data as matterTree).uuid
|
|
currentNode.value = node.data
|
|
}
|
|
|
|
let _page: matterPage = {
|
|
page: 0,
|
|
pageSize: 50,
|
|
orderCreateTime: "DESC",
|
|
orderDir: "DESC",
|
|
puuid: cuuid,
|
|
deleted: false
|
|
};
|
|
|
|
getMatterList(uid, _page).then((resp) => {
|
|
paginInfo.value = { total: resp.data.totalPages, page: resp.data.page }
|
|
matterList.value = resp.data.data
|
|
// .filter((item)=>{
|
|
// return !item.dir
|
|
// })
|
|
let node_data = resp.data.data.filter((item) => {
|
|
return item.dir
|
|
}).map((val) => {
|
|
const copy = structuredClone(val)
|
|
copy.dir = !copy.dir
|
|
return copy
|
|
})
|
|
|
|
resolve(node_data)
|
|
}).catch(() => reject())
|
|
|
|
}
|
|
//树节点单击
|
|
function onNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
|
|
tabName.value="main"
|
|
modRecycling.value=false
|
|
if(!PRIVATESPACE.value) {
|
|
PRIVATESPACE.value=true
|
|
}
|
|
if (data){
|
|
if (currentNode.value.uuid === data.uuid) return;
|
|
//const cuuid = data.uuid
|
|
currentNode.value = data
|
|
}else{
|
|
currentNode.value={uuid:"root"}
|
|
}
|
|
|
|
onLoadMatterList()
|
|
return
|
|
let _page: matterPage = {
|
|
page: 0,
|
|
pageSize: 50,
|
|
orderCreateTime: "DESC",
|
|
orderDir: "DESC",
|
|
puuid: cuuid,
|
|
deleted: false
|
|
};
|
|
|
|
getMatterList(uid, _page).then((resp) => {
|
|
paginInfo.value = { total: resp.data.totalPages, page: resp.data.page }
|
|
matterList.value = resp.data.data.filter((item)=>{
|
|
return !item.dir
|
|
})
|
|
|
|
//展开的时候暂时不做目录更新,看以后的使用情况
|
|
let node_data = resp.data.data.filter((item) => {
|
|
return item.dir
|
|
}).map((item) => {
|
|
item.dir = !item.dir
|
|
return item
|
|
})
|
|
|
|
if (node) {
|
|
if(data.children && data.children.length!==node_data.length){
|
|
treeRef.value.updateKeyChildren(data.uuid,node_data)
|
|
}
|
|
} else if(data.children.length!==node_data.length){
|
|
data.children = node_data
|
|
}
|
|
})
|
|
}
|
|
|
|
function updateListOrGrid(val:boolean){
|
|
modListOrGrild.value=val
|
|
if(val){
|
|
localStorage.setItem("listOrGrid","true")
|
|
}else{
|
|
localStorage.setItem("listOrGrid","false")
|
|
}
|
|
}
|
|
|
|
//文件预览
|
|
function onPrivateView(row:matterInfo){
|
|
if(row.dir){
|
|
alert("目录不支持预览")
|
|
return
|
|
}
|
|
|
|
let _token=document.cookie.match(/hxpan=([\w-]*)/)
|
|
if (_token&&_token.length>1){
|
|
_token=_token[1]
|
|
}
|
|
|
|
const _type=fileType(row.name!)
|
|
if(_type!==""){ //office file
|
|
const info =btoa(encodeURIComponent(`${row.name}`)) //预览模式不需要绝对路径,只核对一下文件名即可
|
|
const _url=`${siteHost}${apiURL}/alien/download/${row.uuid}/${row.name}?access_token=${_token}`
|
|
//前半部分内容是为了校验信息,主要内容是fileurl
|
|
window.open(`/#/onlyoffice?name=${row.name}&dtype=${_type}&info=${info}&fileurl=`+window.btoa(encodeURIComponent(_url)),"_blank")
|
|
return
|
|
}
|
|
|
|
const a = `${row.uuid}${row.name}`
|
|
const _url=`${siteHost}${apiURL}/alien/download/${row.uuid}/${row.name}?access_token=${_token}&fullfilename=${a}`
|
|
|
|
dynamicVNode.value=h(preview,{
|
|
url:`${officeHost}/kkpreview/onlinePreview?url=`+window.btoa(unescape(encodeURIComponent(_url))),
|
|
closeFunc:()=>dynamicVNode.value=null
|
|
})
|
|
}
|
|
|
|
//onlyoffice在线编辑
|
|
async function onlyOfficeEdit(row:matterInfo){
|
|
const _type=fileType(row.name!)
|
|
if(_type===""){
|
|
alert("暂不支持该类型编辑")
|
|
return
|
|
}
|
|
|
|
let _token=document.cookie.match(/hxpan=([\w-]*)/)
|
|
if (_token&&_token.length>1){
|
|
_token=_token[1]
|
|
}
|
|
|
|
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=`${siteHost}${apiURL}/alien/download/${row.uuid}/${row.name}?access_token=${_token}`
|
|
window.open(`/#/onlyoffice?name=${row.name}&dtype=${_type}&info=${info}&verify=${_verify}&fileurl=`+window.btoa(encodeURIComponent(_url)),"_blank")
|
|
})
|
|
}
|
|
|
|
//--------------UPGRADE: multy file upload section----------------
|
|
|
|
//自定义上传,目的是支持多文件上传, 使用html原生组件,是为了解决并发问题
|
|
async function onCustomUpload(e:Event){
|
|
const files = e.target!.files??[]
|
|
onprogress.value=true
|
|
const count=files.length
|
|
let result=""
|
|
for(let index = 0; index < count; index++){
|
|
onprogress.value=true
|
|
const ff = files[index]
|
|
await handleSingleFile(ff).catch((err)=>{
|
|
console.log(err)
|
|
result+=err
|
|
})
|
|
percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100
|
|
}
|
|
if(result!=""){
|
|
dynamicVNode.value=h(uploadlog,{
|
|
content:result
|
|
})
|
|
}
|
|
|
|
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+"上传失败! ")
|
|
throw new Error(ff.name+"上传失败!<br> ")
|
|
}
|
|
}
|
|
|
|
//文件夹上传,原理:自定义的input组件,一次拿到所有需要上传的文件列表,然后依次上传
|
|
//能减少并发问题
|
|
async function uploadFolder(e:Event){
|
|
onprogress.value=true
|
|
const files = e.target!.files??[]
|
|
const count=files.length
|
|
let result=""
|
|
for(let index = 0; index < count; index++){
|
|
onprogress.value=true
|
|
const f = files[index]
|
|
await handleFolderFile(f).catch((err)=>{
|
|
console.log(err)
|
|
result+= (f as File).name+"上传失败<br>"
|
|
})
|
|
percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100
|
|
}
|
|
if(result!=""){
|
|
dynamicVNode.value=h(uploadlog,{
|
|
content:result
|
|
})
|
|
}
|
|
|
|
onLoadMatterList() //刷新
|
|
}
|
|
|
|
async function handleFolderFile(option:File){
|
|
//根据路径,来判断需不需要重建目录
|
|
const _path = option.webkitRelativePath
|
|
const _dir=_path.replace(/\/[^/]+\w+$/,"") //只保留文件夹目录,[^/]就是用来限制,只能是最后一个目录
|
|
const node = matterList.value.filter((item)=>{
|
|
return item.dir && _dir.endsWith(item.name!)
|
|
})
|
|
|
|
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 && _dir.endsWith(item.name!)
|
|
})
|
|
|
|
if(newnodes.length>0){
|
|
puuid=newnodes[0].uuid
|
|
}else{
|
|
console.log(_path+"上传失败! ")
|
|
throw new Error(_path +" 上传失败!<br> ")
|
|
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+"上传失败! ")
|
|
throw new Error(_path +" 上传失败!<br> ")
|
|
}
|
|
}
|
|
|
|
//创建多级目录,并返回matterinfos数组
|
|
async function doCreateMultyDir(path:string,uuid:string){
|
|
const list=[];
|
|
const dirs=path.split("/")
|
|
for(let i=0;i<dirs.length;i++){
|
|
const node = matterList.value.filter((item)=>{
|
|
return item.dir && dirs[i].endsWith(item.name!)
|
|
})
|
|
if(node.length>0) {
|
|
uuid = node[0].uuid
|
|
continue;
|
|
}
|
|
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){
|
|
currentHoverRow.value=row.uuid
|
|
}
|
|
//上传成功
|
|
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)
|
|
}
|
|
//-----------------------------------------------------------
|
|
|
|
|
|
//-------------------space feature---------------------
|
|
function onNewSpace(){
|
|
tabName.value="main"
|
|
const newname=ref("")
|
|
ElMessageBox({
|
|
title:"请输入空间名称",
|
|
customStyle: { bottom:'200px'},
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
message: () => h(ElInput, {
|
|
style: { width:'360px' },
|
|
modelValue: newname.value,
|
|
'onUpdate:modelValue': (val) => {
|
|
newname.value = val
|
|
},
|
|
}),
|
|
}).then(() => {
|
|
if(newname.value!==""){
|
|
doCreateSpace(uid,newname.value).then((resp)=>{
|
|
//spaceTreeRef.value.append({name:resp.data.name,uuid:resp.data.uuid,dir:false,userUuid:resp.data.userUuid})
|
|
router.replace({ query: { t: Date.now() } }) //直接刷新
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
function onSpaceNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
|
|
tabName.value="main"
|
|
if(PRIVATESPACE.value) { //如果打开了个人空间,突然点击共享空间,要及时切换状态
|
|
PRIVATESPACE.value=false
|
|
modRecycling.value=false
|
|
}
|
|
//如果在单个组件上重复点击,不在刷新请求
|
|
if(spaceNodeUid==data.uuid) return;
|
|
spaceNodeUid=data.uuid
|
|
|
|
if(data.uuid.startsWith("s0") && data.uuid!=SpaceID.value.uuid){ //切换空间
|
|
SpaceID.value={
|
|
name: data.name ?? "",
|
|
uuid: data.uuid ?? "",
|
|
userUuid: data.userUuid ?? "",
|
|
manager: data.manager ?? false,
|
|
permits:data.permits ?? { id: 0, MatterUuid: '', data: '{}' }
|
|
};
|
|
PRIVATESPACE.value=false;
|
|
}else{
|
|
let matter= {
|
|
uuid:data.uuid==SpaceID.value.uuid?"root":data.uuid,
|
|
puuid: data.puuid,
|
|
name:data.name,
|
|
agent:data.agent,
|
|
dir:true,
|
|
permits:data.permits,
|
|
path:data.path,
|
|
permitVal:data.permitVal
|
|
}
|
|
//打开具体的节点
|
|
spaceEleRef.value.handleDoubleClick(matter)
|
|
}
|
|
}
|
|
|
|
function flushSpaceTree(uuid:string,data:matterTree[]){
|
|
if(uuid==="root") uuid=SpaceID.value.uuid
|
|
spaceTreeRef.value.updateKeyChildren(uuid,data)
|
|
}
|
|
|
|
//空间成员管理
|
|
function onAccessManage(){
|
|
dynamicVNode.value = h(sharePermission, {
|
|
uid: uid,
|
|
uuid: "",
|
|
spaceid:SpaceID.value.uuid, //
|
|
confirmFunc: (_list: string[],_infos:string[]) => {
|
|
// 组织权限数据
|
|
//_len=_list.length
|
|
let permited = btoa(_list.join("|"))
|
|
doAccessManage(uid,{
|
|
"space":SpaceID.value.uuid,
|
|
"roles":permited,
|
|
"owner":SpaceID.value.userUuid,
|
|
"len":_list.length
|
|
}).then(()=>{
|
|
|
|
})
|
|
},
|
|
closeFunc: () => {
|
|
dynamicVNode.value=null
|
|
}
|
|
})
|
|
}
|
|
|
|
//删除空间
|
|
function onDeleteSpace(row:matterInfo){
|
|
ElMessageBox.confirm(`确认删除空间 ( ${row.name}) ? 空间内所有文件将不可恢复!取消则放弃删除操作。`, "警告", {
|
|
confirmButtonText: "确定删除",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
}).then(()=>{
|
|
doDelSpace(uid,{
|
|
"space":row.uuid,
|
|
}).then(()=>{
|
|
router.replace({ query: { t: Date.now() } })
|
|
})
|
|
})
|
|
}
|
|
|
|
//删除空间matter
|
|
function onDelSpaceMatter(row:matterInfo){
|
|
if(row.puuid){
|
|
spaceEleRef.value.onDelMatter(row)
|
|
}else{
|
|
onDeleteSpace(row)
|
|
}
|
|
}
|
|
//空间权限控制管理
|
|
function onSpacePManage(row:matterInfo){
|
|
dynamicVNode.value=h(spacePermission,{
|
|
uid:rawUid, //当前用户的uuid
|
|
uuid:row.uuid, //文档的uuid
|
|
suid:SpaceID.value.userUuid,
|
|
spaceid:SpaceID.value.uuid, //空间uuid
|
|
closeFunc:()=>dynamicVNode.value=null
|
|
})
|
|
}
|
|
|
|
//处理空间智能体创建
|
|
function onAiAgent(row:matterInfo){
|
|
if(row.agent){
|
|
alert("当前目录已经是智能体目录")
|
|
return
|
|
}
|
|
|
|
ElMessageBox.confirm(`确认创建智能体( ${row.name}) ?`, "警告", {
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
}).then(()=>{
|
|
doCreateAiagent(uid,{
|
|
space:SpaceID.value.uuid,
|
|
matter:row.uuid
|
|
}).then(()=>{
|
|
router.replace({ query: { t: Date.now() } })
|
|
})
|
|
})
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
//http://172.20.2.87:6010/api/alien/preview/5a10aaf6-396e-4d9a-7e87-3c5c8029d4db/123.png?ir=fill_100_100
|
|
//渲染完页面再执行
|
|
onMounted(() => {
|
|
// treeRef.value.append(
|
|
// {name:'个人空间',uuid:'root',dir:false},
|
|
// currentNode.value.uuid
|
|
// )
|
|
|
|
currentNode.value={uuid:"root"}
|
|
//加载我的空间列表
|
|
getMySpaces(uid,{roles:Departs.value}).then((resp)=>{
|
|
resp.data.forEach((item)=>{
|
|
let ismanager=false
|
|
if(item.userUuid==rawUid || item.managers.includes(rawUid)) ismanager=true;
|
|
spaceTreeRef.value.append({name:item.name,uuid:item.uuid,dir:false,userUuid:item.userUuid,manager:ismanager,permits:item.permits})
|
|
})
|
|
})
|
|
let val =localStorage.getItem("listOrGrid")
|
|
if(val&&val=="false"){
|
|
modListOrGrild.value=false
|
|
}else{
|
|
modListOrGrild.value=true
|
|
}
|
|
});
|
|
|
|
const handleSelectionChange = (val:matterInfo[]) => {
|
|
tabSelected.value = val
|
|
}
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<el-dialog v-model="onprogress" width="50%" title="上传进度">
|
|
请勿关闭页面。。。
|
|
<el-progress style="width: 90%;" :text-inside="true" :stroke-width="26" :percentage="percentage" />
|
|
</el-dialog>
|
|
<div style="display:grid;grid-template-columns:1fr 4fr; width: 100%;height: 100%;">
|
|
<div class="menus_tree">
|
|
<div class="area_header">
|
|
<el-icon :size="20"><House /></el-icon><span class="area_name" > 个人空间</span>
|
|
</div>
|
|
<el-tree
|
|
ref="treeRef"
|
|
style="max-width: 600px"
|
|
:data="treeData"
|
|
node-key="uuid"
|
|
highlight-current
|
|
lazy
|
|
:props="{label: 'name',children:'children',isLeaf:'dir'}"
|
|
:load="onNodeExpand"
|
|
:default-expanded-keys="['root']"
|
|
@node-click="onNodeClick"
|
|
>
|
|
<template #default="{ node, data }">
|
|
<div class="tree-item">
|
|
<span>{{ node.label }}</span>
|
|
<el-dropdown>
|
|
<el-button size="small" :icon="Setting" circle ></el-button>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); onDelNodeMatter(data)}">删除</el-dropdown-item>
|
|
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); onMatterRename(data)}">重命名</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
</div>
|
|
</template>
|
|
</el-tree>
|
|
<div class="area_header">
|
|
<el-icon :size="20"><Collection /></el-icon> <span class="area_name" > 共享空间</span>
|
|
<el-button size="small" style="margin: 0 5px 0 auto;" :icon="Plus" @click="onNewSpace"> </el-button>
|
|
</div>
|
|
<el-tree
|
|
ref="spaceTreeRef"
|
|
style="max-width: 600px"
|
|
node-key="uuid"
|
|
accordion
|
|
highlight-current
|
|
lazy
|
|
:props="{label: 'name',children:'children',isLeaf:'dir'}"
|
|
@node-click="onSpaceNodeClick"
|
|
>
|
|
<template #default="{ node, data }">
|
|
<div class="tree-item">
|
|
<span>{{ node.label}}</span>
|
|
<el-dropdown v-if="data.manager">
|
|
<el-button size="small" :icon="Setting" circle ></el-button>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item v-if="!data.puuid" @click="(e)=>{ e.stopPropagation(); onAccessManage()}">成员管理</el-dropdown-item>
|
|
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); onSpacePManage(data)}">权限管理</el-dropdown-item>
|
|
<el-dropdown-item v-if="data.puuid" @click="(e)=>{ e.stopPropagation(); onAiAgent(data)}">创建智能体</el-dropdown-item>
|
|
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); onDelSpaceMatter(data)}">删除</el-dropdown-item>
|
|
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); spaceEleRef.onSpaceMatterRename(data)}">重命名</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
</div>
|
|
</template>
|
|
</el-tree>
|
|
<div class="area_header" @click="tabName='logs'">
|
|
<el-icon :size="20"><Bell /></el-icon><span class="area_name" > 上传日志</span>
|
|
</div>
|
|
<div class="area_header" @click="showRecycling">
|
|
<el-icon :size="20"><Delete /></el-icon><span class="area_name" > 回收站</span>
|
|
</div>
|
|
</div>
|
|
|
|
<el-tabs v-model="tabName">
|
|
<el-tab-pane name="main">
|
|
<div v-if="PRIVATESPACE" class="app_container">
|
|
<el-row v-if="modRecycling"><span style="margin: 12px 0px;font-weight: bold;font-size: 20px;"> 回收站 </span> </el-row>
|
|
<el-row v-else :gutter="24" style="margin: 12px 0px;">
|
|
<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;align-content: center;">根目录</span>/
|
|
</el-link>
|
|
<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 v-if="!modRecycling" :gutter="24">
|
|
<el-col :span="14">
|
|
<div class="el-button el-button--default" style="position: relative;">
|
|
<input type="file" style="position: absolute;opacity: 0;width: 50px;"
|
|
@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"
|
|
webkitdirectory
|
|
multiple
|
|
/>
|
|
文件夹上传
|
|
</div>
|
|
<el-button @click="createDir">新建目录</el-button>
|
|
<span v-if="tabSelected.length>1" style="margin:12px">
|
|
<el-button @click="onShareMatter()">分享</el-button>
|
|
<el-button @click="onDelMatBatch">删除</el-button>
|
|
</span>
|
|
</el-col>
|
|
<el-button-group style="margin:0 50px 0 auto;">
|
|
<el-button :icon="List" @click="updateListOrGrid(true)"></el-button>
|
|
<el-button :icon="Grid" @click="updateListOrGrid(false)"></el-button>
|
|
</el-button-group>
|
|
</el-row>
|
|
|
|
<el-row :gutter="24" style="overflow-y: auto; height: 90%;">
|
|
<el-table v-if="modListOrGrild"
|
|
stripe
|
|
:data="matterList"
|
|
ref="multipleTableRef"
|
|
:header-cell-style="{ background: '#f5f8fd' }"
|
|
style="width: 100%"
|
|
row-key="uuid"
|
|
:row-style ="() => ({ lineHeight: '36px' })"
|
|
@selection-change="handleSelectionChange"
|
|
@cell-mouse-enter="handleMouseEnter">
|
|
<!-- <el-table-column type="selection" width="50" /> -->
|
|
<el-table-column width="450" property="name" label="文件名">
|
|
<template #default="scope">
|
|
<input v-if="scope.row.name===''" v-model="newdirName" type="text" autofocus placeholder="文件夹名" style="border:groove;height:30px;" @change="onCreateDir" />
|
|
<div v-else style="display: flex; align-items: center;" @dblclick="onDBclickMatter(scope.row)">
|
|
<svg-icon v-if="scope.row.dir" icon-class="folder-icon" size="30px"/>
|
|
<el-image v-else-if="getFileIcon(scope.row.name)==='img'" style="width: 30px;" :preview-src-list="[getImageDownloadURL(scope.row.uuid,scope.row.name)]" :src="getImagePreivewURL(scope.row.uuid,scope.row.name)" />
|
|
<svg-icon v-else :icon-class="getFileIcon(scope.row.name)+'-icon'" size="30px" />
|
|
|
|
<span style="margin-left: 10px">{{ scope.row.name }}</span>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column width="360" align="center">
|
|
<template #default="scope">
|
|
<div v-show="currentHoverRow === scope.row.uuid">
|
|
<span v-if="scope.row.deleted">
|
|
<el-button type="text" @click="restoreMatter(scope.row)">恢复</el-button>
|
|
</span>
|
|
<span v-else>
|
|
<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="Download" circle @click="onDownload(scope.row)"></el-button>
|
|
<el-button size="small" :icon="Edit" circle @click="onlyOfficeEdit(scope.row)"></el-button>
|
|
<el-button size="small" :icon="Delete" circle @click="onDelMatter(scope.row)"></el-button>
|
|
<el-button size="small" circle @click="onMatterRename(scope.row)">改</el-button>
|
|
</span>
|
|
</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>
|
|
<div class="table-grid" v-else>
|
|
<div class="grid-item" v-for="row in matterList">
|
|
<div class="grid">
|
|
<div v-if="row.name===''">
|
|
<svg-icon icon-class="folder-icon" size="80px"/>
|
|
<input v-model="newdirName" type="text" autofocus placeholder="文件夹名" style="border:groove;height:30px;" @change="onCreateDir" />
|
|
</div>
|
|
|
|
<div v-else class="grid-box" @dblclick="onDBclickMatter(row)" @contextmenu.prevent="handleMouseEnter(row)" >
|
|
<svg-icon v-if="row.dir" icon-class="folder-icon" size="80px"/>
|
|
<el-image v-else-if="getFileIcon(row.name)==='img'" style="width: 80px;" :preview-src-list="[getImageDownloadURL(row.uuid,row.name)]" :src="getImagePreivewURL(row.uuid,row.name)" />
|
|
<svg-icon v-else :icon-class="getFileIcon(row.name)+'-icon'" size="80px"/>
|
|
<span style="margin: 5px 0;text-wrap-mode:nowrap;">{{ row.name }}</span>
|
|
</div>
|
|
</div>
|
|
<ul v-if="row.name!=''" class="grid-menus" v-show="currentHoverRow === row.uuid" @mouseleave="currentHoverRow=''">
|
|
<span v-if="row.deleted">
|
|
<li @click="restoreMatter(row)">恢复</li>
|
|
</span>
|
|
<span v-else>
|
|
<li v-if="getFileIcon(row.name)!='img'" @click="onPrivateView(row)">预览</li>
|
|
<li @click="onShareMatter(row)">分享</li>
|
|
<li @click="onDownload(row)">下载</li>
|
|
<li @click="onlyOfficeEdit(row)">编辑</li>
|
|
<li @click="onDelMatter(row)">删除</li>
|
|
<li @click="onMatterRename(row)">重命名</li>
|
|
</span>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</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-else class="app_container">
|
|
<space ref="spaceEleRef" :uid="uid" :raw-uid="rawUid" :listOrGrid="modListOrGrild"
|
|
:spaceid="SpaceID.uuid" :roles="Departs" :spacename="SpaceID.name" :space-permit="SpaceID.permits"
|
|
:owner="SpaceID.userUuid" :ismanager="SpaceID.manager"
|
|
:officeHost="officeHost" :site-host="siteHost" :api-u-r-l="apiURL"
|
|
:flushSpaceTree="flushSpaceTree"></space>
|
|
</div>
|
|
</el-tab-pane>
|
|
<el-tab-pane name="logs">
|
|
Comming soon....
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
|
|
<div v-if="dynamicVNode">
|
|
<component :is="dynamicVNode" />
|
|
</div>
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.menus_tree{
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 20px 0px 0px 15px;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
.el-tree{
|
|
--el-color-primary-light-9:#6eb3f8;
|
|
--el-tree-node-hover-bg-color:#a1c7ee;
|
|
--el-tree-node-content-height:43px;
|
|
--el-tree-expand-icon-color:#4c4c4e;
|
|
}
|
|
}
|
|
.area_header{
|
|
display: flex;
|
|
margin-top: 9px;
|
|
padding: 12px 7px;
|
|
background-color: white;
|
|
align-items: center;
|
|
.area_name{
|
|
align-content: center;
|
|
margin-left: 8px;
|
|
color: #686854;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
|
|
.tree-item{
|
|
display: flex;
|
|
width: 100%;
|
|
button{
|
|
display: none;
|
|
margin: 0 5px 0 auto;
|
|
}
|
|
}
|
|
.tree-item:hover button{
|
|
display: block;
|
|
}
|
|
|
|
.el-dropdown{
|
|
margin: 0 5px 0 auto;
|
|
}
|
|
|
|
.app_container {
|
|
padding: 10px 30px 0px 30px;
|
|
height: calc(100% - 10px);
|
|
overflow: hidden;
|
|
overflow-y: auto;
|
|
position: relative;
|
|
}
|
|
|
|
.search{
|
|
margin-left: auto;
|
|
margin-right: 26px;
|
|
display:inherit;
|
|
}
|
|
.shareDialog{
|
|
--el-messagebox-width:'800px';
|
|
padding:40px;
|
|
.el-text{
|
|
align-self: flex-start;
|
|
}
|
|
}
|
|
|
|
.spaceitem{
|
|
height: 50px;
|
|
align-content: center;
|
|
padding: 14px;
|
|
color: #606266;
|
|
font-weight:bold;
|
|
}
|
|
|
|
.dynamic-width-message-box-byme .el-message-box__message{
|
|
width: 100%;
|
|
}
|
|
|
|
.el-overlay-message-box{
|
|
top: 200x;
|
|
bottom: auto;
|
|
}
|
|
|
|
.table-grid{
|
|
display: flex;
|
|
flex-wrap: wrap; /* 关键属性,允许子元素自动换行 */
|
|
align-content: flex-start;
|
|
.grid-item{
|
|
position: relative;
|
|
width: 134px;
|
|
height: 145px;
|
|
margin: 5px;
|
|
.grid-box{
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
align-items: center;
|
|
text-align:center;
|
|
}
|
|
.grid-menus{
|
|
position: absolute;
|
|
top: 10px;
|
|
left: 72px;
|
|
background-color: #ffffff;
|
|
width: 60px;
|
|
line-height: 27px;
|
|
text-align: center;
|
|
color: #878989;
|
|
z-index: 90;
|
|
box-shadow:0px 0px 12px rgba(0,0,0,0.12);
|
|
li{
|
|
cursor: pointer;
|
|
border-bottom: 1px solid #ccc;
|
|
}
|
|
}
|
|
.grid{
|
|
:hover {
|
|
background-color: #c4c4c4; /* 悬停时的背景色 */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
</style>
|
|
<style>
|
|
:root{
|
|
--el-index-normal:auto;
|
|
}
|
|
|
|
.el-tabs__header{
|
|
display: none;
|
|
}
|
|
</style>
|