Browse Source

Merge branch 'han_v0'

han_v3
herenshan112 4 months ago
parent
commit
34e43f8d34
  1. 30
      src/api/doc/index.ts
  2. 78
      src/api/doc/space.ts
  3. 14
      src/api/hr/people/share_ctrol.ts
  4. 83
      src/views/doc/manage.vue
  5. 1
      src/views/doc/share.vue
  6. 18
      src/views/doc/sharePermission.vue
  7. 376
      src/views/doc/space.vue
  8. 12
      src/views/doc/tools.ts

30
src/api/doc/index.ts

@ -24,6 +24,36 @@ export function getShareList( uid:string,data?: matterPage): AxiosPromise<matter
});
}
/**
*
*/
export function doCreateSpace( uid:string,_name:string) {
return request({
url: '/hxpan/api/space/create',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {name:_name}
});
}
/**
*
*/
export function getMySpaces(uid:string,data?: any){
return request({
url: '/hxpan/api/space/list',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}
/**
* share browse
*/

78
src/api/doc/space.ts

@ -0,0 +1,78 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { matterPage,createDir,matterTreeList} from './type';
/**
*
*/
export function getSpaceMatterList( uid:string,data?: matterPage): AxiosPromise<matterTreeList> {
return request({
url: '/hxpan/api/space/page',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}
/**
*
*/
export function doCreateSpaceDir(uid:string,data?: createDir){
return request({
url: '/hxpan/api/space/directory',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}
/**
*
*/
export function doAccessManage(uid:string,data?: any){
return request({
url: '/hxpan/api/space/access',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}
/**
*
*/
export function doDelSpace(uid:string,data?: any){
return request({
url: '/hxpan/api/space/delete',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}
/**
*
*/
export function doDelSpaceMatter(uid:string,data?: any){
return request({
url: '/hxpan/api/space/materdelete',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}

14
src/api/hr/people/share_ctrol.ts

@ -71,6 +71,20 @@ export function getPermitedList(uid:string,data:{uuid:string}): AxiosPromise<{pe
data: data
});
}
/**
*
*/
export function getSpaceMemberList(uid:string,data:{space?:string}): AxiosPromise<{members:string[],dprts:string[]}> {
return request({
url: '/hxpan/api/space/members',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}
/**
*

83
src/views/doc/manage.vue

@ -7,7 +7,7 @@
import { getExpirTime, getFileIcon, readableSize} from "./tools"
import sharePermission from './sharePermission.vue';
import { useUserStore } from "@/store/modules/user";
import { getMatterList,postCreateDir,postDelMatter,postCreateShare,postMatterRename,postDelMatBatch} 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 { h } from 'vue'
import {
@ -19,11 +19,13 @@ import {
Edit,
Promotion,
Folder,
Avatar
Avatar,
Plus
} from '@element-plus/icons-vue'
import {ElSelect,ElOption, ElText,ElInput,TableInstance,ElMessage,UploadFile,UploadFiles,ElPagination,ElTree,TreeInstance} from "element-plus";
import type { TreeNode } from 'element-plus/es/components/tree/src/tree.type'
import preview from './preview.vue';
import space from './space.vue';
//TODO: add file icons done!
//TODO: click on table-item 1preview on file .....................
@ -50,16 +52,21 @@ const currentHoverRow=ref("") //table 行的按钮控制
const selectedValue = ref("sixhour") //
const tabSelected=ref<matterInfo[]>([]) //table
//to support tree mode refactor
const treeData=ref<matterTree[]>([{name:'root',uuid:'root',children:[]}])
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 formHeaders=ref({
"user-id":uid,
const PRIVATESPACE = ref(true) // 2
const SpaceID= ref<{name:string,uuid:string,userUuid:string}>({}) //spaceid
const SpaceList=ref<{name:string,uuid:string,userUuid:string}[]>([])
const Departs = computed(() => {
return `${userStore.userInfoCont.company},${userStore.userInfoCont.department},${userStore.userInfoCont.organization}`
})
const uploadFormData = computed(() => {
return {
userUuid: uid, // uuid
@ -312,7 +319,7 @@ function onNodeExpand(node: TreeNode, resolve: (data: matterTree[]) => void, rej
if ((node.data as matterTree).uuid) {
const cuuid = (node.data as matterTree).uuid
currentNode.value = node.data
let _page: matterPage = {
page: 0,
pageSize: 50,
@ -340,6 +347,9 @@ function onNodeExpand(node: TreeNode, resolve: (data: matterTree[]) => void, rej
}
//
function onNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
if(!PRIVATESPACE.value) {
PRIVATESPACE.value=true
}
if (currentNode.value.uuid === data.uuid) return;
const cuuid = data.uuid
currentNode.value = data
@ -407,11 +417,39 @@ interface uploadError{
function handleSigLoadErr(error: Error, uploadFile: UploadFile, uploadFiles:UploadFiles){
ElMessage.error(JSON.parse(error.message).msg)
}
//-------------------space feature---------------------
function onNewSpace(){
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)=>{
SpaceList.value.push({name:resp.data.name,uuid:resp.data.uuid,userUuid:"p0"+userStore.userInfoCont.userId})
})
}
})
}
//------------------------------------------------------
//http://172.20.2.87:6010/api/alien/preview/5a10aaf6-396e-4d9a-7e87-3c5c8029d4db/123.png?ir=fill_100_100
//
onMounted(() => {
onNodeClick(treeData.value[0], null as unknown as TreeNode, null, null)
//
getMySpaces(uid,{roles:Departs}).then((resp)=>{
SpaceList.value=resp.data
})
});
const handleSelectionChange = (val:matterInfo[]) => {
@ -434,8 +472,13 @@ const handleSelectionChange = (val:matterInfo[]) => {
:default-expanded-keys="['root']"
@node-click="onNodeClick"
/>
<el-button style="margin: 10px 0;" :icon="Plus" @click="onNewSpace"> 共享空间</el-button>
<ul style="background-color: white;">
<li class="spaceitem" v-for="sp in SpaceList" @click="()=>{SpaceID=sp; PRIVATESPACE=false;}">{{ sp.name }}</li>
</ul>
</div>
<div class="app_container">
<div v-if="PRIVATESPACE" class="app_container">
<el-row :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;">根目录</span>/
@ -446,7 +489,6 @@ const handleSelectionChange = (val:matterInfo[]) => {
<el-row :gutter="24">
<el-col :span="14">
<el-upload class="el-button el-button--default" :file-list="fileList"
:headers="formHeaders"
:data="uploadFormData"
:on-success="handleSingleUpload"
:on-error="handleSigLoadErr"
@ -522,10 +564,15 @@ const handleSelectionChange = (val:matterInfo[]) => {
<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 :uid="uid" :spaceid="SpaceID.uuid" :roles="Departs" :spacename="SpaceID.name" :owner="SpaceID.userUuid"
:officeHost="officeHost" :site-host="siteHost" :api-u-r-l="apiURL"></space>
</div>
<div v-if="dynamicVNode">
<component :is="dynamicVNode" />
</div>
<div v-if="dynamicVNode">
<component :is="dynamicVNode" />
</div>
</div>
</template>
@ -559,9 +606,21 @@ const handleSelectionChange = (val:matterInfo[]) => {
}
}
.spaceitem{
height: 50px;
align-content: center;
padding: 14px;
color: #606266;
font-weight:bold;
}
.dynamic-width-message-box-byme .el-message-box__message{
width: 100%;
}
</style>
.el-overlay-message-box{
top: 200x;
bottom: auto;
}
</style>

1
src/views/doc/share.vue

@ -154,7 +154,6 @@ async function onlyOfficeEdit(row:matterInfo){
}
let filepath=""
await getShareBrowse("",{shareUuid:row.uuid,code:row.code,puuid:'root',rootUuid:'root',dprt:udprt}).then((resp)=>{
console.log(resp.data,"<<<<<<<<<<<")
filepath= resp.data.matters[0].path
})
ElMessageBox.confirm("线上资源有限,确定继续线上编辑吗", "提示", {

18
src/views/doc/sharePermission.vue

@ -5,6 +5,7 @@ import {
shareOrgInfo,
memberInfo,
getPermitedList,
getSpaceMemberList,
postPermitedList} from '@/api/hr/people/share_ctrol'
import { ElDialog, ElMessageBox,TableInstance, TreeInstance } from 'element-plus';
@ -12,6 +13,7 @@ import { ElDialog, ElMessageBox,TableInstance, TreeInstance } from 'element-plus
const props = withDefaults(defineProps<{
uid:string, //uuid
uuid:string, //uuid
spaceid?:string, //feature
confirmFunc?:(data:string[],infos:string[])=>void, //
closeFunc:(refresh?:boolean)=>void, //
}>(),{})
@ -122,7 +124,21 @@ onMounted(()=>{
permitedInfos.add(item)
})
})
}else if(props.spaceid!==""){
//
getSpaceMemberList(props.uid,{space:props.spaceid}).then(resp=>{
resp.data?.members?.forEach(item=>{
permited.add(item) //userUuids constitue the permited list
});
resp.data?.dprts?.forEach(val=>{
if(val.match("[a-z]")) return; //
treeSelected.push(parseInt(val))
})
})
}
getOrgTreeList({}).then(resp=>{
treeData.value=resp.data
@ -137,7 +153,7 @@ onMounted(()=>{
<template #header>
<span>权限管理</span>
</template>
<div style="display: grid;grid-template-columns:1fr 1fr;">
<div style="display: grid;width: 100%;grid-template-columns:1fr 1fr;">
<div class="menus_tree">
<el-tree
ref="treeRef"

376
src/views/doc/space.vue

@ -0,0 +1,376 @@
<!--
@ 作者: 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, // uuidroot
}
});
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 index1apiindex0
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>

12
src/views/doc/tools.ts

@ -92,14 +92,16 @@ export function readableSize(val:matterInfo){
export function fileType(name:string){
const suffix=name.match(/(\.[a-zA-Z]+$)/)[0]
if (".doc, .docx, .html, .md, .txt, .wps, .xml".includes(suffix)){
const suffix=name.match(/(\.[a-zA-Z]+$)/)
if(suffix==null){ return ""}
if (".doc, .docx, .html, .md, .txt, .wps, .xml".includes(suffix[0])){
return "word"
} else if(".csv, .xls, .xlsb, .xlsm, .xlsx".includes(suffix)){
} else if(".csv, .xls, .xlsb, .xlsm, .xlsx".includes(suffix[0])){
return "cell"
}else if(".ppt, .pptm, .pptx".includes(suffix)){
}else if(".ppt, .pptm, .pptx".includes(suffix[0])){
return "slide "
}else if (suffix===".pdf"){
}else if (suffix[0]===".pdf"){
return "pdf"
}
return ""

Loading…
Cancel
Save