This commit is contained in:
sx1989827 2023-06-29 15:47:54 +08:00
parent d4874f0472
commit 3bd5997cb7
51 changed files with 6177 additions and 361 deletions

View File

@ -9,17 +9,20 @@
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/vue-fontawesome": "^3.0.3",
"@logicflow/core": "^1.1.31",
"blueimp-md5": "^2.19.0",
"eventemitter3": "^5.0.0",
"mediasoup-client": "^3.6.84",
"moment": "^2.29.4",
"moment-timezone": "^0.5.42",
"pinia": "^2.0.28",
"pinia": "^2.1.4",
"socket.io-client": "^4.6.1",
"uuid": "^9.0.0",
"vue": "^3.3.4",
"vue-router": "^4.1.6"
"vue-router": "^4.2.2"
},
"devDependencies": {
"@arco-design/web-vue": "^2.47.1",
@ -27,14 +30,14 @@
"@types/blueimp-md5": "^2.18.0",
"@types/node": "^18.15.11",
"@types/uuid": "^9.0.0",
"@vitejs/plugin-vue": "^4.2.0",
"@vitejs/plugin-vue": "^4.2.3",
"rollup-plugin-typescript2": "^0.34.1",
"tslib": "^2.4.1",
"ttypescript": "^1.5.15",
"typescript": "^4.6.4",
"vite": "^4.3.2",
"vite": "^4.3.9",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-typescript": "^1.0.4",
"vue-tsc": "^1.4.4"
"vue-tsc": "^1.8.2"
}
}

View File

