Browse Source

文档权限:多项优化

space2
han2015 2 months ago
parent
commit
86967f6404
  1. 20
      src/store/modules/orgMember.ts
  2. 110
      src/views/doc/agent.vue
  3. 28
      src/views/doc/manage.vue
  4. 59
      src/views/doc/space.vue
  5. 95
      src/views/doc/spacePermission.vue

20
src/store/modules/orgMember.ts

@ -1,33 +1,35 @@
import { defineStore } from 'pinia'
import { ref} from 'vue';
import request from "@/utils/request";
import { id } from 'element-plus/es/locale';
export const useOrgMemberStore = defineStore('orgMember', () => {
interface OrgMemberItem {
id: string;
label: string;
children?:OrgMemberItem[];
name: string;
child?:OrgMemberItem[];
}
const listMap = ref<Record<string, string>>({})
const dataTree = ref<OrgMemberItem>({ id: '', label: '', children: [] })
const dataTree = ref<OrgMemberItem>({ id: '', name: '', child: [] })
async function init() {
await request({
url: "/javasys/lowCode/transfer/getOrgAndManTree",
url: "/systemapi/app/get_org_everyone_people",//"172.20.2.87:39168",
method: "post",
data:{id:"313",all: 1}
}).then((response) => {
// assuming response.data is an array of OrgMemberItem
dataTree.value=response.data
handleChildren(response.data.children)
dataTree.value={id:"313",name:"集团公司",child:response.data}
handleChildren(response.data)
});
}
function handleChildren(childs:any[]){
childs.forEach(item => {
listMap.value[item.id] = item.label;
if(item.children){
handleChildren(item.children)
listMap.value[item.id] = item.name;
if(item.child){
handleChildren(item.child)
}
});
}

110
src/views/doc/agent.vue

@ -26,7 +26,7 @@ const inputState=ref(true)
const conversations=ref<chatRecord[]>([])
const centHoverItem=ref("")
const interact_msg=ref<{ask:boolean,think:string,content:string}[]>([])
const interact_msg=ref<{ask:boolean,think:string,content:string,docinfo?:any[]}[]>([])
const props = withDefaults(defineProps<{
userid:string,
closefunc:()=>void,
@ -75,7 +75,7 @@ async function onSendTextToAI(){
}else{
interact_msg.value.push({ask:true,think:"", content:myquestion.value})
}
let docinfo:any=[]
controller.value = new AbortController();
try{
const res= await doAiChat(`${baseURL}/aibot/agents/${props.agent.uuid}/chat`,{
@ -98,7 +98,6 @@ async function onSendTextToAI(){
let chunk = ''; //
while (true) {
const {done, value} = await reader.read()
if (done) break;
//
chunk += decoder.decode(value, {stream: true});
@ -112,9 +111,13 @@ async function onSendTextToAI(){
if(json.event==="message"){
conversation.value=json.conversation_id
respMsg.value+=json.answer
}else if(json.event==="message_end"){
docinfo=json.metadata.retriever_resources
}
}
}
if (done) break;//
}
}catch (e: any) {
if (e.name === 'AbortError') {
@ -126,14 +129,13 @@ async function onSendTextToAI(){
const arr=respMsg.value.split("</think>")
if(arr.length===1){
interact_msg.value.push({ask:false,think:"",content:arr[0]})
interact_msg.value.push({ask:false,think:"",content:arr[0],docinfo:docinfo})
}else{
//
interact_msg.value.push({ask:false,think:arr[0],content:arr[1]})
interact_msg.value.push({ask:false,think:arr[0],content:arr[1],docinfo:docinfo})
}
respMsg.value=""
setAiChat({
"userid":atob(props.userid),
"uuid":conversation.value,
@ -180,6 +182,16 @@ function handleMouseLeave(){
}
function newContext(){
const c =conversations.value.find(c=>c.uuid==conversation.value)
if(!c){
conversations.value.push({
"agentuuid":props.agent.uuid,
"uuid":conversation.value,
"brief":interact_msg.value[0].content,
"messages":interact_msg.value
})
}
inputState.value=true
interact_msg.value=[]
conversation.value=""
@ -192,6 +204,13 @@ function resetContext(){
props.closefunc()
}
//ai
function formatRefContent(content:string){
let result=content.replaceAll(/"/g,'')
result=result.replaceAll(/Unnamed: /g,' ')
return result
}
//
onMounted(() => {
loadKnownLibList()
@ -212,7 +231,8 @@ onMounted(() => {
<li class="action_menu" @click="newContext">
新建会话
</li>
<li class="list_item" v-for="item in conversations" @mouseover="handleMouseEnter(item)" @mouseleave="handleMouseLeave()" @click="showChat(item.uuid)">{{ item.brief }}
<li class="list_item" v-for="item in conversations" @mouseover="handleMouseEnter(item)" @mouseleave="handleMouseLeave()" @click="showChat(item.uuid)">
<span>{{ item.brief }}</span>
<el-button v-show="centHoverItem == item.uuid" icon="Delete" size="small" circle @click="(e)=>{e.stopPropagation();onDelChat(item.uuid)}"></el-button>
</li>
</ul>
@ -224,6 +244,13 @@ onMounted(() => {
<div v-else class="t_resp">
<el-text style="white-space: pre-line" v-html="msg.think"></el-text>
<VueMarkdown :markdown="msg.content" :rehype-plugins="[rehypeRaw]" :remark-plugins="[remarkGfm]" ></VueMarkdown>
<div v-if="msg.docinfo" class="doc_ref">
引用<hr>
<el-tooltip v-for="doc in msg.docinfo" placement="top" effect="dark"
:content="formatRefContent(doc.content)">
<span>{{doc.document_name}}</span>
</el-tooltip>
</div>
</div>
</template>
@ -306,14 +333,38 @@ onMounted(() => {
background-color: #dddddd;
padding: 10px 8px;
margin: 3px 14px 3px 0;
border-radius: 8px;
span{
width: 90%;
overflow: hidden;
text-overflow: ellipsis;
border-radius: 8px;
text-wrap-mode: nowrap;
}
button{
margin-left: auto;
}
}
.doc_ref{
margin: 16px;
display: flex;
flex-wrap: wrap;
hr{
width: 90%;
margin: inherit;
border: none;
border-top: .5px solid rgb(26 25 25 / 23%);
}
span{
width: 300px;
overflow: hidden;
text-overflow: ellipsis;
text-wrap-mode: nowrap;
margin: 2px 10px;
padding: 0 10px;
background-color: #d7d5d5;
border-radius: 6px;
}
}
</style>
<style>
@ -322,4 +373,47 @@ onMounted(() => {
margin-bottom: 8px;
display: block;
}
.t_resp{
h2,h3,h4,h5{
margin: 12px 0;
}
p{
margin-left: 16px;
}
p+ul{
margin-left: 56px;
}
li p{
margin-left: 0;
}
ol{
margin-left: 14px;
}
ol>li{
list-style-type: decimal;
}
ul{
margin-left: 30px;
list-style: disc;
ul{
margin-left: 30px;
}
}
table{
border: 0px solid;
border-spacing: 1px;
border-collapse: collapse;
th{
background-color: #e5e5e5;
border: 1px solid;
min-width: 180px;
}
td{
border: 1px solid;
min-width: 180px;
}
}
}
</style>

28
src/views/doc/manage.vue

@ -22,7 +22,7 @@ import preview from './preview.vue';
import space from './space.vue';
import spacePermission from './spacePermission.vue';
import SvgIcon from "@/components/SvgIcon/index.vue";
import {doDelSpace} from "@/api/doc/space"
import {doDelSpace,doAccessManage} from "@/api/doc/space"
import Space from "./space.vue";
//TODO: add file icons done!
@ -658,6 +658,31 @@ function flushSpaceTree(uuid:string,data:matterTree[]){
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}) ? 空间内所有文件将不可恢复!取消则放弃删除操作。`, "警告", {
@ -763,6 +788,7 @@ const handleSelectionChange = (val:matterInfo[]) => {
<el-button size="small" :icon="Setting" circle ></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); onAccessManage(data)}">空间成员管理</el-dropdown-item>
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); onDelSpaceMatter(data)}">删除</el-dropdown-item>
<el-dropdown-item @click="(e)=>{ e.stopPropagation(); onSpacePManage(data)}">权限管理</el-dropdown-item>
</el-dropdown-menu>

59
src/views/doc/space.vue

@ -15,10 +15,10 @@ import {
Delete,
View,
Download,
Plus,
Plus,Search,
Edit,
Setting,
Promotion,
Grid,List,
} from '@element-plus/icons-vue'
import {ElMessage,UploadFile,UploadFiles,ElPagination} from "element-plus";
import aiagent from './agent.vue';
@ -29,6 +29,7 @@ const orgMembers = useOrgMemberStore() //为了初始化
const defaultAiAgent=import.meta.env.VITE_DEFAULT_AI_AGENT
const matterList = ref<matterInfo[]>([])
const searchname=ref("") //
const newdirName=ref("") //
const currentHoverRow=ref("") //table
const breadcrumbList=ref<matterInfo[]>([{name:"根目录",uuid:"root", dir:true}]) //
@ -41,6 +42,7 @@ const dynamicVNode = ref<VNode | null>(null) //permission 组件的父组件
const paginInfo = ref({ page: 0, total: 0 })
const CutLevelPermit=ref(0)
const modListOrGrild=ref(true) //
enum PERMITS {
FORBID, //0
VIEW, //1
@ -88,8 +90,18 @@ const uploadFormData = computed(() => {
}
});
function updateListOrGrid(val:boolean){
modListOrGrild.value=val
if(val){
localStorage.setItem("listOrGrid","true")
}else{
localStorage.setItem("listOrGrid","false")
}
}
//--------------&-------------
function onAccessManage(){
//manage
dynamicVNode.value = h(sharePermission, {
uid: props.uid,
uuid: "",
@ -159,10 +171,9 @@ function onDownload(row:matterInfo){
})
}
//
function onLoadMatterList(){
let _page: matterPage = {
function onLoadMatterList(name?:string){
let _page: matterPage= {
page: paginInfo.value.page,
pageSize: 50,
orderCreateTime: "DESC",
@ -172,6 +183,18 @@ function onLoadMatterList(){
space:props.spaceid,
};
if(name){
_page={
pageSize: 50,
orderCreateTime: "DESC",
orderDir: "DESC",
deleted:false,
name:name,
space:props.spaceid,
}
}
getSpaceMatterList(props.uid,_page).then((resp)=>{
//page+1 index1apiindex0
paginInfo.value={total:resp.data.totalPages, page:resp.data.page}
@ -508,6 +531,13 @@ onMounted(() => {
//AI
currentAgent.value={name:"通用AI",model:false,uuid:defaultAiAgent,path:"root"}
let val =localStorage.getItem("listOrGrid")
if(val&&val=="false"){
modListOrGrild.value=false
}else{
modListOrGrild.value=true
}
if (props.ismanager) {
CutLevelPermit.value=PERMITS.MANAGER
}else{
@ -547,6 +577,11 @@ function isOwner(){
</el-breadcrumb-item>
</el-breadcrumb>
<span v-if="currentNode.uuid!='root'" 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="onLoadMatterList(searchname)"></el-button>
</el-col>
</el-row>
<el-row :gutter="24">
@ -568,15 +603,18 @@ function isOwner(){
<el-button @click="createDir">新建目录</el-button>
</el-col>
<el-button style="margin-left: auto;" @click="()=>currentAgent.model=true">AI助手</el-button>
<el-button-group v-if="isOwner()" class="control" style="margin: 0 10px;">
<el-button :icon="Plus" @click="onAccessManage">成员</el-button>
<el-button :icon="Plus" @click="onAiAgent">创建智能体</el-button>
<!-- <el-button :icon="Delete" @click="onDeleteSpace">删除</el-button> -->
</el-button-group>
<el-button-group style="margin-right:20px;">
<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="height: 84%;overflow-y: auto;">
<el-table v-if="listOrGrid"
<el-table v-if="modListOrGrild"
stripe
:data="matterList"
:header-cell-style="{ background: '#f5f8fd' }"
@ -671,6 +709,11 @@ function isOwner(){
align-self: flex-start;
}
}
.search{
margin-left: auto;
margin-right: 20px;
display:inherit;
}
.table-grid{
display: flex;

95
src/views/doc/spacePermission.vue

@ -8,6 +8,7 @@ import {
import { ElDialog, ElMessageBox,ElStep,TreeInstance} from 'element-plus';
import { useOrgMemberStore } from "@/store/modules/orgMember";
import { dataTool } from 'echarts';
const props = withDefaults(defineProps<{
uid:string, //uuid
uuid:string, //uuid
@ -21,16 +22,17 @@ const spacePermit=ref<{id:number,data:string,matterUid:string}>({}) //权限
const treeRef=ref<TreeInstance>() //tree
const managerMode=ref(false)
const managers=ref<string[]>([])
const firstLevelKeys = ref<string[]>([]) //
let resultPermits: Record<string, number> = {};
interface Tree {
id: string
label:string
parentId?:string
name:string
superior?:string
indeterminate?:boolean,
radio?:number[];
children?: Tree[]
child?: Tree[]
ismanager?:boolean,
}
@ -48,7 +50,7 @@ async function onSavePermChange(){
managers.value.push("p0"+item.id)
}
if(item.children) collectManager(item)
if(item.child) collectManager(item)
})
await addSpaceManager(btoa(props.uid),{
@ -69,7 +71,7 @@ async function onSavePermChange(){
resultPermits[item.id]=item.radio[0]
}
if(item.children) collectNodePermits(item)
if(item.child) collectNodePermits(item)
})
spacePermit.value.data=JSON.stringify(resultPermits)
@ -85,18 +87,18 @@ async function onSavePermChange(){
}
function collectManager(node:Tree){
node.children?.forEach(ele => {
node.child?.forEach(ele => {
if(ele.ismanager){
managers.value.push("p0"+ele.id)
}
if(ele.children) collectManager(ele)
if(ele.child) collectManager(ele)
});
}
//
function collectNodePermits(node:Tree){
node.children?.forEach(ele => {
node.child?.forEach(ele => {
if(ele.radio&&ele.radio.length>0){
if(ele.indeterminate) {
ele.radio[0]+=10
@ -104,7 +106,7 @@ function collectNodePermits(node:Tree){
resultPermits[ele.id]=ele.radio[0]
}
if(ele.children){
if(ele.child){
collectNodePermits(ele)
}
});
@ -114,25 +116,25 @@ function collectNodePermits(node:Tree){
//
function onGroupValueChange(node:Tree, val:number[]){
if(node.indeterminate) node.indeterminate=false;
node.children?.forEach(ele => {
node.child?.forEach(ele => {
ele.radio=val
if(ele.children){
if(ele.child){
onGroupValueChange(ele,val)
}
});
if(node.parentId){
if(node.superior){
updateParentNode(node)
}
}
//
function updateParentNode(node:Tree){
if(node.parentId){
if(node.superior){
const pnode = treeRef.value?.getNode(node.parentId);
const pnode = treeRef.value?.getNode(node.superior);
if(pnode){
const tdata=pnode.data as Tree
if (tdata.children?.every(ele=>{
if (tdata.child?.every(ele=>{
if(ele.indeterminate) return !ele.indeterminate;//
if(ele.radio?.length==0) return ele.radio.length==tdata.radio?.length;
@ -165,10 +167,23 @@ function onShowManagers(){
const node = treeRef.value?.getNode(item.replace("p0",""));
if(node && node.data){
node.data.ismanager=true
setParentIndeterminate(node.data as Tree)
}
})
}
function setParentIndeterminate(node:Tree){
if(node.superior){
const pnode = treeRef.value?.getNode(node.superior);
if(pnode){
const tdata=pnode.data as Tree
tdata.indeterminate=true
setParentIndeterminate(tdata)
}
}
}
function refreshSpaceData(){
getSpaceMemberList(
btoa(props.uid),
@ -183,13 +198,17 @@ function refreshSpaceData(){
addNode(item,orgMembers.dataTree)
});
if(dataSource.value.length>0){//
firstLevelKeys.value.push(dataSource.value[0].id)
}
resp.data?.members?.forEach(item=>{
const mid=item.replace("p0","")
for(let data of dataSource.value){
if(checkNode(mid,data)) return;
}
dataSource.value.push({id:mid,label:orgMembers.listMap[mid],radio:[2]})
dataSource.value.push({id:mid,name:orgMembers.listMap[mid],radio:[]})
});
managers.value=resp.data?.managers.split("|")
@ -197,7 +216,6 @@ function refreshSpaceData(){
spacePermit.value.matterUid=resp.data?.permits.MatterUuid
spacePermit.value.data=resp.data.permits.data!="" ? resp.data.permits.data : "{}"
nextTick(() => {
const permitJson = JSON.parse(spacePermit.value.data) //object
Object.keys(permitJson).forEach(key=>{
@ -217,28 +235,28 @@ function refreshSpaceData(){
}
function addNode(key:string,node:Tree){
node.children?.forEach(ele => {
node.child?.forEach(ele => {
if(ele.id==key){
const eleClone=structuredClone(toRaw(ele)) //eleProxyclonetoRawobject
eleClone.radio=[2]
eleClone.parentId=ele.parentId
dataSource.value.push(eleClone) //{id:ele.id,label:ele.label,radio:2,stage:level,children:ele.children}
//eleClone.radio=[2]
eleClone.superior=ele.superior
dataSource.value.push(eleClone) //{id:ele.id,name:ele.name,radio:2,stage:level,children:ele.children}
return
}
if(ele.children){
if(ele.child){
addNode(key,ele)
}
});
}
function checkNode(key:string,node:Tree):boolean{
for (const child of node.children||[]) {
if(child.id==key){
for (const ch of node.child||[]) {
if(ch.id==key){
return true
}
if(child.children){
if(ch.child){
//returnreturnfortruereturn
if(checkNode(key,child)) return true;
if(checkNode(key,ch)) return true;
}
}
@ -266,11 +284,26 @@ onMounted(()=>{
<span>管理员</span>
</div>
<div v-else class="buttons">
<span>禁止</span>
<el-tooltip placement="top" effect="dark"
content="禁止访问">
<span>不可见</span>
</el-tooltip>
<el-tooltip placement="top" effect="dark"
content="仅可预览,不可下载">
<span>仅预览</span>
</el-tooltip>
<el-tooltip placement="top" effect="dark"
content="可下载可见文件,不可上传文件">
<span>可下载</span>
</el-tooltip>
<el-tooltip placement="top" effect="dark"
content="可上传和下载可见文件">
<span>上传下载</span>
</el-tooltip>
<el-tooltip placement="top" effect="dark"
content="可上传、下载、编辑文档">
<span>编辑</span>
</el-tooltip>
</div>
</div>
<div class="tablelist">
@ -278,15 +311,17 @@ onMounted(()=>{
ref="treeRef"
:data="dataSource"
node-key="id"
default-expand-all
accordion
:props="{label: 'name',children:'child'}"
:default-expanded-keys="firstLevelKeys"
:expand-on-click-node="false"
>
<template #default="{ node, data }">
<div class="tree-node">
<span>{{ node.label }}</span>
<span>{{ data.name }}</span>
<div v-if="managerMode" class="buttons">
<el-checkbox v-if="!data.children" v-model="data.ismanager" />
<el-checkbox v-model="data.ismanager" :indeterminate="data.indeterminate" />
</div>
<div v-else class="buttons">
<el-checkbox-group :min="0" :max="1" v-model="data.radio" @change="(val)=>onGroupValueChange(data,val)">

Loading…
Cancel
Save