diff --git a/src/api/doc/space.ts b/src/api/doc/space.ts index fbc3ff2..219160e 100644 --- a/src/api/doc/space.ts +++ b/src/api/doc/space.ts @@ -74,6 +74,22 @@ export function doDelSpace(uid:string,data?: any){ }); } +/** + * 重命名 + */ +export function spaceMatterRename(uid:string,data?: any){ + return request({ + url: '/hxpan/api/space/rename', + method: 'post', + headers: { + 'Identifier':uid, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + data: data + }); +} + + /** * 删除文件或目录 */ diff --git a/src/store/modules/orgMember.ts b/src/store/modules/orgMember.ts index 5cc4dc1..0d1bdce 100644 --- a/src/store/modules/orgMember.ts +++ b/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>({}) - const dataTree = ref({ id: '', label: '', children: [] }) + const dataTree = ref({ 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) } }); } diff --git a/src/views/doc/agent.vue b/src/views/doc/agent.vue index 6952b09..ad59e20 100644 --- a/src/views/doc/agent.vue +++ b/src/views/doc/agent.vue @@ -26,7 +26,7 @@ const inputState=ref(true) const conversations=ref([]) 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`,{ @@ -86,7 +86,7 @@ async function onSendTextToAI(){ user:atob(props.userid),//这里已经base64解析了 },controller.value.signal ) - + if (!res.ok) { throw new Error(`HTTP ${res.status} ${res.statusText}`); } @@ -94,11 +94,10 @@ async function onSendTextToAI(){ myquestion.value="" const reader = res.body!.getReader(); const decoder = new TextDecoder('utf-8'); - + 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("") 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(() => {
  • 【新建会话】
  • -
  • {{ item.brief }} +
  • + {{ item.brief }}
  • @@ -224,6 +244,15 @@ onMounted(() => {
    +
    + 引用
    + + + {{doc.document_name}} + +
    @@ -306,14 +335,38 @@ onMounted(() => { background-color: #dddddd; padding: 10px 8px; margin: 3px 14px 3px 0; - overflow: hidden; - text-overflow: ellipsis; border-radius: 8px; - text-wrap-mode: nowrap; + span{ + width: 90%; + overflow: hidden; + text-overflow: ellipsis; + 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; + } +} \ No newline at end of file diff --git a/src/views/doc/manage.vue b/src/views/doc/manage.vue index 1392f46..8825cbc 100644 --- a/src/views/doc/manage.vue +++ b/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! @@ -254,10 +254,12 @@ function onMatterRename(row:matterInfo){ }, }), }).then(() => { - postMatterRename(uid,{ - uuid:row.uuid, - name:newname.value, - }).then(()=>onLoadMatterList()) + if(newname.value&&newname.value!=""){ + postMatterRename(uid,{ + uuid:row.uuid, + name:newname.value, + }).then(()=>onLoadMatterList()) + } }) } @@ -658,6 +660,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 +790,7 @@ const handleSelectionChange = (val:matterInfo[]) => { - + @@ -876,10 +905,11 @@ const handleSelectionChange = (val:matterInfo[]) => {
    • 预览
    • -
    • 分享
    • -
    • 下载
    • -
    • 编辑
    • -
    • 删除
    • +
    • 分享
    • +
    • 下载
    • +
    • 编辑
    • +
    • 删除
    • +
    • 重命名
    @@ -976,12 +1006,13 @@ const handleSelectionChange = (val:matterInfo[]) => { .table-grid{ display: flex; flex-wrap: wrap; /* 关键属性,允许子元素自动换行 */ + align-content: flex-start; .grid-item{ position: relative; - width: 134px; + width: 134px; + height: 145px; margin: 5px; .grid-box{ - height: 150px; display: flex; flex-direction: column; overflow: hidden; @@ -997,6 +1028,7 @@ const handleSelectionChange = (val:matterInfo[]) => { 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; diff --git a/src/views/doc/onlyoffice.vue b/src/views/doc/onlyoffice.vue index dce2bfe..64a2ebe 100644 --- a/src/views/doc/onlyoffice.vue +++ b/src/views/doc/onlyoffice.vue @@ -53,22 +53,22 @@ onMounted(()=>{ const _url=decodeURIComponent(window.atob(query.fileurl)) const name=query.name?.toString()??"" const dtype=query.dtype?.toString()??"word" + //验证一下文件名是否合规 - if (_info.includes(name)){ - config.value.document.url=_url - config.value.document.title=name - const _key=_url.match(/(\w+-\w+-\w+)/)![0] - if(_key) config.value.document.key=_key+(new Date().getTime().toString()) //合作编辑必须要有一个unique key - config.value.documentType=dtype - config.value.editorConfig.callbackUrl+=`?info=${_info}` - //设置编辑模式 - if(!query.verify) return; - const _verify = atob(query.verify) - //1:必须是验证通过,以true结尾 2:校验uuid是否匹配 - if(_verify.endsWith("true") && _key.includes(_verify.replace("true",""))){ - config.value.editorConfig.mode="edit" - } + config.value.document.url=_url + config.value.document.title=name + const _key=_url.match(/(\w+-\w+-\w+)/)![0] + if(_key) config.value.document.key=_key+(new Date().getTime().toString()) //合作编辑必须要有一个unique key + config.value.documentType=dtype + config.value.editorConfig.callbackUrl+=`?info=${_info}` + //设置编辑模式 + if(!query.verify) return; + const _verify = atob(query.verify) + //1:必须是验证通过,以true结尾 2:校验uuid是否匹配 + if(_verify.endsWith("true") && _key.includes(_verify.replace("true",""))){ + config.value.editorConfig.mode="edit" } + } }) diff --git a/src/views/doc/recentVisited.vue b/src/views/doc/recentVisited.vue index 447f9e5..b700af9 100644 --- a/src/views/doc/recentVisited.vue +++ b/src/views/doc/recentVisited.vue @@ -24,9 +24,9 @@ const props = withDefaults(defineProps<{ function onlyOfficeView(row:shareItem){ const _type=fileType(row.name!) if(_type!==""){ //office file - const info =btoa(encodeURIComponent(`${row.userUuid}/root${row.matters[0].path}`)) + const info =btoa(encodeURIComponent(`${row.name}`)) const _url=`${siteHost}${apiURL}/share/zip?shareUuid=${row.uuid}&code=${row.code}&uid=${props.uid}&dprt=${props.udprt}&puuid=root&rootUuid=root` - window.open(`/#/onlyoffice?name=${row.name}&dtype=${_type}&info=${info}&fileurl=`+encodeURIComponent(_url),"_blank") + window.open(`/#/onlyoffice?name=${row.name}&dtype=${_type}&info=${info}&fileurl=`+window.btoa(encodeURIComponent(_url)),"_blank") }else{ //by kkFilePreview let a = row.name ?? ''; diff --git a/src/views/doc/space.vue b/src/views/doc/space.vue index bda7b1a..de14972 100644 --- a/src/views/doc/space.vue +++ b/src/views/doc/space.vue @@ -9,16 +9,16 @@ import sharePermission from './sharePermission.vue'; import spacePermission from './spacePermission.vue'; import { matterPage,matterInfo,matterTree,doFileUpload,matterPermit} from "@/api/doc/type" import { doAccessManage,getSpaceMatterList,doCreateSpaceDir,doDelSpaceMatter, - doAiTraining ,doCreateAiagent} from "@/api/doc/space" + doAiTraining ,doCreateAiagent,spaceMatterRename} from "@/api/doc/space" import { h } from 'vue' 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([]) +const searchname=ref("") //按文件名查找文件 const newdirName=ref("") //创建新目录时的目录名 const currentHoverRow=ref("") //table 行的按钮控制 const breadcrumbList=ref([{name:"根目录",uuid:"root", dir:true}]) //面包屑导航 @@ -41,6 +42,7 @@ const dynamicVNode = ref(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 是由于分页的起始index是1,而后端api的分页index起始是0 paginInfo.value={total:resp.data.totalPages, page:resp.data.page} @@ -251,6 +274,31 @@ function onCreateDir(){ }) } +//文件重命名 +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!=""){ + spaceMatterRename(props.uid,{ + space:props.spaceid, + uuid:row.uuid, + name:newname.value, + }).then(()=>onLoadMatterList()) + } + }) +} + //------------------------------------------ // @cell-dblclick="handleDoubleClick" 取消了目录双击打开功能 //打开一个目录 @@ -445,7 +493,11 @@ function handleAiUpload(info:matterInfo){ //只有当前路径是智能体,上传文件才会进行训练 if (info.path?.startsWith(currentAgent.value.path)){ doAiTraining(`/agents/${currentAgent.value.uuid}/updates`,{"matter":info.uuid}).then(resp=>{ - console.log(resp) + ElMessage({ + message: '已成功安排训练', + type: 'success', + plain: true, + }) }) }else{ alert("当前路径没有智能体") @@ -508,6 +560,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{ @@ -537,7 +596,6 @@ function isOwner(){
    @@ -640,10 +709,13 @@ function isOwner(){
    • 预览
    • 下载
    • -
    • 编辑
    • + +
    • 编辑
    • +
    • 重命名
    • +
    • 删除
    • -
    • 权限
    • +
    • 权限
    @@ -653,7 +725,6 @@ function isOwner(){ - @@ -671,16 +742,22 @@ function isOwner(){ align-self: flex-start; } } +.search{ + margin-left: auto; + margin-right: 20px; + display:inherit; +} .table-grid{ display: flex; flex-wrap: wrap; /* 关键属性,允许子元素自动换行 */ + align-content: flex-start; .grid-item{ position: relative; width: 134px; + height: 135px; margin: 5px; .grid-box{ - height: 150px; display: flex; flex-direction: column; overflow: hidden; @@ -696,7 +773,7 @@ function isOwner(){ line-height: 27px; text-align: center; color: #878989; - z-index: 999; + z-index: 90; box-shadow:0px 0px 12px rgba(0,0,0,0.12); li{ cursor: pointer; diff --git a/src/views/doc/spacePermission.vue b/src/views/doc/spacePermission.vue index d407b49..8f61fd6 100644 --- a/src/views/doc/spacePermission.vue +++ b/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() //tree组件的引用,为了实现初始选中状态 const managerMode=ref(false) const managers=ref([]) +const firstLevelKeys = ref([]) //默认展开的部门 let resultPermits: Record = {}; 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,21 +198,24 @@ 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("|") spacePermit.value.id=resp.data?.permits.ID 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)) //这里ele是Proxy,无法clone,所以要toRaw拿到原始object - 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){ //这里不能直接return,因为return后,就中断了for循环,但是true的情况下可以直接return - if(checkNode(key,child)) return true; + if(checkNode(key,ch)) return true; } } @@ -266,11 +284,26 @@ onMounted(()=>{ 管理员
    - 禁止 - 仅预览 - 可下载 - 上传下载 - 编辑 + + 不可见 + + + 仅预览 + + + 可下载 + + + 上传下载 + + + 编辑 +
    @@ -278,15 +311,17 @@ onMounted(()=>{ ref="treeRef" :data="dataSource" node-key="id" - default-expand-all - :expand-on-click-node="false" + accordion + :props="{label: 'name',children:'child'}" + :default-expanded-keys="firstLevelKeys" + :expand-on-click-node="false" >