Browse Source

Merge branch 'space2'

han2015 1 month ago
parent
commit
ea914f85e7
  1. 14
      src/api/doc/type.ts
  2. 16
      src/views/doc/agent.vue
  3. 14
      src/views/doc/logpanel.vue
  4. 62
      src/views/doc/manage.vue
  5. 25
      src/views/doc/space.vue
  6. 78
      src/views/doc/uploadlog.vue

14
src/api/doc/type.ts

@ -98,4 +98,18 @@ export function doFileUpload(params:FormData,_url:string): AxiosPromise<matterIn
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
} }
}); });
}
/**
*
*/
export function logUploadError(params:{content:string},_url:string): AxiosPromise<matterInfo> {
return request({
url: _url,
method: 'post',
data: params,
headers: {
'Content-Type': 'multipart/form-data'
}
});
} }

16
src/views/doc/agent.vue

@ -27,6 +27,7 @@ const inputState=ref(true)
const conversations=ref<chatRecord[]>([]) const conversations=ref<chatRecord[]>([])
const centHoverItem=ref("") const centHoverItem=ref("")
const currentAgent=ref<{name:string,uuid:string}>({}) const currentAgent=ref<{name:string,uuid:string}>({})
const queryUrl=ref("")
const interact_msg=ref<{ask:boolean,think:string,content:string,docinfo?:any[]}[]>([]) const interact_msg=ref<{ask:boolean,think:string,content:string,docinfo?:any[]}[]>([])
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
@ -69,12 +70,20 @@ async function onSendTextToAI(){
const params={ const params={
"onlineSearch":"否", "onlineSearch":"否",
"useDataset":"否" "useDataset":"否",
"queryUrl":""
} }
for (let item of checkedModel.value){ for (let item of checkedModel.value){
if(item==="onlineSearch") params.onlineSearch="是" if(item==="onlineSearch"){
params.onlineSearch="是"
if(queryUrl.value!="") {
params.queryUrl=queryUrl.value
//queryUrl.value="" //
}
}
if(item==="useDataset") params.useDataset="是" if(item==="useDataset") params.useDataset="是"
} }
if (conversation.value==""){ if (conversation.value==""){
// //
interact_msg.value=[{ask:true,think:"", content:myquestion.value}] interact_msg.value=[{ask:true,think:"", content:myquestion.value}]
@ -325,6 +334,9 @@ onMounted(() => {
{{ mod.name }} {{ mod.name }}
</el-checkbox-button> </el-checkbox-button>
</el-checkbox-group> </el-checkbox-group>
<div v-if="agent.name.startsWith('法')&&checkedModel.length>0 && checkedModel[0]=='onlineSearch' " style="width: 98%;margin-bottom: 5px;">
<el-input placeholder="是否指定网站, 多个地址以逗号分隔。" v-model="queryUrl" input-style="border-radius: 2px;"/>
</div>
<el-input placeholder="问灵犀..." v-model="myquestion" input-style="border-radius: 20px;" <el-input placeholder="问灵犀..." v-model="myquestion" input-style="border-radius: 20px;"
resize="none" :autosize="{minRows: 4}" type="textarea" /> resize="none" :autosize="{minRows: 4}" type="textarea" />

14
src/views/doc/logpanel.vue

@ -0,0 +1,14 @@
<!--
@ 作者: han2015
@ 时间: 2025-05-12 15:39:13
@ 备注: 文档管理组件
-->
<script lang="ts" setup>
</script>
<template>
<el-table></el-table>
</template>

62
src/views/doc/manage.vue

@ -16,10 +16,12 @@ import {
Delete,View,Download,Share,Search,Edit, Delete,View,Download,Share,Search,Edit,
Avatar,Plus,Grid,List, Avatar,Plus,Grid,List,
Setting, Setting,
Bell,
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import {ElSelect,ElOption, ElText,ElInput,TableInstance,ElMessage,UploadFile, import {ElSelect,ElOption, ElText,ElInput,TableInstance,ElMessage,UploadFile,
UploadFiles,ElPagination,ElTree,TreeNode,ElDropdown,ElDropdownItem} from "element-plus"; UploadFiles,ElPagination,ElTree,TreeNode,ElDropdown,ElDropdownItem} from "element-plus";
import preview from './preview.vue'; import preview from './preview.vue';
import uploadlog from './uploadlog.vue';
import space from './space.vue'; import space from './space.vue';
import spacePermission from './spacePermission.vue'; import spacePermission from './spacePermission.vue';
import SvgIcon from "@/components/SvgIcon/index.vue"; import SvgIcon from "@/components/SvgIcon/index.vue";
@ -74,6 +76,7 @@ const modRecycling=ref(false)
// //
const percentage=ref(0) const percentage=ref(0)
const onprogress=ref(false) const onprogress=ref(false)
const tabName = ref('logs')
const Departs = computed(() => { const Departs = computed(() => {
return `${'p0'+userStore.userInfoCont.userId},${userStore.userInfoCont.company},${userStore.userInfoCont.department},${userStore.userInfoCont.organization}` return `${'p0'+userStore.userInfoCont.userId},${userStore.userInfoCont.company},${userStore.userInfoCont.department},${userStore.userInfoCont.organization}`
@ -294,10 +297,25 @@ function onSearchFile(name?:string){
// //
function showRecycling(){ function showRecycling(){
tabName.value="main"
modRecycling.value=true modRecycling.value=true
currentNode.value={} // currentNode.value={} //
if(!PRIVATESPACE.value) { // if(!PRIVATESPACE.value) { //
PRIVATESPACE.value=true 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)=>{ getRecyclingList(uid,{}).then((resp)=>{
@ -305,6 +323,7 @@ function showRecycling(){
matterList.value=resp.data matterList.value=resp.data
}) })
} }
// //
function restoreMatter(row:matterInfo){ function restoreMatter(row:matterInfo){
if (row.uuid){ if (row.uuid){
@ -431,6 +450,7 @@ function onNodeExpand(node: TreeNode, resolve: (data: matterTree[]) => void, rej
} }
// //
function onNodeClick(data:matterTree,node:TreeNode,self:any,env:any){ function onNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
tabName.value="main"
modRecycling.value=false modRecycling.value=false
if(!PRIVATESPACE.value) { if(!PRIVATESPACE.value) {
PRIVATESPACE.value=true PRIVATESPACE.value=true
@ -561,11 +581,16 @@ async function onCustomUpload(e:Event){
}) })
percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100 percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100
} }
if(result!="") alert(result) if(result!=""){
dynamicVNode.value=h(uploadlog,{
content:result
})
}
onLoadMatterList() // onLoadMatterList() //
} }
async function handleSingleFile(ff:File){ async function handleSingleFile(ff:File){
const fields=new FormData() const fields=new FormData()
fields.append("userUuid",uploadFormData.value.userUuid) fields.append("userUuid",uploadFormData.value.userUuid)
@ -575,7 +600,7 @@ async function handleSingleFile(ff:File){
const res = await doFileUpload(fields,'/hxpan/api/matter/upload') const res = await doFileUpload(fields,'/hxpan/api/matter/upload')
if(res.code!=200){ if(res.code!=200){
console.log(ff.name+"上传失败! ") console.log(ff.name+"上传失败! ")
throw new Error(ff.name+"上传失败!\n ") throw new Error(ff.name+"上传失败!<br> ")
} }
} }
@ -591,11 +616,15 @@ async function uploadFolder(e:Event){
const f = files[index] const f = files[index]
await handleFolderFile(f).catch((err)=>{ await handleFolderFile(f).catch((err)=>{
console.log(err) console.log(err)
result+= (f as File).name+"上传失败\n" result+= (f as File).name+"上传失败<br>"
}) })
percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100 percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100
} }
if(result!="") alert(result) if(result!=""){
dynamicVNode.value=h(uploadlog,{
content:result
})
}
onLoadMatterList() // onLoadMatterList() //
} }
@ -622,7 +651,7 @@ async function handleFolderFile(option:File){
puuid=newnodes[0].uuid puuid=newnodes[0].uuid
}else{ }else{
console.log(_path+"上传失败! ") console.log(_path+"上传失败! ")
throw new Error(_path +" 上传失败!\n ") throw new Error(_path +" 上传失败!<br> ")
return return
} }
}else{ }else{
@ -636,7 +665,7 @@ async function handleFolderFile(option:File){
const res = await doFileUpload(fields,'/hxpan/api/matter/upload') const res = await doFileUpload(fields,'/hxpan/api/matter/upload')
if(res.code!=200){ if(res.code!=200){
console.log(_path+"上传失败! ") console.log(_path+"上传失败! ")
throw new Error(_path +" 上传失败!\n ") throw new Error(_path +" 上传失败!<br> ")
} }
} }
@ -685,6 +714,7 @@ function handleSigLoadErr(error: Error, uploadFile: UploadFile, uploadFiles:Uplo
//-------------------space feature--------------------- //-------------------space feature---------------------
function onNewSpace(){ function onNewSpace(){
tabName.value="main"
const newname=ref("") const newname=ref("")
ElMessageBox({ ElMessageBox({
title:"请输入空间名称", title:"请输入空间名称",
@ -709,6 +739,7 @@ function onNewSpace(){
} }
function onSpaceNodeClick(data:matterTree,node:TreeNode,self:any,env:any){ function onSpaceNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
tabName.value="main"
if(PRIVATESPACE.value) { // if(PRIVATESPACE.value) { //
PRIVATESPACE.value=false PRIVATESPACE.value=false
modRecycling.value=false modRecycling.value=false
@ -835,6 +866,7 @@ onMounted(() => {
// {name:'',uuid:'root',dir:false}, // {name:'',uuid:'root',dir:false},
// currentNode.value.uuid // currentNode.value.uuid
// ) // )
currentNode.value={uuid:"root"} currentNode.value={uuid:"root"}
// //
getMySpaces(uid,{roles:Departs.value}).then((resp)=>{ getMySpaces(uid,{roles:Departs.value}).then((resp)=>{
@ -927,11 +959,16 @@ const handleSelectionChange = (val:matterInfo[]) => {
</div> </div>
</template> </template>
</el-tree> </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"> <div class="area_header" @click="showRecycling">
<el-icon :size="20"><Delete /></el-icon><span class="area_name" > </span> <el-icon :size="20"><Delete /></el-icon><span class="area_name" > </span>
</div> </div>
</div> </div>
<el-tabs v-model="tabName">
<el-tab-pane name="main">
<div v-if="PRIVATESPACE" class="app_container"> <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-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-row v-else :gutter="24" style="margin: 12px 0px;">
@ -1066,10 +1103,16 @@ const handleSelectionChange = (val:matterInfo[]) => {
:officeHost="officeHost" :site-host="siteHost" :api-u-r-l="apiURL" :officeHost="officeHost" :site-host="siteHost" :api-u-r-l="apiURL"
:flushSpaceTree="flushSpaceTree"></space> :flushSpaceTree="flushSpaceTree"></space>
</div> </div>
</el-tab-pane>
<el-tab-pane name="logs">
Comming soon....
</el-tab-pane>
</el-tabs>
<div v-if="dynamicVNode"> <div v-if="dynamicVNode">
<component :is="dynamicVNode" /> <component :is="dynamicVNode" />
</div> </div>
</div> </div>
</template> </template>
@ -1117,7 +1160,6 @@ const handleSelectionChange = (val:matterInfo[]) => {
margin: 0 5px 0 auto; margin: 0 5px 0 auto;
} }
.app_container { .app_container {
padding: 10px 30px 0px 30px; padding: 10px 30px 0px 30px;
height: calc(100% - 10px); height: calc(100% - 10px);
@ -1202,4 +1244,8 @@ const handleSelectionChange = (val:matterInfo[]) => {
:root{ :root{
--el-index-normal:auto; --el-index-normal:auto;
} }
.el-tabs__header{
display: none;
}
</style> </style>

25
src/views/doc/space.vue

@ -22,6 +22,7 @@ import {
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import {ElMessage,UploadFile,UploadFiles,ElPagination, ElProgress} from "element-plus"; import {ElMessage,UploadFile,UploadFiles,ElPagination, ElProgress} from "element-plus";
import aiagent from './agent.vue'; import aiagent from './agent.vue';
import uploadlog from './uploadlog.vue';
import router from "@/router"; import router from "@/router";
import SvgIcon from "@/components/SvgIcon/index.vue"; import SvgIcon from "@/components/SvgIcon/index.vue";
import { useOrgMemberStore } from "@/store/modules/orgMember"; import { useOrgMemberStore } from "@/store/modules/orgMember";
@ -331,6 +332,8 @@ function handleDoubleClick(row:matterInfo,ind?:number){
if(row.agent){ if(row.agent){
currentAgent.value={name:row.name,model:false,uuid:row.uuid,path:row.path} currentAgent.value={name:row.name,model:false,uuid:row.uuid,path:row.path}
}else if(row.uuid=="root"){
currentAgent.value={name:"通用AI",model:false,uuid:defaultAiAgent,path:"root"}
} }
isNewNode=true isNewNode=true
//1 //1
@ -391,7 +394,11 @@ async function onCustomUpload(e:Event){
}) })
percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100 percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100
} }
if(result!="") alert(result) if(result!=""){
dynamicVNode.value=h(uploadlog,{
content:result
})
}
onLoadMatterList() // onLoadMatterList() //
} }
@ -405,7 +412,7 @@ async function handleSingleFile(ff:File){
const res = await doFileUpload(fields,'/hxpan/api/space/upload') const res = await doFileUpload(fields,'/hxpan/api/space/upload')
if(res.code!=200){ if(res.code!=200){
console.log(ff.name+"上传失败! ") console.log(ff.name+"上传失败! ")
throw new Error(ff.name+"上传失败!\n ") throw new Error(ff.name+"上传失败!<br> ")
} }
//AI //AI
@ -424,11 +431,15 @@ async function uploadFolder(e:Event){
const f = files[index] const f = files[index]
await handleFolderFile(f).catch((err)=>{ await handleFolderFile(f).catch((err)=>{
console.log(err) console.log(err)
result+= (f as File).name+"上传失败\n" result+= (f as File).name+"上传失败<br>"
}) })
percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100 percentage.value = Number(((index + 1) / count).toPrecision(2)) * 100
} }
if(result!="") alert(result) if(result!=""){
dynamicVNode.value=h(uploadlog,{
content:result
})
}
onLoadMatterList() // onLoadMatterList() //
} }
@ -454,7 +465,7 @@ async function handleFolderFile(option:File){
puuid=newnodes[0].uuid puuid=newnodes[0].uuid
}else{ }else{
console.log(_path+"上传失败! ") console.log(_path+"上传失败! ")
throw new Error(_path +" 上传失败!\n ") throw new Error(_path +" 上传失败!<br> ")
} }
}else{ }else{
puuid=node[0].uuid puuid=node[0].uuid
@ -467,7 +478,7 @@ async function handleFolderFile(option:File){
const res = await doFileUpload(fields,'/hxpan/api/space/upload') const res = await doFileUpload(fields,'/hxpan/api/space/upload')
if(res.code!=200){ if(res.code!=200){
console.log(_path+"上传失败! ") console.log(_path+"上传失败! ")
throw new Error(_path+"上传失败!\n ") throw new Error(_path+"上传失败!<br> ")
} }
//AIhandleAiUpload //AIhandleAiUpload
@ -743,7 +754,7 @@ function isOwner(){
<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-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> </el-row>
<aiagent :agent="currentAgent" :userid="uid" :uuid="currentAgent" :closefunc="()=>{currentAgent.model=false}"></aiagent> <aiagent :agent="currentAgent" :userid="uid" :closefunc="()=>{currentAgent.model=false}"></aiagent>
<div v-if="dynamicVNode"> <div v-if="dynamicVNode">
<component :is="dynamicVNode" /> <component :is="dynamicVNode" />

78
src/views/doc/uploadlog.vue

@ -0,0 +1,78 @@
<!--
@ 作者: han2015
@ 时间: 2025-10-23 15:39:13
@ 备注: upload log组件
-->
<script lang="ts" setup>
import {logUploadError} from "@/api/doc/type"
const props = withDefaults(defineProps<{
content:string,
}>(),{})
const moduleDrawer =ref(true)
const logState=ref(false)
const onClosePopup=(done: (cancel?: boolean) => void) => {
if (logState.value) {
moduleDrawer.value=false
return
}
ElMessageBox.confirm("请确保错误日志安全保存!", "提示", {
confirmButtonText: "确定",
type: "warning",
}).then(()=>{
handleUploadErrors(props.content)
})
}
async function handleUploadErrors(result:string){
await logUploadError({content:result},'/hxpan/api/matter/uploadlog').then(data=>{
logState.value=true;
ElMessage({
message: '保存成功!',
type: 'success',
plain: true,
})
}).catch(err=>{
//
ElMessage({
message: '日志保存失败,开始重传!',
type: 'error',
plain: true,
})
setTimeout(()=>handleUploadErrors(props.content),2000)
})
}
//
onMounted(() => {
handleUploadErrors(props.content)
});
</script>
<template>
<el-drawer
:model-value="moduleDrawer"
title="上传错误记录:"
direction="rtl"
size="30%"
:close-on-click-modal="false"
destroy-on-close
:close-on-press-escape="false"
:before-close="onClosePopup"
:style="{padding:'17px',backgroundColor:'#f3f3f3'}">
<div v-html="content"></div>
</el-drawer>
</template>
<style lang="scss" scoped>
.app_container {
padding: 10px 30px 0px 30px;
height: 100%;
width: 100%;
}
</style>
Loading…
Cancel
Save