@ -23,15 +23,15 @@
<script setup lang="ts">
import {apiIssue, apiOrganization, apiRelease, apiUser, DCSType} from "../../request/request";
import {
ICommon_Route_Req_ProjectIssue_Field,
ICommon_Route_Res_Project_filter_Item,
ICommon_Route_Res_Release_Item,
ICommon_Route_Res_Workflow_Node_Field
ICommon_Route_Req_ProjectIssue_Field,
ICommon_Route_Res_Project_Issue_filter_Item,
ICommon_Route_Res_Release_Item,
ICommon_Route_Res_Workflow_Node_Field
} from "../../../../../../common/routes/response";
import {ECommon_Field_Type} from "../../../../../../common/field/type";
import {ref} from "vue";
import {
ECommon_Model_Workflow_Node_Field_Type_Label_Type
ECommon_Model_Workflow_Node_Field_Type_Label_Type
} from "../../../../../../common/model/workflow_node_field_type";
import {SessionStorage} from "../../storage/session";
@ -89,12 +89,12 @@ switch (props.item.fieldType.type) {
default:
break
}
const multiLabelList=ref<DCSType<ICommon_Route_Res_Project_filter_Item[] | ICommon_Route_Res_Release_Item[] | {
const multiLabelList=ref<DCSType<ICommon_Route_Res_Project_Issue_filter_Item[] | ICommon_Route_Res_Release_Item[] | {
name:string,
id:string,
photo:string
}[]>>([])
const labelList=ref<DCSType<ICommon_Route_Res_Project_filter_Item[] | ICommon_Route_Res_Release_Item[] | {
const labelList=ref<DCSType<ICommon_Route_Res_Project_Issue_filter_Item[] | ICommon_Route_Res_Release_Item[] | {
name:string,
id:string,
photo:string

View File

@ -1,14 +1,21 @@
import {nextTick, reactive, Ref} from "vue";
import {ELineConfigType, Line, LineConfig, LineStyle} from "./type";
import {AppContext, nextTick, reactive, Ref} from "vue";
import {RichEditorHandle} from "./handle";
import {RichEditorFunc} from "./func";
import {
ECommon_Wiki_Content_Line_Config_Type,
ICommon_Wiki_Content_Line,
ICommon_Wiki_Content_Line_Config,
ICommon_Wiki_Content_Line_Style
} from "../../../../../../common/model/wiki_item_content";
import {renderComponent} from "../../../../teamOS/common/util/component";
import PopMenu from "./popMenu.vue";
export class RichEditorEvent {
private selectElementList:HTMLElement[]=[]
private isMouseDown=false
private root:Ref<HTMLElement>
private elementList:Ref<HTMLElement[]>
private lineList=reactive<Line[]>([])
private lineList=reactive<ICommon_Wiki_Content_Line[]>([])
private imageHelperElement:HTMLElement
private resizeImage:HTMLElement
private resizeObserver:ResizeObserver
@ -17,10 +24,33 @@ export class RichEditorEvent {
private selectionLinkElement:HTMLElement
private selectionColorElement:HTMLElement
private linkEditElement:HTMLElement
private popMenuElement:HTMLElement
private appContext:AppContext
private destroyFunc:()=>void
private popMenuList:{
type: any,
title: string
}[]=[]
onUploadFileFunc:(file:File,handleFunc:(fileId:string,path:string)=>void)=>void
onPopMenuClickFunc:(type:any,handleFunc:(item:ICommon_Wiki_Content_Line_Config)=>void)=>void
clear() {
this.destroyFunc?.()
this.imageHelperElement?.remove()
this.resizeObserver?.disconnect()
this.selectionMenuElement?.remove()
this.linkEditElement?.remove()
this.popMenuElement?.parentElement.removeChild(this.popMenuElement)
}
setPopMenuList(popMenuList:{
type: any,
title: string
}[]) {
this.popMenuList=popMenuList
}
getLineList(){
return this.lineList
}
setLineList(data:Line[]) {
setLineList(data:ICommon_Wiki_Content_Line[]) {
this.lineList.splice(0,this.lineList.length,...data)
}
getSelectElementList() {
@ -29,13 +59,13 @@ export class RichEditorEvent {
getRoot() {
return this.root
}
addLine(value:string,style?:LineStyle){
let line:Line={
addLine(value:string,style?:ICommon_Wiki_Content_Line_Style){
let line:ICommon_Wiki_Content_Line={
arr:[
{
value:value,
...style,
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
],
selectEndIndexPath:[],
@ -43,7 +73,7 @@ export class RichEditorEvent {
}
this.lineList.push(line)
}
onFocus(item:Line,event:MouseEvent){
onFocus(item:ICommon_Wiki_Content_Line, event:MouseEvent){
if(this.selectElementList.length==0) {
this.selectElementList=[event.currentTarget as HTMLElement]
}
@ -52,7 +82,7 @@ export class RichEditorEvent {
this.selectElementList=[event.currentTarget as HTMLElement]
}
onBlur(item:Line,event:FocusEvent){
onBlur(item:ICommon_Wiki_Content_Line, event:FocusEvent){
RichEditorHandle.handleInnerHtml(item,event.currentTarget as HTMLElement,true)
if(this.selectionMenuElement) {
if(!this.selectionMenuElement.contains(event.relatedTarget as HTMLElement) && !this.selectionLinkElement.contains(event.relatedTarget as HTMLElement)) {
@ -66,17 +96,17 @@ export class RichEditorEvent {
}
}
onEnter(line:Line,index:number,event:MouseEvent){
onEnter(line:ICommon_Wiki_Content_Line, index:number, event:MouseEvent){
event.preventDefault()
event.stopPropagation()
RichEditorHandle.handleInnerHtml(line,event.currentTarget as HTMLElement)
let obj=line.arr[line.selectStartIndexPath[0]]
let newLine:Line={
let newLine:ICommon_Wiki_Content_Line={
arr:line.arr.slice(line.selectStartIndexPath[0]+1),
selectStartIndexPath:[],
selectEndIndexPath:[]
}
if(!obj || obj.type!=ELineConfigType.IMAGE) {
if(!obj || obj.type!=ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
let value=obj?.value.substring(line.selectStartIndexPath[1])
if(value) {
newLine.arr.unshift({
@ -84,14 +114,14 @@ export class RichEditorEvent {
style:{
...obj.style
},
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
})
obj.value=obj.value.substring(0,line.selectStartIndexPath[1])
} else {
newLine.arr.unshift({
value:"",
style:{},
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
})
}
}
@ -108,7 +138,7 @@ export class RichEditorEvent {
selection.addRange(range)
})
}
onDelete(index,item:Line,event:MouseEvent){
onDelete(index, item:ICommon_Wiki_Content_Line, event:MouseEvent){
let ele=event.currentTarget as HTMLElement
let selection=window.getSelection()
let range=selection.getRangeAt(0)
@ -123,7 +153,7 @@ export class RichEditorEvent {
RichEditorHandle.handleInnerHtml(item,ele)
let preLine=this.lineList[index-1]
item.arr=item.arr.filter(obj=>{
return obj.value.length>0 || obj.type==ELineConfigType.IMAGE
return obj.value.length>0 || obj.type==ECommon_Wiki_Content_Line_Config_Type.IMAGE
})
let path=[preLine.arr.length-1>=0?preLine.arr.length-1:0,preLine.arr.length>0?preLine.arr[preLine.arr.length-1].value.length:0]
preLine.arr=preLine.arr.concat(item.arr)
@ -157,6 +187,9 @@ export class RichEditorEvent {
}
onMouseDown(event:MouseEvent){
if(this.popMenuElement) {
this.popMenuElement.style.display="none"
}
if(event.detail==1) {
this.selectElementList=[event.currentTarget as HTMLElement]
this.isMouseDown=true
@ -183,7 +216,9 @@ export class RichEditorEvent {
if(this.selectElementList.length>0) {
range=range.cloneRange()
for(let ele of this.selectElementList) {
ele.contentEditable="true"
nextTick(()=>{
ele.contentEditable="true"
})
}
let startContainer=range.startContainer as HTMLElement
while (startContainer.tagName!=="DIV") {
@ -632,7 +667,7 @@ export class RichEditorEvent {
let remove=this.linkEditElement.querySelector("[name='remove']") as HTMLElement
remove.onclick=()=>{
let item=this.lineList[lineIndex].arr[index]
item.type=ELineConfigType.TEXT
item.type=ECommon_Wiki_Content_Line_Config_Type.TEXT
delete item.link
RichEditorHandle.fixLine(this.lineList[lineIndex])
this.linkEditElement.style.display="none"
@ -641,34 +676,74 @@ export class RichEditorEvent {
}
}
onCopy(event:ClipboardEvent) {
const range=window.getSelection().getRangeAt(0)
const content=range.cloneContents()
const ele=document.createElement("div")
ele.setAttribute("copy","teamlinker")
ele.appendChild(content)
event.clipboardData.setData("text/html",ele.outerHTML)
event.preventDefault()
event.stopPropagation()
}
onPaste(event:ClipboardEvent) {
let types=event.clipboardData.types
let elementList=this.getSelectElementList()
if(types?.includes("text/html")) {
if(types.includes("Files")) {
let file=event.clipboardData.files[0]
if(file) {
let selection=window.getSelection()
let range=selection.getRangeAt(0)
let container=range.startContainer as HTMLElement
while (container.tagName!="DIV") {
container=container.parentElement
}
let startIndex=Array.from(this.root.value.children).indexOf(container)
let selectLine=this.lineList[startIndex]
RichEditorHandle.handleInnerHtml(selectLine,container,true)
this.onUploadFileFunc?.(file,(fileId, path) => {
let originItem=selectLine.arr[selectLine.selectStartIndexPath[0]]
let objItem:ICommon_Wiki_Content_Line_Config={
value:fileId,
link:path,
type:ECommon_Wiki_Content_Line_Config_Type.IMAGE
}
let index:number
if(originItem) {
let objTemp:ICommon_Wiki_Content_Line_Config=JSON.parse(JSON.stringify(originItem))
objTemp.value=objTemp.value.substring(selectLine.selectStartIndexPath[1])
let appendItemList=[...selectLine.arr.slice(selectLine.selectStartIndexPath[0]+1)]
if(objTemp.type!=ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
appendItemList.unshift(objTemp)
}
originItem.value=originItem.value.substring(0,selectLine.selectStartIndexPath[1])
selectLine.arr.splice(selectLine.selectStartIndexPath[0]+1,0,objItem)
index=selectLine.arr.length-1
selectLine.arr=selectLine.arr.concat(appendItemList)
} else {
selectLine.arr=[objItem]
index=0
}
RichEditorHandle.fixLine(selectLine)
nextTick(()=>{
let range=document.createRange()
range.selectNode(container.childNodes[index])
range.collapse(false)
selection.removeAllRanges()
selection.addRange(range)
})
})
}
} else if(types?.includes("text/html")) {
event.stopPropagation()
event.preventDefault();
let ele=document.createElement("div")
ele.innerHTML=event.clipboardData.getData("text/html")
let divList=ele.getElementsByTagName("div")
let isEdit=true
Array.from(divList).forEach(item=>{
if(item.contentEditable!="true") {
isEdit=false
}
})
let isPlain=false
if(divList.length==0) {
let spanList=ele.getElementsByTagName("span")
let linkList=ele.getElementsByTagName("a")
let imgList=ele.getElementsByTagName("img")
if(spanList.length+linkList.length+imgList.length==ele.children.length-1) {
isPlain=true
let div=ele.cloneNode(true) as HTMLDivElement
div.removeChild(div.firstChild)
let wrapper=document.createElement("div")
wrapper.appendChild(div)
divList=wrapper.getElementsByTagName("div")
}
let isFromTeamLinker=false
if(divList.length==1 && divList[0].getAttribute("copy")==="teamlinker") {
isFromTeamLinker=true
}
let selection=window.getSelection()
let range=selection.getRangeAt(0)
@ -679,10 +754,10 @@ export class RichEditorEvent {
let startIndex=Array.from(this.root.value.children).indexOf(container)
let selectLine=this.lineList[Array.from(this.root.value.children).indexOf(container)]
RichEditorHandle.handleInnerHtml(selectLine,container,true)
if((isEdit && divList.length==ele.children.length-1) || isPlain) {
let appendItemList:LineConfig[],indexPath:number[]=[];
if(isFromTeamLinker) {
let appendItemList:ICommon_Wiki_Content_Line_Config[],indexPath:number[]=[];
for(let i=0;i<divList.length;i++) {
let line:Line={
let line:ICommon_Wiki_Content_Line={
arr:[],
selectEndIndexPath:[],
selectStartIndexPath:[]
@ -693,7 +768,7 @@ export class RichEditorEvent {
let text="",isImg=false
selectLine.arr.forEach(item=>{
text+=item.value
if(item.type==ELineConfigType.IMAGE) {
if(item.type==ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
isImg=true
}
})
@ -701,10 +776,10 @@ export class RichEditorEvent {
this.lineList.splice(this.lineList.indexOf(selectLine),1,line)
} else {
let originItem=selectLine.arr[selectLine.selectStartIndexPath[0]]
let objTemp:LineConfig=JSON.parse(JSON.stringify(originItem))
let objTemp:ICommon_Wiki_Content_Line_Config=JSON.parse(JSON.stringify(originItem))
objTemp.value=objTemp.value.substring(selectLine.selectStartIndexPath[1])
appendItemList=[...selectLine.arr.slice(selectLine.selectStartIndexPath[0]+1)]
if(objTemp.type!=ELineConfigType.IMAGE) {
if(objTemp.type!=ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
appendItemList.unshift(objTemp)
}
originItem.value=originItem.value.substring(0,selectLine.selectStartIndexPath[1])
@ -772,7 +847,7 @@ export class RichEditorEvent {
} else {
selectLine.arr.push({
value:ele.innerText,
type:ELineConfigType.TEXT,
type:ECommon_Wiki_Content_Line_Config_Type.TEXT,
style:{}
})
nextTick(()=>{
@ -793,8 +868,10 @@ export class RichEditorEvent {
let ele=event.target as HTMLElement
if(ele.tagName=="IMG") {
let parent=ele.offsetParent;
let left=ele.offsetLeft
let top=ele.offsetTop
let parentRect=parent.getBoundingClientRect()
let rect=ele.getBoundingClientRect();
let left=rect.left-parentRect.left
let top=rect.top-parentRect.top
let width=ele.offsetWidth
let height=ele.offsetHeight
if(!this.imageHelperElement) {
@ -831,29 +908,121 @@ export class RichEditorEvent {
}
onKeyDown(event:KeyboardEvent) {
if((event.metaKey || event.ctrlKey) && event.key=="a") {
event.stopPropagation()
event.preventDefault()
if(event.metaKey || event.ctrlKey) {
if(event.key=="a") {
event.stopPropagation()
event.preventDefault()
let selection=window.getSelection()
let range=selection.getRangeAt(0)
let element=range.startContainer as HTMLElement
while (element.tagName!="DIV") {
element=element.parentElement
}
let index=Array.from(this.root.value.children).indexOf(element)
let line=this.lineList[index]
RichEditorHandle.handleInnerHtml(line,element)
this.selectElementList=Array.from(this.root.value.children) as HTMLElement[]
nextTick(()=>{
let range=document.createRange()
let startNode:Node,endNode:Node
for(let i=0;i<this.root.value.childNodes.length;i++) {
let node=this.root.value.childNodes[i]
for(let j=0;j<node.childNodes.length;j++) {
let node1=node.childNodes[j]
if(node1) {
startNode=node1
break
}
}
if(startNode) {
break
}
}
for(let i=this.root.value.childNodes.length-1;i>=0;i--) {
let node=this.root.value.childNodes[i]
for(let j=node.childNodes.length-1;j>=0;j--) {
let node1=node.childNodes[j]
if(node1) {
endNode=node1
break
}
}
if(endNode) {
break
}
}
if(startNode && endNode) {
range.setStartBefore(startNode)
range.setEndAfter(endNode)
selection.removeAllRanges()
selection.addRange(range)
}
})
}
}
if(event.key==="/") {
if(!this.popMenuList || this.popMenuList.length==0) {
return;
}
if(!this.popMenuElement) {
this.popMenuElement=document.createElement("div")
this.popMenuElement.style.position="absolute"
this.popMenuElement.style.display="none"
this.popMenuElement.style.boxShadow="0px 0px 2px 2px rgba(169, 169, 169, 0.2)"
this.popMenuElement.style.backgroundColor="white"
this.popMenuElement.style.border="1px solid rgba(169, 169, 169, 0.2)"
this.popMenuElement.style.width="150px"
this.popMenuElement.style.height="200px"
this.popMenuElement.style.overflow="auto"
this.popMenuElement.style.backgroundColor="white";
this.popMenuElement.style.outlineWidth="0"
this.popMenuElement.tabIndex=-1;
(event.target as HTMLElement).offsetParent.appendChild(this.popMenuElement)
this.popMenuElement.onclick=()=>{
this.popMenuElement.style.display="none"
}
this.destroyFunc=renderComponent(this.popMenuElement,PopMenu,this.appContext,{
objEditor:this,
popMenuList:this.popMenuList
});
}
let selection=window.getSelection()
let range=selection.getRangeAt(0)
let element=range.startContainer as HTMLElement
while (element.tagName!="DIV") {
element=element.parentElement
let rect=range.getBoundingClientRect()
if(rect.left==0 && rect.top==0 && rect.width==0 && rect.height==0) {
let ele=range.startContainer as HTMLElement
if(ele.tagName=="DIV" && range.startOffset!==0) {
return
}
rect=(range.startContainer as HTMLElement).getBoundingClientRect()
}
let parent=(event.target as HTMLElement).offsetParent
let rectParent=parent.getBoundingClientRect()
let right=rectParent.right-rect.right+rect.width
let bottom=rectParent.bottom-rect.bottom
let left:number,top:number
if(right>160) {
left=rect.left-rectParent.left
} else {
left=rect.left-rectParent.left-150
}
if(bottom>210) {
top=rect.top-rectParent.top+rect.height
} else {
top=rect.top-rectParent.top-200
}
this.popMenuElement.style.left=left+"px"
this.popMenuElement.style.top=top+"px"
this.popMenuElement.style.display="block"
} else {
if(this.popMenuElement) {
this.popMenuElement.style.display="none"
}
let index=Array.from(this.root.value.children).indexOf(element)
let line=this.lineList[index]
RichEditorHandle.handleInnerHtml(line,element)
nextTick(()=>{
let range=document.createRange()
range.setStartBefore(this.root.value.children[0].firstChild)
range.setEndAfter(this.root.value.children[this.root.value.children.length-1].lastChild)
selection.removeAllRanges()
selection.addRange(range)
})
}
}
constructor(root:Ref<HTMLElement>,elementList:Ref<HTMLElement[]>){
constructor(root:Ref<HTMLElement>,elementList:Ref<HTMLElement[]>,appContext:AppContext){
this.appContext=appContext
this.root=root;
this.elementList=elementList
document.addEventListener("keydown",(event:KeyboardEvent)=>{
@ -861,113 +1030,109 @@ export class RichEditorEvent {
if(this.selectElementList.length>0 && !window.getSelection().getRangeAt(0)?.collapsed) {
event.preventDefault()
event.stopPropagation()
let selection=window.getSelection()
let range=selection.getRangeAt(0)
let startOffset=range.startOffset
let endOffset=range.endOffset
let startContainer=range.startContainer as HTMLElement
let endContainer=range.endContainer as HTMLElement
let selectStartIndexPath=[],selectEndIndexPath=[]
selectStartIndexPath.unshift(startOffset)
if(startContainer.tagName=="DIV") {
selectStartIndexPath.unshift(0)
} else {
let parentElement=startContainer.parentElement
if(parentElement.tagName=="DIV") {
startOffset=Array.from(parentElement.childNodes).indexOf(startContainer as Element)
selectStartIndexPath.unshift(startOffset)
startContainer=parentElement
} else {
startOffset=Array.from(parentElement.parentElement.childNodes).indexOf(parentElement as Element)
selectStartIndexPath.unshift(startOffset)
startContainer=parentElement.parentElement
}
let list=this.getSelectionItemList()
let objStartInfo={
startItem:null,
startIndex:0
}
selectEndIndexPath.unshift(endOffset)
if(endContainer.tagName=="DIV") {
selectEndIndexPath.unshift(0)
} else {
let parentElement=endContainer.parentElement
if(parentElement.tagName=="DIV") {
endOffset=Array.from(parentElement.childNodes).indexOf(endContainer as Element)
selectEndIndexPath.unshift(endOffset)
endContainer=parentElement
if(list.length==1) {
let line=list[0].line
let startItem=line.arr[line.selectStartIndexPath[0]]
let endItem=line.arr[line.selectEndIndexPath[0]]
if(startItem===endItem) {
if(startItem.type===ECommon_Wiki_Content_Line_Config_Type.TEXT || startItem.type===ECommon_Wiki_Content_Line_Config_Type.LINK) {
let start=startItem.value.substring(0,line.selectStartIndexPath[1])
let end=startItem.value.substring(line.selectEndIndexPath[1])
startItem.value=start+end
} else {
line.arr.splice(line.selectStartIndexPath[0],1)
}
objStartInfo.startItem=startItem
objStartInfo.startIndex=line.selectStartIndexPath[1]
} else {
endOffset=Array.from(parentElement.parentElement.childNodes).indexOf(parentElement as Element)
selectEndIndexPath.unshift(endOffset)
endContainer=parentElement.parentElement
}
}
if(startContainer==endContainer) {
let index=Array.from(root.value.children).indexOf(startContainer as HTMLElement)
let line=this.lineList[index];
RichEditorHandle.handleInnerHtml(line,startContainer as HTMLElement)
if(line.selectEndIndexPath[0]>line.selectStartIndexPath[0]) {
let startItem=line.arr[line.selectStartIndexPath[0]]
let endItem=line.arr[line.selectEndIndexPath[0]]
for(let i=line.selectStartIndexPath[0]; i<=line.selectEndIndexPath[0]; i++) {
let item=line.arr[i]
if(item==startItem) {
if(item.type==ELineConfigType.IMAGE) {
line.arr.splice(i,1)
i--;
line.selectEndIndexPath[0]--
for(let obj of list[0].data) {
if(obj===startItem) {
if(startItem.type===ECommon_Wiki_Content_Line_Config_Type.TEXT || startItem.type===ECommon_Wiki_Content_Line_Config_Type.LINK) {
startItem.value=startItem.value.substring(0,line.selectStartIndexPath[1])
} else {
item.value=item.value.substring(0,line.selectStartIndexPath[1])
line.arr.splice(line.arr.indexOf(startItem),1)
}
} else if(item==endItem) {
if(item.type==ELineConfigType.IMAGE) {
line.arr.splice(i,1)
i--;
line.selectEndIndexPath[0]--
objStartInfo.startItem=startItem
objStartInfo.startIndex=line.selectStartIndexPath[1]
} else if(obj===endItem) {
if(endItem.type===ECommon_Wiki_Content_Line_Config_Type.TEXT || endItem.type===ECommon_Wiki_Content_Line_Config_Type.LINK) {
endItem.value=endItem.value.substring(line.selectEndIndexPath[1])
} else {
item.value=item.value.substring(line.selectEndIndexPath[1])
line.arr.splice(line.arr.indexOf(endItem),1)
}
} else {
line.arr.splice(i,1)
i--;
line.selectEndIndexPath[0]--
line.arr.splice(line.arr.indexOf(obj),1)
}
}
RichEditorHandle.fixLine(line)
} else {
let item=line.arr[line.selectStartIndexPath[0]]
item.value=item.value.substring(0,line.selectStartIndexPath[1])+item.value.substring(line.selectEndIndexPath[1])
RichEditorHandle.fixLine(line)
}
RichEditorHandle.fixLine(line,objStartInfo)
} else {
let indexStart=Array.from(root.value.children).indexOf(startContainer as HTMLElement)
let indexEnd=Array.from(root.value.children).indexOf(endContainer as HTMLElement)
let lineStart=this.lineList[indexStart];
let lineEnd=this.lineList[indexEnd]
let startItem=lineStart.arr[selectStartIndexPath[0]]
let endItem=lineEnd.arr[selectEndIndexPath[0]]
lineStart.arr.splice(selectStartIndexPath[0]+1)
lineEnd.arr.splice(0,selectEndIndexPath[0])
this.lineList.splice(indexStart+1,indexEnd-indexStart)
startItem.value=startItem.value.substring(0,selectStartIndexPath[1])
endItem.value=endItem.value.substring(selectEndIndexPath[1])
lineStart.arr=lineStart.arr.concat(lineEnd.arr)
RichEditorHandle.fixLine(lineStart)
let startLine:ICommon_Wiki_Content_Line
for(let i=0;i<list.length;i++) {
let obj=list[i]
let line=obj.line
if(i==0 || i==list.length-1) {
if(i==0) {
startLine=line
for(let i=0;i<obj.data.length;i++) {
let startItem=obj.data[i]
if(i==0) {
if(startItem.type===ECommon_Wiki_Content_Line_Config_Type.TEXT || startItem.type===ECommon_Wiki_Content_Line_Config_Type.LINK) {
startItem.value=startItem.value.substring(0,line.selectStartIndexPath[1])
} else {
line.arr.splice(line.arr.indexOf(startItem),1)
}
} else {
line.arr.splice(line.arr.indexOf(startItem),1)
}
}
} else {
for(let i=0;i<obj.data.length;i++) {
let endItem=obj.data[i]
if(i==obj.data.length-1) {
if(endItem.type===ECommon_Wiki_Content_Line_Config_Type.TEXT || endItem.type===ECommon_Wiki_Content_Line_Config_Type.LINK) {
endItem.value=endItem.value.substring(line.selectEndIndexPath[1])
} else {
line.arr.splice(line.arr.indexOf(endItem),1)
}
} else {
line.arr.splice(line.arr.indexOf(endItem),1)
}
}
startLine.arr=startLine.arr.concat(line.arr)
this.lineList.splice(this.lineList.indexOf(line),1)
RichEditorHandle.fixLine(startLine)
}
} else {
this.lineList.splice(this.lineList.indexOf(line),1)
}
}
}
nextTick(()=>{
let selection=window.getSelection()
let range=document.createRange()
let ele=startContainer.childNodes[selectStartIndexPath[0]] as HTMLElement
let index=this.lineList.indexOf(list[0].line)
let ele=this.root.value.children[index].childNodes[list[0].line.selectStartIndexPath[0]] as HTMLElement
if(!ele) {
return
}
if(ele.nodeType==Node.TEXT_NODE) {
range.setStart(ele,selectStartIndexPath[1])
range.setEnd(ele,selectStartIndexPath[1])
} else if(ele.nodeType==Node.ELEMENT_NODE) {
if(ele.tagName=="IMG") {
range.selectNode(ele)
range.collapse(false)
if(ele.nodeType===Node.TEXT_NODE || ele.tagName==="SPAN") {
let text=ele.tagName==="SPAN"?ele.innerText:ele.textContent
if(list[0].line.selectStartIndexPath[1]>=text.length) {
range.setStartAfter(ele)
range.setEndAfter(ele)
} else {
range.setStart(ele.firstChild,selectStartIndexPath[1])
range.setEnd(ele.firstChild,selectStartIndexPath[1])
range.setStart(ele,list[0].line.selectStartIndexPath[1])
range.setEnd(ele,list[0].line.selectStartIndexPath[1])
}
} else {
range.setStartAfter(ele)
range.setEndAfter(ele)
}
selection.removeAllRanges()
selection.addRange(range)
@ -1046,8 +1211,11 @@ export class RichEditorEvent {
}
})
}
getSelectionItemList():LineConfig[] {
let selectLineList:Line[]=[]
getSelectionItemList():{
line:ICommon_Wiki_Content_Line,
data:ICommon_Wiki_Content_Line_Config[]
}[] {
let selectLineList:ICommon_Wiki_Content_Line[]=[]
this.selectElementList.forEach((ele)=>{
selectLineList.push(this.lineList[Array.from(this.root.value.children).indexOf(ele)])
})
@ -1066,9 +1234,11 @@ export class RichEditorEvent {
if(parentElement.tagName=="DIV") {
startOffset=Array.from(parentElement.childNodes).indexOf(startContainer as Element)
selectStartIndexPath.unshift(startOffset)
startContainer=parentElement
} else {
startOffset=Array.from(parentElement.parentElement.childNodes).indexOf(parentElement as Element)
selectStartIndexPath.unshift(startOffset)
startContainer=parentElement.parentElement
}
}
if(endContainer.tagName=="DIV") {
@ -1079,31 +1249,57 @@ export class RichEditorEvent {
if(parentElement.tagName=="DIV") {
endOffset=Array.from(parentElement.childNodes).indexOf(endContainer as Element)
selectEndIndexPath.unshift(endOffset)
endContainer=parentElement
} else {
endOffset=Array.from(parentElement.parentElement.childNodes).indexOf(parentElement as Element)
selectEndIndexPath.unshift(endOffset)
endContainer=parentElement.parentElement
}
}
let startLine=selectLineList[0]
startLine.selectStartIndexPath=selectStartIndexPath
let endLine=selectLineList[selectLineList.length-1]
let itemList:LineConfig[]=[]
endLine.selectEndIndexPath=selectEndIndexPath
let itemList:{
line:ICommon_Wiki_Content_Line,
data:ICommon_Wiki_Content_Line_Config[]
}[]=[]
if(selectLineList.length==1) {
itemList=[...selectLineList[0].arr.slice(selectStartIndexPath[0],selectEndIndexPath[0]+1)]
RichEditorHandle.handleInnerHtml(selectLineList[0],startContainer)
itemList=[{
line:selectLineList[0],
data:[...selectLineList[0].arr.slice(selectStartIndexPath[0],selectEndIndexPath[0]+1)]
}]
} else {
for(let line of selectLineList) {
let obj=itemList.find(value => {
return value.line===line
})
if(!obj) {
obj={
line:line,
data:[]
}
itemList.push(obj)
}
if(line==startLine) {
RichEditorHandle.handleInnerHtml(line,startContainer)
for (let i = selectStartIndexPath[0]; i < startLine.arr.length; i++) {
let item=startLine.arr[i]
itemList.push(item)
obj.data.push(item)
}
} else if(line==endLine) {
RichEditorHandle.handleInnerHtml(line,endContainer)
for (let i = 0; i <= selectEndIndexPath[0]; i++) {
let item=endLine.arr[i]
itemList.push(item)
if(item) {
obj.data.push(item)
}
}
} else {
RichEditorHandle.handleInnerHtml(line,this.root.value.children[this.lineList.indexOf(line)] as HTMLElement)
line.arr.forEach(item=>{
itemList.push(item)
obj.data.push(item)
})
}
}
@ -1112,9 +1308,9 @@ export class RichEditorEvent {
}
getCurrentInfo():{
element:HTMLElement,
line:Line,
line:ICommon_Wiki_Content_Line,
lineIndex:number,
item:LineConfig,
item:ICommon_Wiki_Content_Line_Config,
itemIndex
}{
let selection=window.getSelection()
@ -1126,7 +1322,7 @@ export class RichEditorEvent {
}
let lineIndex=Array.from(this.root.value.children).indexOf(element)
let line=this.lineList[lineIndex]
let item:LineConfig,itemIndex:number
let item:ICommon_Wiki_Content_Line_Config,itemIndex:number
if(container!=element) {
itemIndex=Array.from(element.childNodes).indexOf(container as HTMLElement)
item=line.arr[itemIndex]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

View File

@ -1,7 +1,11 @@
import {ELineConfigType, Line, LineConfig} from "./type";
import {nextTick} from "vue";
import {RichEditorEvent} from "./event";
import {RichEditorHandle} from "./handle";
import {
ECommon_Wiki_Content_Line_Config_Type,
ICommon_Wiki_Content_Line,
ICommon_Wiki_Content_Line_Config
} from "../../../../../../common/model/wiki_item_content";
function handleLink(obj:RichEditorEvent,link:string) {
let lineList=obj.getLineList()
@ -10,11 +14,11 @@ function handleLink(obj:RichEditorEvent,link:string) {
if(selectElementList.length==1) {
let selectLine=lineList[Array.from(root.value.children).indexOf(selectElementList[0])]
if(selectLine.selectStartIndexPath.length>0 && selectLine.selectEndIndexPath.length>0) {
let newItem:LineConfig,newItemValueLength:number
let newItem:ICommon_Wiki_Content_Line_Config,newItemValueLength:number
if(selectLine.selectEndIndexPath[0]>selectLine.selectStartIndexPath[0]) {
let startItem=selectLine.arr[selectLine.selectStartIndexPath[0]]
let endItem=selectLine.arr[selectLine.selectEndIndexPath[0]]
let newItem:LineConfig
let newItem:ICommon_Wiki_Content_Line_Config
for(let i=selectLine.selectStartIndexPath[0]; i<=selectLine.selectEndIndexPath[0]; i++) {
let item=selectLine.arr[i]
if(item==startItem) {
@ -24,7 +28,7 @@ function handleLink(obj:RichEditorEvent,link:string) {
...item.style
},
value: item.value.substring(selectLine.selectStartIndexPath[1]),
type:ELineConfigType.LINK,
type:ECommon_Wiki_Content_Line_Config_Type.LINK,
link:link
}
item.value=item.value.substring(0,selectLine.selectStartIndexPath[1])
@ -34,7 +38,7 @@ function handleLink(obj:RichEditorEvent,link:string) {
startItem=newItem
} else {
item.link=link
item.type=ELineConfigType.LINK
item.type=ECommon_Wiki_Content_Line_Config_Type.LINK
startItem=item
}
} else if(item==endItem) {
@ -64,7 +68,7 @@ function handleLink(obj:RichEditorEvent,link:string) {
if(!item) {
return;
}
let startIndex:number,endIndex:number,selectItem:LineConfig
let startIndex:number,endIndex:number,selectItem:ICommon_Wiki_Content_Line_Config
let str=item.value.substring(selectLine.selectStartIndexPath[1],selectLine.selectEndIndexPath[1])
let startStr=item.value.substring(0,selectLine.selectStartIndexPath[1])
let endStr=item.value.substring(selectLine.selectEndIndexPath[1])
@ -74,7 +78,7 @@ function handleLink(obj:RichEditorEvent,link:string) {
style:{
...item.style
},
type:ELineConfigType.LINK,
type:ECommon_Wiki_Content_Line_Config_Type.LINK,
link:link
}
selectLine.arr.splice(selectLine.selectStartIndexPath[0]+1,0,newItem,{
@ -113,8 +117,8 @@ function handleImage(obj:RichEditorEvent,link:string,width:number) {
let selectLine=lineList[Array.from(root.value.children).indexOf(selectElementList[0])]
RichEditorHandle.handleInnerHtml(selectLine,root.value.children[lineList.indexOf(selectLine)] as HTMLElement)
if(selectLine.arr.length==0) {
let imageItem:LineConfig={
type:ELineConfigType.IMAGE,
let imageItem:ICommon_Wiki_Content_Line_Config={
type:ECommon_Wiki_Content_Line_Config_Type.IMAGE,
link,
value:"",
width
@ -122,14 +126,14 @@ function handleImage(obj:RichEditorEvent,link:string,width:number) {
selectLine.arr.push(imageItem)
} else {
let selectItem=selectLine.arr[selectLine.selectStartIndexPath[0]]
let imageItem:LineConfig={
type:ELineConfigType.IMAGE,
let imageItem:ICommon_Wiki_Content_Line_Config={
type:ECommon_Wiki_Content_Line_Config_Type.IMAGE,
value:"",
link,
width
}
let newItem:LineConfig=JSON.parse(JSON.stringify(selectItem))
if(newItem.type==ELineConfigType.IMAGE) {
let newItem:ICommon_Wiki_Content_Line_Config=JSON.parse(JSON.stringify(selectItem))
if(newItem.type==ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
selectLine.arr.splice(selectLine.selectStartIndexPath[0]+1,0,imageItem)
} else {
newItem.value=newItem.value.substring(selectLine.selectStartIndexPath[1])
@ -147,7 +151,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
if(selectElementList.length==1) {
let selectLine=lineList[Array.from(root.value.children).indexOf(selectElementList[0])]
if(selectLine.selectStartIndexPath.length>0 && selectLine.selectEndIndexPath.length>0) {
let newItem:LineConfig,newItemValueLength:number
let newItem:ICommon_Wiki_Content_Line_Config,newItemValueLength:number
if(selectLine.selectEndIndexPath[0]>selectLine.selectStartIndexPath[0]) {
let startItem=selectLine.arr[selectLine.selectStartIndexPath[0]]
let endItem=selectLine.arr[selectLine.selectEndIndexPath[0]]
@ -156,14 +160,14 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
let item=selectLine.arr[i]
if(item==startItem) {
if(selectLine.selectStartIndexPath[1]>0 && item.style[style]!=value) {
let newItem:LineConfig
let newItem:ICommon_Wiki_Content_Line_Config
if(value) {
newItem = {
style: Object.assign({}, item.style, {
[style]: value
}),
value: item.value.substring(selectLine.selectStartIndexPath[1]),
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
} else {
let objStyle={
@ -173,7 +177,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
newItem = {
style: objStyle,
value: item.value.substring(selectLine.selectStartIndexPath[1]),
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
}
item.value=item.value.substring(0,selectLine.selectStartIndexPath[1])
@ -195,14 +199,14 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
}
} else if(item==endItem) {
if(selectLine.selectEndIndexPath[1]<item.value.length && item.style[style]!=value) {
let newItem:LineConfig
let newItem:ICommon_Wiki_Content_Line_Config
if(value) {
newItem={
style:Object.assign({},item.style,{
[style]:value
}),
value:item.value.substring(0,selectLine.selectEndIndexPath[1]),
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
} else {
let objStyle={
@ -212,7 +216,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
newItem = {
style: objStyle,
value: item.value.substring(0,selectLine.selectEndIndexPath[1]),
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
}
item.value=item.value.substring(selectLine.selectEndIndexPath[1])
@ -280,7 +284,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
if(!item) {
return;
}
let startIndex:number,endIndex:number,selectItem:LineConfig
let startIndex:number,endIndex:number,selectItem:ICommon_Wiki_Content_Line_Config
if(item.style[style]!=value) {
let str=item.value.substring(selectLine.selectStartIndexPath[1],selectLine.selectEndIndexPath[1])
let startStr=item.value.substring(0,selectLine.selectStartIndexPath[1])
@ -306,7 +310,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
style:{
...item.style
},
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
})
RichEditorHandle.fixLine(selectLine)
startIndex=0;
@ -341,7 +345,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
}
}
} else if(selectElementList.length>1) {
let selectLineList:Line[]=[]
let selectLineList:ICommon_Wiki_Content_Line[]=[]
selectElementList.forEach((ele)=>{
selectLineList.push(lineList[Array.from(root.value.children).indexOf(ele)])
})
@ -382,7 +386,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
let endLine=selectLineList[selectLineList.length-1]
let startItem=startLine.arr[selectStartIndexPath[0]]
let endItem=endLine.arr[selectEndIndexPath[0]]
let startSelectItem:LineConfig=startItem,endSelectItem:LineConfig=endItem
let startSelectItem:ICommon_Wiki_Content_Line_Config=startItem,endSelectItem:ICommon_Wiki_Content_Line_Config=endItem
let startIndex,endIndex
for(let line of selectLineList) {
if(line==startLine) {
@ -390,7 +394,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
let item=startLine.arr[i]
if(item.style[style]!=value) {
if(item==startItem) {
let newItem:LineConfig
let newItem:ICommon_Wiki_Content_Line_Config
if(value) {
newItem={
value:item.value.substring(selectStartIndexPath[1]),
@ -398,7 +402,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
...item.style,
[style]:value
},
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
} else {
let objStyle={
@ -408,7 +412,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
newItem={
value:item.value.substring(selectStartIndexPath[1]),
style:objStyle,
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
}
item.value=item.value.substring(0,selectStartIndexPath[1])
@ -434,7 +438,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
let item=endLine.arr[i]
if(item.style[style]!=value) {
if(item==endItem) {
let newItem:LineConfig
let newItem:ICommon_Wiki_Content_Line_Config
if(value) {
newItem={
value:item.value.substring(0,selectEndIndexPath[1]),
@ -442,7 +446,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
...item.style,
[style]:value
},
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
} else {
let objStyle={
@ -452,7 +456,7 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
newItem={
value:item.value.substring(0,selectEndIndexPath[1]),
style:objStyle,
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
}
item.value=item.value.substring(selectEndIndexPath[1])
@ -528,7 +532,12 @@ function handle(obj:RichEditorEvent,style:string,value:string) {
}
function check(obj:RichEditorEvent,style:string,value:string):boolean {
let itemList=obj.getSelectionItemList()
let itemList=[]
obj.getSelectionItemList().forEach(item=>{
itemList.push(...item.data.filter(item=>{
return item.type===ECommon_Wiki_Content_Line_Config_Type.TEXT || item.type===ECommon_Wiki_Content_Line_Config_Type.LINK
}))
})
for(let item of itemList) {
if(!item.style || item.style[style]!=value) {
return false

View File

@ -1,71 +1,105 @@
import {nextTick, toRaw} from "vue";
import {ELineConfigType, Line, LineConfig} from "./type";
import {
ECommon_Wiki_Content_Line_Config_Type,
ICommon_Wiki_Content_Line,
ICommon_Wiki_Content_Line_Config
} from "../../../../../../common/model/wiki_item_content";
export class RichEditorHandle {
static handle(line:Line){
static handle(line:ICommon_Wiki_Content_Line){
let str=""
for(let obj of line.arr) {
let ele:HTMLElement
if(obj.type==ELineConfigType.TEXT) {
if(obj.type==ECommon_Wiki_Content_Line_Config_Type.TEXT) {
if(obj.style && Object.keys(obj.style).length>0) {
ele=document.createElement("span")
ele.innerText=obj.value
if(obj.style) {
for(let key in obj.style) {
ele.style[key]=obj.style[key]
}
}
} else {
str+=obj.value
continue;
}
} else if(obj.type==ELineConfigType.LINK) {
} else if(obj.type==ECommon_Wiki_Content_Line_Config_Type.LINK) {
ele=document.createElement("a")
ele.setAttribute("href",obj.link)
ele.setAttribute("target","_blank")
ele.style.cursor="pointer"
} else if(obj.type==ELineConfigType.IMAGE) {
ele.innerText=obj.value
if(obj.style) {
for(let key in obj.style) {
ele.style[key]=obj.style[key]
}
}
} else if(obj.type==ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
ele=document.createElement("img")
ele.setAttribute("src",obj.link)
ele.setAttribute("width",String(obj.width))
ele.setAttribute("height","auto")
}
if(obj.style) {
for(let key in obj.style) {
ele.style[key]=obj.style[key]
}
ele.setAttribute("fileId",obj.value)
} else if(obj.type===ECommon_Wiki_Content_Line_Config_Type.FILE) {
ele=document.createElement("a")
ele.setAttribute("href",obj.link)
ele.setAttribute("download",obj.label)
ele.setAttribute("fileId",obj.value)
ele.style.margin="0 2px 0 2px"
ele.style.cursor="pointer"
ele.contentEditable="false"
ele.innerText=obj.label
ele.style.color="black"
let icon=document.createElement("i")
icon.className="fa fa-file"
icon.style.marginRight="5px"
icon.style.color="gray"
ele.prepend(icon)
}
if(ele) {
ele.innerText=obj.value
str+=ele.outerHTML
}
}
return str;
}
static handleInnerHtml(item:Line,root:HTMLElement,relocate:boolean=false){
static handleInnerHtml(item:ICommon_Wiki_Content_Line, root:HTMLElement, relocate:boolean=false){
item.selectEndIndexPath=[]
item.selectStartIndexPath=[]
item.arr=[]
root.childNodes.forEach(node=>{
if(node.nodeType==Node.TEXT_NODE) {
let objTemp:LineConfig={
let objTemp:ICommon_Wiki_Content_Line_Config={
value:node.nodeValue,
style:{},
type:ELineConfigType.TEXT
type:ECommon_Wiki_Content_Line_Config_Type.TEXT
}
item.arr.push(objTemp)
} else if(node.nodeType==Node.ELEMENT_NODE) {
let obj=<LineConfig>{
let obj=<ICommon_Wiki_Content_Line_Config>{
value:"",
style:{}
}
let ele=node as HTMLElement
if(ele.tagName=="SPAN") {
obj.type=ELineConfigType.TEXT
obj.type=ECommon_Wiki_Content_Line_Config_Type.TEXT
obj.value=ele.innerText??""
} else if(ele.tagName=="A") {
obj.type=ELineConfigType.LINK
obj.link=ele.getAttribute("href")
obj.value=ele.innerText??""
let fileId=ele.getAttribute("fileId")
if(fileId) {
obj.type=ECommon_Wiki_Content_Line_Config_Type.FILE
obj.link=ele.getAttribute("href")
obj.value=fileId
obj.label=ele.innerText??""
} else {
obj.type=ECommon_Wiki_Content_Line_Config_Type.LINK
obj.link=ele.getAttribute("href")
obj.value=ele.innerText??""
}
} else if(ele.tagName=="IMG") {
obj.type=ELineConfigType.IMAGE
obj.type=ECommon_Wiki_Content_Line_Config_Type.IMAGE
obj.link=ele.getAttribute("src")
obj.width=parseInt(ele.getAttribute("width"))
obj.value=ele.getAttribute("fileId")
}
if(ele.style?.color) {
obj.style.color=ele.style?.color
@ -99,7 +133,7 @@ export class RichEditorHandle {
if(startContainer.childNodes.length==0 || startOffset>=startContainer.childNodes.length) {
item.selectStartIndexPath=[startOffset>0?(startOffset-1):0,startContainer.childNodes.length==0?0:(startContainer.lastChild.textContent?startContainer.lastChild.textContent.length:0)]
} else {
item.selectStartIndexPath=[startOffset,startContainer.lastChild.textContent.length]
item.selectStartIndexPath=[startOffset,0]
}
} else {
item.selectStartIndexPath.unshift(startOffset)
@ -180,11 +214,11 @@ export class RichEditorHandle {
})
}
}
static fixLine(line:Line,objItem?:{startItem?:LineConfig,endItem?:LineConfig,startIndex?:number,endIndex?:number}){
static fixLine(line:ICommon_Wiki_Content_Line, objItem?:{startItem?:ICommon_Wiki_Content_Line_Config,endItem?:ICommon_Wiki_Content_Line_Config,startIndex?:number,endIndex?:number}){
for(let i=1;i<line.arr.length;i++) {
let obj=line.arr[i]
let preObj=line.arr[i-1]
if((obj.style && preObj.style && JSON.stringify(obj.style)===JSON.stringify(preObj.style) && obj.type==preObj.type && obj.type==ELineConfigType.TEXT) || (!obj.value && obj.type!=ELineConfigType.IMAGE)) {
if((obj.style && preObj.style && JSON.stringify(obj.style)===JSON.stringify(preObj.style) && obj.type==preObj.type && obj.type==ECommon_Wiki_Content_Line_Config_Type.TEXT) || (!obj.value && obj.type!=ECommon_Wiki_Content_Line_Config_Type.IMAGE)) {
let len=preObj.value.length
preObj.value+=obj.value
line.arr.splice(i,1)
@ -203,7 +237,7 @@ export class RichEditorHandle {
}
for(let i=0;i<line.arr.length;i++) {
let obj=line.arr[i]
if(!obj.value && obj.type!=ELineConfigType.IMAGE) {
if(!obj.value && obj.type!=ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
line.arr.splice(i,1)
i--
}

View File

@ -0,0 +1,46 @@
<template>
<div style="width: 100%">
<div class="item" v-for="(item,index) in list" style="height: 35px;display:flex;align-items: center;justify-content: center;cursor: pointer" :key="item.type" :style="{borderBottom:index!==list.length-1?'rgb(241,241,241) 1px solid':''}" @mousedown="onClick(item)">
{{item.title}}
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from "vue";
import {ICommon_Wiki_Content_Line_Config} from "../../../../../../common/model/wiki_item_content";
import {RichEditorEvent} from "./event";
import {RichEditorHandle} from "./handle";
const props=defineProps<{
objEditor:RichEditorEvent,
popMenuList?:{
type: any,
title: string
}[]
}>()
const list = ref(props.popMenuList??[])
const onClick=async (item:{
type:any,
title:string
})=>{
let itemList=props.objEditor.getSelectionItemList()
let selectStartIndexPath=JSON.parse(JSON.stringify(itemList[0].line.selectStartIndexPath))
props.objEditor.onPopMenuClickFunc?.(item.type,item1 => {
let line=itemList[0].line
let originItem=line.arr[selectStartIndexPath[0]]
let objTemp:ICommon_Wiki_Content_Line_Config=JSON.parse(JSON.stringify(originItem))
objTemp.value=originItem.value.substring(selectStartIndexPath[1])
originItem.value=originItem.value.substring(0,selectStartIndexPath[1]-1)
line.arr.splice(selectStartIndexPath[0]+1,0,item1,objTemp)
RichEditorHandle.fixLine(line)
})
}
</script>
<style scoped>
.item:hover {
background-color: lightgray;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div>
<div ref="root" @mouseover="onMouseOver" @keydown="onKeyDown" style="padding: 10px" :style="{border:border?'border: 1px solid lightgray;':'0px'}">
<div ref="root" @mouseover="onMouseOver" @keydown="onKeyDown" style="padding: 10px" :style="{border:border?'border: 1px solid lightgray;':'0px'}" @copy="onCopy">
<div v-for="(item,index) in lineList" :key="index" contenteditable="true" @blur="onBlur(item,$event)" ref="elementList" v-html="RichEditorHandle.handle(item)" @keydown.enter="onEnter(item,index,$event)" @keydown.delete="onDelete(index,item,$event)" style="line-height: 1.5" @focus="onFocus(item,$event)" @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onMouseMove" @dblclick="onDbClick" @paste="onPaste" placeholder="type your content" v-if="!readonly">
</div>
<div v-for="(item,index) in lineList" :key="index+1" v-html="RichEditorHandle.handle(item)" style="line-height: 1.5;min-height: 21px" v-else>
@ -10,53 +10,96 @@
</template>
<script setup lang="ts">
import {ref, watch} from "vue";
import {getCurrentInstance, onBeforeUnmount, ref, watch} from "vue";
import {RichEditorEvent} from "./event";
import {Line} from "./type";
import {RichEditorHandle} from "./handle";
import {
ICommon_Wiki_Content_Line,
ICommon_Wiki_Content_Line_Config
} from "../../../../../../common/model/wiki_item_content";
import "./font/css/font-awesome.min.css"
const emit=defineEmits<{
(e:"update:modelValue",value:Line[]):void
(e:"update:modelValue",value:ICommon_Wiki_Content_Line[]):void
(e:"uploadFile", file:File, handleFunc:(fileId:string, path:string)=>void):void
(e:"popMenuClick",type:any,handleFunc:(item:ICommon_Wiki_Content_Line_Config)=>void):void
}>()
const props=defineProps<{
readonly?:boolean,
modelValue:Line[],
border?:boolean
modelValue:ICommon_Wiki_Content_Line[],
border?:boolean,
popMenuList?:{
type: any,
title: string
}[]
}>()
const root=ref<HTMLElement>()
const elementList=ref<HTMLElement[]>([])
const objEditor=new RichEditorEvent(root,elementList)
const objEditor=new RichEditorEvent(root,elementList,getCurrentInstance().appContext)
if(props.popMenuList) {
objEditor.setPopMenuList(props.popMenuList)
}
objEditor.onUploadFileFunc=(file, handleFunc) => {
emit("uploadFile",file,handleFunc)
}
objEditor.onPopMenuClickFunc=(type, handleFunc) => {
emit("popMenuClick",type,handleFunc)
}
const lineList=objEditor.getLineList()
let historyList:ICommon_Wiki_Content_Line[][]=[]
let recall=false
watch(lineList,()=>{
emit("update:modelValue",lineList)
},{
deep:true
})
watch(()=>props.modelValue,()=>{
watch(()=>props.modelValue,(value, oldValue, onCleanup)=>{
objEditor.setLineList(props.modelValue)
if(lineList.length==0) {
objEditor.addLine("")
}
if(oldValue && oldValue.length>0 && !recall) {
let obj=JSON.parse(JSON.stringify(oldValue))
obj.forEach(obj=>{
delete obj.selectStartIndexPath
delete obj.selectEndIndexPath
})
let str=JSON.stringify(obj)
if(historyList.length==0) {
historyList.unshift(obj)
} else {
let objFirst=historyList[0]
if(JSON.stringify(objFirst)!==str) {
historyList.unshift(obj)
}
}
if(historyList.length>10) {
historyList.pop()
}
} else if(recall===true) {
recall=false
}
},{
deep:true,
immediate:true
})
const onFocus=(item:Line,event:MouseEvent)=>{
const onFocus=(item:ICommon_Wiki_Content_Line, event:MouseEvent)=>{
objEditor.onFocus(item,event)
}
const onDbClick=(event:MouseEvent)=>{
objEditor.onDbClick(event)
}
const onBlur=(item:Line,event:FocusEvent)=>{
const onBlur=(item:ICommon_Wiki_Content_Line, event:FocusEvent)=>{
objEditor.onBlur(item,event)
}
const onEnter=(line:Line,index:number,event:MouseEvent)=>{
const onEnter=(line:ICommon_Wiki_Content_Line, index:number, event:MouseEvent)=>{
objEditor.onEnter(line,index,event)
}
const onDelete=(index,item:Line,event:MouseEvent)=>{
const onDelete=(index, item:ICommon_Wiki_Content_Line, event:MouseEvent)=>{
objEditor.onDelete(index,item,event)
}
@ -80,12 +123,28 @@ const onMouseOver=(event:MouseEvent)=>{
}
}
const onCopy=(event:ClipboardEvent)=>{
objEditor.onCopy(event)
}
const onKeyDown=(event:KeyboardEvent)=>{
if(!props.readonly) {
objEditor.onKeyDown(event)
if(event.key=="z" && (event.metaKey || event.ctrlKey)) {
event.stopPropagation()
event.preventDefault()
if(historyList.length>0) {
let list=historyList.shift()
objEditor.setLineList(list)
}
recall=true
}
}
}
onBeforeUnmount(()=>{
objEditor.clear()
})
</script>
<style scoped>
@ -102,4 +161,7 @@ div {
pointer-events: none;
display: block;
}
:deep a[fileId]:hover {
background-color: lightgray;
}
</style>

View File

@ -1,26 +0,0 @@
export enum ELineConfigType {
TEXT,
LINK,
IMAGE
}
export type LineStyle={
fontStyle?:string
fontWeight?:string,
color?:string,
backgroundColor?:string,
textDecoration?:string,
fontSize?:string
}
export type LineConfig={
style?:LineStyle,
value:string
link?:string,
type:ELineConfigType,
width?:number
}
export type Line={
arr:LineConfig[],
selectStartIndexPath?:number[],
selectEndIndexPath?:number[]
}

View File

@ -1,5 +1,5 @@
<template>
<div style="width: 100%;height: 100%;display: grid;grid-template-rows:repeat(auto-fill,120px);grid-template-columns: repeat(auto-fill,100px);align-items: center;justify-items: center" v-drop.file.shortcut.folder.disk="objFinderHandle.onDrop.bind(objFinderHandle,props.folderId)" v-select v-menu.self="objFinderHandle.contextMenuFunc.bind(objFinderHandle)" :key="folderId" @click="$event.currentTarget===$event.target && selectItem()">
<div style="width: 100%;height: 100%;display: grid;grid-template-rows:repeat(auto-fill,120px);grid-template-columns: repeat(auto-fill,100px);align-items: center;justify-items: center;" v-drop.file.shortcut.folder.disk="objFinderHandle.onDrop.bind(objFinderHandle,props.folderId)" v-select v-menu.self="objFinderHandle.contextMenuFunc.bind(objFinderHandle)" :key="folderId" @click="$event.currentTarget===$event.target && selectItem()" onselectstart="return false">
<template v-for="(item,index) in itemList" :key="item.meta.id">
<IconItem :item="item" :index="index" v-if="item.meta.type===ECommon_Model_Finder_Item_Type.FILE" v-selectable.file="item.meta.id" @click="selectItem(item.meta)">
</IconItem>
@ -37,7 +37,6 @@ const emit=defineEmits<{
selectItem:[item:DCSType<ICommon_Model_Finder_Item>]
}>()
const appContext=getCurrentInstance().appContext
const root=getRootNavigatorRef()
const objFinderHandle=new FinderHandle(root,appContext,props.folderId)

View File

@ -14,7 +14,6 @@ import {
DCSType
} from "../../../common/request/request";
import {EClient_EVENTBUS_TYPE, eventBus} from "../../../common/event/event";
import {Message} from "@arco-design/web-vue";
import {useDesktopStore} from "../../desktop/store/desktop";
import {
ECommon_Model_Finder_Item_Type,
@ -26,6 +25,7 @@ import {selectedSelectableItems} from "../../../../teamOS/common/directive/selec
import {IClient_Drag_Element} from "../../../../teamOS/common/directive/drag";
import {DropParam} from "../../../../teamOS/common/directive/drop";
import {SessionStorage} from "../../../common/storage/session";
import {Message} from "@arco-design/web-vue";
export class FinderHandle {
private itemList=ref<Icon[]>([])

View File

@ -14,7 +14,7 @@
import {onDialogOk} from "../../../../common/component/dialog/dialog";
import {reactive, ref} from "vue";
import {apiIssue, DCSType} from "../../../../common/request/request";
import {ICommon_Route_Res_Project_filter_Item} from "../../../../../../../common/routes/response";
import {ICommon_Route_Res_Project_Issue_filter_Item} from "../../../../../../../common/routes/response";
import {dialogFuncGenerator} from "../../../../common/util/helper";
const props=defineProps<{
@ -25,7 +25,7 @@ const eleForm=ref(null)
const form=reactive({
issueId:""
})
const issueList=ref<DCSType<ICommon_Route_Res_Project_filter_Item[]>>([])
const issueList=ref<DCSType<ICommon_Route_Res_Project_Issue_filter_Item[]>>([])
const onSearch=async (keyword:string)=>{
let res=await apiIssue.filter({
name:keyword,

View File

@ -87,19 +87,19 @@ import {ECommon_Model_Workflow_Node_Status} from "../../../../../../../common/mo
import {getCurrentInstance, inject, markRaw, onBeforeMount, reactive, ref} from "vue";
import {apiIssue, apiOrganization, apiProject, DCSType} from "../../../../common/request/request";
import {
ICommon_Route_Res_Organization_User_Item,
ICommon_Route_Res_Project_CreateModule_Data,
ICommon_Route_Res_Project_filter_Item
ICommon_Route_Res_Organization_User_Item,
ICommon_Route_Res_Project_CreateModule_Data,
ICommon_Route_Res_Project_Issue_filter_Item
} from "../../../../../../../common/routes/response";
import {injectProjectInfo} from "../../../../common/util/symbol";
import {checkPermission} from "../../../../common/util/helper";
import {Permission_Types} from "../../../../../../../common/permission/permission";
import {Dialog} from "../../../../common/component/dialog/dialog";
import {
ETeamOS_Navigator_Action,
getCurrentNavigator,
getRootNavigatorRef,
onNavigatorShow
ETeamOS_Navigator_Action,
getCurrentNavigator,
getRootNavigatorRef,
onNavigatorShow
} from "../../../../../teamOS/common/component/navigator/navigator";
import ProjectIssueCreate from "./projectIssueCreate.vue";
import UserAvatar from "../../../../common/component/userAvatar.vue";
@ -169,7 +169,7 @@ const assignerId=ref("all")
const assignerList=ref<DCSType<ICommon_Route_Res_Organization_User_Item[]>>([])
const reporterId=ref("all")
const reporterList=ref<DCSType<ICommon_Route_Res_Organization_User_Item[]>>([])
const issueList=ref<DCSType<ICommon_Route_Res_Project_filter_Item[]>>([])
const issueList=ref<DCSType<ICommon_Route_Res_Project_Issue_filter_Item[]>>([])
const root=getRootNavigatorRef();
const appContext=getCurrentInstance().appContext
const navigator=getCurrentNavigator();

View File

@ -7,7 +7,7 @@
</template>
<script setup lang="ts">
import {defineProps, onBeforeMount, reactive, ref} from "vue"
import {onBeforeMount, reactive, ref} from "vue"
import {ICommon_Route_Res_Workflow_Node_List_Item} from "../../../../../../../common/routes/response";
import {apiField, apiWorkflow} from "../../../../common/request/request";
import {onDialogOk} from "../../../../common/component/dialog/dialog";

View File

@ -0,0 +1,15 @@
<template>
<div style="width: 100%;height: 100%">
<a-row>
<a-input-search></a-input-search>
</a-row>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>

View File

@ -33,7 +33,7 @@
<a-input style="width: 80%;margin-top: 10px;border: 0px;background-color: transparent" autofocus placeholder="type you title" :input-attrs="{style:{fontSize:'20px'}}" v-model="title" :readonly="!isWrite"></a-input>
</a-row>
<a-row style="width: 100%;height: calc(100% - 60px);margin-top: 10px;justify-content: center;overflow-y: auto">
<RichEditor v-model="content" :readonly="!isWrite" style="width: 80%"></RichEditor>
<RichEditor v-model="content" :readonly="!isWrite" style="width: 80%" @upload-file="onUploadFile" :pop-menu-list="popMenuList" @pop-menu-click="onPopMenuClick"></RichEditor>
</a-row>
</a-row>
</div>
@ -41,12 +41,16 @@
<script setup lang="ts">
import {onBeforeMount, ref, watch} from "vue";
import {apiWiki, DCSType} from "../../../common/request/request";
import {apiFile, apiWiki, DCSType} from "../../../common/request/request";
import moment from "moment";
import {ICommon_Model_Wiki_Item_Content} from "../../../../../../common/model/wiki_item_content";
import {
ECommon_Wiki_Content_Line_Config_Type,
ICommon_Model_Wiki_Item_Content,
ICommon_Wiki_Content_Line,
ICommon_Wiki_Content_Line_Config
} from "../../../../../../common/model/wiki_item_content";
import UserAvatar from "../../../common/component/userAvatar.vue";
import RichEditor from "../../../common/component/richEditor/richEditor.vue";
import {Line} from "../../../common/component/richEditor/type";
import {checkPermission} from "../../../common/util/helper";
import {Permission_Types} from "../../../../../../common/permission/permission";
import {ECommon_Model_Finder_Shortcut_Type} from "../../../../../../common/model/finder_item";
@ -70,7 +74,46 @@ const title=ref(props.name)
const path=ref(props.path)
const isWrite=ref(false)
const info=ref<DCSType<ICommon_Model_Wiki_Item_Content>>()
const content=ref<Line[]>([])
const content=ref<ICommon_Wiki_Content_Line[]>([])
const popMenuList=ref([
{
type: ECommon_Wiki_Content_Line_Config_Type.IMAGE,
title: "Image"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.FILE,
title: "File"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.PROJECT,
title: "Project"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.PROJECT_ISSUE,
title: "Project Issue"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.PROJECT_RELEASE,
title: "Project Release"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.WIKI,
title: "Wiki Space"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.WIKI_ITEM,
title: "Wiki Item"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.CALENDAR_EVENT,
title: "Calendar Event"
},
{
type: ECommon_Wiki_Content_Line_Config_Type.MEETING_ROOM,
title: "Meeting Room"
},
])
let loadDate:number
watch(()=>props.name,()=>{
title.value=props.name
})
@ -95,8 +138,11 @@ watch(title,()=>{
titleTimer=null;
},500)
})
watch(content,()=>{
watch(content,(value, oldValue, onCleanup)=>{
let now=Date.now()
if(!loadDate || now-loadDate<300) {
return
}
if(contentTimerStamps==0 || now-contentTimerStamps<500) {
if(contentTimer) {
clearInterval(contentTimer)
@ -125,8 +171,56 @@ const getContent=async ()=>{
if(res?.code==0) {
info.value=res.data
content.value=info.value.content?JSON.parse(info.value.content):[]
loadDate=Date.now()
}
}
const onUploadFile=async (file, handleFunc) => {
let res=await apiFile.upload({
file:file
})
if(res?.code==0) {
handleFunc(res.data.id,res.data.path)
}
}
const onPopMenuClick=async (type:ECommon_Wiki_Content_Line_Config_Type,handleFunc:(item:ICommon_Wiki_Content_Line_Config)=>void)=>{
if(type===ECommon_Wiki_Content_Line_Config_Type.IMAGE || type===ECommon_Wiki_Content_Line_Config_Type.FILE) {
let input = document.createElement('input');
input.type = 'file';
if(type===ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
input.accept=".png,.jpg,.gif,.jpeg,.bmp,.webp"
}
input.onchange=async (ev:InputEvent) => {
let files=Array.from((ev.target as HTMLInputElement).files)
if(files.length>0) {
let res=await apiFile.upload({
file:files[0] as any
})
if(res?.code==0) {
let fileId=res.data.id
let path=res.data.path
let objImageItem:ICommon_Wiki_Content_Line_Config=type===ECommon_Wiki_Content_Line_Config_Type.IMAGE?{
value:fileId,
link:path,
type:ECommon_Wiki_Content_Line_Config_Type.IMAGE,
width:200
}:{
value:fileId,
link:path,
type:ECommon_Wiki_Content_Line_Config_Type.FILE,
label:files[0].name
}
handleFunc(objImageItem)
}
}
}
input.click();
} else if(type===ECommon_Wiki_Content_Line_Config_Type.PROJECT) {
}
}
onBeforeMount(()=>{
getContent()
})

View File

@ -16,6 +16,8 @@ import RegisterCode from "./business/controller/login/registerCode.vue";
import Reset from "./business/controller/login/reset.vue";
import ResetCode from "./business/controller/login/resetCode.vue";
import {ECommon_Application_Mode} from "../../common/types";
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
const Desktop =()=>import("./business/controller/desktop/desktop.vue")
const routes=[
@ -69,6 +71,7 @@ app.use(ArcoVueIcon)
app.use(createPinia())
app.use(router);
app.component("sicon",sicon)
app.component("icon",FontAwesomeIcon)
app.mount('#app')
apiGateway.deployInfo().then(async value => {

View File

@ -1,5 +1,41 @@
import {BaseModel} from "./base"
export enum ECommon_Wiki_Content_Line_Config_Type {
TEXT,
LINK,
IMAGE,
FILE,
PROJECT,
PROJECT_ISSUE,
PROJECT_RELEASE,
WIKI,
WIKI_ITEM,
CALENDAR_EVENT,
MEETING_ROOM
}
export type ICommon_Wiki_Content_Line_Style ={
fontStyle?:string
fontWeight?:string,
color?:string,
backgroundColor?:string,
textDecoration?:string,
fontSize?:string
}
export type ICommon_Wiki_Content_Line_Config ={
style?:ICommon_Wiki_Content_Line_Style,
value:string
link?:string,
type:ECommon_Wiki_Content_Line_Config_Type,
width?:number,
label?:string
}
export type ICommon_Wiki_Content_Line ={
arr:ICommon_Wiki_Content_Line_Config[],
selectStartIndexPath?:number[],
selectEndIndexPath?:number[]
}
export interface ICommon_Model_Wiki_Item_Content {
id :string ,
wiki_item_id:string,

View File

@ -1,7 +1,11 @@
import {ECommon_Services} from "../types"
import {ECommon_HttpApi_Method} from "./types"
import {ECommon_Calendar_Color, ICommon_Model_Calendar} from "../model/calendar";
import {ICommon_Route_Res_Calendar_Event_Info, ICommon_Route_Res_Calendar_ListEvent_Item} from "./response";
import {
ICommon_Route_Res_Calendar_Event_Filter,
ICommon_Route_Res_Calendar_Event_Info,
ICommon_Route_Res_Calendar_ListEvent_Item
} from "./response";
import {ECommon_Calendar_Recurring_Type} from "../model/calendar_event";
import {ECommon_Calendar_WeekDay, ICommon_Model_Calendar_Setting} from "../model/calendar_setting";
import {Permission_Types} from "../permission/permission";
@ -140,6 +144,18 @@ const api= {
res: <ICommon_Model_Calendar>{},
permission: [Permission_Types.Organization.READ]
},
filterEvent:{
method: ECommon_HttpApi_Method.GET,
path: "/calendar/event/filter",
req: <{
calendarId?:string,
name?:string,
page:number,
size:number
}>{},
res: <ICommon_Route_Res_Calendar_Event_Filter>{},
permission: [Permission_Types.Organization.READ]
}
}
}
export default api

View File

@ -14,6 +14,7 @@ const api={
}>{},
res:<{
id:string,
path:string,
meta?:string
}>{}
},

View File

@ -8,7 +8,7 @@ import {ICommon_Model_Project_Module} from './../model/project_module';
import {
ICommon_Route_Req_ProjectIssue_Field,
ICommon_Route_Req_ProjectIssue_Field_Value,
ICommon_Route_Res_Project_filter,
ICommon_Route_Res_Project_Issue_filter,
ICommon_Route_Res_ProjectIssue_BasicInfo,
ICommon_Route_Res_ProjectIssue_fieldsInfo,
ICommon_Route_Res_Workflow_Node_Field
@ -292,7 +292,7 @@ const api={
method:ECommon_HttpApi_Method.GET,
path:"/filter",
req:<{
projectId :string,
projectId? :string,
createdBy?:string,
issueTypeId?:string,
name?:string,
@ -305,7 +305,7 @@ const api={
page:number,
size:number
}>{},
res:<ICommon_Route_Res_Project_filter>{},
res:<ICommon_Route_Res_Project_Issue_filter>{},
permission:[Permission_Types.Project.READ]
},
releaseList:{

View File

@ -17,7 +17,7 @@ const api={
method:ECommon_HttpApi_Method.GET,
path:"/list",
req:<{
projectId:string,
projectId?:string,
name?:string,
status?:ECommon_Model_Project_Release_Status,
page:number,

View File

@ -19,7 +19,7 @@ import {ICommon_Model_Member_Tag} from "../model/member_tag";
import {ICommon_Field_Type} from "../field/type";
import {ICommon_Model_Wiki} from "../model/wiki";
import {ICommon_Model_Calendar_Event} from "../model/calendar_event";
import {ECommon_Calendar_Color} from "../model/calendar";
import {ECommon_Calendar_Color, ICommon_Model_Calendar} from "../model/calendar";
import {ICommon_Model_Finder_Item} from "../model/finder_item";
import {ICommon_Model_File} from "../model/file";
import {ICommon_Model_Wiki_Item} from "../model/wiki_item";
@ -67,6 +67,13 @@ export type ICommon_Route_Res_Wiki_Item_Info=ICommon_Model_Wiki_Item & {
wiki:ICommon_Model_Wiki
}
export type ICommon_Route_Res_Wiki_Item_Filter={
data:ICommon_Route_Res_Wiki_Item_Info[],
count:number,
totalPage:number,
page:number
}
export type ICommon_Route_Res_User_List = {
count:number,
totalPage:number,
@ -189,7 +196,7 @@ export interface ICommon_Route_Res_userWikiList {
data:ICommon_Model_Wiki[]
}
export interface ICommon_Route_Res_Project_filter_Item {
export interface ICommon_Route_Res_Project_Issue_filter_Item {
id:string,
name:string,
unique_id:number,
@ -203,11 +210,12 @@ export interface ICommon_Route_Res_Project_filter_Item {
reporter_id:string,
created_time:Date,
created_by:string,
priority:ECommon_Model_Project_Issue_Priority
priority:ECommon_Model_Project_Issue_Priority,
project:ICommon_Model_Project
}
export interface ICommon_Route_Res_Project_filter {
data:ICommon_Route_Res_Project_filter_Item[],
export interface ICommon_Route_Res_Project_Issue_filter {
data:ICommon_Route_Res_Project_Issue_filter_Item[],
count:number,
totalPage:number,
page:number,
@ -223,7 +231,8 @@ export interface ICommon_Route_Res_Release_List {
export interface ICommon_Route_Res_Release_Item extends ICommon_Model_Project_Release {
notstart:number,
inprogress:number,
done:number
done:number,
project:ICommon_Model_Project
}
export type ICommon_Route_Res_Release_Info_Issue_Item = Omit<ICommon_Model_Project_Issue,"issue_type_id"|"workflow_node_id"> & {
@ -332,6 +341,15 @@ export type ICommon_Route_Res_Calendar_Event_Info = ICommon_Model_Calendar_Event
}[]
}
export type ICommon_Route_Res_Calendar_Event_Filter={
count:number,
totalPage:number,
page:number,
data:(ICommon_Model_Calendar_Event & {
calendar:ICommon_Model_Calendar
})[]
}
export type ICommon_Route_Res_Organization_FilterUserAndTeam = {
users:{
id:string,

View File

@ -7,7 +7,7 @@ import {
ICommon_Route_Res_Role_List,
ICommon_Route_Res_Role_ListMember,
ICommon_Route_Res_userWikiList,
ICommon_Route_Res_Wiki_Info,
ICommon_Route_Res_Wiki_Info, ICommon_Route_Res_Wiki_Item_Filter,
ICommon_Route_Res_Wiki_Item_Info
} from "./response";
import {ICommon_Model_Wiki_Item} from "../model/wiki_item";
@ -26,7 +26,7 @@ const api= {
}>{},
res: <ICommon_Model_Wiki[]>{},
permission: [Permission_Types.Wiki.READ]
permission: [Permission_Types.Organization.READ]
},
userWikiList: {
method: ECommon_HttpApi_Method.GET,
@ -38,7 +38,7 @@ const api= {
type:"all"|"created"|"joined"
}>{},
res: <ICommon_Route_Res_userWikiList>{},
permission: [Permission_Types.Wiki.READ]
permission: [Permission_Types.Organization.READ]
},
list: {
method: ECommon_HttpApi_Method.GET,
@ -50,7 +50,7 @@ const api= {
organizationUserId?:string
}>{},
res: <ICommon_Route_Res_userWikiList>{},
permission: [Permission_Types.Wiki.READ]
permission: [Permission_Types.Organization.READ]
},
addWiki: {
method: ECommon_HttpApi_Method.POST,
@ -265,6 +265,18 @@ const api= {
res: <ICommon_Route_Res_Wiki_Item_Info>{},
permission: [Permission_Types.Wiki.READ]
},
filterWikiItem:{
method: ECommon_HttpApi_Method.GET,
path: "/item/filter",
req: <{
wikiId?:string,
name?:string,
size:number,
page:number
}>{},
res: <ICommon_Route_Res_Wiki_Item_Filter>{},
permission: [Permission_Types.Wiki.READ]
}
}
}
export default api

View File

@ -118,6 +118,10 @@ export namespace Err {
code:3004,
msg:"project id not match"
},
projectKeywordDuplicate:{
code:3004,
msg:"project keyword duplicate"
},
Label:{
labelSizeEmpty:{
code:3100,

View File

@ -98,4 +98,10 @@ class PermissionClear {
await obj.del()
}
@DEventListener("roleChange")
async roleChange(roleId:string) {
let obj=new REDIS_AUTH.Permission.Role.RoleInfo(roleId)
await obj.del()
}
}

View File

@ -205,4 +205,10 @@ class CalendarController {
}
return obj.getItem()
}
@DHttpApi(calendarApi.routes.filterEvent)
async filterEvent(@DHttpReqParam("calendarId") calendarId:string,@DHttpReqParam("name") name:string,@DHttpReqParamRequired("size") size:number,@DHttpReqParamRequired("page") page:number,@DHttpUser user:IUserSession):Promise<typeof calendarApi.routes.filterEvent.res> {
let ret=await CalendarEventService.filterEvent(user.organizationInfo.organizationUserId,calendarId,name,page,size)
return ret;
}
}

View File

@ -4,6 +4,7 @@ import {calendarSettingModel, ECommon_Calendar_WeekDay} from "../../../common/mo
import {Err} from "../../../common/status/error";
import {getMysqlInstance} from "../../common/db/mysql";
import {
convertCountSql,
generateBatchCreateSql,
generateDeleteInnerJoin2Sql,
generateDeleteInnerJoinSql,
@ -17,6 +18,8 @@ import {calendarEventModel, ECommon_Calendar_Recurring_Type} from "../../../comm
import {calendarEventGuestModel, ICommon_Model_Calendar_Event_Guest} from "../../../common/model/calendar_event_guest";
import {ICommon_Route_Res_Calendar_ListEvent_Item} from "../../../common/routes/response";
import * as moment from "moment";
import {keys} from "../../../common/transform";
import CommonUtil from "../../common/util/common";
class CalendarMapper extends Mapper<typeof calendarModel> {
@ -663,6 +666,61 @@ class CalendarEventMapper extends Mapper<typeof calendarEventModel> {
}
return isMatch
}
async filterEvent(organizationUserId:string,calendarId:string,name:string,page:number,size:number) {
if(page===undefined || page<0 || size===undefined || size<=0) {
throw Err.Common.paramError
}
let mysql=getMysqlInstance()
let sql=generateLeftJoinSql({
model:calendarEventModel,
columns:keys<typeof calendarEventModel["model"]>().map(item=>item.name)
},{
model:calendarModel,
columns:keys<typeof calendarModel["model"]>().map(item=>item.name),
aggregation:"calendar",
expression:{
id:{
model:calendarEventModel,
field:"calendar_id"
}
}
},{
organization_user_id:{
model:calendarModel,
value:organizationUserId
},
...(calendarId && {
calendar_id:{
model:calendarEventModel,
value:calendarId
}
}),
...(name && {
name:{
model:calendarEventModel,
value:{
exp:"%like%",
value:name
}
}
}),
},"and",{
model:calendarEventModel,
field:"name",
type:"asc"
},page*size,size)
let countSql=convertCountSql(sql);
let count=Number(Object.values(await mysql.executeOne(countSql))[0])
let totalPage=CommonUtil.pageTotal(count,size)
let ret=await mysql.execute(sql)
return {
count:count,
totalPage:totalPage,
page:page,
data:ret
}
}
}
export const calendarEventMapper=new CalendarEventMapper

View File

@ -136,6 +136,11 @@ export class CalendarEventService extends Entity<typeof calendarEventModel,typeo
let arr=calendarEventMapper.getMemberList(this.getId(),this.getItem().calendar_id)
return arr;
}
static async filterEvent(organizationUserId:string,calendarId:string,name:string,page:number,size:number) {
let ret=await calendarEventMapper.filterEvent(organizationUserId,calendarId,name,page,size)
return ret;
}
}
export class CalendarSettingService extends Entity<typeof calendarSettingModel,typeof calendarSettingMapper> {

View File

@ -4,8 +4,8 @@ export interface IServer_Common_Event_Types {
userDelete:(userId:string)=>void
projectDelete:(projectId:string)=>void
teamDelete:(teamId:string)=>void
fileRef:(fileId:string)=>void
fileUnref:(fileId:string)=>void
fileRef:(fileId:string,count?:number)=>void
fileUnref:(fileId:string,count?:number)=>void
fileUpload:(meta:string,fileId:string)=>void
issueTypeDelete:(issueTypeId:string)=>void
organizationUserAdd:(organizationId:string,organizationUserId:string)=>void
@ -18,4 +18,5 @@ export interface IServer_Common_Event_Types {
teamUserAdd:(teamId:string,organizationUserId:string)=>void
teamUserEdit:(teamId:string,organizationUserId:string)=>void
teamUserDelete:(teamId:string,organizationUserId:string)=>void
roleChange:(roleId:string)=>void
}

View File

@ -298,7 +298,7 @@ class IssueController {
}
@DHttpApi(projectIssueApi.routes.filter)
async filter(@DHttpReqParamRequired("projectId") projectId :string,
async filter(@DHttpReqParam("projectId") projectId :string,
@DHttpReqParam("createdBy") createdBy :string,
@DHttpReqParam("issueTypeId") issueTypeId :string,
@DHttpReqParam("name") name :string,
@ -309,8 +309,8 @@ class IssueController {
@DHttpReqParam("moduleId") moduleId :string,
@DHttpReqParam("labelId") labelId :string,
@DHttpReqParamRequired("page") page :number,
@DHttpReqParamRequired("size") size :number):Promise<typeof projectIssueApi.routes.filter.res> {
let ret=await ProjectIssueService.filter(projectId ,page ,size ,createdBy ,issueTypeId,name,priority,assignerId,reporterId,status,moduleId,labelId)
@DHttpReqParamRequired("size") size :number,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.filter.res> {
let ret=await ProjectIssueService.filter(user.organizationInfo.organizationId,projectId ,page ,size ,createdBy ,issueTypeId,name,priority,assignerId,reporterId,status,moduleId,labelId)
return ret;
}

View File

@ -21,6 +21,13 @@ class ProjectController {
@DHttpApi(projectApi.routes.create)
async createProject(@DHttpReqParamRequired("name") name:string,@DHttpReqParamRequired("keyword") keyword:string,@DHttpReqParam("photo") photo:string,@DHttpReqParam("description") description:string,@DHttpUser user:IUserSession):Promise<typeof projectApi.routes.create.res>{
let objProject=await ProjectService.getItemByExp({
organization_id:user.organizationInfo.organizationId,
keyword
})
if(objProject) {
throw Err.Project.projectKeywordDuplicate
}
let obj=new ProjectService()
obj.assignItem({
keyword:keyword,

View File

@ -10,8 +10,8 @@ import {IUserSession} from "../../user/types/config";
@DHttpController(releaseApi)
class ReleaseController {
@DHttpApi(releaseApi.routes.list)
async list(@DHttpReqParamRequired("projectId") projectId:string,@DHttpReqParamRequired("page") page:number,@DHttpReqParamRequired("size") size:number,@DHttpReqParam("name") name:string,@DHttpReqParam("status") status:ECommon_Model_Project_Release_Status):Promise<typeof releaseApi.routes.list.res> {
let list=await ProjectReleaseService.list(projectId,page,size,name,status)
async list(@DHttpReqParam("projectId") projectId:string,@DHttpReqParamRequired("page") page:number,@DHttpReqParamRequired("size") size:number,@DHttpReqParam("name") name:string,@DHttpReqParam("status") status:ECommon_Model_Project_Release_Status,@DHttpUser user:IUserSession):Promise<typeof releaseApi.routes.list.res> {
let list=await ProjectReleaseService.list(user.organizationInfo.organizationId,projectId,page,size,name,status)
return list;
}
@DHttpApi(releaseApi.routes.info)

View File

@ -3,7 +3,7 @@ import {ICommon_Model_Project_Issue, projectIssueModel} from "../../../common/mo
import {ICommon_Model_Project_Label, projectLabelModel} from "../../../common/model/project_label";
import {ICommon_Model_Project_Module, projectModuleModel} from "../../../common/model/project_module";
import {workflowNodeModel} from "../../../common/model/workflow_node";
import {ICommon_Route_Res_Project_filter} from "../../../common/routes/response";
import {ICommon_Route_Res_Project_Issue_filter} from "../../../common/routes/response";
import {Err} from "../../../common/status/error";
import {keys} from "../../../common/transform";
import {getMysqlInstance} from '../../common/db/mysql';
@ -14,7 +14,6 @@ import {
generateBatchCreateSql,
generateCreateSql,
generateDeleteSql,
generateLeftJoin2Sql,
generateLeftJoin3Sql,
generateLeftJoinSql,
generateQuerySql,
@ -607,11 +606,21 @@ class ProjectIssueMapper extends Mapper<typeof projectIssueModel> {
}
}
async filter(projectId :string,page :number,size :number,createdBy? :string,issueTypeId? :string,name? :string,priority? :number,assignerId? :string,reporterId? :string,status? :number,moduleId? :string,labelId? :string):Promise<ICommon_Route_Res_Project_filter> {
if(!projectId) {
throw Err.Project.projectNotFound
}
async filter(organizationId:string,projectId :string,page :number,size :number,createdBy? :string,issueTypeId? :string,name? :string,priority? :number,assignerId? :string,reporterId? :string,status? :number,moduleId? :string,labelId? :string):Promise<ICommon_Route_Res_Project_Issue_filter> {
let mysql=getMysqlInstance()
if(!projectId && name && name.includes("-")) {
let arr=name.split("-")
let projectUniqueId=arr[0]
name=arr[1]
let obj=await mysql.executeOne(generateQuerySql(projectModel,["id"],{
keyword:projectUniqueId,
organization_id:organizationId
}))
if(!obj) {
throw Err.Project.projectNotFound
}
projectId=obj.id
}
let setId:Set<string>=new Set;
if(moduleId) {
let arrModuleIssueId=(await mysql.execute(generateQuerySql(projectModuleIssueModel,["id"],{
@ -629,7 +638,7 @@ class ProjectIssueMapper extends Mapper<typeof projectIssueModel> {
setId.add(obj)
}
}
let sql=generateLeftJoin2Sql({
let sql=generateLeftJoin3Sql({
model:projectIssueModel,
columns:["id","assigner_id","reporter_id","unique_id","name","unique_id","created_time","created_by","priority"]
},{
@ -652,10 +661,26 @@ class ProjectIssueMapper extends Mapper<typeof projectIssueModel> {
}
}
},{
project_id:{
model:projectIssueModel,
value:projectId
model:projectModel,
columns:keys<typeof projectModel["model"]>().map(item=>item.name),
aggregation:"project",
expression:{
id:{
model:projectIssueModel,
field:"project_id"
}
}
},{
organization_id:{
model:projectModel,
value:organizationId
},
...(projectId && {
project_id:{
model:projectIssueModel,
value:projectId
}
}),
...(createdBy && {
created_by:{
model:projectIssueModel,

View File

@ -5,10 +5,11 @@ import {getMysqlInstance} from "../../common/db/mysql";
import {Mapper} from "../../common/entity/mapper";
import CommonUtil from "../../common/util/common";
import {
generateCountSql,
convertCountSql,
generateCreateSql,
generateDeleteSql,
generateGroupLeftJoin2Sql,
generateGroupLeftJoin3Sql,
generateLeftJoin3Sql,
generateLeftJoinSql,
generateQuerySql,
@ -27,47 +28,69 @@ import {
ICommon_Route_Res_Release_Item,
ICommon_Route_Res_Release_List
} from './../../../common/routes/response';
import {projectModel} from "../../../common/model/project";
class ReleaseMapper extends Mapper<typeof projectReleaseModel> {
constructor() {
super(projectReleaseModel)
}
async list(projectId:string,page:number,size:number,name?:string,status?:ECommon_Model_Project_Release_Status):Promise<ICommon_Route_Res_Release_List> {
if(!projectId || page===undefined || page<0 || size===undefined || size<=0) {
async list(organizationId:string,projectId:string,page:number,size:number,name?:string,status?:ECommon_Model_Project_Release_Status):Promise<ICommon_Route_Res_Release_List> {
if(page===undefined || page<0 || size===undefined || size<=0) {
throw Err.Common.paramError
}
let mysql=getMysqlInstance()
let ret:ICommon_Route_Res_Release_Item[]=[]
let count=Number(Object.values(await mysql.executeOne(generateCountSql(projectReleaseModel,{
...(name && {
name:{
exp:"%like%",
value:name
let sql=generateLeftJoinSql({
model:projectReleaseModel,
columns:keys<typeof projectReleaseModel["model"]>().map(item=>item.name)
},{
model:projectModel,
columns:keys<typeof projectModel["model"]>().map(item=>item.name),
aggregation:"project",
expression:{
id:{
model:projectReleaseModel,
field:"project_id"
}
}
},{
organization_id:{
model:projectModel,
value:organizationId
},
...(projectId && {
project_id:{
model:projectReleaseModel,
value:projectId
}
}),
...(status && {
status:status
})
})))[0])
let totalPage=CommonUtil.pageTotal(count,size)
if(count>0) {
let arr=await mysql.execute(generateQuerySql(projectReleaseModel,null,{
project_id:projectId,
...(name && {
name:{
...(name && {
name:{
model:projectReleaseModel,
value:{
exp:"%like%",
value:name
}
}),
...(status && {
status:status
})
},"and",{
field:"name",
type:"asc"
},page*size,size))
}
}),
...(status && {
status:{
model:projectReleaseModel,
value:status
}
})
},"and",{
model:projectReleaseModel,
field:"name",
type:"asc"
},page*size,size)
let countSql=convertCountSql(sql);
let count=Number(Object.values(await mysql.executeOne(countSql))[0])
let totalPage=CommonUtil.pageTotal(count,size)
if(count>0) {
let arr=await mysql.execute(sql)
if(arr.length>0) {
let sql=generateGroupLeftJoin2Sql({
let sql=generateGroupLeftJoin3Sql({
model:projectReleaseIssueModel,
columns:{
columns:["project_release_id"],
@ -81,6 +104,14 @@ class ReleaseMapper extends Mapper<typeof projectReleaseModel> {
field:"project_issue_id"
}
}
},{
model:projectModel,
expression:{
id:{
model:projectReleaseIssueModel,
field:"project_id"
}
}
},{
model:workflowNodeModel,
expression:{
@ -138,7 +169,7 @@ class ReleaseMapper extends Mapper<typeof projectReleaseModel> {
...obj,
notstart:objTemp[obj.id]?Number(objTemp[obj.id].notstart):0,
inprogress:objTemp[obj.id]?Number(objTemp[obj.id].inprogress):0,
done:objTemp[obj.id]?Number(objTemp[obj.id].done):0
done:objTemp[obj.id]?Number(objTemp[obj.id].done):0,
})
}
}
@ -156,9 +187,26 @@ class ReleaseMapper extends Mapper<typeof projectReleaseModel> {
throw Err.Project.Release.releaseNotFound
}
let mysql=getMysqlInstance()
let objRelease = await mysql.executeOne(generateQuerySql(projectReleaseModel,[],{
id:projectReleaseId
}))
let sqlRelease=generateLeftJoinSql({
model:projectReleaseModel,
columns:keys<typeof projectReleaseModel["model"]>().map(item=>item.name)
},{
model:projectModel,
columns:keys<typeof projectModel["model"]>().map(item=>item.name),
aggregation:"project",
expression:{
id:{
model:projectReleaseModel,
field:"project_id"
}
}
},{
id:{
model:projectReleaseModel,
value:projectReleaseId
}
})
let objRelease = await mysql.executeOne(sqlRelease)
if(!objRelease) {
throw Err.Project.Release.releaseNotFound
}

View File

@ -381,12 +381,12 @@ export class ProjectIssueService extends Entity<typeof projectIssueModel,typeof
}
static async filter(projectId :string,page :number,size :number,createdBy? :string,issueTypeId? :string,name? :string,priority? :number,assignerId? :string,reporterId? :string,status? :number,moduleId? :string,labelId? :string) {
static async filter(organizationId:string,projectId :string,page :number,size :number,createdBy? :string,issueTypeId? :string,name? :string,priority? :number,assignerId? :string,reporterId? :string,status? :number,moduleId? :string,labelId? :string) {
let project=await ProjectService.getItemById(projectId)
if(!project) {
throw Err.Project.projectNotFound
}
let ret=await projectIssueMapper.filter(projectId ,page ,size ,createdBy ,issueTypeId,name,priority,assignerId,reporterId,status,moduleId,labelId)
let ret=await projectIssueMapper.filter(organizationId,projectId ,page ,size ,createdBy ,issueTypeId,name,priority,assignerId,reporterId,status,moduleId,labelId)
return ret;
}

View File

@ -13,11 +13,11 @@ export class ProjectReleaseService extends Entity<typeof projectReleaseModel,typ
constructor(){
super(releaseMapper)
}
static async list(projectId:string,page:number,size:number,name?:string,status?:ECommon_Model_Project_Release_Status){
static async list(organizationId:string,projectId:string,page:number,size:number,name?:string,status?:ECommon_Model_Project_Release_Status){
if(page<0 || size<=0){
throw Err.Project.Release.releaseSizeEmpty
}
let ret=await releaseMapper.list(projectId,page,size,name,status)
let ret=await releaseMapper.list(organizationId,projectId,page,size,name,status)
return ret
}

View File

@ -7,24 +7,24 @@ import {fileModel} from './../../../common/model/file';
@DComponent
class FileEvents {
@DEventListener("fileRef")
async refFile(fileId:string){
async refFile(fileId:string,count?:number){
let mysql=getMysqlInstance()
await mysql.execute(generateUpdateSql(fileModel,{
ref:{
exp:"+",
value:1
value:count??1
}
},{
id:fileId
}))
}
@DEventListener("fileUnref")
async unrefFile(fileId:string){
async unrefFile(fileId:string,count?:number){
let mysql=getMysqlInstance()
await mysql.execute(generateUpdateSql(fileModel,{
ref:{
exp:"-",
value:1
value:count??1
}
},{
id:fileId

View File

@ -23,7 +23,8 @@ class AdminController {
id:obj.getItem().id,
...(meta && {
meta
})
}),
path:"/file"+obj.getItem().path
}
} else {
let objFile=new FileService()
@ -38,7 +39,8 @@ class AdminController {
id:ret,
...(meta && {
meta
})
}),
path:"/file"+objFile.getItem().path
}
}
}

View File

@ -2,7 +2,7 @@
"compilerOptions": {
"outDir": "./dist",
"module": "commonjs",
"target": "ES2017",
"target": "ES2021",
"sourceMap": true,
"resolveJsonModule":true,
"moduleResolution":"node",

View File

@ -150,11 +150,13 @@ export class OrganizationService extends Entity<typeof organizationModel,typeof
description,
value:value
});
emitServiceEvent("roleChange",roleId)
return ret;
}
async removeRole(roleId:string){
let ret=await rpcAuthApi.removeRole(roleId);
emitServiceEvent("roleChange",roleId)
return ret;
}

View File

@ -272,4 +272,10 @@ class WikiController {
wiki:objWiki.getItem()
}
}
@DHttpApi(wikiApi.routes.filterWikiItem)
async filterWikiItem(@DHttpReqParam("wikiId") wikiId:string,@DHttpReqParam("name") name:string,@DHttpReqParamRequired("size") size:number,@DHttpReqParamRequired("page") page:number,@DHttpUser user:IUserSession):Promise<typeof wikiApi.routes.filterWikiItem.res>{
let ret=await WikiItemService.filterWikiItem(user.organizationInfo.organizationId,wikiId,name,page,size)
return ret;
}
}

View File

@ -4,10 +4,12 @@ import {wikiItemModel} from "../../../common/model/wiki_item";
import {Err} from "../../../common/status/error";
import {getMysqlInstance} from "../../common/db/mysql";
import {
convertCountSql,
generateBatchCreateOnUpdateSql,
generateDeleteSql,
generateGroupLeftJoin2Sql,
generateLeftJoin3Sql,
generateLeftJoinSql,
generateQuerySql,
generateSnowId,
generateUpdateSql
@ -448,6 +450,61 @@ class WikiItemMapper extends Mapper<typeof wikiItemModel> {
}));
return ret;
}
async filterWikiItem(organizationId:string,wikiId:string,name:string,page:number,size:number) {
if(page===undefined || page<0 || size===undefined || size<=0) {
throw Err.Common.paramError
}
let mysql=getMysqlInstance()
let sql=generateLeftJoinSql({
model:wikiItemModel,
columns:keys<typeof wikiItemModel["model"]>().map(item=>item.name)
},{
model:wikiModel,
columns:keys<typeof wikiModel["model"]>().map(item=>item.name),
aggregation:"wiki",
expression:{
id:{
model:wikiItemModel,
field:"wiki_id"
}
}
},{
organization_id:{
model:wikiModel,
value:organizationId
},
...(wikiId && {
wiki_id:{
model:wikiItemModel,
value:wikiId
}
}),
...(name && {
name:{
model:wikiItemModel,
value:{
exp:"%like%",
value:name
}
}
}),
},"and",{
model:wikiItemModel,
field:"name",
type:"asc"
},page*size,size)
let countSql=convertCountSql(sql);
let count=Number(Object.values(await mysql.executeOne(countSql))[0])
let totalPage=CommonUtil.pageTotal(count,size)
let ret=await mysql.execute(sql)
return {
count:count,
totalPage:totalPage,
page:page,
data:ret
}
}
}
export const wikiItemMapper=new WikiItemMapper()

View File

@ -8,6 +8,11 @@ import {ECommon_Model_Role_Reserved, ECommon_Model_Role_Type} from "../../../com
import {ECommon_Model_Organization_Member_Type} from "../../../common/model/organization";
import {Err} from "../../../common/status/error";
import {IServer_Common_Event_Types} from "../../common/event/types";
import {
ECommon_Wiki_Content_Line_Config_Type,
ICommon_Wiki_Content_Line
} from "../../../common/model/wiki_item_content";
import {emitServiceEvent} from "../../common/event/event";
export class WikiService extends Entity<typeof wikiModel,typeof wikiMapper> {
constructor() {
@ -33,6 +38,8 @@ export class WikiService extends Entity<typeof wikiModel,typeof wikiMapper> {
}
}
override async delete(eventPublish?: keyof IServer_Common_Event_Types): Promise<void> {
await super.delete(eventPublish);
await wikiMapper.clearWiki(this.getId());
@ -155,11 +162,63 @@ export class WikiItemService extends Entity<typeof wikiItemModel,typeof wikiItem
}
async saveContent(content:string,modified_by:string) {
let oldObjFile=await this.generateLineObj()
content=content.replaceAll("\\\"","\\\\\"")
await wikiItemMapper.saveContent(this.getId(),content,modified_by);
let newObjFile=await this.generateLineObj()
for(let key in oldObjFile) {
let oldCount=oldObjFile[key]
let newCount=newObjFile[key]
if(newCount!==undefined) {
if(oldCount!==newCount) {
if(oldCount>newCount) {
emitServiceEvent("fileUnref",key,oldCount-newCount)
} else {
emitServiceEvent("fileRef",key,newCount-oldCount)
}
}
delete newObjFile[key]
} else {
emitServiceEvent("fileUnref",key,oldCount)
}
}
for(let key in newObjFile) {
let count=newObjFile[key]
emitServiceEvent("fileRef",key,count)
}
}
async generateLineObj():Promise<{
[param:string]:number
}> {
let objFile:{
[param:string]:number
}={}
let content=await this.getContent()
if(content.content) {
let arr=JSON.parse(content.content) as ICommon_Wiki_Content_Line[]
for(let obj of arr) {
for(let obj1 of obj.arr) {
if(obj1.type===ECommon_Wiki_Content_Line_Config_Type.FILE || obj1.type===ECommon_Wiki_Content_Line_Config_Type.IMAGE) {
if(!objFile[obj1.value]) {
objFile[obj1.value]=1
} else {
objFile[obj1.value]++
}
}
}
}
}
return objFile
}
async getContent() {
let ret=await wikiItemMapper.getContent(this.getId());
return ret;
}
static async filterWikiItem(organizationId:string,wikiId:string,name:string,page:number,size:number) {
let ret=await wikiItemMapper.filterWikiItem(organizationId,wikiId,name,page,size)
return ret;
}
}