fix
@ -12,23 +12,20 @@
|
||||
"@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",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"@socket.io/redis-emitter": "^5.1.0",
|
||||
"@logicflow/core": "^1.2.10",
|
||||
"blueimp-md5": "^2.19.0",
|
||||
"eventemitter3": "^5.0.0",
|
||||
"mediasoup-client": "^3.6.98",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.42",
|
||||
"pinia": "^2.1.4",
|
||||
"socket.io": "^4.7.1",
|
||||
"socket.io-client": "^4.6.1",
|
||||
"uuid": "^9.0.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arco-design/web-vue": "^2.48.1",
|
||||
"@arco-design/web-vue": "^2.49.1",
|
||||
"@rollup/plugin-typescript": "^10.0.1",
|
||||
"@types/blueimp-md5": "^2.18.0",
|
||||
"@types/node": "^18.15.11",
|
||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<a-spin style="width: 100%;min-height: 100px;border: 1px solid lightgray" :loading="loading" v-drop.file.shortcut.disk="onDrop">
|
||||
<RichEditor v-model="content" style="width: 100%" @upload-file="onUploadFile" :pop-menu-list="popMenuList" @pop-menu-click="onPopMenuClick" @custom-anchor-click="onCustomAnchorClick" ref="objEditorUser"></RichEditor>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {getCurrentInstance, inject, Ref, ref} from "vue";
|
||||
import RichEditor from "@/business/common/component/richEditor/richEditor.vue";
|
||||
import {
|
||||
ECommon_Content_Line_Config_Type,
|
||||
ICommon_Content_Line,
|
||||
ICommon_Content_Line_Config
|
||||
} from "../../../../../../common/model/content";
|
||||
import {RichEditorEventHandle} from "@/business/common/component/richEditorEventHandle";
|
||||
import {DropParam, vDrop} from "@/teamOS/common/directive/drop";
|
||||
import {apiFile} from "@/business/common/request/request";
|
||||
import {onDialogOk} from "@/business/common/component/dialog/dialog";
|
||||
|
||||
const loading=ref(false)
|
||||
const root =inject("dialogRootRef") as Ref<HTMLElement>
|
||||
const appContext=getCurrentInstance().appContext
|
||||
const content = ref<ICommon_Content_Line[]>([])
|
||||
const popMenuList=ref(RichEditorEventHandle.popMenuList)
|
||||
const objEditorUser=ref<InstanceType<typeof RichEditor>>()
|
||||
const onCustomAnchorClick=(type:ECommon_Content_Line_Config_Type,value:string,link:string,label:string)=>{
|
||||
RichEditorEventHandle.onCustomAnchorClick(type,value,link,label)
|
||||
}
|
||||
|
||||
const onDrop=(data?:DropParam)=>{
|
||||
RichEditorEventHandle.onDrop(objEditorUser,data)
|
||||
}
|
||||
|
||||
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=(type:ECommon_Content_Line_Config_Type,handleFunc:(item:ICommon_Content_Line_Config)=>void)=>{
|
||||
RichEditorEventHandle.onPopMenuClick(type,root,appContext,loading,handleFunc)
|
||||
}
|
||||
|
||||
onDialogOk(async ()=>{
|
||||
return content.value
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,6 +1,7 @@
|
||||
import {renderComponent} from "../../../../teamOS/common/util/component";
|
||||
import DialogView from "./dialogView.vue";
|
||||
import {AppContext, Component, inject, ref} from "vue";
|
||||
import {AppContext, Component, inject, markRaw, ref} from "vue";
|
||||
import DiaglogRich from "@/business/common/component/dialog/diaglogRich.vue";
|
||||
|
||||
export function onDialogOk(func:()=>void){
|
||||
const events:any=inject("dialogEvents");
|
||||
@ -104,4 +105,49 @@ export class Dialog {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static inputRich(el:HTMLElement,appContext: AppContext,title:string) {
|
||||
return new Promise((resolve,reject)=>{
|
||||
let ele=document.createElement("div")
|
||||
const events:{
|
||||
onOk:()=>any,
|
||||
onClose:()=>void
|
||||
}={
|
||||
onOk:null,
|
||||
onClose:null
|
||||
}
|
||||
const loading=ref(false);
|
||||
let destroyFunc=renderComponent(ele,DialogView,appContext,{
|
||||
component:markRaw(DiaglogRich),
|
||||
title,
|
||||
events,
|
||||
onOk,
|
||||
onClose,
|
||||
loading
|
||||
});
|
||||
el.appendChild(ele);
|
||||
async function onOk(){
|
||||
if(events.onOk) {
|
||||
loading.value=true;
|
||||
let ret=await events.onOk();
|
||||
loading.value=false
|
||||
if(ret!==false) {
|
||||
destroyFunc();
|
||||
ele.parentNode.removeChild(ele);
|
||||
resolve(ret)
|
||||
}
|
||||
} else {
|
||||
destroyFunc();
|
||||
ele.parentNode.removeChild(ele);
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
function onClose(){
|
||||
events.onClose?.();
|
||||
destroyFunc();
|
||||
ele.parentNode.removeChild(ele);
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div style="position: absolute;left: 0;top: 0;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;background-color: rgba(29,33,41,0.6);z-index: 10000">
|
||||
<div style="position: absolute;left: 0;top: 0;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;background-color: rgba(29,33,41,0.6);z-index: 10000" ref="root">
|
||||
<div style="background-color: white;width: 60%;height:auto;max-height: 80%;border-radius: 5px;display: flex;flex-direction: column">
|
||||
<div style="height: 35px;line-height: 35px;width: 100%;text-align: center;color: rgb(93,93,93);border-bottom: 1px solid gainsboro;flex: 1 1 auto">
|
||||
<b>{{component?title:input?"Input":"Alert"}}</b>
|
||||
@ -42,6 +42,8 @@ const props=defineProps<{
|
||||
}
|
||||
}>()
|
||||
const inputValue=props.input?props.input.text:ref("")
|
||||
const root=ref()
|
||||
provide("dialogRootRef",root)
|
||||
if(props.events) {
|
||||
provide("dialogEvents",props.events);
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="!isEdit">
|
||||
<UserAvatar v-if="showValue" :photo="showValue.photo" :name="showValue.nickname" :organization-user-id="showValue.organizationUserId"></UserAvatar>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">No User</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-select allow-search allow-clear v-model="editValue" @search="onSearchAssigner">
|
||||
<a-option v-for="item in assignerList" :label="item.organizationUser.nickname" :value="item.organizationUser.id">
|
||||
<a-avatar :size="24" :image-url="item.user.photo"></a-avatar>
|
||||
{{ item.organizationUser.nickname }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import UserAvatar from "../../userAvatar.vue";
|
||||
import {ref, watch} from "vue";
|
||||
import {apiIssue, apiOrganization, DCSType} from "../../../request/request";
|
||||
import {ICommon_Route_Res_Organization_User_Item} from "../../../../../../../common/routes/response";
|
||||
import {SessionStorage} from "../../../storage/session";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:{
|
||||
id:string,
|
||||
organizationUserId?:string,
|
||||
photo?:string,
|
||||
nickname?:string
|
||||
},
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:{
|
||||
id:string,
|
||||
organizationUserId?:string,
|
||||
photo?:string,
|
||||
nickname?:string
|
||||
}]
|
||||
}>()
|
||||
const editValue=ref("")
|
||||
const assignerList=ref<DCSType<ICommon_Route_Res_Organization_User_Item>[]>([])
|
||||
|
||||
const assignValue=()=>{
|
||||
editValue.value=""
|
||||
}
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
|
||||
const onClick=async()=>{
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
assignerId:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",res.data.assigner_id)
|
||||
}
|
||||
}
|
||||
|
||||
const onSearchAssigner=async (keyword:string)=>{
|
||||
let res=await apiOrganization.listUser({
|
||||
organizationId:SessionStorage.get("organizationId"),
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
assignerList.value=res.data.data
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div>
|
||||
<RichEditor v-if="!isEdit" :model-value="showValue?JSON.parse(showValue):[]" :readonly="true" @custom-anchor-click="onCustomAnchorClick"></RichEditor>
|
||||
<template v-else>
|
||||
<div style="width: 100%">
|
||||
<div>
|
||||
<a-spin :loading="loading" v-drop.file.shortcut.disk="onDrop" style="width: 100%">
|
||||
<RichEditor v-model="editValue" @upload-file="onUploadFile" :pop-menu-list="popMenuList" @pop-menu-click="onPopMenuClick" @custom-anchor-click="onCustomAnchorClick" @quote-list="onQuoteList" ref="objEditor"></RichEditor>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {apiFile, apiIssue} from "../../../request/request";
|
||||
import {getCurrentInstance, ref, watch} from "vue";
|
||||
import RichEditor from "../../richEditor/richEditor.vue";
|
||||
import {
|
||||
ECommon_Content_Line_Config_Type,
|
||||
ICommon_Content_Line,
|
||||
ICommon_Content_Line_Config
|
||||
} from "../../../../../../../common/model/content";
|
||||
import {RichEditorEventHandle} from "../../richEditorEventHandle";
|
||||
import {DropParam, vDrop} from "../../../../../teamOS/common/directive/drop";
|
||||
import {getRootNavigatorRef} from "../../../../../teamOS/common/component/navigator/navigator";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:string,
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:string]
|
||||
}>()
|
||||
|
||||
const root=getRootNavigatorRef()
|
||||
const appContext=getCurrentInstance().appContext
|
||||
const editValue=ref<ICommon_Content_Line[]>([])
|
||||
const objEditor=ref<InstanceType<typeof RichEditor>>()
|
||||
const loading=ref(false)
|
||||
const popMenuList=ref(RichEditorEventHandle.popMenuList)
|
||||
const assignValue=()=>{
|
||||
editValue.value=props.showValue?JSON.parse(props.showValue):[]
|
||||
}
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
const onClick=async()=>{
|
||||
let value=JSON.stringify(editValue.value.map(item=>{
|
||||
return {
|
||||
arr:item.arr
|
||||
}
|
||||
}))
|
||||
let res=await apiIssue.editDescription({
|
||||
projectIssueId:props.projectIssueId,
|
||||
description:JSON.stringify(editValue.value.map(item=>{
|
||||
return {
|
||||
arr:item.arr
|
||||
}
|
||||
}))
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",value)
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
|
||||
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=(type:ECommon_Content_Line_Config_Type,handleFunc:(item:ICommon_Content_Line_Config)=>void)=>{
|
||||
RichEditorEventHandle.onPopMenuClick(type,root,appContext,loading,handleFunc)
|
||||
}
|
||||
|
||||
const onCustomAnchorClick=(type:ECommon_Content_Line_Config_Type,value:string,link:string,label:string)=>{
|
||||
RichEditorEventHandle.onCustomAnchorClick(type,value,link,label)
|
||||
}
|
||||
|
||||
const onQuoteList=(keyword:string,handleFunc:(list:{
|
||||
value:string,
|
||||
label:string,
|
||||
photo:string
|
||||
}[])=>void)=>{
|
||||
RichEditorEventHandle.onQuoteList(keyword,handleFunc)
|
||||
}
|
||||
|
||||
const onDrop=(data?:DropParam)=>{
|
||||
RichEditorEventHandle.onDrop(objEditor,data,loading)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="!isEdit">
|
||||
<a-space wrap size="mini" v-if="(showValue as ICommon_Model_Project_Release[]).length>0">
|
||||
<ProjectReleasePreview v-for="item in (showValue as ICommon_Model_Project_Release[])" :key="item.id" :project-release-id="item.id" :name="item.name"></ProjectReleasePreview>
|
||||
</a-space>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-space size="mini" wrap>
|
||||
<a-tag v-for="(item,index) in (editValue as {id:string,name:string}[])" :closable="true" @close="onCloseLabelTag(index)" :key="item.id">
|
||||
{{item.name}}
|
||||
</a-tag>
|
||||
<a-select v-model="addValue" allow-search @search="onSearchRelease" v-if="showInput" @change="onAddChange">
|
||||
<a-option v-for="item in labelList" :label="item.name" :value="item.id"></a-option>
|
||||
</a-select>
|
||||
<a-tag v-else :style="{backgroundColor: 'var(--color-fill-2)',border: '1px dashed var(--color-fill-3)',cursor: 'pointer',}" @click="showInput=true">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
Add
|
||||
</a-tag>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {ICommon_Model_Project_Release} from "../../../../../../../common/model/project_release";
|
||||
import ProjectReleasePreview from "../../../../controller/app/project/release/projectReleasePreview.vue";
|
||||
import {inject, ref, watch} from "vue";
|
||||
import {injectProjectInfo} from "../../../util/symbol";
|
||||
import {apiIssue, apiRelease, DCSType} from "../../../request/request";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:DCSType<ICommon_Model_Project_Release>[],
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:DCSType<ICommon_Model_Project_Release>[]]
|
||||
}>()
|
||||
const labelList=ref<{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>([])
|
||||
const editValue=ref<{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>()
|
||||
const showInput=ref(false)
|
||||
const addValue=ref("")
|
||||
const projectId=inject(injectProjectInfo).id;
|
||||
|
||||
const assignValue=()=>{
|
||||
editValue.value=props.showValue.length>0?props.showValue.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
}):[]
|
||||
showInput.value=false
|
||||
addValue.value=""
|
||||
}
|
||||
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
|
||||
|
||||
const onCloseLabelTag=(index:number)=>{
|
||||
editValue.value.splice(index,1)
|
||||
}
|
||||
|
||||
const onAddChange=()=>{
|
||||
let arr=editValue.value as {
|
||||
id:string,
|
||||
name:string
|
||||
}[]
|
||||
let index=labelList.value.findIndex((item)=>{
|
||||
if(item.id==addValue.value) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
arr.push({
|
||||
id:labelList.value[index].id,
|
||||
name:labelList.value[index].name
|
||||
})
|
||||
addValue.value=""
|
||||
showInput.value=false
|
||||
}
|
||||
|
||||
const onSearchRelease=async (keyword:string)=>{
|
||||
let res=await apiRelease.list({
|
||||
projectId,
|
||||
name:keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
labelList.value=res.data.data
|
||||
}
|
||||
}
|
||||
|
||||
const onClick=async ()=>{
|
||||
let arr=editValue.value as {
|
||||
id:string,
|
||||
name:string
|
||||
}[]
|
||||
let arrId=Array.from(new Set(arr.map(item=>item.id)));
|
||||
let res=await apiIssue.bindReleases({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectReleaseIds:arrId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",res.data)
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="!isEdit">
|
||||
<a-space wrap size="mini" v-if="(showValue as ICommon_Model_Project_Label[]).length>0">
|
||||
<a-tag v-for="item in (showValue as ICommon_Model_Project_Label[])" color="blue">{{item.name}}</a-tag>
|
||||
</a-space>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-space size="mini" wrap>
|
||||
<a-tag v-for="(item,index) in (editValue as {id:string,name:string}[])" :closable="true" @close="onCloseLabelTag(index)" :key="item.id">
|
||||
{{item.name}}
|
||||
</a-tag>
|
||||
<a-select v-model="addValue" allow-search @search="onSearchLabel" v-if="showInput" @change="onAddChange">
|
||||
<a-option v-for="item in labelList" :label="item.name" :value="item.id"></a-option>
|
||||
</a-select>
|
||||
<a-tag v-else :style="{backgroundColor: 'var(--color-fill-2)',border: '1px dashed var(--color-fill-3)',cursor: 'pointer',}" @click="showInput=true">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
Add
|
||||
</a-tag>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {ICommon_Model_Project_Label} from "../../../../../../../common/model/project_label";
|
||||
import {inject, ref, watch} from "vue";
|
||||
import {apiIssue, apiProject} from "../../../request/request";
|
||||
import {injectProjectInfo} from "../../../util/symbol";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:ICommon_Model_Project_Label[],
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:ICommon_Model_Project_Label[]]
|
||||
}>()
|
||||
const labelList=ref<{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>([])
|
||||
const editValue=ref<{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>()
|
||||
const showInput=ref(false)
|
||||
const addValue=ref("")
|
||||
const projectId=inject(injectProjectInfo).id;
|
||||
|
||||
const assignValue=()=>{
|
||||
editValue.value=props.showValue.length>0?props.showValue.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
}):[]
|
||||
showInput.value=false
|
||||
addValue.value=""
|
||||
}
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
|
||||
const onCloseLabelTag=(index:number)=>{
|
||||
editValue.value.splice(index,1)
|
||||
}
|
||||
|
||||
const onSearchLabel=async (keyword:string)=>{
|
||||
let res=await apiProject.listLabel({
|
||||
projectId,
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
labelList.value=res.data.data
|
||||
}
|
||||
}
|
||||
|
||||
const onAddChange=()=>{
|
||||
let arr=editValue.value as {
|
||||
id:string,
|
||||
name:string
|
||||
}[]
|
||||
let index=labelList.value.findIndex((item)=>{
|
||||
if(item.id==addValue.value) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
arr.push({
|
||||
id:labelList.value[index].id,
|
||||
name:labelList.value[index].name
|
||||
})
|
||||
addValue.value=""
|
||||
showInput.value=false
|
||||
}
|
||||
|
||||
const onClick=async ()=>{
|
||||
let arr=editValue.value as {
|
||||
id:string,
|
||||
name:string
|
||||
}[]
|
||||
let arrId=Array.from(new Set(arr.map(item=>item.id)));
|
||||
let res=await apiIssue.bindLabel({
|
||||
projectIssueId:props.projectIssueId,
|
||||
labelIds:arrId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",res.data)
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="!isEdit">
|
||||
<a-space wrap size="mini" v-if="showValue.length>0">
|
||||
<template #split>
|
||||
/
|
||||
</template>
|
||||
<span v-for="item in showValue" style="color: blue">{{item.name}}</span>
|
||||
</a-space>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-cascader v-model="editValue" :field-names="fields" :options="moduleList" placeholder="please select" :format-label="format" check-strictly allow-clear allow-search></a-cascader>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {ICommon_Model_Project_Module} from "../../../../../../../common/model/project_module";
|
||||
import {inject, ref, watch} from "vue";
|
||||
import {injectProjectInfo} from "../../../util/symbol";
|
||||
import {ICommon_Route_Res_Project_CreateModule_Data} from "../../../../../../../common/routes/response";
|
||||
import {apiIssue, apiProject} from "../../../request/request";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:ICommon_Model_Project_Module[],
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:ICommon_Model_Project_Module[]]
|
||||
}>()
|
||||
const labelList=ref<{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>([])
|
||||
const editValue=ref("")
|
||||
const fields={
|
||||
label:"name",
|
||||
value:"id",
|
||||
children:"data"
|
||||
}
|
||||
const projectId=inject(injectProjectInfo).id;
|
||||
const moduleList=ref<ICommon_Route_Res_Project_CreateModule_Data[]>([])
|
||||
const assignValue=()=>{
|
||||
editValue.value=props.showValue.length>0?props.showValue[props.showValue.length-1].id:""
|
||||
}
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
watch(()=>props.isEdit,()=>{
|
||||
getModuleList()
|
||||
})
|
||||
const format = (options) => {
|
||||
const labels = options.map(option => option.name)
|
||||
return labels.join('/')
|
||||
}
|
||||
|
||||
const getModuleList=async ()=>{
|
||||
let res=await apiProject.listModule({
|
||||
projectId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
moduleList.value=res.data
|
||||
}
|
||||
}
|
||||
|
||||
const onClick=async ()=>{
|
||||
let res=await apiIssue.bindModule({
|
||||
projectIssueId:props.projectIssueId,
|
||||
moduleId:editValue.value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",res.data)
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div>
|
||||
<span style="font-size: 24px;font-weight: bold" v-if="!isEdit">{{ showValue }}</span>
|
||||
<template v-else>
|
||||
<a-row style="padding-right: 10px">
|
||||
<a-input v-model="editValue"></a-input>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, watch} from "vue";
|
||||
import {apiIssue} from "../../../request/request";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:string,
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:string]
|
||||
}>()
|
||||
const editValue=ref("")
|
||||
const assignValue=()=>{
|
||||
editValue.value=props.showValue
|
||||
}
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
|
||||
const onClick=async()=>{
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
name:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",res.data.name)
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<FieldPriority :priority="showValue" v-if="!isEdit"></FieldPriority>
|
||||
<template v-else>
|
||||
<a-space size="mini">
|
||||
<a-select v-model="editValue">
|
||||
<a-option label="low" :value="ECommon_Model_Project_Issue_Priority.LOW"></a-option>
|
||||
<a-option label="medium" :value="ECommon_Model_Project_Issue_Priority.MEDIUM"></a-option>
|
||||
<a-option label="high" :value="ECommon_Model_Project_Issue_Priority.HIGH"></a-option>
|
||||
<a-option label="urgent" :value="ECommon_Model_Project_Issue_Priority.URGENT"></a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {ECommon_Model_Project_Issue_Priority} from "../../../../../../../common/model/project_issue";
|
||||
import FieldPriority from "../fieldPriority.vue";
|
||||
import {ref, watch} from "vue";
|
||||
import {apiIssue} from "../../../request/request";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:ECommon_Model_Project_Issue_Priority,
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:ECommon_Model_Project_Issue_Priority]
|
||||
}>()
|
||||
|
||||
const editValue=ref<ECommon_Model_Project_Issue_Priority>()
|
||||
|
||||
const assignValue=()=>{
|
||||
editValue.value=props.showValue
|
||||
}
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
|
||||
const onClick=async ()=>{
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
priority:editValue.value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",editValue.value)
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="!isEdit">
|
||||
<UserAvatar v-if="showValue" :photo="showValue.photo" :name="showValue.nickname" :organization-user-id="showValue.organizationUserId"></UserAvatar>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">No User</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-select allow-search allow-clear v-model="editValue" @search="onSearchReporter">
|
||||
<a-option v-for="item in reporterList" :label="item.organizationUser.nickname" :value="item.organizationUser.id">
|
||||
<a-avatar :size="24" :image-url="item.user.photo"></a-avatar>
|
||||
{{ item.organizationUser.nickname }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import UserAvatar from "../../userAvatar.vue";
|
||||
import {ref, watch} from "vue";
|
||||
import {apiIssue, apiOrganization, DCSType} from "../../../request/request";
|
||||
import {ICommon_Route_Res_Organization_User_Item} from "../../../../../../../common/routes/response";
|
||||
import {SessionStorage} from "../../../storage/session";
|
||||
|
||||
const props=defineProps<{
|
||||
isEdit:boolean,
|
||||
showValue?:{
|
||||
id:string,
|
||||
organizationUserId?:string,
|
||||
photo?:string,
|
||||
nickname?:string
|
||||
},
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
cancel:[],
|
||||
update:[value:{
|
||||
id:string,
|
||||
organizationUserId?:string,
|
||||
photo?:string,
|
||||
nickname?:string
|
||||
}]
|
||||
}>()
|
||||
const editValue=ref("")
|
||||
const reporterList=ref<DCSType<ICommon_Route_Res_Organization_User_Item>[]>([])
|
||||
const assignValue=()=>{
|
||||
editValue.value=""
|
||||
}
|
||||
watch(()=>props.showValue,()=>{
|
||||
assignValue()
|
||||
},{
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
|
||||
const onClick=async()=>{
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
reporterId:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("update",res.data.reporter_id)
|
||||
}
|
||||
}
|
||||
|
||||
const onSearchReporter=async (keyword:string)=>{
|
||||
let res=await apiOrganization.listUser({
|
||||
organizationId:SessionStorage.get("organizationId"),
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
reporterList.value=res.data.data
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
emit('cancel')
|
||||
assignValue()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,291 @@
|
||||
<template>
|
||||
<div @focus="onFocus" tabindex="-1" class="hover">
|
||||
<template v-if="!isEdit">
|
||||
<template v-if="showValue.id">
|
||||
<UserAvatar :organization-user-id="showValue.id" v-if="type==='user'"></UserAvatar>
|
||||
<ProjectReleasePreview v-else-if="type==='release'" :project-release-id="showValue.id" :name="showValue.name"></ProjectReleasePreview>
|
||||
<ProjectIssuePreview :name="showValue.name" :project-issue-id="showValue.id" v-else-if="type==='issue'"></ProjectIssuePreview>
|
||||
<a-tag v-else>
|
||||
{{showValue.name}}
|
||||
</a-tag>
|
||||
</template>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-space size="mini" wrap>
|
||||
<a-select v-model="addValue" allow-search allow-clear @search="onSearch" v-model:input-value="label">
|
||||
<a-option v-for="item1 in searchValueList" :label="item1.name" :value="item1.id"></a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="onSubmit">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, watch, watchEffect} from "vue";
|
||||
import {
|
||||
apiField,
|
||||
apiIssue,
|
||||
apiOrganization,
|
||||
apiProject,
|
||||
apiRelease,
|
||||
apiTeam,
|
||||
apiWorkflow
|
||||
} from "@/business/common/request/request";
|
||||
import UserAvatar from "@/business/common/component/userAvatar.vue";
|
||||
import ProjectIssuePreview from "@/business/controller/app/project/issue/projectIssuePreview.vue";
|
||||
import ProjectReleasePreview from "@/business/controller/app/project/release/projectReleasePreview.vue";
|
||||
|
||||
const emit=defineEmits<{
|
||||
(e:"update:modelValue",value:string):void
|
||||
}>()
|
||||
const props=defineProps<{
|
||||
modelValue:string,
|
||||
type:"user"|"team"|"release"|"label"|"tag"|"issue"|"field",
|
||||
projectId?:string,
|
||||
workflowNodeId?:string
|
||||
}>()
|
||||
const showValue=ref<{
|
||||
name?:string,
|
||||
id:string,
|
||||
}>({} as any);
|
||||
const isEdit=ref(false)
|
||||
const searchValueList=ref<{
|
||||
name:string,
|
||||
id:string,
|
||||
}[]>([])
|
||||
const addValue=ref("")
|
||||
const label=ref("")
|
||||
const request=async ()=>{
|
||||
if(props.modelValue && props.modelValue.length>0) {
|
||||
let ret:{
|
||||
id:string,
|
||||
name:string
|
||||
}
|
||||
if(props.type==="team") {
|
||||
ret=await apiTeam.info({
|
||||
teamId:props.modelValue
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="user") {
|
||||
ret=await apiOrganization.user({
|
||||
organizationUserId:props.modelValue
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.nickname
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="release") {
|
||||
ret=await apiRelease.info({
|
||||
projectReleaseId:props.modelValue
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="label") {
|
||||
ret=await apiProject.getLabel({
|
||||
labelId:props.modelValue
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="tag") {
|
||||
ret=await apiOrganization.getTag({
|
||||
memberTagId:props.modelValue
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="issue") {
|
||||
ret=await apiIssue.basicInfo({
|
||||
projectIssueId:props.modelValue
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.project.keyword+"-"+data.data.unique_id
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="field") {
|
||||
ret=await apiField.workflowNodeFieldInfo({
|
||||
workflowNodeFieldTypeId:props.modelValue
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.field.id,
|
||||
name:data.data.field.name
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
showValue.value=ret
|
||||
label.value=showValue.value.name
|
||||
} else {
|
||||
showValue.value={} as any
|
||||
}
|
||||
}
|
||||
watchEffect(()=>{
|
||||
request()
|
||||
})
|
||||
|
||||
watch(isEdit,async ()=>{
|
||||
if(isEdit.value) {
|
||||
if(props.type==="field") {
|
||||
let res=await apiWorkflow.listApprovalField({
|
||||
workflowNodeId:props.workflowNodeId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const onFocus=()=>{
|
||||
isEdit.value=true
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
isEdit.value=false
|
||||
}
|
||||
|
||||
const onSubmit=()=>{
|
||||
isEdit.value=false
|
||||
emit("update:modelValue",addValue.value)
|
||||
}
|
||||
|
||||
|
||||
const onSearch=async (keyword:string)=>{
|
||||
if(keyword) {
|
||||
if(props.type==="team") {
|
||||
let res=await apiTeam.list({
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="user") {
|
||||
let res=await apiOrganization.listUser({
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.organizationUser.id,
|
||||
name:item.organizationUser.nickname
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="release") {
|
||||
let res=await apiRelease.list({
|
||||
page:0,
|
||||
size:10,
|
||||
name:keyword,
|
||||
projectId:props.projectId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="label") {
|
||||
let res=await apiProject.listLabel({
|
||||
page:0,
|
||||
size:10,
|
||||
keyword,
|
||||
projectId:props.projectId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="tag") {
|
||||
let res=await apiOrganization.listTag({
|
||||
keyword
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="issue") {
|
||||
let res=await apiIssue.filter({
|
||||
page:0,
|
||||
size:10,
|
||||
projectId:props.projectId,
|
||||
name:keyword
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hover:hover {
|
||||
background-color: rgb(230,231,237);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div @focus="onFocus" tabindex="-1" class="hover">
|
||||
<template v-if="!isEdit">
|
||||
<a-space wrap v-if="showValue && showValue.length>0">
|
||||
<template v-for="(item,index) in editValue" :key="item.id">
|
||||
<UserAvatar :organization-user-id="item.id" v-if="type==='user'"></UserAvatar>
|
||||
<ProjectReleasePreview v-else-if="type==='release'" :project-release-id="item.id" :name="item.name"></ProjectReleasePreview>
|
||||
<ProjectIssuePreview :name="item.name" :project-issue-id="item.id" v-else-if="type==='issue'"></ProjectIssuePreview>
|
||||
<a-tag v-else>
|
||||
{{item.name}}
|
||||
</a-tag>
|
||||
</template>
|
||||
</a-space>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-space size="mini" wrap>
|
||||
<a-tag v-for="(value,index) in editValue" :closable="true" @close="onClose(index)" :key="value.id">
|
||||
{{value.name}}
|
||||
</a-tag>
|
||||
<a-select v-model="addValue" allow-search @search="onSearch" v-if="showInput" @change="onChange">
|
||||
<a-option v-for="item1 in searchValueList" :label="item1.name" :value="item1.id"></a-option>
|
||||
</a-select>
|
||||
<a-tag v-else :style="{backgroundColor: 'var(--color-fill-2)',border: '1px dashed var(--color-fill-3)',cursor: 'pointer',}" @click="showInput=true">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
Add
|
||||
</a-tag>
|
||||
<a-button type="text" @click="onSubmit">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, watchEffect} from "vue";
|
||||
import {apiIssue, apiOrganization, apiProject, apiRelease, apiTeam} from "@/business/common/request/request";
|
||||
import UserAvatar from "@/business/common/component/userAvatar.vue";
|
||||
import ProjectIssuePreview from "@/business/controller/app/project/issue/projectIssuePreview.vue";
|
||||
import ProjectReleasePreview from "@/business/controller/app/project/release/projectReleasePreview.vue";
|
||||
|
||||
const emit=defineEmits<{
|
||||
(e:"update:modelValue",value:string[]):void
|
||||
}>()
|
||||
const props=defineProps<{
|
||||
modelValue:string[],
|
||||
type:"user"|"team"|"release"|"label"|"tag"|"issue",
|
||||
projectId?:string
|
||||
}>()
|
||||
const showValue=ref<{
|
||||
name?:string,
|
||||
id:string,
|
||||
}[]>([]);
|
||||
const editValue=ref<{
|
||||
name?:string,
|
||||
id:string
|
||||
}[]>([]);
|
||||
const isEdit=ref(false)
|
||||
const searchValueList=ref<{
|
||||
name:string,
|
||||
id:string,
|
||||
}[]>([])
|
||||
const addValue=ref("")
|
||||
const showInput = ref(false);
|
||||
const request=async ()=>{
|
||||
if(props.modelValue && props.modelValue.length>0) {
|
||||
let arr=await Promise.all(props.modelValue.map(item=>{
|
||||
if(props.type==="team") {
|
||||
return apiTeam.info({
|
||||
teamId:item
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="user") {
|
||||
return apiOrganization.user({
|
||||
organizationUserId:item
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.nickname
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="release") {
|
||||
return apiRelease.info({
|
||||
projectReleaseId:item
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="label") {
|
||||
return apiProject.getLabel({
|
||||
labelId:item
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="tag") {
|
||||
return apiOrganization.getTag({
|
||||
memberTagId:item
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.name
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if(props.type==="issue") {
|
||||
return apiIssue.basicInfo({
|
||||
projectIssueId:item
|
||||
}).then(data=>{
|
||||
if(data.code==0) {
|
||||
return {
|
||||
id:data.data.id,
|
||||
name:data.data.project.keyword+"-"+data.data.unique_id
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
arr=arr.filter(item=>{
|
||||
return item!=null
|
||||
})
|
||||
showValue.value=arr
|
||||
editValue.value=[...arr]
|
||||
} else {
|
||||
showValue.value=[]
|
||||
editValue.value=[]
|
||||
}
|
||||
}
|
||||
watchEffect(()=>{
|
||||
request()
|
||||
})
|
||||
|
||||
const onFocus=()=>{
|
||||
isEdit.value=true
|
||||
}
|
||||
|
||||
const onBlur=()=>{
|
||||
isEdit.value=false
|
||||
}
|
||||
|
||||
const onSubmit=()=>{
|
||||
isEdit.value=false
|
||||
emit("update:modelValue",editValue.value.map(item=>item.id))
|
||||
}
|
||||
|
||||
const onChange=()=>{
|
||||
showInput.value=false
|
||||
editValue.value=[...editValue.value,searchValueList.value.find(item=>{
|
||||
if(item.id==addValue.value) {
|
||||
return true
|
||||
}
|
||||
})]
|
||||
addValue.value=""
|
||||
}
|
||||
const onClose=(index:number)=>{
|
||||
editValue.value.splice(index,1)
|
||||
}
|
||||
const onSearch=async (keyword:string)=>{
|
||||
if(keyword) {
|
||||
if(props.type==="team") {
|
||||
let res=await apiTeam.list({
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="user") {
|
||||
let res=await apiOrganization.listUser({
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.organizationUser.id,
|
||||
name:item.organizationUser.nickname
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="release") {
|
||||
let res=await apiRelease.list({
|
||||
page:0,
|
||||
size:10,
|
||||
name:keyword,
|
||||
projectId:props.projectId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="label") {
|
||||
let res=await apiProject.listLabel({
|
||||
page:0,
|
||||
size:10,
|
||||
keyword,
|
||||
projectId:props.projectId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="tag") {
|
||||
let res=await apiOrganization.listTag({
|
||||
keyword
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if(props.type==="issue") {
|
||||
let res=await apiIssue.filter({
|
||||
page:0,
|
||||
size:10,
|
||||
projectId:props.projectId,
|
||||
name:keyword
|
||||
})
|
||||
if(res?.code==0) {
|
||||
searchValueList.value=res.data.data.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hover:hover {
|
||||
background-color: rgb(230,231,237);
|
||||
}
|
||||
</style>
|
@ -1,206 +1,13 @@
|
||||
<template>
|
||||
<span style="display: inline-block;width: 100%;cursor: text" @mouseenter="onEnter" @mouseleave="onLeave" tabindex="-1" @focusin="onFocus" ref="element">
|
||||
<template v-if="type==EClient_Field_Basic_Type.NAME">
|
||||
<span style="font-size: 24px;font-weight: bold" v-if="!isEdit">{{ showValue }}</span>
|
||||
<template v-else>
|
||||
<a-row style="padding-right: 10px">
|
||||
<a-input v-model="editValue"></a-input>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="type==EClient_Field_Basic_Type.DESCRIPTION">
|
||||
<span style="line-height: 30px" v-if="!isEdit">{{ showValue }}</span>
|
||||
<template v-else>
|
||||
<a-row style="padding-right: 10px">
|
||||
<a-textarea v-model="editValue" allow-clear></a-textarea>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="type==EClient_Field_Basic_Type.ASSIGNER">
|
||||
<template v-if="!isEdit">
|
||||
<UserAvatar v-if="showValue" :photo="showValue.photo" :name="showValue.nickname" :organization-user-id="showValue.organizationUserId"></UserAvatar>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">No User</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-select allow-search allow-clear v-model="editValue" @search="onSearchAssigner">
|
||||
<a-option v-for="item in assignerList" :label="item.organizationUser.nickname" :value="item.organizationUser.id">
|
||||
<a-avatar :size="24" :image-url="item.user.photo"></a-avatar>
|
||||
{{ item.organizationUser.nickname }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</template>
|
||||
<template v-if="type==EClient_Field_Basic_Type.REPORTER">
|
||||
<template v-if="!isEdit">
|
||||
<UserAvatar v-if="showValue" :photo="showValue.photo" :name="showValue.nickname" :organization-user-id="showValue.organizationUserId"></UserAvatar>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">No User</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-row style="padding-right: 10px">
|
||||
<a-select allow-search allow-clear v-model="editValue" @search="onSearchReporter">
|
||||
<a-option v-for="item in reporterList" :label="item.organizationUser.nickname" :value="item.organizationUser.id">
|
||||
<a-avatar :size="24" :image-url="item.user.photo"></a-avatar>
|
||||
{{item.organizationUser.nickname}}
|
||||
</a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="type==EClient_Field_Basic_Type.PRIORITY">
|
||||
<FieldPriority :priority="showValue" v-if="!isEdit"></FieldPriority>
|
||||
<template v-else>
|
||||
<a-space size="mini">
|
||||
<a-select v-model="editValue">
|
||||
<a-option label="low" :value="ECommon_Model_Project_Issue_Priority.LOW"></a-option>
|
||||
<a-option label="medium" :value="ECommon_Model_Project_Issue_Priority.MEDIUM"></a-option>
|
||||
<a-option label="high" :value="ECommon_Model_Project_Issue_Priority.HIGH"></a-option>
|
||||
<a-option label="urgent" :value="ECommon_Model_Project_Issue_Priority.URGENT"></a-option>
|
||||
</a-select>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="type==EClient_Field_Basic_Type.LABEL">
|
||||
<template v-if="!isEdit">
|
||||
<a-space wrap size="mini" v-if="showValue.length>0">
|
||||
<a-tag v-for="item in showValue" color="blue">{{item.name}}</a-tag>
|
||||
</a-space>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-space size="mini" wrap>
|
||||
<a-tag v-for="(item,index) in editValue" :closable="true" @close="onCloseLabelTag(index)" :key="item.id">
|
||||
{{item.name}}
|
||||
</a-tag>
|
||||
<a-select v-model="addValue" allow-search @search="onSearchLabel" v-if="showInput" @change="onAddChange">
|
||||
<a-option v-for="item in labelList" :label="item.name" :value="item.id"></a-option>
|
||||
</a-select>
|
||||
<a-tag v-else :style="{backgroundColor: 'var(--color-fill-2)',border: '1px dashed var(--color-fill-3)',cursor: 'pointer',}" @click="showInput=true">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
Add
|
||||
</a-tag>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-row>
|
||||
</template>
|
||||
<template v-if="type==EClient_Field_Basic_Type.MODULE">
|
||||
<template v-if="!isEdit">
|
||||
<a-space wrap size="mini" v-if="showValue.length>0">
|
||||
<template #split>
|
||||
/
|
||||
</template>
|
||||
<span v-for="item in showValue" style="color: blue">{{item.name}}</span>
|
||||
</a-space>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-cascader v-model="editValue" :field-names="fields" :options="moduleList" placeholder="please select" :format-label="format" check-strictly allow-clear allow-search></a-cascader>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-row>
|
||||
</template>
|
||||
<template v-if="type==EClient_Field_Basic_Type.FIXVERSIONS">
|
||||
<template v-if="!isEdit">
|
||||
<a-space wrap size="mini" v-if="showValue.length>0">
|
||||
<ProjectReleasePreview v-for="item in showValue" :key="item.id" :project-release-id="item.id" :name="item.name"></ProjectReleasePreview>
|
||||
</a-space>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</template>
|
||||
<a-row style="padding-right: 10px" v-else>
|
||||
<a-space size="mini" wrap>
|
||||
<a-tag v-for="(item,index) in editValue" :closable="true" @close="onCloseLabelTag(index)" :key="item.id">
|
||||
{{item.name}}
|
||||
</a-tag>
|
||||
<a-select v-model="addValue" allow-search @search="onSearchRelease" v-if="showInput" @change="onAddChange">
|
||||
<a-option v-for="item in labelList" :label="item.name" :value="item.id"></a-option>
|
||||
</a-select>
|
||||
<a-tag v-else :style="{backgroundColor: 'var(--color-fill-2)',border: '1px dashed var(--color-fill-3)',cursor: 'pointer',}" @click="showInput=true">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
Add
|
||||
</a-tag>
|
||||
<a-button type="text" @click="onClick">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="onBlur">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-row>
|
||||
</template>
|
||||
<span style="display: inline-block;width: 100%;cursor: text" @mouseenter="onEnter" @mouseleave="onLeave" tabindex="-1" @focus="onFocus" ref="element">
|
||||
<FieldEditBasicName :is-edit="isEdit" :show-value="showValue as string" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.NAME" @update="onUpdate" @cancel="onBlur"></FieldEditBasicName>
|
||||
<FieldEditBasicDescription :is-edit="isEdit" :show-value="showValue as string" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.DESCRIPTION" @update="onUpdate" @cancel="onBlur"></FieldEditBasicDescription>
|
||||
<FieldEditBasicAssigner :is-edit="isEdit" :show-value="showValue as User" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.ASSIGNER" @update="onUpdate" @cancel="onBlur"></FieldEditBasicAssigner>
|
||||
<FieldEditBasicReporter :is-edit="isEdit" :show-value="showValue as User" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.REPORTER" @update="onUpdate" @cancel="onBlur"></FieldEditBasicReporter>
|
||||
<FieldEditBasicPriority :is-edit="isEdit" :show-value="showValue as ECommon_Model_Project_Issue_Priority" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.PRIORITY" @update="onUpdate" @cancel="onBlur"></FieldEditBasicPriority>
|
||||
<FieldEditBasicLabel :is-edit="isEdit" :show-value="showValue as ICommon_Model_Project_Label[]" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.LABEL" @update="onUpdate" @cancel="onBlur"></FieldEditBasicLabel>
|
||||
<FieldEditBasicModule :is-edit="isEdit" :show-value="showValue as ICommon_Model_Project_Module[]" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.MODULE" @update="onUpdate" @cancel="onBlur"></FieldEditBasicModule>
|
||||
<FieldEditBasicFixVersion :is-edit="isEdit" :show-value="showValue as DCSType<ICommon_Model_Project_Release>[]" :project-issue-id="projectIssueId" v-if="type==EClient_Field_Basic_Type.FIXVERSIONS" @update="onUpdate" @cancel="onBlur"></FieldEditBasicFixVersion>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@ -208,118 +15,61 @@
|
||||
import {EClient_Field_Basic_Type} from "./fieldBasicType";
|
||||
import {ICommon_Model_Project_Label} from "../../../../../../common/model/project_label";
|
||||
import {ICommon_Model_Project_Module} from "../../../../../../common/model/project_module";
|
||||
import {inject, onMounted, ref, watch} from "vue";
|
||||
import {apiIssue, apiOrganization, apiProject, apiRelease, DCSType} from "../../request/request";
|
||||
import UserAvatar from "../userAvatar.vue";
|
||||
import {
|
||||
ICommon_Route_Res_Organization_User_Item,
|
||||
ICommon_Route_Res_Project_CreateModule_Data
|
||||
} from "../../../../../../common/routes/response";
|
||||
import FieldPriority from "./fieldPriority.vue";
|
||||
import {inject, ref, watch} from "vue";
|
||||
import {DCSType} from "../../request/request";
|
||||
import {ECommon_Model_Project_Issue_Priority} from "../../../../../../common/model/project_issue";
|
||||
import {injectProjectInfo} from "../../util/symbol";
|
||||
import {checkPermission, Permission_Types} from "../../../../../../common/permission/permission";
|
||||
import {ICommon_Model_Project_Release} from "../../../../../../common/model/project_release";
|
||||
import ProjectReleasePreview from "../../../controller/app/project/release/projectReleasePreview.vue";
|
||||
import {SessionStorage} from "../../storage/session";
|
||||
import FieldEditBasicName from "./basic/fieldEditBasicName.vue";
|
||||
import FieldEditBasicDescription from "./basic/fieldEditBasicDescription.vue";
|
||||
import FieldEditBasicAssigner from "./basic/fieldEditBasicAssigner.vue";
|
||||
import FieldEditBasicPriority from "./basic/fieldEditBasicPriority.vue";
|
||||
import FieldEditBasicReporter from "./basic/fieldEditBasicReporter.vue";
|
||||
import FieldEditBasicLabel from "./basic/fieldEditBasicLabel.vue";
|
||||
import FieldEditBasicModule from "./basic/fieldEditBasicModule.vue";
|
||||
import FieldEditBasicFixVersion from "./basic/fieldEditBasicFixVersion.vue";
|
||||
|
||||
type User={
|
||||
id:string,
|
||||
organizationUserId:string,
|
||||
organizationUserId?:string,
|
||||
photo?:string,
|
||||
nickname?:string
|
||||
}
|
||||
type Value=string|ECommon_Model_Project_Issue_Priority|User|ICommon_Model_Project_Label[]|ICommon_Model_Project_Module[]|number|ICommon_Model_Project_Release[]
|
||||
const props=defineProps<{
|
||||
projectIssueId:string,
|
||||
type:EClient_Field_Basic_Type,
|
||||
value?:Value
|
||||
}>()
|
||||
const fields={
|
||||
label:"name",
|
||||
value:"id",
|
||||
children:"data"
|
||||
}
|
||||
const showInput=ref(false)
|
||||
const addValue=ref("")
|
||||
type Props={
|
||||
projectIssueId:string
|
||||
} & (
|
||||
{
|
||||
type:EClient_Field_Basic_Type.NAME | EClient_Field_Basic_Type.DESCRIPTION,
|
||||
value?:string
|
||||
} | {
|
||||
type:EClient_Field_Basic_Type.PRIORITY,
|
||||
value?:number
|
||||
} | {
|
||||
type:EClient_Field_Basic_Type.ASSIGNER | EClient_Field_Basic_Type.REPORTER,
|
||||
value?:User
|
||||
} | {
|
||||
type:EClient_Field_Basic_Type.LABEL,
|
||||
value?:ICommon_Model_Project_Label[]
|
||||
} | {
|
||||
type:EClient_Field_Basic_Type.MODULE,
|
||||
value?:ICommon_Model_Project_Module[]
|
||||
} | {
|
||||
type:EClient_Field_Basic_Type.FIXVERSIONS,
|
||||
value?:ICommon_Model_Project_Release[]
|
||||
}
|
||||
)
|
||||
const props=defineProps<Props>()
|
||||
const element=ref<HTMLSpanElement>(null)
|
||||
const showValue=ref(props.value)
|
||||
const editValue=ref<string|ECommon_Model_Project_Issue_Priority|string[]|{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>(null)
|
||||
const assignEditValue=()=>{
|
||||
switch (props.type) {
|
||||
case EClient_Field_Basic_Type.DESCRIPTION:
|
||||
case EClient_Field_Basic_Type.NAME: {
|
||||
editValue.value=showValue.value as string
|
||||
break
|
||||
}
|
||||
case EClient_Field_Basic_Type.PRIORITY:{
|
||||
editValue.value=showValue.value as number
|
||||
break
|
||||
}
|
||||
case EClient_Field_Basic_Type.REPORTER:
|
||||
case EClient_Field_Basic_Type.ASSIGNER:{
|
||||
editValue.value=""
|
||||
break
|
||||
}
|
||||
case EClient_Field_Basic_Type.LABEL:{
|
||||
let value=showValue.value as ICommon_Model_Project_Label[]
|
||||
editValue.value=value.length>0?value.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
}):[]
|
||||
break
|
||||
}
|
||||
case EClient_Field_Basic_Type.MODULE:{
|
||||
let value=showValue.value as ICommon_Model_Project_Module[]
|
||||
editValue.value=value.length>0?value[value.length-1].id:""
|
||||
break
|
||||
}
|
||||
case EClient_Field_Basic_Type.FIXVERSIONS:{
|
||||
let value=showValue.value as ICommon_Model_Project_Release[]
|
||||
editValue.value=value.length>0?value.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
name:item.name
|
||||
}
|
||||
}):[]
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
const projectId=inject(injectProjectInfo).id;
|
||||
const permission=inject(injectProjectInfo).permission
|
||||
const isEdit=ref(false)
|
||||
const assignerList=ref<DCSType<ICommon_Route_Res_Organization_User_Item[]>>([])
|
||||
const reporterList=ref<DCSType<ICommon_Route_Res_Organization_User_Item[]>>([])
|
||||
const labelList=ref<{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>([])
|
||||
const moduleList=ref<ICommon_Route_Res_Project_CreateModule_Data[]>([])
|
||||
watch(()=>props.value,(newValue,oldValue)=>{
|
||||
showValue.value=props.value
|
||||
if(props.type===EClient_Field_Basic_Type.DESCRIPTION) {
|
||||
if(!showValue.value) {
|
||||
isEdit.value=true
|
||||
} else {
|
||||
isEdit.value=false
|
||||
}
|
||||
}
|
||||
assignEditValue()
|
||||
},{
|
||||
immediate:true
|
||||
})
|
||||
watch(isEdit,()=>{
|
||||
if(isEdit.value && props.type==EClient_Field_Basic_Type.MODULE) {
|
||||
getModuleList()
|
||||
}
|
||||
immediate:true,
|
||||
deep:true
|
||||
})
|
||||
const onEnter=(event:MouseEvent)=>{
|
||||
if(!isEdit.value) {
|
||||
@ -339,180 +89,14 @@ const onFocus=async (event:MouseEvent)=>{
|
||||
}
|
||||
}
|
||||
const onBlur=async ()=>{
|
||||
if(props.type===EClient_Field_Basic_Type.DESCRIPTION && !editValue.value) {
|
||||
return
|
||||
}
|
||||
isEdit.value=false
|
||||
showInput.value=false
|
||||
addValue.value=""
|
||||
assignEditValue()
|
||||
}
|
||||
const onClick=async ()=>{
|
||||
if(props.type==EClient_Field_Basic_Type.NAME) {
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
name:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=editValue.value as string
|
||||
onBlur()
|
||||
}
|
||||
} else if(props.type==EClient_Field_Basic_Type.DESCRIPTION) {
|
||||
let res=await apiIssue.editDescription({
|
||||
projectIssueId:props.projectIssueId,
|
||||
description:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=editValue.value as string
|
||||
onBlur()
|
||||
}
|
||||
} else if(props.type==EClient_Field_Basic_Type.ASSIGNER) {
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
assignerId:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=res.data.assigner_id as User
|
||||
onBlur()
|
||||
}
|
||||
} else if(props.type==EClient_Field_Basic_Type.REPORTER) {
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
reporterId:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=res.data.reporter_id as User
|
||||
onBlur()
|
||||
}
|
||||
} else if(props.type==EClient_Field_Basic_Type.PRIORITY) {
|
||||
let res=await apiIssue.editBasicField({
|
||||
projectIssueId:props.projectIssueId,
|
||||
priority:editValue.value as ECommon_Model_Project_Issue_Priority
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=editValue.value as number
|
||||
onBlur()
|
||||
}
|
||||
} else if(props.type==EClient_Field_Basic_Type.LABEL) {
|
||||
let arr=editValue.value as {
|
||||
id:string,
|
||||
name:string
|
||||
}[]
|
||||
let arrId=Array.from(new Set(arr.map(item=>item.id)));
|
||||
let res=await apiIssue.bindLabel({
|
||||
projectIssueId:props.projectIssueId,
|
||||
labelIds:arrId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=res.data
|
||||
onBlur()
|
||||
}
|
||||
} else if(props.type==EClient_Field_Basic_Type.MODULE) {
|
||||
let res=await apiIssue.bindModule({
|
||||
projectIssueId:props.projectIssueId,
|
||||
moduleId:editValue.value as string
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=res.data
|
||||
onBlur()
|
||||
}
|
||||
} else if(props.type==EClient_Field_Basic_Type.FIXVERSIONS) {
|
||||
let arr=editValue.value as {
|
||||
id:string,
|
||||
name:string
|
||||
}[]
|
||||
let arrId=Array.from(new Set(arr.map(item=>item.id)));
|
||||
let res=await apiIssue.bindReleases({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectReleaseIds:arrId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
showValue.value=res.data
|
||||
onBlur()
|
||||
}
|
||||
}
|
||||
}
|
||||
const onSearchAssigner=async (keyword:string)=>{
|
||||
let res=await apiOrganization.listUser({
|
||||
organizationId:SessionStorage.get("organizationId"),
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
assignerList.value=res.data.data
|
||||
}
|
||||
}
|
||||
const onSearchReporter=async (keyword:string)=>{
|
||||
let res=await apiOrganization.listUser({
|
||||
organizationId:SessionStorage.get("organizationId"),
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
reporterList.value=res.data.data
|
||||
}
|
||||
}
|
||||
const onSearchLabel=async (keyword:string)=>{
|
||||
let res=await apiProject.listLabel({
|
||||
projectId,
|
||||
keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
labelList.value=res.data.data
|
||||
}
|
||||
}
|
||||
const onSearchRelease=async (keyword:string)=>{
|
||||
let res=await apiRelease.list({
|
||||
projectId,
|
||||
name:keyword,
|
||||
page:0,
|
||||
size:10
|
||||
})
|
||||
if(res?.code==0) {
|
||||
labelList.value=res.data.data
|
||||
}
|
||||
}
|
||||
const getModuleList=async ()=>{
|
||||
let res=await apiProject.listModule({
|
||||
projectId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
moduleList.value=res.data
|
||||
}
|
||||
}
|
||||
const format = (options) => {
|
||||
const labels = options.map(option => option.name)
|
||||
return labels.join('/')
|
||||
}
|
||||
onMounted(()=>{
|
||||
if(props.type==EClient_Field_Basic_Type.DESCRIPTION && showValue.value=="") {
|
||||
element.value.focus()
|
||||
}
|
||||
})
|
||||
const onCloseLabelTag=(index:number)=>{
|
||||
(editValue.value as any[]).splice(index,1)
|
||||
}
|
||||
const onAddChange=()=>{
|
||||
let arr=editValue.value as {
|
||||
id:string,
|
||||
name:string
|
||||
}[]
|
||||
let index=labelList.value.findIndex((item)=>{
|
||||
if(item.id==addValue.value) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
arr.push({
|
||||
id:labelList.value[index].id,
|
||||
name:labelList.value[index].name
|
||||
})
|
||||
addValue.value=""
|
||||
showInput.value=false
|
||||
|
||||
const onUpdate=(value)=>{
|
||||
showValue.value=value
|
||||
isEdit.value=false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<a-space v-if="!isEdit">
|
||||
<a-space v-if="!isEdit" wrap>
|
||||
<template v-if="showValue && showValue.length>0">
|
||||
<a-tag v-for="item in showValue">{{item.value}}</a-tag>
|
||||
</template>
|
||||
<span v-else style="line-height: 30px;width: 100%;color: gray">None</span>
|
||||
</a-space>
|
||||
<template v-else>
|
||||
<a-space size="mini">
|
||||
<a-space size="mini" wrap>
|
||||
<a-select v-model="editValue" allow-clear allow-search multiple>
|
||||
<a-option v-for="item1 in list" :label="item1.value" :value="item1.id"></a-option>
|
||||
</a-select>
|
||||
@ -18,7 +18,7 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, watchEffect} from "vue";
|
||||
import {
|
||||
ICommon_Model_Workflow_Node_Field_Type_Config
|
||||
ICommon_Model_Workflow_Node_Field_Type_Config
|
||||
} from "../../../../../../common/model/workflow_node_field_type_config";
|
||||
|
||||
const props=defineProps<{
|
||||
|
@ -5,17 +5,17 @@
|
||||
<a-textarea :default-value="item.field.default_string_value" v-else-if="item.fieldType.type===ECommon_Field_Type.MULTITEXT" v-model="item.fieldValue.value"></a-textarea>
|
||||
<a-date-picker v-else-if="item.fieldType.type===ECommon_Field_Type.DATE" v-model="item.fieldValue.value"/>
|
||||
<a-time-picker v-else-if="item.fieldType.type===ECommon_Field_Type.TIME" v-model="item.fieldValue.value"/>
|
||||
<a-select v-else-if="item.fieldType.type===ECommon_Field_Type.SELECT" :default-value="item.values.filter(item=>item.selected).map(item=>item.id)" v-model="item.fieldValue.value">
|
||||
<a-select v-else-if="item.fieldType.type===ECommon_Field_Type.SELECT" :default-value="item.values.filter((item1:ICommon_Model_Workflow_Node_Field_Type_Config)=>item1.selected).map((item1:ICommon_Model_Workflow_Node_Field_Type_Config)=>item1.id)" v-model="item.fieldValue.value">
|
||||
<a-option v-for="item in item.values" :label="item.value" :value="item.id"></a-option>
|
||||
</a-select>
|
||||
<a-select multiple v-else-if="item.fieldType.type===ECommon_Field_Type.MULTISELECT" :default-value="item.values.filter(item=>item.selected).map(item=>item.id)" v-model="item.fieldValue.value">
|
||||
<a-select multiple v-else-if="item.fieldType.type===ECommon_Field_Type.MULTISELECT" :default-value="item.values.filter((item1:ICommon_Model_Workflow_Node_Field_Type_Config)=>item1.selected).map((item1:ICommon_Model_Workflow_Node_Field_Type_Config)=>item1.id)" v-model="item.fieldValue.value">
|
||||
<a-option v-for="item in item.values" :label="item.value" :value="item.id"></a-option>
|
||||
</a-select>
|
||||
<a-select v-else-if="item.fieldType.type===ECommon_Field_Type.MULTILABEL" allow-search @search="onSearchMultiLabel" v-model="item.fieldValue.value" multiple>
|
||||
<a-option v-for="item in multiLabelList" :value="item.id" :label="item.name"></a-option>
|
||||
<a-option v-for="item1 in multiLabelList" :value="item1.id" :label="item1.name"></a-option>
|
||||
</a-select>
|
||||
<a-select v-else-if="item.fieldType.type===ECommon_Field_Type.LABEL" allow-search @search="onSearchLabel" v-model="item.fieldValue.value">
|
||||
<a-option v-for="item in labelList" :value="item.id" :label="item.name"></a-option>
|
||||
<a-option v-for="item1 in labelList" :value="item1.id" :label="item1.name"></a-option>
|
||||
</a-select>
|
||||
</span>
|
||||
</template>
|
||||
@ -34,6 +34,9 @@ import {
|
||||
ECommon_Model_Workflow_Node_Field_Type_Label_Type
|
||||
} from "../../../../../../common/model/workflow_node_field_type";
|
||||
import {SessionStorage} from "../../storage/session";
|
||||
import {
|
||||
ICommon_Model_Workflow_Node_Field_Type_Config
|
||||
} from "../../../../../../common/model/workflow_node_field_type_config";
|
||||
|
||||
const props=defineProps<{
|
||||
item:DCSType<ICommon_Route_Res_Workflow_Node_Field & {
|
||||
@ -89,16 +92,16 @@ switch (props.item.fieldType.type) {
|
||||
default:
|
||||
break
|
||||
}
|
||||
const multiLabelList=ref<DCSType<ICommon_Route_Res_Project_Issue_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_Issue_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
|
||||
}[]>>([])
|
||||
}>[]>([])
|
||||
const onSearchMultiLabel=async (value:string)=>{
|
||||
if(props.item.field.label_type===ECommon_Model_Workflow_Node_Field_Type_Label_Type.ISSUE) {
|
||||
let res=await apiIssue.filter({
|
||||
|
51
code/client/src/business/common/component/flow/approval.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import {DiamondNode, DiamondNodeModel} from "@logicflow/core";
|
||||
import {NodeTextTheme} from "@logicflow/core/types/constant/DefaultTheme";
|
||||
import {
|
||||
ECommon_Model_Workflow_Node_Status,
|
||||
ICommon_Model_Workflow_Node
|
||||
} from "../../../../../../common/model/workflow_node";
|
||||
|
||||
class approvalModel extends DiamondNodeModel {
|
||||
override getNodeStyle(): { [p: string]: any; width?: number; height?: number; radius?: number; fill?: string; stroke?: string; strokeWidth?: number } {
|
||||
const style=super.getNodeStyle();
|
||||
style.strokeWidth=0
|
||||
let props=this.getProperties() as ICommon_Model_Workflow_Node
|
||||
if(props.status==ECommon_Model_Workflow_Node_Status.INPROGRESS) {
|
||||
style.fill="rgb(4,57,192)"
|
||||
} else if(props.status==ECommon_Model_Workflow_Node_Status.DONE) {
|
||||
style.fill="rgb(15,119,72)"
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
override getTextStyle(): NodeTextTheme {
|
||||
const style=super.getTextStyle();
|
||||
let props=this.getProperties() as ICommon_Model_Workflow_Node
|
||||
if(props.status==ECommon_Model_Workflow_Node_Status.INPROGRESS) {
|
||||
style.fill="white"
|
||||
style.stroke="white"
|
||||
} else if(props.status==ECommon_Model_Workflow_Node_Status.DONE) {
|
||||
style.fill="white"
|
||||
style.stroke="white"
|
||||
}
|
||||
style.fontSize=13
|
||||
style.strokeWidth=0.8
|
||||
return style;
|
||||
}
|
||||
|
||||
override initNodeData(data: any) {
|
||||
super.initNodeData(data);
|
||||
this.rx=40
|
||||
this.ry=40
|
||||
}
|
||||
}
|
||||
|
||||
class approvalView extends DiamondNode {
|
||||
|
||||
}
|
||||
|
||||
export const flowApproval= {
|
||||
type: "approval",
|
||||
view: approvalView,
|
||||
model: approvalModel
|
||||
};
|
@ -132,6 +132,24 @@
|
||||
|
||||
to you
|
||||
</template>
|
||||
<template v-else-if="obj.type===ECommon_Model_Notification_Type.ISSUE_APPROVAL_RESOLVE">
|
||||
ISSUE:
|
||||
|
||||
<UserAvatar :organization-user-id="obj.operationOrganizationUser.id" :name="obj.operationOrganizationUser.name" :photo="obj.operationOrganizationUser.photo" :organization-id="obj.organization_id"></UserAvatar>
|
||||
|
||||
resolved the approval of issue
|
||||
|
||||
<a href="javascript:void(0)" @click="onIssue">{{(obj.data as ISSUE)?.project.keyword}}-{{(obj.data as ISSUE)?.issue.unique_id}}</a>
|
||||
</template>
|
||||
<template v-else-if="obj.type===ECommon_Model_Notification_Type.ISSUE_APPROVAL_REJECT">
|
||||
ISSUE:
|
||||
|
||||
<UserAvatar :organization-user-id="obj.operationOrganizationUser.id" :name="obj.operationOrganizationUser.name" :photo="obj.operationOrganizationUser.photo" :organization-id="obj.organization_id"></UserAvatar>
|
||||
|
||||
rejected the approval of issue
|
||||
|
||||
<a href="javascript:void(0)" @click="onIssue">{{(obj.data as ISSUE)?.project.keyword}}-{{(obj.data as ISSUE)?.issue.unique_id}}</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -264,6 +264,9 @@ export class RichEditorEvent {
|
||||
onMouseUp(event:MouseEvent){
|
||||
this.isMouseDown=false
|
||||
let selection=window.getSelection()
|
||||
if(selection.rangeCount==0) {
|
||||
return
|
||||
}
|
||||
let range=selection.getRangeAt(0)
|
||||
if(this.selectElementList.length>0) {
|
||||
range=range.cloneRange()
|
||||
|
Before Width: | Height: | Size: 434 KiB |
@ -59,7 +59,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("dragType",EClient_Drag_Type.FILE)
|
||||
ele.setAttribute("dragValue",obj.value)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-file"
|
||||
icon.className="svg svg-file"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="gray"
|
||||
ele.prepend(icon)
|
||||
@ -86,7 +86,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("shortcutRefId",obj.value)
|
||||
ele.setAttribute("shortcutName",obj.label)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-list-alt"
|
||||
icon.className="svg svg-project"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="green"
|
||||
ele.prepend(icon)
|
||||
@ -114,7 +114,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("shortcutRefId",obj.value)
|
||||
ele.setAttribute("shortcutName",obj.label)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-link"
|
||||
icon.className="svg svg-project-issue"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="green"
|
||||
ele.prepend(icon)
|
||||
@ -142,7 +142,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("shortcutRefId",obj.value)
|
||||
ele.setAttribute("shortcutName",obj.label)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-tasks"
|
||||
icon.className="svg svg-project-release"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="green"
|
||||
ele.prepend(icon)
|
||||
@ -169,7 +169,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("shortcutRefId",obj.value)
|
||||
ele.setAttribute("shortcutName",obj.label)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-files-o"
|
||||
icon.className="svg svg-wiki"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="rgb(0,120,212)"
|
||||
ele.prepend(icon)
|
||||
@ -197,7 +197,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("shortcutRefId",obj.value)
|
||||
ele.setAttribute("shortcutName",obj.label)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-file-text-o"
|
||||
icon.className="svg svg-wiki-item"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="rgb(0,120,212)"
|
||||
ele.prepend(icon)
|
||||
@ -224,7 +224,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("shortcutRefId",obj.value)
|
||||
ele.setAttribute("shortcutName",obj.label)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-calendar-o"
|
||||
icon.className="svg svg-calendar-event"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="orange"
|
||||
ele.prepend(icon)
|
||||
@ -252,7 +252,7 @@ export class RichEditorHandle {
|
||||
ele.setAttribute("shortcutRefId",obj.value)
|
||||
ele.setAttribute("shortcutName",obj.label)
|
||||
let icon=document.createElement("i")
|
||||
icon.className="fa fa-video-camera"
|
||||
icon.className="svg svg-meeting-room"
|
||||
icon.style.marginRight="5px"
|
||||
icon.style.color="red"
|
||||
ele.prepend(icon)
|
||||
|
@ -21,7 +21,6 @@ import {
|
||||
ICommon_Content_Line,
|
||||
ICommon_Content_Line_Config
|
||||
} from "../../../../../../common/model/content";
|
||||
import "./font/css/font-awesome.min.css"
|
||||
import UserShortView from "../userShortView.vue";
|
||||
import {dragElementList, EClient_Drag_Type} from "../../../../teamOS/common/directive/drag";
|
||||
import {ECommon_Model_Finder_Shortcut_Type} from "../../../../../../common/model/finder_item";
|
||||
@ -147,6 +146,9 @@ const onMouseMove=(event:MouseEvent)=>{
|
||||
|
||||
const calculateIndex=()=>{
|
||||
let selection=window.getSelection()
|
||||
if(selection.rangeCount==0) {
|
||||
return
|
||||
}
|
||||
let range=selection.getRangeAt(0)
|
||||
let startOffset=range.startOffset
|
||||
selectStartIndexPath=[]
|
||||
@ -343,4 +345,38 @@ div {
|
||||
:deep a[fileId]:hover {
|
||||
background-color: lightgray;
|
||||
}
|
||||
:deep .svg {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
vertical-align: middle;
|
||||
background-position: 50% 50%;
|
||||
background-repeat: repeat;
|
||||
background-size: contain;
|
||||
height: 1em;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
:deep .svg-file {
|
||||
background-image: url("@/assert/custom/file.svg");
|
||||
}
|
||||
:deep .svg-project {
|
||||
background-image: url("@/assert/custom/project_item.svg");
|
||||
}
|
||||
:deep .svg-project-issue {
|
||||
background-image: url("@/assert/custom/project_issue_item.svg");
|
||||
}
|
||||
:deep .svg-project-release {
|
||||
background-image: url("@/assert/custom/project_release_item.svg");
|
||||
}
|
||||
:deep .svg-wiki {
|
||||
background-image: url("@/assert/custom/wiki_space_item.svg");
|
||||
}
|
||||
:deep .svg-wiki-item {
|
||||
background-image: url("@/assert/custom/wiki_item.svg");
|
||||
}
|
||||
:deep .svg-calendar-event {
|
||||
background-image: url("@/assert/custom/calendar_event_item.svg");
|
||||
}
|
||||
:deep .svg-meeting-room {
|
||||
background-image: url("@/assert/custom/meeting_room_item.svg");
|
||||
}
|
||||
</style>
|
@ -62,7 +62,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from "vue";
|
||||
import {computed, onBeforeMount, ref, watch} from "vue";
|
||||
import {ICommon_Model_Organization_User} from "../../../../../common/model/organization_user";
|
||||
import {apiOrganization, DCSType} from "../request/request";
|
||||
import {EClient_EVENTBUS_TYPE, eventBus} from "../event/event";
|
||||
@ -73,7 +73,7 @@ import {Message} from "@arco-design/web-vue";
|
||||
|
||||
const loading=ref(true)
|
||||
const props=defineProps<{
|
||||
name:string,
|
||||
name?:string,
|
||||
photo?:string,
|
||||
organizationUserId:string,
|
||||
closeable?:boolean,
|
||||
@ -87,13 +87,21 @@ const emit=defineEmits<{
|
||||
const myOrganizationUserId=SessionStorage.get("organizationUserId")
|
||||
const showCloseable=ref(false)
|
||||
const root=ref(null);
|
||||
const name=ref("")
|
||||
const photo=ref("")
|
||||
const status=ref(ECommon_User_Online_Status.OFFLINE)
|
||||
watch(()=>[props.name,props.photo],()=>{
|
||||
name.value=props.name
|
||||
photo.value=props.photo
|
||||
},{
|
||||
immediate:true
|
||||
})
|
||||
const imgName=computed(()=>{
|
||||
if(props.name.includes(" ")) {
|
||||
let arr=props.name.split(" ")
|
||||
if(name.value?.includes(" ")) {
|
||||
let arr=name.value.split(" ")
|
||||
return arr[0][0].toUpperCase()+arr[1][0].toUpperCase()
|
||||
} else {
|
||||
return props.name[0].toUpperCase()
|
||||
return name.value?name.value[0].toUpperCase():""
|
||||
}
|
||||
})
|
||||
const info=ref<DCSType<ICommon_Model_Organization_User>>(null)
|
||||
@ -141,6 +149,20 @@ const onMessage=()=>{
|
||||
}
|
||||
eventBus.emit(EClient_EVENTBUS_TYPE.OPEN_IM_CHAT,props.organizationUserId,ECommon_IM_Message_EntityType.USER)
|
||||
}
|
||||
onBeforeMount(async ()=>{
|
||||
if(props.organizationUserId && !props.name) {
|
||||
let res=await apiOrganization.user({
|
||||
organizationUserId:props.organizationUserId,
|
||||
...(props.organizationId && {
|
||||
organizationId:props.organizationId
|
||||
})
|
||||
})
|
||||
if(res?.code==0) {
|
||||
name.value=res.data.nickname
|
||||
photo.value=res.data.user.photo
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -72,7 +72,7 @@
|
||||
<template v-if="calendarEventInfo?.meeting">
|
||||
<a-switch v-model="form.meeting"></a-switch>
|
||||
<a-popover position="right" v-if="form.meeting" trigger="click">
|
||||
<a-button type="primary" status="danger" style="margin-left: 20px">
|
||||
<a-button type="primary" status="success" style="margin-left: 20px">
|
||||
Start Meeting
|
||||
</a-button>
|
||||
<template #content>
|
||||
@ -91,7 +91,7 @@
|
||||
<a-switch v-model="form.meeting" v-else></a-switch>
|
||||
</template>
|
||||
</template>
|
||||
<a-button type="primary" status="danger" @click="onMeeting" v-else>
|
||||
<a-button type="primary" status="success" @click="onMeeting" v-else>
|
||||
Join Meeting
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
|
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
<div style="flex: 1 1 auto;display: flex;align-items: center">
|
||||
<a-popover position="right" trigger="click" v-if="isSelf">
|
||||
<a-button type="primary" status="danger" size="mini">
|
||||
<a-button type="primary" status="success" size="mini">
|
||||
Start Meeting
|
||||
</a-button>
|
||||
<template #content>
|
||||
@ -58,7 +58,7 @@
|
||||
</a-row>
|
||||
</template>
|
||||
</a-popover>
|
||||
<a-button type="primary" status="danger" size="mini" @click="onMeeting" v-else>
|
||||
<a-button type="primary" status="success" size="mini" @click="onMeeting" v-else>
|
||||
Join Meeting
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -106,7 +106,11 @@ const onPopMenuClick=(type:ECommon_Content_Line_Config_Type,handleFunc:(item:ICo
|
||||
|
||||
const onSend = () => {
|
||||
if(content.value.length>0) {
|
||||
props.meetingClient.sendMessage(JSON.stringify(content.value))
|
||||
props.meetingClient.sendMessage(JSON.stringify(content.value.map(item=>{
|
||||
return {
|
||||
arr:item.arr
|
||||
}
|
||||
})))
|
||||
}
|
||||
content.value = []
|
||||
}
|
||||
|
@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-comment :avatar="store.userInfo.photo" style="margin-right: 10px">
|
||||
<template #content>
|
||||
<a-spin :loading="loading" style="width: 100%;border: 1px solid lightgray" v-drop.file.shortcut.disk="onDropAdd">
|
||||
<RichEditor v-model="commentAdd" @upload-file="onUploadFile" :pop-menu-list="popMenuList" @pop-menu-click="onPopMenuClick" @custom-anchor-click="onCustomAnchorClick" @quote-list="onQuoteList" ref="objEditor"></RichEditor>
|
||||
</a-spin>
|
||||
</template>
|
||||
<template #actions>
|
||||
<a-button type="primary" size="small" @click="onCommentAdd" style="margin-top: 10px">Save</a-button>
|
||||
</template>
|
||||
</a-comment>
|
||||
<a-comment v-for="(item,index) in commentList" :author="item.value.created_by.nickname" :datetime="moment(item.value.created_time).format('YYYY-MM-DD HH:mm:ss')">
|
||||
<template #avatar>
|
||||
<UserAvatar :organization-user-id="item.value.created_by.organizationUserId" :name="item.value.created_by.nickname" :photo="item.value.created_by.photo" :only-photo="true"></UserAvatar>
|
||||
</template>
|
||||
<template #content>
|
||||
<template v-if="!item.isEdit">
|
||||
<RichEditor :model-value="JSON.parse(item.value.content)" :readonly="true" @custom-anchor-click="onCustomAnchorClick"></RichEditor>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-spin :loading="loading" style="width: 100%" v-drop.file.shortcut.disk="onDrop.bind(null,index)">
|
||||
<RichEditor v-model="item.editContent" @upload-file="onUploadFile" :pop-menu-list="popMenuList" @pop-menu-click="onPopMenuClick" @custom-anchor-click="onCustomAnchorClick" @quote-list="onQuoteList" ref="objEditor"></RichEditor>
|
||||
</a-spin>
|
||||
</template>
|
||||
</template>
|
||||
<template #actions v-if="checkPermission(permission,Permission_Types.Project.ADMIN) || item.value.created_by.id==store.userInfo.id">
|
||||
<template v-if="!item.isEdit">
|
||||
<a-link href="javascript:void(0)" style="color: gray;font-size: 13px;padding: 0px" @click="onEdit(item)">
|
||||
Edit
|
||||
</a-link>
|
||||
<a-link href="javascript:void(0)" style="color: gray;font-size: 13px;padding: 0px" @click="onRemoveComment(index)">
|
||||
Delete
|
||||
</a-link>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button type="text" @click="onSave(item)">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="item.editContent=[],item.isEdit=false">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-comment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import moment from "moment/moment";
|
||||
import {checkPermission, Permission_Types} from "../../../../../../../common/permission/permission";
|
||||
import {getCurrentInstance, inject, onBeforeMount, ref} from "vue";
|
||||
import {apiFile, apiIssue, DCSType} from "../../../../common/request/request";
|
||||
import {
|
||||
ECommon_Content_Line_Config_Type,
|
||||
ICommon_Content_Line,
|
||||
ICommon_Content_Line_Config,
|
||||
ICommon_Model_Content
|
||||
} from "../../../../../../../common/model/content";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
import {Dialog} from "../../../../common/component/dialog/dialog";
|
||||
import {getRootNavigatorRef} from "../../../../../teamOS/common/component/navigator/navigator";
|
||||
import {useDesktopStore} from "../../../desktop/store/desktop";
|
||||
import {injectProjectInfo} from "../../../../common/util/symbol";
|
||||
import RichEditor from "../../../../common/component/richEditor/richEditor.vue";
|
||||
import {RichEditorEventHandle} from "../../../../common/component/richEditorEventHandle";
|
||||
import {DropParam, vDrop} from "../../../../../teamOS/common/directive/drop";
|
||||
import UserAvatar from "../../../../common/component/userAvatar.vue";
|
||||
|
||||
const props=defineProps<{
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const commentList=ref<DCSType<{
|
||||
value:ICommon_Model_Content,
|
||||
isEdit:boolean,
|
||||
editContent:ICommon_Content_Line[]
|
||||
}>[]>([])
|
||||
const commentAdd=ref<ICommon_Content_Line[]>([])
|
||||
const permission=inject(injectProjectInfo).permission
|
||||
const root=getRootNavigatorRef()
|
||||
const appContext=getCurrentInstance().appContext
|
||||
const store=useDesktopStore()
|
||||
const objEditor=ref<InstanceType<typeof RichEditor>>()
|
||||
const objEditorAdd=ref<InstanceType<typeof RichEditor>>()
|
||||
const loading=ref(false)
|
||||
const popMenuList=ref(RichEditorEventHandle.popMenuList)
|
||||
const getCommentList=async ()=>{
|
||||
let res=await apiIssue.commentList({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
commentList.value=res.data.map(item=>{
|
||||
return {
|
||||
value:item,
|
||||
isEdit:false,
|
||||
editContent:[]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const onCommentAdd=async ()=>{
|
||||
let value=JSON.stringify(commentAdd.value.map(item=>{
|
||||
return {
|
||||
arr:item.arr
|
||||
}
|
||||
}))
|
||||
let res=await apiIssue.commentCreate({
|
||||
projectIssueId:props.projectIssueId,
|
||||
content:value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
commentList.value.unshift({
|
||||
value:res.data,
|
||||
isEdit:false,
|
||||
editContent:[]
|
||||
})
|
||||
commentAdd.value=[]
|
||||
}
|
||||
}
|
||||
const onSave=async (item:DCSType<{
|
||||
value:ICommon_Model_Content,
|
||||
isEdit:boolean,
|
||||
editContent:ICommon_Content_Line[]
|
||||
}>)=>{
|
||||
let value=JSON.stringify(item.editContent.map(item=>{
|
||||
return {
|
||||
arr:item.arr
|
||||
}
|
||||
}))
|
||||
if(value.length==0) {
|
||||
Message.error("content can not be empty")
|
||||
return
|
||||
}
|
||||
let res=await apiIssue.commentEdit({
|
||||
contentId:item.value.id,
|
||||
content:value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
item.value=res.data
|
||||
item.isEdit=false
|
||||
}
|
||||
}
|
||||
const onRemoveComment=async (index:number)=>{
|
||||
let item=commentList.value[index];
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to delete this comment?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.commentRemove({
|
||||
contentId:item.value.id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
commentList.value.splice(index,1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onEdit=async (item:DCSType<{
|
||||
value:ICommon_Model_Content,
|
||||
isEdit:boolean,
|
||||
editContent:ICommon_Content_Line[]
|
||||
}>)=>{
|
||||
item.isEdit=true
|
||||
item.editContent=JSON.parse(item.value.content)
|
||||
}
|
||||
|
||||
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=(type:ECommon_Content_Line_Config_Type,handleFunc:(item:ICommon_Content_Line_Config)=>void)=>{
|
||||
RichEditorEventHandle.onPopMenuClick(type,root,appContext,loading,handleFunc)
|
||||
}
|
||||
|
||||
const onCustomAnchorClick=(type:ECommon_Content_Line_Config_Type,value:string,link:string,label:string)=>{
|
||||
RichEditorEventHandle.onCustomAnchorClick(type,value,link,label)
|
||||
}
|
||||
|
||||
const onQuoteList=(keyword:string,handleFunc:(list:{
|
||||
value:string,
|
||||
label:string,
|
||||
photo:string
|
||||
}[])=>void)=>{
|
||||
RichEditorEventHandle.onQuoteList(keyword,handleFunc)
|
||||
}
|
||||
|
||||
const onDrop=(index:number,data?:DropParam)=>{
|
||||
RichEditorEventHandle.onDrop(objEditor.value[index],data,loading)
|
||||
}
|
||||
|
||||
const onDropAdd=(data?:DropParam)=>{
|
||||
RichEditorEventHandle.onDrop(objEditorAdd.value,data,loading)
|
||||
}
|
||||
|
||||
onBeforeMount(()=>{
|
||||
getCommentList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="issueProfileDetail">
|
||||
<a-collapse :default-active-key="['detail']">
|
||||
<a-collapse-item key="detail" header="Detail">
|
||||
<a-form layout="vertical" :model="{}">
|
||||
<a-form-item label="Issue Type">
|
||||
{{info.issueType.name}}
|
||||
</a-form-item>
|
||||
<a-form-item label="Assigner">
|
||||
<FieldEditBasic :project-issue-id="info.id" :type="EClient_Field_Basic_Type.ASSIGNER" :value="info.assigner_id"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Reporter">
|
||||
<FieldEditBasic :project-issue-id="info.id" :type="EClient_Field_Basic_Type.REPORTER" :value="info.reporter_id"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Priority">
|
||||
<FieldEditBasic :project-issue-id="info.id" :type="EClient_Field_Basic_Type.PRIORITY" :value="info.priority"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Module">
|
||||
<FieldEditBasic :project-issue-id="info.id" :type="EClient_Field_Basic_Type.MODULE" :value="moduleList"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Label">
|
||||
<FieldEditBasic :project-issue-id="info.id" :type="EClient_Field_Basic_Type.LABEL" :value="labelList"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Fix Versions">
|
||||
<FieldEditBasic :project-issue-id="info.id" :type="EClient_Field_Basic_Type.FIXVERSIONS" :value="releaseList"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-collapse-item>
|
||||
<a-collapse-item key="more" header="More">
|
||||
<a-form layout="vertical" :model="{}" v-if="fieldList.length>0">
|
||||
<a-form-item v-for="item in fieldList" :label="item.nodeField.field.name" :key="item.issueFieldValue.id">
|
||||
<FieldEdit :item="item"></FieldEdit>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-collapse-item>
|
||||
</a-collapse>
|
||||
<a-row style="margin-top: 10px;color: gray;font-size: 12px;line-height: 1.3;padding-left: 5px;margin-bottom: 10px">
|
||||
Created {{moment(info.created_time).format('YYYY-MM-DD HH:mm:ss')}}
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {EClient_Field_Basic_Type} from "../../../../common/component/field/fieldBasicType";
|
||||
import moment from "moment/moment";
|
||||
import FieldEdit from "../../../../common/component/field/fieldEdit.vue";
|
||||
import FieldEditBasic from "../../../../common/component/field/fieldEditBasic.vue";
|
||||
import {apiIssue, DCSType} from "../../../../common/request/request";
|
||||
import {
|
||||
ICommon_Route_Res_ProjectIssue_BasicInfo,
|
||||
ICommon_Route_Res_ProjectIssue_fieldsInfo
|
||||
} from "../../../../../../../common/routes/response";
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {ICommon_Model_Project_Label} from "../../../../../../../common/model/project_label";
|
||||
import {ICommon_Model_Project_Module} from "../../../../../../../common/model/project_module";
|
||||
import {ICommon_Model_Project_Release} from "../../../../../../../common/model/project_release";
|
||||
|
||||
const props=defineProps<{
|
||||
info:DCSType<ICommon_Route_Res_ProjectIssue_BasicInfo>,
|
||||
moduleList:DCSType<ICommon_Model_Project_Module>[],
|
||||
labelList:DCSType<ICommon_Model_Project_Label>[],
|
||||
fieldList:ICommon_Route_Res_ProjectIssue_fieldsInfo[]
|
||||
}>()
|
||||
const releaseList=ref<DCSType<ICommon_Model_Project_Release[]>>([])
|
||||
|
||||
const getReleaseList=async ()=>{
|
||||
let res=await apiIssue.releaseList({
|
||||
projectIssueId:props.info.id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
releaseList.value=res.data
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(()=>{
|
||||
getReleaseList()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.issueProfileDetail :deep(div[role="region"]) {
|
||||
background-color: white !important;
|
||||
padding-left: 5px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
.issueProfileDetail :deep .arco-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.issueProfileDetail :deep .arco-form-item-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,91 @@
|
||||
<template xmlns="http://www.w3.org/1999/html">
|
||||
<div>
|
||||
<a-list hoverable>
|
||||
<a-list-item v-for="item in historyList">
|
||||
<div style="width: 100%;">
|
||||
<template v-if="item.type===ECommon_Model_Project_Issue_History_Type.CREATE_ISSUE">
|
||||
<div style="width: 100%;align-items: center;display: flex">
|
||||
<UserAvatar :organization-user-id="item.organization_user_id"></UserAvatar>
|
||||
created the issue
|
||||
<span style="color: gray">{{moment(item.created_time).format("YYYY-MM-DD HH:mm:ss")}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="item.type===ECommon_Model_Project_Issue_History_Type.APPROVAL_RESOLVE">
|
||||
<div style="width: 100%;align-items: center;display: flex">
|
||||
<UserAvatar :organization-user-id="item.organization_user_id"></UserAvatar>
|
||||
resolved your approval
|
||||
<span style="color: gray">{{moment(item.created_time).format("YYYY-MM-DD HH:mm:ss")}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="item.type===ECommon_Model_Project_Issue_History_Type.APPROVAL_REJECT">
|
||||
<div style="width: 100%;align-items: center;display: flex">
|
||||
<UserAvatar :organization-user-id="item.organization_user_id"></UserAvatar>
|
||||
rejected your approval
|
||||
<span style="color: gray">{{moment(item.created_time).format("YYYY-MM-DD HH:mm:ss")}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="item.type===ECommon_Model_Project_Issue_History_Type.UPDATE_FIELD">
|
||||
<div style="width: 100%;align-items: center;display: flex">
|
||||
<UserAvatar :organization-user-id="item.organization_user_id"></UserAvatar>
|
||||
updated the field
|
||||
<span style="color: blue">
|
||||
{{item.name}}
|
||||
</span>
|
||||
<span style="color: gray">{{moment(item.created_time).format("YYYY-MM-DD HH:mm:ss")}}</span>
|
||||
</div>
|
||||
<div v-if="item.value" style="display: flex;align-items: center">
|
||||
new value :
|
||||
<UserAvatar v-if="/\d{19,}/.test(item.value)" :organization-user-id="item.value"></UserAvatar>
|
||||
<span v-else style="color: gray">{{item.value}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="item.type===ECommon_Model_Project_Issue_History_Type.UPDATE_NODE">
|
||||
<div style="width: 100%;align-items: center;display: flex">
|
||||
<UserAvatar :organization-user-id="item.organization_user_id"></UserAvatar>
|
||||
updated the workflow
|
||||
<span style="color: #544646">
|
||||
{{item.name}}
|
||||
</span>
|
||||
<span style="color: gray">{{moment(item.created_time).format("YYYY-MM-DD HH:mm:ss")}}</span>
|
||||
</div>
|
||||
<div>
|
||||
new workflow : <span style="color: gray">{{item.value}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {apiIssue, DCSType} from "../../../../common/request/request";
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {
|
||||
ECommon_Model_Project_Issue_History_Type,
|
||||
ICommon_Model_Project_Issue_History
|
||||
} from "../../../../../../../common/model/project_issue_history";
|
||||
import UserAvatar from "../../../../common/component/userAvatar.vue";
|
||||
import moment from "moment";
|
||||
|
||||
const props=defineProps<{
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const historyList=ref<DCSType<ICommon_Model_Project_Issue_History>[]>([])
|
||||
const listHistory=async ()=>{
|
||||
let res=await apiIssue.listHistory({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
historyList.value=res.data
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(()=>{
|
||||
listHistory()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -50,6 +50,7 @@
|
||||
</a-space>
|
||||
</a-row>
|
||||
<a-divider></a-divider>
|
||||
<span style="font-size: small;color: gray">If you wanna open in a new tab,hold command(Mac) or control(Win) Key,click the link below</span>
|
||||
<a-table style="margin-top: 10px" :columns="columns" :data="issueList" :pagination="pagination" @pageChange="onPageChange">
|
||||
<template #type="{record}">
|
||||
{{record.issueType.name}}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a-layout style="height: 100%">
|
||||
<a-layout-content id="issueProfileContent">
|
||||
<a-layout-content class="issueProfileContent">
|
||||
<a-row style="color: gray" v-drag.shortcut="()=>({
|
||||
shortcutType:ECommon_Model_Finder_Shortcut_Type.PROJECT_ISSUE,
|
||||
shortcutRefId:projectIssueId,
|
||||
@ -11,15 +11,28 @@
|
||||
<a-row style="margin-top: 10px">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.NAME" :value="info.name" v-if="info"></FieldEditBasic>
|
||||
</a-row>
|
||||
<a-row style="margin-top: 10px">
|
||||
<a-space>
|
||||
<a-row v-if="info?.approval?.type===ECommon_Model_Project_Issue_Approval_Type.REJECTED" style="align-items: center;margin-top: 10px;">
|
||||
<UserAvatar :organization-user-id="info.approval.approval_organization_user_id"></UserAvatar>
|
||||
<span style="color: orange;font-weight: bold"> rejected your approval</span>
|
||||
<a-popover v-if="info.approval.reason" position="right">
|
||||
<icon-info-circle-fill style="color: orange;margin-left: 10px"></icon-info-circle-fill>
|
||||
<template #content>
|
||||
<RichEditor style="color: black" :model-value="JSON.parse(info.approval.reason)" :readonly="true" @custom-anchor-click="onCustomAnchorClick"></RichEditor>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-row>
|
||||
<a-row style="margin-top: 20px">
|
||||
<a-space wrap>
|
||||
<a-dropdown>
|
||||
<a-button :disabled="actionList.length==0" :type="info?.workflowNode.status===ECommon_Model_Workflow_Node_Status.NOTSTART?'secondary':'primary'" :status="info?.workflowNode.status===ECommon_Model_Workflow_Node_Status.DONE?'success':'normal'">{{info?.workflowNode.name}} <icon-down></icon-down>
|
||||
<a-button :disabled="actionList.length==0" :type="info?.workflowNode.status===ECommon_Model_Workflow_Node_Status.NOTSTART?'secondary':'primary'" :status="info?.approval?.type===ECommon_Model_Project_Issue_Approval_Type.REJECTED?'danger':info?.workflowNode.status===ECommon_Model_Workflow_Node_Status.DONE?'success':'normal'">{{calculateApprovalName}} <icon-down></icon-down>
|
||||
</a-button>
|
||||
<template #content>
|
||||
<a-doption v-for="item in actionList" :key="item.id" @click="onAction(item)">{{item.name}}</a-doption>
|
||||
<a-doption v-for="item in actionList as any[]" :key="item.isApproval?item.name:item.id" @click="onAction(item)">{{item.name}}</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-button v-if="!parentIssue" @click="onCreateSubIssue">
|
||||
Create Child Issue
|
||||
</a-button>
|
||||
<a-dropdown-button>
|
||||
Link Issue
|
||||
<template #icon>
|
||||
@ -52,6 +65,7 @@
|
||||
</a-row>
|
||||
</template>
|
||||
</a-popover>
|
||||
<a-doption v-if="checkPermission(permission,Permission_Types.Project.EDIT)" @click="onCopy">Copy</a-doption>
|
||||
<a-doption v-if="checkPermission(permission,Permission_Types.Project.ADMIN) || info?.created_by.id===store.userInfo.id" @click="onDelete" style="color: red">Delete</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
@ -61,151 +75,26 @@
|
||||
Description
|
||||
</a-row>
|
||||
<a-row style="margin-top: 10px">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.DESCRIPTION" :value="description"></FieldEditBasic>
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.DESCRIPTION" :value="description" style="margin-right: 10px;box-sizing: border-box"></FieldEditBasic>
|
||||
</a-row>
|
||||
<template v-if="parentIssue">
|
||||
<a-row style="margin-top: 20px;font-weight: bold">Parent Issue</a-row>
|
||||
<a-list style="margin-top: 10px;margin-right: 10px" size="small" hoverable>
|
||||
<a-list-item>
|
||||
<a-space size="large">
|
||||
{{key+"-"+parentIssue.unique_id}}
|
||||
<a-link href="javascript:void(0)">{{parentIssue.name}}</a-link>
|
||||
<FieldPriority :priority="parentIssue.priority"></FieldPriority>
|
||||
</a-space>
|
||||
<template #actions>
|
||||
<a-button type="text" size="mini" @click="onRemoveItem('parent')">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
<template v-if="childIssueList.length>0">
|
||||
<a-row style="margin-top: 20px;font-weight: bold">Child Issues</a-row>
|
||||
<a-list style="margin-top: 10px;margin-right: 10px" size="small" hoverable>
|
||||
<a-list-item v-for="(item,index) in childIssueList" :key="item.id">
|
||||
<a-space size="large">
|
||||
{{key+"-"+item.unique_id}}
|
||||
<a-link href="javascript:void(0)">{{item.name}}</a-link>
|
||||
<FieldPriority :priority="item.priority"></FieldPriority>
|
||||
</a-space>
|
||||
<template #actions>
|
||||
<a-button type="text" size="mini" @click="onRemoveItem('child',index)">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
<template v-if="relatedIssueList.length>0">
|
||||
<a-row style="margin-top: 20px;font-weight: bold">Related Issues</a-row>
|
||||
<a-list style="margin-top: 10px;margin-right: 10px" size="small" hoverable>
|
||||
<a-list-item v-for="(item,index) in relatedIssueList" :key="item.id">
|
||||
<a-space size="large">
|
||||
{{key+"-"+item.unique_id}}
|
||||
<a-link href="javascript:void(0)">{{item.name}}</a-link>
|
||||
<FieldPriority :priority="item.priority"></FieldPriority>
|
||||
</a-space>
|
||||
<template #actions>
|
||||
<a-button type="text" size="mini" @click="onRemoveItem('related',index)">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
<a-row style="margin-top: 50px;font-weight: bold">Comments</a-row>
|
||||
<a-comment v-for="(item,index) in commentList" :author="item.value.created_by.nickname" :datetime="moment(item.value.created_time).format('YYYY-MM-DD HH:mm:ss')" :avatar="item.value.created_by.photo" style="margin-right: 10px">
|
||||
<template #content>
|
||||
<template v-if="!item.isEdit">
|
||||
{{item.value.content}}
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-textarea allow-clear v-model="item.editContent"></a-textarea>
|
||||
</template>
|
||||
</template>
|
||||
<template #actions v-if="checkPermission(permission,Permission_Types.Project.ADMIN) || item.value.created_by.id==store.userInfo.id">
|
||||
<template v-if="!item.isEdit">
|
||||
<a-link href="javascript:void(0)" style="color: gray;font-size: 13px;padding: 0px" @click="item.isEdit=true">
|
||||
Edit
|
||||
</a-link>
|
||||
<a-link href="javascript:void(0)" style="color: gray;font-size: 13px;padding: 0px" @click="onRemoveComment(index)">
|
||||
Delete
|
||||
</a-link>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button type="text" @click="onEditComment(item)">
|
||||
<template #icon>
|
||||
<icon-check></icon-check>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button type="text" @click="item.editContent='',item.isEdit=false">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-comment>
|
||||
<a-comment :avatar="store.userInfo.photo" style="margin-right: 10px">
|
||||
<template #content>
|
||||
<a-textarea placeholder="please type your comment" v-model="commentAdd"></a-textarea>
|
||||
</template>
|
||||
<template #actions>
|
||||
<a-button type="primary" size="small" @click="onCommentAdd">Save</a-button>
|
||||
</template>
|
||||
</a-comment>
|
||||
<ProjectIssueRelated :project-issue-id="projectIssueId" :child-issue-list="childIssueList" :parent-issue="parentIssue" :related-issue-list="relatedIssueList" @remove-parent="parentIssue=null"></ProjectIssueRelated>
|
||||
<a-tabs type="rounded" style="margin-top: 50px;" lazy-load size="small">
|
||||
<a-tab-pane key="comment" title="Comments">
|
||||
<ProjectIssueComment style="margin-top: 10px" :project-issue-id="projectIssueId"></ProjectIssueComment>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="history" title="History">
|
||||
<ProjectIssueHistory :project-issue-id="projectIssueId"></ProjectIssueHistory>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-layout-content>
|
||||
<a-layout-sider :resize-directions="['left']" :width="200" style="overflow-y: auto">
|
||||
<a-collapse :default-active-key="['detail']" style="margin: 5px 5px 0px 5px" id="issueProfileDetail">
|
||||
<a-collapse-item key="detail" header="Detail">
|
||||
<a-form layout="vertical" :model="{}">
|
||||
<a-form-item label="Issue Type">
|
||||
{{info?.issueType.name}}
|
||||
</a-form-item>
|
||||
<a-form-item label="Assigner">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.ASSIGNER" :value="info.assigner_id" v-if="info"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Reporter">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.REPORTER" :value="info.reporter_id" v-if="info"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Priority">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.PRIORITY" :value="info.priority" v-if="info"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Module">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.MODULE" :value="moduleList"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Label">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.LABEL" :value="labelList"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
<a-form-item label="Fix Versions">
|
||||
<FieldEditBasic :project-issue-id="projectIssueId" :type="EClient_Field_Basic_Type.FIXVERSIONS" :value="releaseList"></FieldEditBasic>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-collapse-item>
|
||||
<a-collapse-item key="more" header="More">
|
||||
<a-form layout="vertical" :model="{}" v-if="fieldList.length>0">
|
||||
<a-form-item v-for="item in fieldList" :label="item.nodeField.field.name" :key="item.issueFieldValue.id">
|
||||
<FieldEdit :item="item"></FieldEdit>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-collapse-item>
|
||||
</a-collapse>
|
||||
<a-row style="margin-top: 10px;color: gray;font-size: 12px;line-height: 1.3;padding-left: 5px;margin-bottom: 10px">
|
||||
Created {{moment(info?.created_time).format('YYYY-MM-DD HH:mm:ss')}}
|
||||
</a-row>
|
||||
<ProjectIssueField :label-list="labelList" :module-list="moduleList" :field-list="fieldList" :info="info" v-if="info" style="margin: 5px 5px 0px 5px"></ProjectIssueField>
|
||||
</a-layout-sider>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {getCurrentInstance, inject, markRaw, onBeforeMount, ref, watch} from "vue";
|
||||
import {computed, getCurrentInstance, inject, markRaw, onBeforeMount, ref, watch} from "vue";
|
||||
import {injectProjectInfo} from "../../../../common/util/symbol";
|
||||
import {apiIssue, apiMeeting, DCSType} from "../../../../common/request/request";
|
||||
import {
|
||||
@ -216,28 +105,30 @@ import FieldEditBasic from "../../../../common/component/field/fieldEditBasic.vu
|
||||
import {EClient_Field_Basic_Type} from "../../../../common/component/field/fieldBasicType";
|
||||
import {ICommon_Model_Project_Label} from "../../../../../../../common/model/project_label";
|
||||
import {ICommon_Model_Project_Module} from "../../../../../../../common/model/project_module";
|
||||
import FieldEdit from "../../../../common/component/field/fieldEdit.vue";
|
||||
import moment from "moment";
|
||||
import {ICommon_Model_Project_Issue} from "../../../../../../../common/model/project_issue";
|
||||
import FieldPriority from "../../../../common/component/field/fieldPriority.vue";
|
||||
import {Dialog} from "../../../../common/component/dialog/dialog";
|
||||
import {getCurrentNavigator, getRootNavigatorRef} from "../../../../../teamOS/common/component/navigator/navigator";
|
||||
import ProjectIssueBind from "./projectIssueBind.vue";
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
import {ICommon_Model_Content} from "../../../../../../../common/model/content";
|
||||
import {useDesktopStore} from "../../../desktop/store/desktop";
|
||||
import {checkPermission, Permission_Types} from "../../../../../../../common/permission/permission";
|
||||
import {ICommon_Model_Workflow_Action} from "../../../../../../../common/model/workflow_action";
|
||||
import {ECommon_Model_Workflow_Node_Status} from "../../../../../../../common/model/workflow_node";
|
||||
import ProjectIssueNext from "./projectIssueNext.vue";
|
||||
import {ICommon_Model_Project_Release} from "../../../../../../../common/model/project_release";
|
||||
import {vDrag} from "../../../../../teamOS/common/directive/drag";
|
||||
import {ECommon_Model_Finder_Shortcut_Type} from "../../../../../../../common/model/finder_item";
|
||||
import UserAvatar from "../../../../common/component/userAvatar.vue";
|
||||
import {SessionStorage} from "../../../../common/storage/session";
|
||||
import {TableRowSelection} from "@arco-design/web-vue/es/table/interface";
|
||||
import {EClient_EVENTBUS_TYPE, eventBus} from "../../../../common/event/event";
|
||||
import {ECommon_Model_Organization_Member_Type} from "../../../../../../../common/model/organization";
|
||||
import ProjectIssueComment from "./projectIssueComment.vue";
|
||||
import ProjectIssueField from "./projectIssueField.vue";
|
||||
import ProjectIssueRelated from "./projectIssueRelated.vue";
|
||||
import ProjectIssueHistory from "./projectIssueHistory.vue";
|
||||
import {ECommon_Model_Project_Issue_Approval_Type} from "../../../../../../../common/model/project_issue_approval";
|
||||
import RichEditor from "@/business/common/component/richEditor/richEditor.vue";
|
||||
import {ECommon_Content_Line_Config_Type} from "../../../../../../../common/model/content";
|
||||
import {RichEditorEventHandle} from "@/business/common/component/richEditorEventHandle";
|
||||
|
||||
const props=defineProps<{
|
||||
projectIssueId:string
|
||||
@ -250,20 +141,16 @@ const key=inject(injectProjectInfo).key
|
||||
const permission=inject(injectProjectInfo).permission
|
||||
const info=ref<DCSType<ICommon_Route_Res_ProjectIssue_BasicInfo>>()
|
||||
const description=ref("")
|
||||
const labelList=ref<DCSType<ICommon_Model_Project_Label[]>>([])
|
||||
const moduleList=ref<DCSType<ICommon_Model_Project_Module[]>>([])
|
||||
const fieldList=ref<ICommon_Route_Res_ProjectIssue_fieldsInfo[]>([])
|
||||
const parentIssue=ref<DCSType<ICommon_Model_Project_Issue>>();
|
||||
const relatedIssueList=ref<DCSType<ICommon_Model_Project_Issue>[]>([])
|
||||
const childIssueList=ref<DCSType<ICommon_Model_Project_Issue>[]>([])
|
||||
const commentList=ref<DCSType<{
|
||||
value:ICommon_Model_Content,
|
||||
isEdit:boolean,
|
||||
editContent:string
|
||||
}[]>>([])
|
||||
const actionList=ref<DCSType<ICommon_Model_Workflow_Action>[]>([])
|
||||
const releaseList=ref<DCSType<ICommon_Model_Project_Release[]>>([])
|
||||
const commentAdd=ref("")
|
||||
const moduleList=ref<DCSType<ICommon_Model_Project_Module>[]>([])
|
||||
const labelList=ref<DCSType<ICommon_Model_Project_Label>[]>([])
|
||||
const actionList=ref<DCSType<ICommon_Model_Workflow_Action>[] | {
|
||||
isApproval:true,
|
||||
name:"Resolve"|"Reject"|"Revoke"|"Commit"
|
||||
}[]>([])
|
||||
const fieldList=ref<ICommon_Route_Res_ProjectIssue_fieldsInfo[]>([])
|
||||
const store=useDesktopStore()
|
||||
const issueRelatedUserList=ref<{
|
||||
id:string,
|
||||
@ -302,7 +189,7 @@ const getDescription=async ()=>{
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
description.value=res.data
|
||||
description.value=res.data?res.data:"[]"
|
||||
}
|
||||
}
|
||||
const getOtherInfo=async ()=>{
|
||||
@ -317,50 +204,7 @@ const getOtherInfo=async ()=>{
|
||||
relatedIssueList.value=res.data.relateds
|
||||
}
|
||||
}
|
||||
const getFieldList=async ()=>{
|
||||
let res=await apiIssue.fieldsInfo({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
fieldList.value=res.data
|
||||
}
|
||||
}
|
||||
const onRemoveItem=async (type:"parent"|"child"|"related",index:number)=>{
|
||||
if(type==="parent") {
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to remove parent issue?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.removeParentIssue({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectIssueParentId:parentIssue.value.id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
parentIssue.value=null;
|
||||
}
|
||||
}
|
||||
} else if(type=="child") {
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to remove child issue?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.removeChildIssue({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectIssueChildId:childIssueList.value[index].id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
childIssueList.value.splice(index,1)
|
||||
}
|
||||
}
|
||||
} else if(type=="related") {
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to remove related issue?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.removeRelatedIssue({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectIssueRelatedId:relatedIssueList.value[index].id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
relatedIssueList.value.splice(index,1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onAddItem=async (type:"parent"|"child"|"related")=>{
|
||||
if(type=="parent") {
|
||||
let ret=await Dialog.open(root.value,appContext,"Parent",markRaw(ProjectIssueBind),{
|
||||
@ -412,64 +256,7 @@ const onAddItem=async (type:"parent"|"child"|"related")=>{
|
||||
}
|
||||
}
|
||||
}
|
||||
const getCommentList=async ()=>{
|
||||
let res=await apiIssue.commentList({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
commentList.value=res.data.map(item=>{
|
||||
return {
|
||||
value:item,
|
||||
isEdit:false,
|
||||
editContent:item.content
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const onCommentAdd=async ()=>{
|
||||
let res=await apiIssue.commentCreate({
|
||||
projectIssueId:props.projectIssueId,
|
||||
content:commentAdd.value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
commentList.value.unshift({
|
||||
value:res.data,
|
||||
isEdit:false,
|
||||
editContent:res.data.content
|
||||
})
|
||||
commentAdd.value=""
|
||||
}
|
||||
}
|
||||
const onEditComment=async (item:DCSType<{
|
||||
value:ICommon_Model_Content,
|
||||
isEdit:boolean,
|
||||
editContent:string
|
||||
}>)=>{
|
||||
if(!item.editContent) {
|
||||
Message.error("content can not be empty")
|
||||
return
|
||||
}
|
||||
let res=await apiIssue.commentEdit({
|
||||
contentId:item.value.id,
|
||||
content:item.editContent
|
||||
})
|
||||
if(res?.code==0) {
|
||||
item.value.content=item.editContent
|
||||
item.isEdit=false
|
||||
}
|
||||
}
|
||||
const onRemoveComment=async (index:number)=>{
|
||||
let item=commentList.value[index];
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to delete this comment?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.commentRemove({
|
||||
contentId:item.value.id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
commentList.value.splice(index,1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onDelete=async ()=>{
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to delete this issue?")
|
||||
if(ret) {
|
||||
@ -490,34 +277,80 @@ const getActionList=async ()=>{
|
||||
actionList.value=res.data
|
||||
}
|
||||
}
|
||||
const onAction=async (item:ICommon_Model_Workflow_Action)=>{
|
||||
let res=await apiIssue.getNextNodeFields({
|
||||
projectIssueId:props.projectIssueId,
|
||||
workflowActionId:item.id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
if(res.data.length>0) {
|
||||
let ret=await Dialog.open(root.value,appContext,item.name,markRaw(ProjectIssueNext),{
|
||||
projectId:projectId,
|
||||
projectIssueId:props.projectIssueId,
|
||||
workflowActionId:item.id,
|
||||
items:res.data
|
||||
})
|
||||
if(!ret) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
await apiIssue.confirmNextNode({
|
||||
projectIssueId:props.projectIssueId,
|
||||
workflowActionId:item.id
|
||||
})
|
||||
}
|
||||
getInfo()
|
||||
getFieldList()
|
||||
getActionList()
|
||||
} else {
|
||||
Message.error(res.msg)
|
||||
}
|
||||
const onAction=async (item:ICommon_Model_Workflow_Action | {
|
||||
isApproval:true,
|
||||
name:"Resolve"|"Reject"|"Revoke"|"Commit"
|
||||
})=>{
|
||||
if("isApproval" in item) {
|
||||
if(item.name==="Revoke") {
|
||||
let res=await apiIssue.revokeApproval({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code!=0) {
|
||||
Message.error(res.msg)
|
||||
return
|
||||
}
|
||||
} else if(item.name==="Resolve") {
|
||||
let res=await apiIssue.resolveApproval({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code!=0) {
|
||||
Message.error(res.msg)
|
||||
return
|
||||
}
|
||||
} else if(item.name==="Reject") {
|
||||
let ret=await Dialog.inputRich(root.value,appContext,"Reject Reason")
|
||||
if(ret) {
|
||||
let res=await apiIssue.rejectApproval({
|
||||
projectIssueId:props.projectIssueId,
|
||||
reason:JSON.stringify(ret)
|
||||
})
|
||||
if(res?.code!=0) {
|
||||
Message.error(res.msg)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else if(item.name==="Commit") {
|
||||
let res=await apiIssue.commitApproval({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code!=0) {
|
||||
Message.error(res.msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let res=await apiIssue.getNextNodeFields({
|
||||
projectIssueId:props.projectIssueId,
|
||||
workflowActionId:item.id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
if(res.data.length>0) {
|
||||
let ret=await Dialog.open(root.value,appContext,item.name,markRaw(ProjectIssueNext),{
|
||||
projectId:projectId,
|
||||
projectIssueId:props.projectIssueId,
|
||||
workflowActionId:item.id,
|
||||
items:res.data
|
||||
})
|
||||
if(!ret) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
await apiIssue.confirmNextNode({
|
||||
projectIssueId:props.projectIssueId,
|
||||
workflowActionId:item.id
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Message.error(res.msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
getInfo()
|
||||
getActionList()
|
||||
getFieldList()
|
||||
}
|
||||
|
||||
const onMeeting=async ()=>{
|
||||
@ -530,13 +363,30 @@ const onMeeting=async ()=>{
|
||||
}
|
||||
}
|
||||
|
||||
const getReleaseList=async ()=>{
|
||||
let res=await apiIssue.releaseList({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
releaseList.value=res.data
|
||||
}
|
||||
const onCopy=async ()=>{
|
||||
let ret=await Dialog.input(root.value,appContext,"type the issue's name")
|
||||
if(ret) {
|
||||
let res=await apiIssue.copy({
|
||||
projectIssueId:props.projectIssueId,
|
||||
name:ret
|
||||
})
|
||||
if(res?.code==0) {
|
||||
eventBus.emit(EClient_EVENTBUS_TYPE.OPEN_PROJECT_ISSUE_PROFILE,projectId,res.data.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onCreateSubIssue=async ()=>{
|
||||
let ret=await Dialog.input(root.value,appContext,"type the issue's name")
|
||||
if(ret) {
|
||||
let res=await apiIssue.createChildIssue({
|
||||
projectIssueId:props.projectIssueId,
|
||||
name:ret
|
||||
})
|
||||
if(res?.code==0) {
|
||||
getOtherInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const showQuickMeeting=async ()=>{
|
||||
@ -548,30 +398,48 @@ const showQuickMeeting=async ()=>{
|
||||
}
|
||||
}
|
||||
|
||||
const getFieldList=async ()=>{
|
||||
let res=await apiIssue.fieldsInfo({
|
||||
projectIssueId:props.projectIssueId
|
||||
})
|
||||
if(res?.code==0) {
|
||||
fieldList.value=res.data
|
||||
}
|
||||
}
|
||||
|
||||
const calculateApprovalName=computed(()=>{
|
||||
if(info.value) {
|
||||
if(info.value.approval) {
|
||||
if(info.value.approval.type===ECommon_Model_Project_Issue_Approval_Type.PENDING) {
|
||||
return `${info.value.approval.workflowNode.name}(Wait For Approval)`
|
||||
} else if(info.value.approval.type===ECommon_Model_Project_Issue_Approval_Type.RESOLVED) {
|
||||
return info.value.workflowNode.name
|
||||
} else if(info.value.approval.type===ECommon_Model_Project_Issue_Approval_Type.REJECTED) {
|
||||
return `${info.value.approval.workflowNode.name}(Rejected)`
|
||||
}
|
||||
} else {
|
||||
return info.value.workflowNode.name
|
||||
}
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
})
|
||||
|
||||
const onCustomAnchorClick=(type:ECommon_Content_Line_Config_Type,value:string,link:string,label:string)=>{
|
||||
RichEditorEventHandle.onCustomAnchorClick(type,value,link,label)
|
||||
}
|
||||
|
||||
onBeforeMount(()=>{
|
||||
getInfo()
|
||||
getDescription()
|
||||
getOtherInfo()
|
||||
getFieldList()
|
||||
getCommentList()
|
||||
getActionList()
|
||||
getReleaseList()
|
||||
getFieldList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#issueProfileDetail :deep(div[role="region"]) {
|
||||
background-color: white !important;
|
||||
padding-left: 5px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
#issueProfileDetail :deep .arco-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#issueProfileDetail :deep .arco-form-item-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
#issueProfileContent :deep .arco-comment-actions {
|
||||
.issueProfileContent :deep .arco-comment-actions {
|
||||
margin-top: 0px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="parentIssue">
|
||||
<a-row style="margin-top: 20px;font-weight: bold">Parent Issue</a-row>
|
||||
<a-list style="margin-top: 10px;margin-right: 10px" size="small" hoverable>
|
||||
<a-list-item>
|
||||
<a-space size="large">
|
||||
{{key+"-"+parentIssue.unique_id}}
|
||||
<a-link href="javascript:void(0)" @click="onOpenIssue(parentIssue.id)">{{parentIssue.name}}</a-link>
|
||||
<FieldPriority :priority="parentIssue.priority"></FieldPriority>
|
||||
</a-space>
|
||||
<template #actions>
|
||||
<a-button type="text" size="mini" @click="onRemoveItem('parent',null)">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
<template v-if="childIssueList.length>0">
|
||||
<a-row style="margin-top: 20px;font-weight: bold">Child Issues</a-row>
|
||||
<a-list style="margin-top: 10px;margin-right: 10px" size="small" hoverable>
|
||||
<a-list-item v-for="(item,index) in childIssueList" :key="item.id">
|
||||
<a-space size="large">
|
||||
{{key+"-"+item.unique_id}}
|
||||
<a-link href="javascript:void(0)" @click="onOpenIssue(item.id)">{{item.name}}</a-link>
|
||||
<FieldPriority :priority="item.priority"></FieldPriority>
|
||||
</a-space>
|
||||
<template #actions>
|
||||
<a-button type="text" size="mini" @click="onRemoveItem('child',index)">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
<template v-if="relatedIssueList.length>0">
|
||||
<a-row style="margin-top: 20px;font-weight: bold">Related Issues</a-row>
|
||||
<a-list style="margin-top: 10px;margin-right: 10px" size="small" hoverable>
|
||||
<a-list-item v-for="(item,index) in relatedIssueList" :key="item.id">
|
||||
<a-space size="large">
|
||||
{{key+"-"+item.unique_id}}
|
||||
<a-link href="javascript:void(0)" @click="onOpenIssue(item.id)">{{item.name}}</a-link>
|
||||
<FieldPriority :priority="item.priority"></FieldPriority>
|
||||
</a-space>
|
||||
<template #actions>
|
||||
<a-button type="text" size="mini" @click="onRemoveItem('related',index)">
|
||||
<template #icon>
|
||||
<icon-close style="color: red"></icon-close>
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import FieldPriority from "../../../../common/component/field/fieldPriority.vue";
|
||||
import {apiIssue, DCSType} from "../../../../common/request/request";
|
||||
import {ICommon_Model_Project_Issue} from "../../../../../../../common/model/project_issue";
|
||||
import {getCurrentInstance, inject} from "vue";
|
||||
import {injectProjectInfo} from "../../../../common/util/symbol";
|
||||
import {Dialog} from "../../../../common/component/dialog/dialog";
|
||||
import {getRootNavigatorRef} from "../../../../../teamOS/common/component/navigator/navigator";
|
||||
import {EClient_EVENTBUS_TYPE, eventBus} from "../../../../common/event/event";
|
||||
|
||||
const props=defineProps<{
|
||||
parentIssue?:DCSType<ICommon_Model_Project_Issue>,
|
||||
childIssueList?:DCSType<ICommon_Model_Project_Issue>[],
|
||||
relatedIssueList?:DCSType<ICommon_Model_Project_Issue>[]
|
||||
projectIssueId:string
|
||||
}>()
|
||||
const emit=defineEmits<{
|
||||
removeParent:[]
|
||||
}>()
|
||||
const key=inject(injectProjectInfo).key
|
||||
const projectId=inject(injectProjectInfo).id
|
||||
const root=getRootNavigatorRef()
|
||||
const appContext=getCurrentInstance().appContext
|
||||
const onRemoveItem=async (type:"parent"|"child"|"related",index:number)=>{
|
||||
if(type==="parent") {
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to remove parent issue?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.removeParentIssue({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectIssueParentId:props.parentIssue.id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
emit("removeParent")
|
||||
}
|
||||
}
|
||||
} else if(type=="child") {
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to remove child issue?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.removeChildIssue({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectIssueChildId:props.childIssueList[index].id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
props.childIssueList.splice(index,1)
|
||||
}
|
||||
}
|
||||
} else if(type=="related") {
|
||||
let ret=await Dialog.confirm(root.value,appContext,"Do you want to remove related issue?")
|
||||
if(ret) {
|
||||
let res=await apiIssue.removeRelatedIssue({
|
||||
projectIssueId:props.projectIssueId,
|
||||
projectIssueRelatedId:props.relatedIssueList[index].id
|
||||
})
|
||||
if(res?.code==0) {
|
||||
props.relatedIssueList.splice(index,1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onOpenIssue=(projectIssueId:string)=>{
|
||||
eventBus.emit(EClient_EVENTBUS_TYPE.OPEN_PROJECT_ISSUE_PROFILE,projectId,projectIssueId)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -2,7 +2,6 @@
|
||||
<a-form style="width: 80%" :model="form" ref="eleForm">
|
||||
<a-form-item field="status" label="type" required>
|
||||
<a-select v-model="form.status">
|
||||
<a-option :value="ECommon_Model_Workflow_Node_Status.NOTSTART">NOTSTART</a-option>
|
||||
<a-option :value="ECommon_Model_Workflow_Node_Status.INPROGRESS">INPROGRESS</a-option>
|
||||
<a-option :value="ECommon_Model_Workflow_Node_Status.DONE">DONE</a-option>
|
||||
</a-select>
|
||||
|
@ -107,7 +107,7 @@ import {ICommon_Route_Res_Workflow_Node_Field} from "../../../../../../../common
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
import {ECommon_Field_Type, Field_Types} from "../../../../../../../common/field/type";
|
||||
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 AddField from "./addField.vue";
|
||||
import {Dialog} from "../../../../common/component/dialog/dialog";
|
||||
@ -146,6 +146,7 @@ const form=reactive({
|
||||
default_number_value:0,
|
||||
label_type:0,
|
||||
values:[] as {
|
||||
id?:string
|
||||
value:string,
|
||||
selected:number
|
||||
}[]
|
||||
@ -244,6 +245,7 @@ const onClickRow=async (item:ICommon_Route_Res_Workflow_Node_Field)=>{
|
||||
form.values=[]
|
||||
for(let obj of item.values) {
|
||||
form.values.push({
|
||||
id:obj.id,
|
||||
value:obj.value,
|
||||
selected:obj.selected
|
||||
})
|
||||
|
@ -5,12 +5,12 @@
|
||||
</a-space>
|
||||
<a-layout style="flex:1 1 auto;border: 1px solid gainsboro">
|
||||
<a-layout-content style="overflow:hidden;">
|
||||
<div ref="eleWorkflow" style="height: 100%;width: 100%" tabindex="1" @keydown.delete="onKeyDelete"></div>
|
||||
<div ref="eleWorkflow" style="height: 100%;width: 100%" tabindex="-1" @keydown.delete="onKeyDelete"></div>
|
||||
</a-layout-content>
|
||||
<a-layout-sider :resize-directions="['left']">
|
||||
<a-form :model="form" layout="vertical" style="margin:10px 5px 0px 5px;width: 90%" v-if="form.type" @submitSuccess="onSubmit">
|
||||
<h3>{{form.type}}</h3>
|
||||
<a-form-item field="status" label="status" v-if="form.type=='node'" required>
|
||||
<a-form-item field="status" label="status" v-if="form.type=='node' || form.type=='approval'" required>
|
||||
<a-select v-model="form.status">
|
||||
<a-option :value="ECommon_Model_Workflow_Node_Status.NOTSTART">NOTSTART</a-option>
|
||||
<a-option :value="ECommon_Model_Workflow_Node_Status.INPROGRESS">INPROGRESS</a-option>
|
||||
@ -23,8 +23,27 @@
|
||||
<a-form-item field="description" label="description">
|
||||
<a-textarea v-model="form.description"></a-textarea>
|
||||
</a-form-item>
|
||||
<template v-if="form.status===ECommon_Model_Workflow_Node_Status.INPROGRESS || form.status===ECommon_Model_Workflow_Node_Status.DONE">
|
||||
<a-form-item field="approval" label="approval">
|
||||
<a-switch :checked-value="1" :unchecked-value="0" v-model="form.approval"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item field="approvalType" label="approval type" v-if="form.approval">
|
||||
<a-select v-model="form.approvalType" @change="onApprovalTypeChange">
|
||||
<a-option :value="ECommon_Model_Workflow_Approval_Type.PERSON">Person</a-option>
|
||||
<a-option :value="ECommon_Model_Workflow_Approval_Type.TEAM">Team</a-option>
|
||||
<a-option :value="ECommon_Model_Workflow_Approval_Type.FIELD">Field</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="approvalValue" :label="form.approvalType===ECommon_Model_Workflow_Approval_Type.PERSON?'choose person':form.approvalType===ECommon_Model_Workflow_Approval_Type.TEAM?'choose team':'choose workflow field'" v-if="form.approval">
|
||||
<FieldCommonMultiLabel style="width: 100%" v-model="form.approvalValue" :type="form.approvalType===ECommon_Model_Workflow_Approval_Type.PERSON?'user':'team'" v-if="form.approvalType===ECommon_Model_Workflow_Approval_Type.PERSON || form.approvalType===ECommon_Model_Workflow_Approval_Type.TEAM"></FieldCommonMultiLabel>
|
||||
<FieldCommonLabel style="width: 100%" v-model="form.approvalValue" type="field" :workflow-node-id="form.id" v-else-if="form.approvalType===ECommon_Model_Workflow_Approval_Type.FIELD"></FieldCommonLabel>
|
||||
</a-form-item>
|
||||
<a-form-item field="approvalExtra" label="choose member tag" v-if="form.approval && form.approvalType===ECommon_Model_Workflow_Approval_Type.TEAM">
|
||||
<FieldCommonLabel style="width: 100%" v-model="form.approvalExtra" type="tag"></FieldCommonLabel>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item>
|
||||
<a-space v-if="form.type=='node'" size="medium">
|
||||
<a-space v-if="form.type=='node' || form.type=='approval'" size="medium">
|
||||
<a-button html-type="submit" type="primary">Save</a-button>
|
||||
<a-button html-type="button" type="primary" @click="onEditFields">Edit Fields</a-button>
|
||||
</a-space>
|
||||
@ -37,7 +56,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {getCurrentInstance, markRaw, onBeforeMount, onMounted, reactive, ref} from "vue";
|
||||
import {getCurrentInstance, markRaw, nextTick, onBeforeMount, onMounted, reactive, ref} from "vue";
|
||||
import LogicFlow from "@logicflow/core";
|
||||
import {apiWorkflow} from "../../../../common/request/request";
|
||||
import {Dialog} from "../../../../common/component/dialog/dialog";
|
||||
@ -47,11 +66,15 @@ import {ECommon_Model_Workflow_Node_Status} from "../../../../../../../common/mo
|
||||
import {Message} from "@arco-design/web-vue";
|
||||
import {getCurrentNavigator} from "../../../../../teamOS/common/component/navigator/navigator";
|
||||
import FieldList from "./fieldList.vue";
|
||||
import {flowApproval} from "@/business/common/component/flow/approval";
|
||||
import {ECommon_Model_Workflow_Approval_Type} from "../../../../../../../common/model/workflow_approval";
|
||||
import FieldCommonMultiLabel from "@/business/common/component/field/common/fieldCommonMultiLabel.vue";
|
||||
import FieldCommonLabel from "@/business/common/component/field/common/fieldCommonLabel.vue";
|
||||
|
||||
const props=defineProps<{
|
||||
issueTypeId:string
|
||||
}>()
|
||||
const eleWorkflow=ref(null);
|
||||
const eleWorkflow=ref<HTMLElement>(null);
|
||||
const root=ref(null)
|
||||
const appContext=getCurrentInstance().appContext
|
||||
const navigator=getCurrentNavigator();
|
||||
@ -60,8 +83,12 @@ const form=reactive({
|
||||
id:"",
|
||||
name:"",
|
||||
description:"",
|
||||
type:"",
|
||||
status:ECommon_Model_Workflow_Node_Status.INPROGRESS
|
||||
type:"" ,
|
||||
status:ECommon_Model_Workflow_Node_Status.INPROGRESS,
|
||||
approval:0,
|
||||
approvalType:ECommon_Model_Workflow_Approval_Type.PERSON,
|
||||
approvalValue:[] as string[]|string,
|
||||
approvalExtra:""
|
||||
})
|
||||
let lf:LogicFlow
|
||||
const onKeyDelete=async (event:KeyboardEvent)=>{
|
||||
@ -75,6 +102,10 @@ const onKeyDelete=async (event:KeyboardEvent)=>{
|
||||
type="node"
|
||||
name=(<any>elements.nodes[0].text).value;
|
||||
id=elements.nodes[0].id
|
||||
if(elements.nodes[0].properties.status===ECommon_Model_Workflow_Node_Status.NOTSTART) {
|
||||
Message.error("not start node can't be removed !!!")
|
||||
return
|
||||
}
|
||||
}
|
||||
if(type) {
|
||||
let ret=await Dialog.confirm(root.value,appContext,`Do you want to delete ${type} ${name}`)
|
||||
@ -89,11 +120,17 @@ const onKeyDelete=async (event:KeyboardEvent)=>{
|
||||
|
||||
}
|
||||
const onSubmit=async ()=>{
|
||||
let res=await (form.type=="node"?apiWorkflow.editNode({
|
||||
let res=await ((form.type=="node" || form.type=="approval")?apiWorkflow.editNode({
|
||||
workflowNodeId:form.id,
|
||||
name:form.name,
|
||||
description:form.description,
|
||||
status:form.status
|
||||
status:form.status,
|
||||
approval:form.approval,
|
||||
...(form.approval && {
|
||||
approvalType:form.approvalType,
|
||||
approvalValue:Array.isArray(form.approvalValue)?form.approvalValue:form.approvalValue?[form.approvalValue]:[],
|
||||
approvalExtra:form.approvalExtra
|
||||
})
|
||||
}):apiWorkflow.editAction({
|
||||
workflowActionId:form.id,
|
||||
name:form.name,
|
||||
@ -101,12 +138,10 @@ const onSubmit=async ()=>{
|
||||
}))
|
||||
if(res?.code==0) {
|
||||
Message.success("update success")
|
||||
if(form.type=="node") {
|
||||
if(form.type=="node" || form.type=="approval") {
|
||||
let obj=lf.getNodeModelById(form.id)
|
||||
obj.updateText(form.name)
|
||||
obj.setProperty("name",form.name)
|
||||
obj.setProperty("description",form.description)
|
||||
obj.setProperty("status",form.status)
|
||||
obj.setProperties(res.data)
|
||||
} else {
|
||||
let obj=lf.getEdgeModelById(form.id)
|
||||
obj.updateText(form.name)
|
||||
@ -122,6 +157,17 @@ const onEditFields=()=>{
|
||||
workflowNodeId:form.id
|
||||
},`${form.name} -> Fields`)
|
||||
}
|
||||
|
||||
const onApprovalTypeChange=()=> {
|
||||
if(form.approvalType===ECommon_Model_Workflow_Approval_Type.PERSON || form.approvalType===ECommon_Model_Workflow_Approval_Type.TEAM) {
|
||||
form.approvalValue=[];
|
||||
form.approvalExtra=''
|
||||
} else if(form.approvalType===ECommon_Model_Workflow_Approval_Type.FIELD) {
|
||||
form.approvalValue="";
|
||||
form.approvalExtra=''
|
||||
}
|
||||
}
|
||||
|
||||
const requestNodes=async ()=>{
|
||||
let res=await apiWorkflow.info({
|
||||
issueTypeId:props.issueTypeId
|
||||
@ -131,7 +177,7 @@ const requestNodes=async ()=>{
|
||||
nodes:res.data.nodes.map(item=>{
|
||||
return {
|
||||
id:item.id,
|
||||
type:"node",
|
||||
type:item.is_approval?"approval":"node",
|
||||
x:item.x,
|
||||
y:item.y,
|
||||
text:item.name,
|
||||
@ -145,7 +191,13 @@ const requestNodes=async ()=>{
|
||||
sourceNodeId: item.source_node_id,
|
||||
targetNodeId: item.dest_node_id,
|
||||
text: item.name,
|
||||
properties: item
|
||||
properties: item,
|
||||
...(item.source_anchor_point && {
|
||||
startPoint:JSON.parse(item.source_anchor_point)
|
||||
}),
|
||||
...(item.end_anchor_point && {
|
||||
endPoint:JSON.parse(item.end_anchor_point)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -156,14 +208,17 @@ const addNode=async ()=>{
|
||||
issueTypeId:props.issueTypeId
|
||||
})
|
||||
if(ret) {
|
||||
lf.dnd.startDrag({
|
||||
id:ret.id,
|
||||
type:"node",
|
||||
text:ret.name,
|
||||
properties:ret
|
||||
})
|
||||
lf.addNode({
|
||||
id:ret.data.id,
|
||||
type:"node",
|
||||
text:ret.data.name,
|
||||
x:(-lf.graphModel.transformModel.TRANSLATE_X+lf.graphModel.width/2-100),
|
||||
y:(-lf.graphModel.transformModel.TRANSLATE_Y+20)*lf.graphModel.transformModel.SCALE_Y,
|
||||
properties:ret.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let requestNodesPromise;
|
||||
onBeforeMount(()=>{
|
||||
requestNodesPromise=requestNodes();
|
||||
@ -178,6 +233,7 @@ onMounted(async ()=>{
|
||||
adjustEdgeStartAndEnd:false,
|
||||
})
|
||||
lf.register(flowNode)
|
||||
lf.register(flowApproval)
|
||||
lf.setTheme({
|
||||
baseEdge:{
|
||||
strokeWidth:1.2,
|
||||
@ -192,24 +248,46 @@ onMounted(async ()=>{
|
||||
x:data.x,
|
||||
y:data.y
|
||||
})
|
||||
let outgoingEdges=lf.getNodeOutgoingEdge(data.properties.id)
|
||||
outgoingEdges.forEach(item=>{
|
||||
apiWorkflow.editAction({
|
||||
workflowActionId:item.id,
|
||||
sourceAnchorPoint:JSON.stringify(item.startPoint)
|
||||
})
|
||||
})
|
||||
let incomingEdges=lf.getNodeIncomingEdge(data.properties.id)
|
||||
incomingEdges.forEach(item=>{
|
||||
apiWorkflow.editAction({
|
||||
workflowActionId:item.id,
|
||||
endAnchorPoint:JSON.stringify(item.endPoint)
|
||||
})
|
||||
})
|
||||
})
|
||||
lf.on("edge:add",async ({data})=>{
|
||||
let res=await apiWorkflow.addAction({
|
||||
issueTypeId:props.issueTypeId,
|
||||
name:"empty",
|
||||
sourceNodeId:data.sourceNodeId,
|
||||
destNodeId:data.targetNodeId
|
||||
destNodeId:data.targetNodeId,
|
||||
sourceAnchorPoint:JSON.stringify(data.startPoint),
|
||||
endAnchorPoint:JSON.stringify(data.endPoint)
|
||||
})
|
||||
if(res?.code==0) {
|
||||
let obj=lf.getEdgeModelById(data.id)
|
||||
obj.id=res.data.id
|
||||
obj.updateText("empty");
|
||||
obj.setProperties(res.data)
|
||||
lf.openEdgeAnimation(obj.id)
|
||||
} else {
|
||||
Message.error(res.msg)
|
||||
lf.deleteEdge(data.id)
|
||||
}
|
||||
})
|
||||
lf.on("edge:delete",async ({data})=>{
|
||||
if(data.properties.id==form.id) {
|
||||
form.type=""
|
||||
} else if(!data.properties.id) {
|
||||
return
|
||||
}
|
||||
apiWorkflow.deleteAction({
|
||||
workflowActionId:data.properties.id
|
||||
@ -219,23 +297,74 @@ onMounted(async ()=>{
|
||||
if(data.properties.id==form.id) {
|
||||
form.type=""
|
||||
}
|
||||
apiWorkflow.deleteNode({
|
||||
workflowNodeId:data.properties.id
|
||||
})
|
||||
apiWorkflow.deleteNode({
|
||||
workflowNodeId:data.properties.id
|
||||
})
|
||||
})
|
||||
lf.on("node:click,edge:click",({data})=>{
|
||||
form.id=data.properties.id
|
||||
form.type=data.type=="node"?"node":"transition"
|
||||
form.type=data.type=="node"?"node":data.type==='approval'?"approval":"transition"
|
||||
form.name=data.properties.name
|
||||
form.description=data.properties.description
|
||||
if(data.type=="node") {
|
||||
if(data.type=="node" || data.type=="approval") {
|
||||
form.status=data.properties.status
|
||||
form.approval=data.properties.is_approval
|
||||
if(form.approval) {
|
||||
form.approvalType=data.properties.approval.type
|
||||
if(form.approvalType===ECommon_Model_Workflow_Approval_Type.TEAM || form.approvalType===ECommon_Model_Workflow_Approval_Type.PERSON) {
|
||||
form.approvalValue=data.properties.approval.value
|
||||
if(form.approvalType===ECommon_Model_Workflow_Approval_Type.TEAM) {
|
||||
form.approvalExtra=data.properties.approval.extra
|
||||
}
|
||||
} else if(form.approvalType===ECommon_Model_Workflow_Approval_Type.FIELD) {
|
||||
form.approvalValue=data.properties.approval.value.length>0?data.properties.approval.value[0]:""
|
||||
}
|
||||
} else {
|
||||
form.approvalType=ECommon_Model_Workflow_Approval_Type.PERSON
|
||||
form.approvalValue=[]
|
||||
form.approvalExtra=""
|
||||
}
|
||||
}
|
||||
})
|
||||
lf.on("blank:click",({date})=>{
|
||||
form.type=""
|
||||
nextTick(()=>{
|
||||
form.type=""
|
||||
})
|
||||
})
|
||||
lf.on("text:update",async (param)=>{
|
||||
if(form.type=="node" || form.type=="approval") {
|
||||
let obj=lf.getNodeModelById(form.id)
|
||||
let res=await apiWorkflow.editNode({
|
||||
workflowNodeId:form.id,
|
||||
name:obj.text.value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
obj.setProperty("name",obj.text.value)
|
||||
form.name=obj.text.value
|
||||
}
|
||||
} else {
|
||||
let obj=lf.getEdgeModelById(form.id)
|
||||
let res=await apiWorkflow.editAction({
|
||||
workflowActionId:form.id,
|
||||
name:obj.text.value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
obj.setProperty("name",obj.text.value)
|
||||
form.name=obj.text.value
|
||||
}
|
||||
}
|
||||
})
|
||||
lf.on("edge:adjust",({data})=>{
|
||||
apiWorkflow.editAction({
|
||||
workflowActionId:data.properties.id,
|
||||
sourceAnchorPoint:JSON.stringify(data.startPoint),
|
||||
endAnchorPoint:JSON.stringify(data.endPoint)
|
||||
})
|
||||
})
|
||||
lf.render(ret)
|
||||
for(let edge of ret.edges) {
|
||||
lf.openEdgeAnimation(edge.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -80,7 +80,7 @@ const onSearch=async (value:string)=>{
|
||||
})
|
||||
}
|
||||
} else if(form.type==ECommon_Model_Organization_Member_Type.MEMBERTAG) {
|
||||
let res=await apiOrganization.listTag()
|
||||
let res=await apiOrganization.listTag({})
|
||||
if(res?.code==0) {
|
||||
userList.value=res.data.map(item=>{
|
||||
return {
|
||||
|
@ -43,7 +43,6 @@ import EditProjectProfile from "./editProjectProfile.vue";
|
||||
import EditProjectAccess from "./editProjectAccess.vue";
|
||||
import LabelList from "./labelList.vue";
|
||||
import ModuleList from "./moduleList.vue";
|
||||
import {SessionStorage} from "../../../../common/storage/session";
|
||||
|
||||
const columns=[
|
||||
{
|
||||
@ -78,7 +77,6 @@ navigator.register("labelList",markRaw(LabelList));
|
||||
navigator.register("moduleList",markRaw(ModuleList));
|
||||
const search=async (page:number)=>{
|
||||
let res=await apiProject.list({
|
||||
organizationUserId:SessionStorage.get("organizationId"),
|
||||
size:pagination.pageSize,
|
||||
page:page-1,
|
||||
keyword:keyword.value
|
||||
|
@ -25,7 +25,7 @@ const tagList=ref<{
|
||||
checked:boolean
|
||||
}[]>([])
|
||||
onBeforeMount(async ()=>{
|
||||
let res=await apiOrganization.listTag();
|
||||
let res=await apiOrganization.listTag({});
|
||||
if(res?.code==0) {
|
||||
let userTagIds=props.tags.map(item=>item.id);
|
||||
tagList.value=res.data.map(item=>{
|
||||
|
@ -40,7 +40,7 @@ const data=ref<ICommon_Model_Member_Tag[]>([])
|
||||
const root=ref(null);
|
||||
const appContext=getCurrentInstance().appContext
|
||||
const search=async ()=>{
|
||||
let ret=await apiOrganization.listTag()
|
||||
let ret=await apiOrganization.listTag({})
|
||||
if(ret?.code==0) {
|
||||
data.value=ret.data
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ const onSearch=async (value:string)=>{
|
||||
})
|
||||
}
|
||||
} else if(form.type==ECommon_Model_Organization_Member_Type.MEMBERTAG) {
|
||||
let res=await apiOrganization.listTag()
|
||||
let res=await apiOrganization.listTag({})
|
||||
if(res?.code==0) {
|
||||
userList.value=res.data.map(item=>{
|
||||
return {
|
||||
|
@ -39,7 +39,6 @@ import {getCurrentNavigator} from "../../../../../teamOS/common/component/naviga
|
||||
import EditWikiProfile from "./editWikiProfile.vue";
|
||||
import EditWikiAccess from "./editWikiAccess.vue";
|
||||
import {ICommon_Model_Wiki} from "../../../../../../../common/model/wiki";
|
||||
import {SessionStorage} from "../../../../common/storage/session";
|
||||
|
||||
const columns=[
|
||||
{
|
||||
@ -72,7 +71,6 @@ const navigator=getCurrentNavigator();
|
||||
navigator.register("editWikiAccess",markRaw(EditWikiAccess));
|
||||
const search=async (page:number)=>{
|
||||
let res=await apiWiki.list({
|
||||
organizationUserId:SessionStorage.get("organizationId"),
|
||||
size:pagination.pageSize,
|
||||
page:page-1,
|
||||
keyword:keyword.value
|
||||
|
@ -32,11 +32,11 @@
|
||||
import {onBeforeMount, onBeforeUnmount, ref} from "vue";
|
||||
import {apiNotification, DCSType} from "../../common/request/request";
|
||||
import {ICommon_Route_Res_Notification_Item} from "../../../../../common/routes/response";
|
||||
import moment from "moment";
|
||||
import NotificationItem from "../../common/component/notificationItem.vue";
|
||||
import {ECommon_Model_Notification_Type} from "../../../../../common/model/notification";
|
||||
import {EClient_EVENTBUS_TYPE, eventBus} from "../../common/event/event";
|
||||
import {type} from "os";
|
||||
import moment from "moment";
|
||||
|
||||
let page=0
|
||||
const bottom=ref(false)
|
||||
@ -51,6 +51,13 @@ const getList=async ()=>{
|
||||
types:types.value
|
||||
})
|
||||
if(res?.code==0) {
|
||||
for(let i=0;i<res.data.length;i++) {
|
||||
let obj=res.data[i]
|
||||
if(!obj.data) {
|
||||
res.data.splice(i,1)
|
||||
i--
|
||||
}
|
||||
}
|
||||
list.value.push(...res.data)
|
||||
if(res.data.length==0) {
|
||||
bottom.value=true
|
||||
@ -101,7 +108,7 @@ const onChange=(value:string)=>{
|
||||
} else if(value==="organization") {
|
||||
types.value=[ECommon_Model_Notification_Type.ORGANIZATION_USER_QUIT,ECommon_Model_Notification_Type.ORGANIZATION_INVITATION,ECommon_Model_Notification_Type.ORGANIZATION_USER_REMOVE,ECommon_Model_Notification_Type.ORGANIZATION_USER_ROLE_CHANGE]
|
||||
} else if(value==="issue") {
|
||||
types.value=[ECommon_Model_Notification_Type.ISSUE_FIELD_CHANGE,ECommon_Model_Notification_Type.ISSUE_REMOVE,ECommon_Model_Notification_Type.ISSUE_ASSIGNER_ASSIGN,ECommon_Model_Notification_Type.ISSUE_WORKFLOW_CHANGE,ECommon_Model_Notification_Type.ISSUE_REPORTER_ASSIGN,ECommon_Model_Notification_Type.ISSUE_COMMENT_ADD,ECommon_Model_Notification_Type.ISSUE_COMMENT_AT]
|
||||
types.value=[ECommon_Model_Notification_Type.ISSUE_FIELD_CHANGE,ECommon_Model_Notification_Type.ISSUE_REMOVE,ECommon_Model_Notification_Type.ISSUE_ASSIGNER_ASSIGN,ECommon_Model_Notification_Type.ISSUE_WORKFLOW_CHANGE,ECommon_Model_Notification_Type.ISSUE_REPORTER_ASSIGN,ECommon_Model_Notification_Type.ISSUE_COMMENT_ADD,ECommon_Model_Notification_Type.ISSUE_COMMENT_AT,ECommon_Model_Notification_Type.ISSUE_APPROVAL_REJECT,ECommon_Model_Notification_Type.ISSUE_APPROVAL_RESOLVE]
|
||||
} else if(value==="team") {
|
||||
types.value=[ECommon_Model_Notification_Type.TEAM_USER_QUIT,ECommon_Model_Notification_Type.TEAM_USER_ROLE_CHANGE,ECommon_Model_Notification_Type.TEAM_USER_REMOVE,ECommon_Model_Notification_Type.TEAM_DISMISS,ECommon_Model_Notification_Type.TEAM_USER_ADD]
|
||||
} else if(value==="calendar") {
|
||||
|
@ -95,6 +95,11 @@ export const useDesktopStore=defineStore("desktop",{
|
||||
if(!this.appList.includes(iconSetting)) {
|
||||
this.appList.push(iconSetting)
|
||||
}
|
||||
} else {
|
||||
let index=this.appList.indexOf(iconSetting)
|
||||
if(index>-1) {
|
||||
this.appList.splice(index,1)
|
||||
}
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
@ -24,7 +24,7 @@ import {desktop} from "./desktop";
|
||||
import {windowManager} from "../window/windowManager";
|
||||
import {ETeamOS_Window_Status} from "../window/window.js";
|
||||
import {iconGroupMap} from "../icon/icon";
|
||||
import SvgIcon from "../../icon/custom/svgIcon.vue";
|
||||
import SvgIcon from "../../icon/svgIcon.vue";
|
||||
|
||||
const list=windowManager.getList();
|
||||
const color=desktop.getBaseColor();
|
||||
|
@ -4,7 +4,7 @@
|
||||
<template v-for="(_, slot) of $slots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>
|
||||
<WindowContainer></WindowContainer>
|
||||
</IconContainer>
|
||||
<DesktopBar style="height: 40px;background-color: rgb(93,92,140)">
|
||||
<DesktopBar style="height: 40px;background-color: rgb(93,92,140);">
|
||||
<template v-for="(_, slot) of $slots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>
|
||||
</DesktopBar>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
import {Icon} from "./icon"
|
||||
import {vMenu} from "../common/directive/menu";
|
||||
import {computed, nextTick, ref, watch} from "vue";
|
||||
import SvgIcon from "../../icon/custom/svgIcon.vue";
|
||||
import SvgIcon from "../../icon/svgIcon.vue";
|
||||
|
||||
const props=defineProps<{
|
||||
item:Icon,
|
||||
|
@ -72,7 +72,7 @@ import {ETeamOS_Window_Status, ETeamOS_Window_Type, Window} from "./window";
|
||||
import {vDrag} from "../common/directive/drag";
|
||||
import {vResize} from "../common/directive/resize";
|
||||
import {v4} from "uuid"
|
||||
import SvgIcon from "../../icon/custom/svgIcon.vue";
|
||||
import SvgIcon from "../../icon/svgIcon.vue";
|
||||
import {iconGroupMap} from "../icon/icon";
|
||||
|
||||
vResize;
|
||||
|
@ -18,7 +18,7 @@ export class WindowManager extends Base{
|
||||
this.show(arr[0].id)
|
||||
} else {
|
||||
let focused=this.getFocused()
|
||||
if(focused.group===window.group) {
|
||||
if(focused?.group===window.group) {
|
||||
focused.nodes.push(...window.nodes)
|
||||
focused.activeKey=window.nodes[0].id
|
||||
this.show(focused.id)
|
||||
|
@ -20,7 +20,10 @@
|
||||
"noImplicitThis": true,
|
||||
"plugins": [
|
||||
{ "transform": "../common/transform/transformer.js" }
|
||||
]
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
@ -6,13 +6,18 @@ import viteCompression from 'vite-plugin-compression'
|
||||
import {createSvgIconsPlugin} from "vite-plugin-svg-icons";
|
||||
|
||||
export default defineConfig({
|
||||
resolve:{
|
||||
alias:{
|
||||
"@":path.join(__dirname,"src")
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
viteCompression({
|
||||
threshold:102400
|
||||
}),
|
||||
createSvgIconsPlugin({
|
||||
iconDirs:[path.join(__dirname,"./src/icon/custom")],
|
||||
iconDirs:[path.join(__dirname,"./src/assert/custom")],
|
||||
symbolId:'[name]'
|
||||
})
|
||||
],
|
||||
|
@ -43,7 +43,8 @@ export enum ECommon_Model_Content_Type {
|
||||
PROJECT_ISSUE_DESCRIPTION,
|
||||
WIKI_ITEM,
|
||||
MEETING_CHAT,
|
||||
CALENDAR_EVENT_AGENDA
|
||||
CALENDAR_EVENT_AGENDA,
|
||||
PROJECT_ISSUE_REJECT
|
||||
}
|
||||
|
||||
export interface ICommon_Model_Content {
|
||||
|
@ -18,7 +18,9 @@ export enum ECommon_Model_Notification_Type {
|
||||
ISSUE_COMMENT_AT,
|
||||
ISSUE_REMOVE,
|
||||
WIKI_ITEM_AT,
|
||||
CALENDAR_EVENT_INVITATION
|
||||
CALENDAR_EVENT_INVITATION,
|
||||
ISSUE_APPROVAL_RESOLVE,
|
||||
ISSUE_APPROVAL_REJECT
|
||||
}
|
||||
|
||||
export enum ECommon_Model_Notification_Status {
|
||||
|
24
code/common/model/project_issue_approval.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {BaseModel} from "./base"
|
||||
|
||||
export enum ECommon_Model_Project_Issue_Approval_Type {
|
||||
PENDING,
|
||||
RESOLVED,
|
||||
REJECTED
|
||||
}
|
||||
|
||||
export interface ICommon_Model_Project_Issue_Approval {
|
||||
id: string,
|
||||
project_issue_id:string
|
||||
workflow_node_id: string,
|
||||
type:ECommon_Model_Project_Issue_Approval_Type,
|
||||
approval_organization_user_id:string
|
||||
}
|
||||
|
||||
export const Table_Project_Issue_Approval = "project_issue_approval"
|
||||
|
||||
class ProjectIssueApprovalModel extends BaseModel {
|
||||
table = Table_Project_Issue_Approval
|
||||
model = <ICommon_Model_Project_Issue_Approval>{}
|
||||
}
|
||||
|
||||
export let projectIssueApprovalModel = new ProjectIssueApprovalModel
|
30
code/common/model/project_issue_history.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import {BaseModel} from "./base";
|
||||
|
||||
export enum ECommon_Model_Project_Issue_History_Type {
|
||||
CREATE_ISSUE,
|
||||
UPDATE_FIELD,
|
||||
UPDATE_NODE,
|
||||
APPROVAL_RESOLVE,
|
||||
APPROVAL_REJECT,
|
||||
ISSUE_TYPE_CONVERT
|
||||
}
|
||||
|
||||
export interface ICommon_Model_Project_Issue_History {
|
||||
id: string,
|
||||
created_time: Date,
|
||||
type:ECommon_Model_Project_Issue_History_Type,
|
||||
project_issue_id:string,
|
||||
organization_user_id:string,
|
||||
project_id:string,
|
||||
name:string,
|
||||
value:string
|
||||
}
|
||||
|
||||
export const Table_Project_Issue_History = "project_issue_history"
|
||||
|
||||
class ProjectIssueHistoryModel extends BaseModel {
|
||||
table = Table_Project_Issue_History
|
||||
model = <ICommon_Model_Project_Issue_History>{}
|
||||
}
|
||||
|
||||
export let projectIssueHistoryModel = new ProjectIssueHistoryModel
|
@ -7,6 +7,8 @@ export interface ICommon_Model_Workflow_Action {
|
||||
source_node_id :string ,
|
||||
dest_node_id :string ,
|
||||
issue_type_id :string,
|
||||
source_anchor_point:string,
|
||||
end_anchor_point:string
|
||||
}
|
||||
export const Table_Workflow_Action="workflow_action"
|
||||
|
||||
|
23
code/common/model/workflow_approval.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {BaseModel} from "./base"
|
||||
|
||||
export enum ECommon_Model_Workflow_Approval_Type {
|
||||
PERSON,
|
||||
TEAM,
|
||||
FIELD
|
||||
}
|
||||
|
||||
export interface ICommon_Model_Workflow_Approval {
|
||||
id :string ,
|
||||
workflow_node_id:string,
|
||||
type:ECommon_Model_Workflow_Approval_Type,
|
||||
value:string[],
|
||||
extra:string
|
||||
}
|
||||
export const Table_Workflow_Approval="workflow_approval"
|
||||
|
||||
class WorkflowApprovalModel extends BaseModel {
|
||||
table=Table_Workflow_Approval
|
||||
model=<ICommon_Model_Workflow_Approval>{}
|
||||
}
|
||||
|
||||
export let workflowApprovalModel=new WorkflowApprovalModel
|
@ -13,7 +13,8 @@ export interface ICommon_Model_Workflow_Node {
|
||||
status :ECommon_Model_Workflow_Node_Status,
|
||||
issue_type_id :string ,
|
||||
x:number,
|
||||
y:number
|
||||
y:number,
|
||||
is_approval:number
|
||||
}
|
||||
export const Table_Workflow_Node="workflow_node"
|
||||
|
||||
|
@ -123,6 +123,7 @@ const api={
|
||||
req: <{
|
||||
workflowNodeFieldTypeId :string,
|
||||
data:{
|
||||
id?:string
|
||||
value :string,
|
||||
selected :number
|
||||
}[]
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
import {ECommon_HttpApi_Method} from "./types";
|
||||
import {ICommon_Model_Project_Issue_Field_Value} from "../model/project_issue_field_value";
|
||||
import {ICommon_Model_Project_Release} from "../model/project_release";
|
||||
import {ICommon_Model_Project_Issue_History} from "../model/project_issue_history";
|
||||
|
||||
const api={
|
||||
baseUrl:"/issue",
|
||||
@ -143,7 +144,10 @@ const api={
|
||||
req:<{
|
||||
projectIssueId :string
|
||||
}>{},
|
||||
res:<ICommon_Model_Workflow_Action[]>{},
|
||||
res:<ICommon_Model_Workflow_Action[] | {
|
||||
isApproval:true,
|
||||
name:"Resolve"|"Reject"|"Revoke"|"Commit"
|
||||
}[]>{},
|
||||
permission:[Permission_Types.Project.READ]
|
||||
},
|
||||
commentList:{
|
||||
@ -188,7 +192,8 @@ const api={
|
||||
method:ECommon_HttpApi_Method.POST,
|
||||
path:"/item/copy",
|
||||
req:<{
|
||||
projectIssueId :string
|
||||
projectIssueId :string,
|
||||
name:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Project_Issue>{},
|
||||
permission:[Permission_Types.Project.EDIT]
|
||||
@ -339,6 +344,73 @@ const api={
|
||||
photo:string
|
||||
}[]>{},
|
||||
permission:[Permission_Types.Project.READ]
|
||||
},
|
||||
createChildIssue:{
|
||||
method:ECommon_HttpApi_Method.POST,
|
||||
path:"/item/createchild",
|
||||
req:<{
|
||||
projectIssueId :string,
|
||||
name:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Project_Issue>{},
|
||||
permission:[Permission_Types.Project.EDIT]
|
||||
},
|
||||
listHistory:{
|
||||
method:ECommon_HttpApi_Method.GET,
|
||||
path:"/history",
|
||||
req:<{
|
||||
projectIssueId :string,
|
||||
}>{},
|
||||
res:<ICommon_Model_Project_Issue_History[]>{},
|
||||
permission:[Permission_Types.Project.READ]
|
||||
},
|
||||
checkApproval:{
|
||||
method:ECommon_HttpApi_Method.GET,
|
||||
path:"/approval/check",
|
||||
req:<{
|
||||
projectIssueId :string,
|
||||
}>{},
|
||||
res:<{
|
||||
access:boolean
|
||||
}>{},
|
||||
permission:[Permission_Types.Project.READ]
|
||||
},
|
||||
revokeApproval:{
|
||||
method:ECommon_HttpApi_Method.POST,
|
||||
path:"/approval/revoke",
|
||||
req:<{
|
||||
projectIssueId :string,
|
||||
}>{},
|
||||
res:{},
|
||||
permission:[Permission_Types.Project.EDIT]
|
||||
},
|
||||
resolveApproval:{
|
||||
method:ECommon_HttpApi_Method.POST,
|
||||
path:"/approval/resolve",
|
||||
req:<{
|
||||
projectIssueId :string,
|
||||
}>{},
|
||||
res:{},
|
||||
permission:[Permission_Types.Project.EDIT]
|
||||
},
|
||||
rejectApproval:{
|
||||
method:ECommon_HttpApi_Method.POST,
|
||||
path:"/approval/reject",
|
||||
req:<{
|
||||
projectIssueId :string,
|
||||
reason:string
|
||||
}>{},
|
||||
res:{},
|
||||
permission:[Permission_Types.Project.EDIT]
|
||||
},
|
||||
commitApproval:{
|
||||
method:ECommon_HttpApi_Method.POST,
|
||||
path:"/approval/commit",
|
||||
req:<{
|
||||
projectIssueId :string
|
||||
}>{},
|
||||
res:{},
|
||||
permission:[Permission_Types.Project.EDIT]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,14 @@ const api= {
|
||||
notificationId:string
|
||||
}>{},
|
||||
res: <ICommon_Route_Res_Notification_Item>{}
|
||||
},
|
||||
remove:{
|
||||
method: ECommon_HttpApi_Method.DELETE,
|
||||
path: "/item",
|
||||
req: <{
|
||||
notificationId:string
|
||||
}>{},
|
||||
res: <ICommon_Route_Res_Notification_Item>{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ const api={
|
||||
method:ECommon_HttpApi_Method.GET,
|
||||
path:"/user/list",
|
||||
req:<{
|
||||
organizationId:string,
|
||||
organizationId?:string,
|
||||
page:number,
|
||||
size:number,
|
||||
keyword?:string
|
||||
@ -193,7 +193,7 @@ const api={
|
||||
method:ECommon_HttpApi_Method.GET,
|
||||
path:"/tag/list",
|
||||
req:<{
|
||||
|
||||
keyword?:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Member_Tag[]>{},
|
||||
permission:[Permission_Types.Organization.READ]
|
||||
@ -217,6 +217,15 @@ const api={
|
||||
res:<ICommon_Model_Member_Tag>{},
|
||||
permission:[Permission_Types.Organization.ADMIN]
|
||||
},
|
||||
getTag:{
|
||||
method:ECommon_HttpApi_Method.GET,
|
||||
path:"/tag",
|
||||
req:<{
|
||||
memberTagId:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Member_Tag>{},
|
||||
permission:[Permission_Types.Organization.READ]
|
||||
},
|
||||
editTag:{
|
||||
method:ECommon_HttpApi_Method.PUT,
|
||||
path:"/tag",
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
} from './response';
|
||||
import {ECommon_HttpApi_Method} from "./types";
|
||||
import {ICommon_Model_Role} from "../model/role";
|
||||
import {ICommon_Model_Project_Label} from "../model/project_label";
|
||||
|
||||
const api={
|
||||
baseUrl:"/project",
|
||||
@ -76,6 +77,15 @@ const api={
|
||||
res:<ICommon_Route_Res_Project_ListTag>{},
|
||||
permission:[Permission_Types.Project.READ]
|
||||
},
|
||||
getLabel:{
|
||||
method:ECommon_HttpApi_Method.GET,
|
||||
path:"/tag/item",
|
||||
req:<{
|
||||
labelId:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Project_Label>{},
|
||||
permission:[Permission_Types.Project.READ]
|
||||
},
|
||||
createLabel:{//创建tag
|
||||
method:ECommon_HttpApi_Method.POST,
|
||||
path:"/tag/item",
|
||||
|
@ -26,6 +26,8 @@ import {ICommon_Model_Wiki_Item} from "../model/wiki_item";
|
||||
import {ICommon_Model_Meeting_Room} from "../model/meeting_room";
|
||||
import {ICommon_Model_Notification} from "../model/notification";
|
||||
import {ICommon_Model_Meeting_Miss_Call} from "../model/meeting_miss_call";
|
||||
import {ICommon_Model_Workflow_Approval} from "../model/workflow_approval";
|
||||
import {ICommon_Model_Project_Issue_Approval} from "../model/project_issue_approval";
|
||||
|
||||
export interface ICommon_Route_Res_Project_CreateModule_Data {
|
||||
id:string,
|
||||
@ -141,7 +143,9 @@ export interface ICommon_Route_Res_Workflow_Info_Action {
|
||||
}
|
||||
|
||||
export interface ICommon_Route_Res_Workflow_Info {
|
||||
nodes:ICommon_Model_Workflow_Node[],
|
||||
nodes:(ICommon_Model_Workflow_Node & {
|
||||
approval?:ICommon_Model_Workflow_Approval
|
||||
})[],
|
||||
actions:ICommon_Model_Workflow_Action[]
|
||||
}
|
||||
|
||||
@ -166,7 +170,11 @@ export type ICommon_Route_Req_ProjectIssue_Field_Value={
|
||||
export interface ICommon_Route_Res_ProjectIssue_BasicInfo extends Omit<ICommon_Model_Project_Issue,"workflow_node_id"|"issue_type_id"|"project_id"> {
|
||||
workflowNode:ICommon_Model_Workflow_Node
|
||||
issueType:ICommon_Model_Issue_Type,
|
||||
project:ICommon_Model_Project
|
||||
project:ICommon_Model_Project,
|
||||
approval?:ICommon_Model_Project_Issue_Approval & {
|
||||
reason?:string,
|
||||
workflowNode:ICommon_Model_Workflow_Node
|
||||
}
|
||||
}
|
||||
|
||||
export type ICommon_Route_Res_ProjectIssue_fieldsInfo = {
|
||||
|
@ -5,6 +5,7 @@ import {ECommon_Services} from './../types';
|
||||
import {ECommon_HttpApi_Method} from "./types";
|
||||
import {ICommon_Route_Res_Workflow_Info, ICommon_Route_Res_Workflow_Node_List_Item} from "./response";
|
||||
import {Permission_Types} from "../permission/permission";
|
||||
import {ECommon_Model_Workflow_Approval_Type, ICommon_Model_Workflow_Approval} from "../model/workflow_approval";
|
||||
|
||||
const api={
|
||||
baseUrl:"/workflow",
|
||||
@ -42,9 +43,15 @@ const api={
|
||||
description? :string,
|
||||
status? :ECommon_Model_Workflow_Node_Status,
|
||||
x?:number,
|
||||
y?:number
|
||||
y?:number,
|
||||
approval?:number,
|
||||
approvalType?:ECommon_Model_Workflow_Approval_Type,
|
||||
approvalValue?:string[],
|
||||
approvalExtra?:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Workflow_Node & {
|
||||
approval?:ICommon_Model_Workflow_Approval
|
||||
}>{},
|
||||
res:<ICommon_Model_Workflow_Node>{},
|
||||
permission:[Permission_Types.Organization.ADMIN]
|
||||
},
|
||||
deleteNode:{
|
||||
@ -65,6 +72,8 @@ const api={
|
||||
description? :string,
|
||||
sourceNodeId:string,
|
||||
destNodeId:string,
|
||||
sourceAnchorPoint:string,
|
||||
endAnchorPoint:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Workflow_Action>{},
|
||||
permission:[Permission_Types.Organization.ADMIN]
|
||||
@ -78,6 +87,8 @@ const api={
|
||||
description? :string,
|
||||
sourceNodeId?:string,
|
||||
destNodeId?:string,
|
||||
sourceAnchorPoint?:string,
|
||||
endAnchorPoint?:string
|
||||
}>{},
|
||||
res:<ICommon_Model_Workflow_Action>{},
|
||||
permission:[Permission_Types.Organization.ADMIN]
|
||||
@ -98,6 +109,18 @@ const api={
|
||||
res:<ICommon_Route_Res_Workflow_Node_List_Item[]>{},
|
||||
permission:[Permission_Types.Organization.READ]
|
||||
},
|
||||
listApprovalField:{
|
||||
method:ECommon_HttpApi_Method.GET,
|
||||
path:"/approval/field",
|
||||
req:<{
|
||||
workflowNodeId:string
|
||||
}>{},
|
||||
res:<{
|
||||
id:string,
|
||||
name:string
|
||||
}[]>{},
|
||||
permission:[Permission_Types.Organization.READ]
|
||||
}
|
||||
}
|
||||
}
|
||||
export default api
|
@ -250,6 +250,18 @@ export namespace Err {
|
||||
unbindReservedWorkflowSolutionForbidden:{
|
||||
code:3514,
|
||||
msg:"unbind reserved workflow solution forbidden"
|
||||
},
|
||||
approvalOnlyHaveOneExport:{
|
||||
code:2515,
|
||||
msg:"approval only have one export"
|
||||
},
|
||||
approvalNotFound:{
|
||||
code:2516,
|
||||
msg:"approval not found"
|
||||
},
|
||||
workflowNodeIsNotApproval:{
|
||||
code:2517,
|
||||
msg:"workflow node is not aproval"
|
||||
}
|
||||
},
|
||||
Field:{
|
||||
|
@ -1,9 +1,8 @@
|
||||
|
||||
import * as mysql from "mysql2";
|
||||
import { Pool as PromisePool } from "mysql2/promise";
|
||||
import {Pool as PromisePool} from "mysql2/promise";
|
||||
import "reflect-metadata";
|
||||
import { Err } from '../../../common/status/error';
|
||||
import { IServer_Common_Config_Mysql } from './../types/config';
|
||||
import {Err} from '../../../common/status/error';
|
||||
import {IServer_Common_Config_Mysql} from './../types/config';
|
||||
|
||||
var g_mysqlConnection:InstanceType<typeof Mysql>
|
||||
export function getMysqlInstance(){
|
||||
@ -84,6 +83,18 @@ export default class Mysql {
|
||||
ret[key]=item[key]
|
||||
}
|
||||
}
|
||||
for(let key in ret) {
|
||||
let obj=ret[key]
|
||||
if(typeof(obj)==="object") {
|
||||
let set=new Set
|
||||
for(let k in obj) {
|
||||
set.add(obj[k])
|
||||
}
|
||||
if(set.size==1 && set.has(null)) {
|
||||
delete ret[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret as any;
|
||||
}
|
||||
} else {
|
||||
@ -131,6 +142,18 @@ export default class Mysql {
|
||||
ret[key as any]=item[key]
|
||||
}
|
||||
}
|
||||
for(let key in ret) {
|
||||
let obj=ret[key]
|
||||
if(typeof(obj)==="object") {
|
||||
let set=new Set
|
||||
for(let k in obj) {
|
||||
set.add(obj[k])
|
||||
}
|
||||
if(set.size==1 && set.has(null)) {
|
||||
delete ret[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret as any;
|
||||
})
|
||||
return <T[]><unknown>rows
|
||||
|
@ -45,7 +45,7 @@ export abstract class Entity<T extends BaseModel,M extends Mapper<T>> {
|
||||
getItem():T["model"] {
|
||||
return this.item;
|
||||
}
|
||||
async create():Promise<T["model"]> {
|
||||
async create(...param:any):Promise<T["model"]> {
|
||||
if(!this.item) {
|
||||
throw Err.Common.itemNotFound;
|
||||
} else if(this.item.id) {
|
||||
@ -60,7 +60,7 @@ export abstract class Entity<T extends BaseModel,M extends Mapper<T>> {
|
||||
})
|
||||
return this.item;
|
||||
}
|
||||
async update():Promise<T["model"]>{
|
||||
async update(...param:any):Promise<T["model"]>{
|
||||
if(!this.item || !this.item.id) {
|
||||
throw Err.Common.itemNotFound;
|
||||
}
|
||||
@ -79,7 +79,7 @@ export abstract class Entity<T extends BaseModel,M extends Mapper<T>> {
|
||||
await this.loadItem();
|
||||
return this.item;
|
||||
}
|
||||
async delete(eventPublish?:keyof IServer_Common_Event_Types){
|
||||
async delete(eventPublish?:keyof IServer_Common_Event_Types,...param:any){
|
||||
await this.mapper.delete(this.item.id);
|
||||
if(eventPublish) {
|
||||
emitServiceEvent(eventPublish,this.item.id);
|
||||
|
@ -85,6 +85,10 @@ class RpcContentApi {
|
||||
}
|
||||
}
|
||||
|
||||
async clearByRefIdAndType(refId:string,type:ECommon_Model_Content_Type) {
|
||||
await ContentService.clearByRefIdAndType(refId,type)
|
||||
}
|
||||
|
||||
async clearByRefId(refId:string) {
|
||||
await ContentService.clearByRefId(refId)
|
||||
}
|
||||
|
@ -23,17 +23,19 @@ export class ContentService extends Entity<typeof contentModel,typeof contentMap
|
||||
|
||||
override async delete(eventPublish?: keyof IServer_Common_Event_Types): Promise<void> {
|
||||
await super.delete(eventPublish);
|
||||
for(let objLine of JSON.parse(this.getItem().content) as ICommon_Content_Line[]) {
|
||||
for(let objConfig of objLine.arr) {
|
||||
if(objConfig.type===ECommon_Content_Line_Config_Type.FILE || objConfig.type===ECommon_Content_Line_Config_Type.IMAGE) {
|
||||
emitServiceEvent("fileUnref",objConfig.value)
|
||||
if(this.getItem().content) {
|
||||
for(let objLine of JSON.parse(this.getItem().content) as ICommon_Content_Line[]) {
|
||||
for(let objConfig of objLine.arr) {
|
||||
if(objConfig.type===ECommon_Content_Line_Config_Type.FILE || objConfig.type===ECommon_Content_Line_Config_Type.IMAGE) {
|
||||
emitServiceEvent("fileUnref",objConfig.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override async create(): Promise<typeof contentModel["model"]> {
|
||||
this.item.content=this.item.content.replaceAll("\\\"","\\\\\"")
|
||||
this.item.content=this.item.content.replaceAll("\\","\\\\")
|
||||
let ret=await super.create();
|
||||
let obj=this.generateLineObj(ret.content)
|
||||
for(let key in obj.file) {
|
||||
@ -61,7 +63,7 @@ export class ContentService extends Entity<typeof contentModel,typeof contentMap
|
||||
|
||||
override async update(): Promise<typeof contentModel["model"]> {
|
||||
let oldObj=this.generateLineObj(this._item.content)
|
||||
this.item.content=this.item.content.replaceAll("\\\"","\\\\\"")
|
||||
this.item.content=this.item.content.replaceAll("\\","\\\\")
|
||||
let ret=await super.update();
|
||||
let newObj=this.generateLineObj(ret.content)
|
||||
for(let key in oldObj.file) {
|
||||
@ -125,7 +127,7 @@ export class ContentService extends Entity<typeof contentModel,typeof contentMap
|
||||
[param:string]:number
|
||||
}={}
|
||||
if(content) {
|
||||
let arr=JSON.parse(content) as ICommon_Content_Line[]
|
||||
let arr=JSON.parse(decodeURIComponent(content)) as ICommon_Content_Line[]
|
||||
for(let obj of arr) {
|
||||
for(let obj1 of obj.arr) {
|
||||
if(obj1.type===ECommon_Content_Line_Config_Type.FILE || obj1.type===ECommon_Content_Line_Config_Type.IMAGE) {
|
||||
@ -161,6 +163,18 @@ export class ContentService extends Entity<typeof contentModel,typeof contentMap
|
||||
await Promise.all(arrPromise)
|
||||
}
|
||||
|
||||
static async clearByRefIdAndType(refId:string,type:ECommon_Model_Content_Type) {
|
||||
let arr=await ContentService.getItemsByExp({
|
||||
ref_id:refId,
|
||||
type
|
||||
})
|
||||
let arrPromise=[]
|
||||
for(let obj of arr) {
|
||||
arrPromise.push(obj.delete())
|
||||
}
|
||||
await Promise.all(arrPromise)
|
||||
}
|
||||
|
||||
static async clearByRefIds(refIds:string[]) {
|
||||
let arrPromise=[]
|
||||
for(let refId of refIds) {
|
||||
|
@ -124,6 +124,7 @@ class FieldController {
|
||||
@DHttpApi(fieldApi.routes.editWorkflowNodeFieldConfig)
|
||||
async addWorkflowNodeFieldConfig(@DHttpReqParamRequired("workflowNodeFieldTypeId") workflowNodeFieldTypeId:string,
|
||||
@DHttpReqParamRequired("data") data:{
|
||||
id?:string,
|
||||
value :string,
|
||||
selected :number
|
||||
}[]): Promise<typeof fieldApi.routes.editWorkflowNodeFieldConfig.res> {
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
import {Err} from "../../../common/status/error";
|
||||
import {DComponent} from "../../common/decorate/component";
|
||||
import {DHttpApi, DHttpController, DHttpReqParam, DHttpReqParamRequired, DHttpUser} from "../../common/http/http";
|
||||
import {ProjectIssueService} from "../service/issue";
|
||||
import {ProjectIssueHistoryService, ProjectIssueService} from "../service/issue";
|
||||
import {ProjectService} from "../service/project";
|
||||
import {IUserSession} from "../../user/types/config";
|
||||
import rpcContentApi from "../../content/rpc/content"
|
||||
@ -15,6 +15,7 @@ import rpcNotificationApi from "../../notification/rpc/notification"
|
||||
import {ECommon_Model_Notification_Type} from "../../../common/model/notification";
|
||||
import rpcCooperationApi from "../rpc/cooperation"
|
||||
import rpcUserApi from "../../user/rpc/user"
|
||||
import {ECommon_Model_Project_Issue_History_Type} from "../../../common/model/project_issue_history";
|
||||
|
||||
@DComponent
|
||||
@DHttpController(projectIssueApi)
|
||||
@ -79,7 +80,7 @@ class IssueController {
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
await projectIssue.confirmNextNode(workflowActionId,values)
|
||||
await projectIssue.confirmNextNode(workflowActionId,values,user.organizationInfo.organizationUserId)
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_WORKFLOW_CHANGE,projectIssueId,null,user.organizationInfo.organizationUserId)
|
||||
return
|
||||
}
|
||||
@ -90,7 +91,7 @@ class IssueController {
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.updateFieldValue(value)
|
||||
let ret=await projectIssue.updateFieldValue(value,user.organizationInfo.organizationUserId)
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_FIELD_CHANGE,projectIssueId,null,user.organizationInfo.organizationUserId)
|
||||
return ret;
|
||||
}
|
||||
@ -120,6 +121,30 @@ class IssueController {
|
||||
if(name || priority) {
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_FIELD_CHANGE,projectIssueId,null,user.organizationInfo.organizationUserId)
|
||||
}
|
||||
let key:string,value:string
|
||||
if(name) {
|
||||
key="Name"
|
||||
value=name
|
||||
} else if(priority!==undefined) {
|
||||
key="Priority"
|
||||
value=String(priority)
|
||||
} else if(assignerId) {
|
||||
key="Assigner"
|
||||
value=assignerId
|
||||
} else if(reporterId) {
|
||||
key="Reporter"
|
||||
value=reporterId
|
||||
}
|
||||
let objHistory=new ProjectIssueHistoryService()
|
||||
objHistory.assignItem({
|
||||
project_issue_id:ret.id,
|
||||
name:key,
|
||||
type:ECommon_Model_Project_Issue_History_Type.UPDATE_FIELD,
|
||||
organization_user_id:user.organizationInfo.organizationUserId,
|
||||
project_id:ret.project_id,
|
||||
value:value
|
||||
})
|
||||
objHistory.create()
|
||||
return ret;
|
||||
}
|
||||
@DHttpApi(projectIssueApi.routes.editDescription)
|
||||
@ -184,12 +209,12 @@ class IssueController {
|
||||
return ret
|
||||
}
|
||||
@DHttpApi(projectIssueApi.routes.actionsInfo)
|
||||
async actionsInfo(@DHttpReqParamRequired("projectIssueId") projectIssueId :string):Promise<typeof projectIssueApi.routes.actionsInfo.res> {
|
||||
async actionsInfo(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.actionsInfo.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.actionsInfo()
|
||||
let ret=await projectIssue.actionsInfo(user.organizationInfo.organizationUserId)
|
||||
return ret
|
||||
}
|
||||
@DHttpApi(projectIssueApi.routes.commentList)
|
||||
@ -214,12 +239,18 @@ class IssueController {
|
||||
return
|
||||
}
|
||||
@DHttpApi(projectIssueApi.routes.copy)
|
||||
async copy(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.copy.res> {
|
||||
async copy(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParam("name") name :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.copy.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.copy()
|
||||
if(name) {
|
||||
ret.assignItem({
|
||||
name
|
||||
})
|
||||
await ret.update()
|
||||
}
|
||||
if(ret.getItem().assigner_id) {
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_ASSIGNER_ASSIGN,ret.getId(),ret.getItem().assigner_id,user.organizationInfo.organizationUserId)
|
||||
}
|
||||
@ -292,21 +323,21 @@ class IssueController {
|
||||
return ret;
|
||||
}
|
||||
@DHttpApi(projectIssueApi.routes.bindLabel)
|
||||
async bindTag(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParamRequired("labelIds") labelIds :string[]):Promise<typeof projectIssueApi.routes.bindLabel.res> {
|
||||
async bindTag(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParamRequired("labelIds") labelIds :string[],@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.bindLabel.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.bindLabels(labelIds)
|
||||
let ret=await projectIssue.bindLabels(labelIds,user.organizationInfo.organizationUserId)
|
||||
return ret;
|
||||
}
|
||||
@DHttpApi(projectIssueApi.routes.bindModule)
|
||||
async bindModule(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParam("moduleId") moduleId :string):Promise<typeof projectIssueApi.routes.bindModule.res> {
|
||||
async bindModule(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParam("moduleId") moduleId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.bindModule.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.bindModule(moduleId)
|
||||
let ret=await projectIssue.bindModule(moduleId,user.organizationInfo.organizationUserId)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -338,12 +369,12 @@ class IssueController {
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.bindReleases)
|
||||
async bindReleases(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParamRequired("projectReleaseIds") projectReleaseIds :string[]):Promise<typeof projectIssueApi.routes.bindReleases.res> {
|
||||
async bindReleases(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParamRequired("projectReleaseIds") projectReleaseIds :string[],@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.bindReleases.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.bindReleases(projectReleaseIds)
|
||||
let ret=await projectIssue.bindReleases(projectReleaseIds,user.organizationInfo.organizationUserId)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -360,4 +391,91 @@ class IssueController {
|
||||
});
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.createChildIssue)
|
||||
async createChildIssue(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParamRequired("name") name :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.createChildIssue.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.copy()
|
||||
ret.assignItem({
|
||||
name
|
||||
})
|
||||
await Promise.all([
|
||||
ret.update(),
|
||||
projectIssue.addChildIssue(ret.getId())
|
||||
])
|
||||
if(ret.getItem().assigner_id) {
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_ASSIGNER_ASSIGN,ret.getId(),ret.getItem().assigner_id,user.organizationInfo.organizationUserId)
|
||||
}
|
||||
if(ret.getItem().reporter_id) {
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_REPORTER_ASSIGN,ret.getId(),ret.getItem().reporter_id,user.organizationInfo.organizationUserId)
|
||||
}
|
||||
return ret.getItem()
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.listHistory)
|
||||
async listHistory(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.listHistory.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await ProjectIssueHistoryService.list(projectIssueId)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.checkApproval)
|
||||
async checkApproval(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.checkApproval.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
let ret=await projectIssue.checkApproval(user.organizationInfo.organizationUserId)
|
||||
return {
|
||||
access:ret
|
||||
}
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.revokeApproval)
|
||||
async revokeApproval(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.revokeApproval.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
await projectIssue.revokeApproval()
|
||||
return
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.resolveApproval)
|
||||
async resolveApproval(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.resolveApproval.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
await projectIssue.resolveApproval(user.organizationInfo.organizationUserId)
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_APPROVAL_RESOLVE,projectIssue.getId(),projectIssue.getItem().assigner_id,user.organizationInfo.organizationUserId)
|
||||
return
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.rejectApproval)
|
||||
async rejectApproval(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpReqParamRequired("reason") reason :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.rejectApproval.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
await projectIssue.rejectApproval(user.organizationInfo.organizationUserId,reason)
|
||||
rpcNotificationApi.createNotification(ECommon_Model_Notification_Type.ISSUE_APPROVAL_REJECT,projectIssue.getId(),projectIssue.getItem().assigner_id,user.organizationInfo.organizationUserId)
|
||||
return
|
||||
}
|
||||
|
||||
@DHttpApi(projectIssueApi.routes.commitApproval)
|
||||
async commitApproval(@DHttpReqParamRequired("projectIssueId") projectIssueId :string,@DHttpUser user:IUserSession):Promise<typeof projectIssueApi.routes.commitApproval.res> {
|
||||
let projectIssue=await ProjectIssueService.getItemById(projectIssueId)
|
||||
if(!projectIssue) {
|
||||
throw Err.Project.ProjectIssue.projectIssueNotFound
|
||||
}
|
||||
await projectIssue.commitApproval()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
@ -2,10 +2,19 @@ import projectApi from "../../../common/routes/project";
|
||||
import {DComponent} from "../../common/decorate/component";
|
||||
import {DHttpApi, DHttpController, DHttpReqParam, DHttpReqParamRequired} from "../../common/http/http";
|
||||
import {ProjectLabelService} from "../service/label";
|
||||
import {Err} from "../../../common/status/error";
|
||||
|
||||
@DComponent
|
||||
@DHttpController(projectApi)
|
||||
class TagController {
|
||||
@DHttpApi(projectApi.routes.getLabel)
|
||||
async getLabel(@DHttpReqParamRequired("labelId") labelId:string):Promise<typeof projectApi.routes.getLabel.res> {
|
||||
let tag=await ProjectLabelService.getItemById(labelId)
|
||||
if(!tag) {
|
||||
throw Err.Project.Label.labelNotfound
|
||||
}
|
||||
return tag.getItem();
|
||||
}
|
||||
@DHttpApi(projectApi.routes.listLabel)
|
||||
async listTag(@DHttpReqParamRequired("projectId") projectId:string,@DHttpReqParamRequired("page") page:number,@DHttpReqParamRequired("size") size:number,@DHttpReqParam("keyword") keyword:string):Promise<typeof projectApi.routes.listLabel.res> {
|
||||
let tag=new ProjectLabelService()
|
||||
|
@ -3,9 +3,10 @@ import workflowApi from "../../../common/routes/workflow";
|
||||
import {Err} from "../../../common/status/error";
|
||||
import {DComponent} from "../../common/decorate/component";
|
||||
import {DHttpApi, DHttpController, DHttpReqParam, DHttpReqParamRequired, DHttpUser} from "../../common/http/http";
|
||||
import {WorkflowActionService, WorkflowService} from "../service/workflow";
|
||||
import {WorkflowActionService, WorkflowApprovalService, WorkflowService} from "../service/workflow";
|
||||
import {WorkflowNodeService} from './../service/workflow';
|
||||
import {IUserSession} from "../../user/types/config";
|
||||
import {ECommon_Model_Workflow_Approval_Type} from "../../../common/model/workflow_approval";
|
||||
|
||||
@DComponent
|
||||
@DHttpController(workflowApi)
|
||||
@ -32,7 +33,16 @@ class WorkflowController {
|
||||
}
|
||||
|
||||
@DHttpApi(workflowApi.routes.editNode)
|
||||
async editNode(@DHttpReqParamRequired("workflowNodeId") workflowNodeId: string, @DHttpReqParam("name") name: string, @DHttpReqParam("description") description: string, @DHttpReqParam("status") status: ECommon_Model_Workflow_Node_Status,@DHttpReqParam("x") x: number,@DHttpReqParam("y") y: number): Promise<typeof workflowApi.routes.editNode.res> {
|
||||
async editNode(@DHttpReqParamRequired("workflowNodeId") workflowNodeId: string,
|
||||
@DHttpReqParam("name") name: string,
|
||||
@DHttpReqParam("description") description: string,
|
||||
@DHttpReqParam("status") status: ECommon_Model_Workflow_Node_Status,
|
||||
@DHttpReqParam("x") x: number,
|
||||
@DHttpReqParam("y") y: number,
|
||||
@DHttpReqParam("approval") approval: number,
|
||||
@DHttpReqParam("approvalType") approvalType: ECommon_Model_Workflow_Approval_Type,
|
||||
@DHttpReqParam("approvalValue") approvalValue: string[],
|
||||
@DHttpReqParam("approvalExtra") approvalExtra: string): Promise<typeof workflowApi.routes.editNode.res> {
|
||||
let obj = await WorkflowNodeService.getItemById(workflowNodeId);
|
||||
if (!obj) {
|
||||
throw Err.Project.Workflow.workflowNodeNotFound
|
||||
@ -45,9 +55,37 @@ class WorkflowController {
|
||||
description: description,
|
||||
status: status,
|
||||
x:x,
|
||||
y:y
|
||||
y:y,
|
||||
is_approval:approval
|
||||
})
|
||||
let ret = await obj.update()
|
||||
await obj.update()
|
||||
if((status===ECommon_Model_Workflow_Node_Status.INPROGRESS || status===ECommon_Model_Workflow_Node_Status.DONE) && approval!==undefined) {
|
||||
let objApproval=await WorkflowApprovalService.getItemByExp({
|
||||
workflow_node_id:obj.getId()
|
||||
})
|
||||
if(approval) {
|
||||
if(objApproval) {
|
||||
objApproval.assignItem({
|
||||
type:approvalType,
|
||||
value:approvalValue,
|
||||
extra:approvalExtra
|
||||
})
|
||||
await objApproval.update()
|
||||
} else {
|
||||
objApproval=new WorkflowApprovalService()
|
||||
objApproval.assignItem({
|
||||
workflow_node_id:obj.getId(),
|
||||
type:approvalType,
|
||||
value:approvalValue,
|
||||
extra:approvalExtra
|
||||
})
|
||||
await objApproval.create()
|
||||
}
|
||||
} else {
|
||||
await objApproval?.delete()
|
||||
}
|
||||
}
|
||||
let ret=await obj.info()
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -65,21 +103,23 @@ class WorkflowController {
|
||||
}
|
||||
|
||||
@DHttpApi(workflowApi.routes.addAction)
|
||||
async addAction(@DHttpReqParamRequired("issueTypeId") issueTypeId: string, @DHttpReqParamRequired("name") name: string, @DHttpReqParam("description") description: string, @DHttpReqParamRequired("sourceNodeId") sourceNodeId: string,@DHttpReqParamRequired("destNodeId") destNodeId: string): Promise<typeof workflowApi.routes.addAction.res> {
|
||||
async addAction(@DHttpReqParamRequired("issueTypeId") issueTypeId: string, @DHttpReqParamRequired("name") name: string, @DHttpReqParam("description") description: string, @DHttpReqParamRequired("sourceNodeId") sourceNodeId: string,@DHttpReqParamRequired("destNodeId") destNodeId: string,@DHttpReqParamRequired("sourceAnchorPoint") sourceAnchorPoint: string,@DHttpReqParamRequired("endAnchorPoint") endAnchorPoint: string): Promise<typeof workflowApi.routes.addAction.res> {
|
||||
let obj = new WorkflowActionService;
|
||||
obj.assignItem({
|
||||
issue_type_id: issueTypeId,
|
||||
name: name,
|
||||
description: description,
|
||||
source_node_id:sourceNodeId,
|
||||
dest_node_id:destNodeId
|
||||
dest_node_id:destNodeId,
|
||||
source_anchor_point:sourceAnchorPoint,
|
||||
end_anchor_point:endAnchorPoint
|
||||
})
|
||||
let ret = await obj.create()
|
||||
return ret;
|
||||
}
|
||||
|
||||
@DHttpApi(workflowApi.routes.editAction)
|
||||
async editAction(@DHttpReqParamRequired("workflowActionId") workflowActionId: string, @DHttpReqParamRequired("name") name: string, @DHttpReqParam("description") description: string, @DHttpReqParam("sourceNodeId") sourceNodeId: string,@DHttpReqParam("destNodeId") destNodeId: string): Promise<typeof workflowApi.routes.editAction.res> {
|
||||
async editAction(@DHttpReqParamRequired("workflowActionId") workflowActionId: string, @DHttpReqParam("name") name: string, @DHttpReqParam("description") description: string, @DHttpReqParam("sourceNodeId") sourceNodeId: string,@DHttpReqParam("destNodeId") destNodeId: string,@DHttpReqParam("sourceAnchorPoint") sourceAnchorPoint: string,@DHttpReqParam("endAnchorPoint") endAnchorPoint: string): Promise<typeof workflowApi.routes.editAction.res> {
|
||||
let obj = await WorkflowActionService.getItemById(workflowActionId);
|
||||
if (!obj) {
|
||||
throw Err.Project.Workflow.workflowActionNotFound
|
||||
@ -88,7 +128,9 @@ class WorkflowController {
|
||||
name: name,
|
||||
description: description,
|
||||
source_node_id:sourceNodeId,
|
||||
dest_node_id:destNodeId
|
||||
dest_node_id:destNodeId,
|
||||
source_anchor_point:sourceAnchorPoint,
|
||||
end_anchor_point:endAnchorPoint
|
||||
})
|
||||
let ret = await obj.update()
|
||||
return ret;
|
||||
@ -110,4 +152,13 @@ class WorkflowController {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@DHttpApi(workflowApi.routes.listApprovalField)
|
||||
async listApprovalField(@DHttpReqParamRequired("workflowNodeId") workflowNodeId: string,@DHttpUser user:IUserSession): Promise<typeof workflowApi.routes.listApprovalField.res> {
|
||||
let obj=await WorkflowNodeService.getItemById(workflowNodeId)
|
||||
if(!obj) {
|
||||
throw Err.Project.Workflow.workflowNodeNotFound
|
||||
}
|
||||
let ret=await obj.listApprovalField()
|
||||
return ret;
|
||||
}
|
||||
}
|