You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
465 lines
14 KiB
465 lines
14 KiB
<!--
|
|
@ 作者: 秦东
|
|
@ 时间: 2026-02-02 14:00:47
|
|
@ 备注: 新流程
|
|
-->
|
|
<script lang='ts' setup>
|
|
import { gainRunTaskFlow } from "@/api/DesignForm/requestapi";
|
|
import SvgIcon from "@/components/SvgIcon/index.vue";
|
|
|
|
//引入图标
|
|
import squareUrlOne from "@/assets/images/1.png"
|
|
import squareUrlTwo from "@/assets/images/2.png"
|
|
import { afreshRunWorkflow, authorizeWorkflow } from "@/api/taskapi/management";
|
|
|
|
const state = reactive({
|
|
circleUrl:squareUrlTwo,
|
|
squareUrl: squareUrlOne,
|
|
sizeList: ['small', '', 'large'] as const,
|
|
})
|
|
const { circleUrl, squareUrl, sizeList } = toRefs(state)
|
|
|
|
const props = defineProps({
|
|
flowKey:{
|
|
type:String,
|
|
default:""
|
|
},
|
|
currentProgress:{
|
|
type:Number,
|
|
default:0
|
|
}
|
|
})
|
|
|
|
const emits = defineEmits(["update:flowary","updatelist"]);
|
|
const ifSendFlow = ref(false)//是否可以写评论
|
|
const flowLoading = ref(false)
|
|
const flowMaps = ref<any[]>();
|
|
// const flowOpinion = ref(false) //是否可以写评论
|
|
const currentStep = ref<number>(props.currentProgress)
|
|
const nextStep = ref<number>(0)
|
|
|
|
//获取任务流程
|
|
const gainRunFlowTask = () =>{
|
|
flowLoading.value = true
|
|
let sendInfo = {
|
|
id:props.flowKey
|
|
}
|
|
gainRunTaskFlow(sendInfo)
|
|
.then((data:any) =>{
|
|
console.log("获取流程--werwerwerwer--->",data)
|
|
flowMaps.value = data.data.flowList
|
|
ifSendFlow.value = data.data.operational
|
|
currentStep.value = data.data.current_step
|
|
nextStep.value = data.data.next_step
|
|
emits("update:flowary", data.data.flowList);
|
|
})
|
|
.finally(()=>{
|
|
flowLoading.value = false
|
|
})
|
|
}
|
|
/**
|
|
@ 作者: 秦东
|
|
@ 时间: 2026-01-29 16:37:56
|
|
@ 功能: 判断审批节点样式
|
|
*/
|
|
const judgeNodeClass = (val:number,stepVal:number):string => {
|
|
console.log("判断审批节点样式",val,stepVal)
|
|
switch(val){
|
|
case 1:
|
|
if(ifSendFlow.value){
|
|
if(currentStep.value == stepVal){
|
|
return "in-progress"
|
|
}else{
|
|
return 'completed'
|
|
}
|
|
}else{
|
|
if(nextStep.value == stepVal){
|
|
return "in-progress"
|
|
}else{
|
|
return 'completed'
|
|
}
|
|
}
|
|
|
|
case 2:
|
|
return 'pending'
|
|
case 3:
|
|
// return 'in-progress'
|
|
if(ifSendFlow.value){
|
|
if(currentStep.value == stepVal){
|
|
return "in-progress"
|
|
}else{
|
|
return 'in-executor'
|
|
}
|
|
}else{
|
|
if(nextStep.value == stepVal){
|
|
return "in-progress"
|
|
}else{
|
|
return 'completed'
|
|
}
|
|
}
|
|
default:
|
|
if(ifSendFlow.value){
|
|
if(currentStep.value == stepVal){
|
|
return "in-progress"
|
|
}else{
|
|
return 'completed'
|
|
}
|
|
}else{
|
|
if(nextStep.value == stepVal){
|
|
return "in-progress"
|
|
}else{
|
|
return 'completed'
|
|
}
|
|
}
|
|
|
|
}
|
|
return 'completed'
|
|
}
|
|
|
|
//组件渲染之前
|
|
onBeforeMount(()=>{
|
|
gainRunFlowTask();
|
|
})
|
|
//判断是否可以添加人员
|
|
const judgeAddUser = (val:any):boolean =>{
|
|
// console.log("获取流程----1111->",val.judgelist)
|
|
if(val.judgelist){
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
let zhiXingStep = 1;
|
|
const presetPersonnel = ref<any>([]); //预设选择人
|
|
const selectedPeople = ref<any>([]); //已选择人
|
|
const openclosebox = ref(false)
|
|
const openOrClose = ref(false)
|
|
const sendFlowInfo = ref<string>() //审批意见
|
|
//添加操作人
|
|
const addPeople = (val:any) =>{
|
|
zhiXingStep = val.step
|
|
presetPersonnel.value = val.pendpers
|
|
selectedPeople.value = val.operator
|
|
if(val.runscope == 1){
|
|
openclosebox.value = true
|
|
}else{
|
|
openOrClose.value = true
|
|
}
|
|
|
|
// console.log("PresetPersonnel.value--------1-------->",val)
|
|
// console.log("PresetPersonnel.value--------2-------->",val.pendpers)
|
|
// console.log("PresetPersonnel.value--------3-------->",val.operator)
|
|
// console.log("PresetPersonnel.value--------4-------->",selectedPeople.value)
|
|
}
|
|
//更新节点操作人
|
|
const updateNode = (val:any) =>{
|
|
|
|
if(flowMaps.value&& flowMaps.value.length > 0){
|
|
|
|
flowMaps.value.forEach((item:any) =>{
|
|
if(item.step == zhiXingStep){
|
|
item.operator = val
|
|
}
|
|
})
|
|
}
|
|
}
|
|
//提交审批
|
|
const yesOrNo = (val:string,agreeOrRefuse:number) =>{
|
|
// console.log("提交审批----1111->",val,agreeOrRefuse,sendFlowInfo.value)
|
|
let sendInfo = {
|
|
id:props.flowKey,
|
|
agreeOrRefuse:agreeOrRefuse,
|
|
suggest:sendFlowInfo.value,
|
|
flowlist:flowMaps.value
|
|
}
|
|
authorizeWorkflow(sendInfo)
|
|
.then((data:any)=>{
|
|
|
|
// console.log("提交审批----22222->",data)
|
|
ElMessage({
|
|
message: '处理完成!',
|
|
type: 'success'
|
|
})
|
|
|
|
})
|
|
.finally(()=>{
|
|
gainRunFlowTask();
|
|
emits("updatelist");
|
|
})
|
|
|
|
}
|
|
//重新提交审核
|
|
const anewSubmit = (type: string,val?:any) => {
|
|
|
|
afreshRunWorkflow({id:props.flowKey})
|
|
.then((data:any) => {
|
|
|
|
})
|
|
.finally(() => {
|
|
gainRunFlowTask()
|
|
})
|
|
|
|
}
|
|
defineExpose({gainRunFlowTask,anewSubmit})
|
|
</script>
|
|
<template>
|
|
<el-card shadow="always">
|
|
<template #header>
|
|
<div class="card-header">
|
|
<div class="svgBox"><SvgIcon icon-class="liuChengBiaoDan" size="25" /></div>
|
|
审批流程
|
|
</div>
|
|
</template>
|
|
<el-scrollbar v-loading="flowLoading" element-loading-text="Loading..." :class="ifSendFlow?'flowBody':'flowBodyNofoot'">
|
|
<div class="approval-steps">
|
|
<div v-for="item in flowMaps" :key="item.step" :class="['step', judgeNodeClass(item.type,item.step)]">
|
|
<div class="step-icon">
|
|
<SvgIcon v-if="item.type==0" icon-class="faqiren" size="25" />
|
|
<SvgIcon v-if="item.type==1" icon-class="spr" size="25" />
|
|
<SvgIcon v-if="item.type==2" icon-class="csr" size="25" />
|
|
<SvgIcon v-if="item.type==3" icon-class="zxr" size="25" />
|
|
<!--SvgIcon v-else icon-class="shenpi" size="25" /-->
|
|
</div>
|
|
<div class="step-content">
|
|
<h3 class="step-title">{{item.nodeName}}</h3>
|
|
<div v-for="items in item.operator" :key="items.id" class="flowLogBox">
|
|
|
|
<div >
|
|
<el-avatar v-if="items.icon!=''" shape="square" fit="cover" :src="items.icon" style="width: 40px;" class="avatarBox" />
|
|
<el-avatar v-else-if="items.iconbase64!=''" shape="square" fit="cover" :src="items.iconbase64" style="width: 40px;" class="avatarBox" />
|
|
<el-avatar v-else shape="square" fit="cover" :src="squareUrl" style="width: 40px;" class="avatarBox" />
|
|
</div>
|
|
<div>
|
|
<div>
|
|
<el-text size="small">{{ items.departmentname }}</el-text>
|
|
<el-text size="small"><span v-if="items.departmentname"> - </span>{{ items.postname }}</el-text>
|
|
<el-text size="small"><span v-if="items.departmentname||items.postname"> - </span>{{ items.name }}</el-text>
|
|
</div>
|
|
<div v-for="(logItem,logIndex) in items.log" :key="logIndex" >
|
|
<div v-if="logItem.state==2" class="step-info">
|
|
<el-text v-if="logItem.cause" type="success" size="small">{{logItem.cause}}</el-text>
|
|
<el-text v-else type="success" size="small" >已同意</el-text>
|
|
</div>
|
|
<div v-if="logItem.state==3" class="step-info">
|
|
<el-text v-if="logItem.cause" type="danger" size="small">{{logItem.cause}}</el-text>
|
|
<el-text v-else type="danger" size="small" >已驳回</el-text>
|
|
</div>
|
|
<div v-else class="step-info">
|
|
<el-text v-if="logItem.cause" size="small">{{logItem.cause}}</el-text>
|
|
<el-text v-else size="small" >未操作</el-text>
|
|
</div>
|
|
<div class="step-time">{{ logItem.time }}</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="judgeAddUser(item)" class="addUser" @click="addPeople(item)">
|
|
<svg-icon icon-class="addxuxian" size="50" />
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</el-scrollbar>
|
|
<template v-if="ifSendFlow" #footer>
|
|
<div class="bootemWorkFlow">
|
|
<el-input v-model="sendFlowInfo" type="textarea" :rows="2" style="width: 100%" placeholder="请输入审批意见"></el-input>
|
|
<div class="bootemWorkFlowBut">
|
|
<el-button class="btn approve-btn" @click="yesOrNo(props.flowKey,1)"><SvgIcon icon-class="kxdg" size="" style="margin-right: 5px" />通过审批</el-button>
|
|
<el-button class="btn reject-btn" @click="yesOrNo(props.flowKey,2)" ><SvgIcon icon-class="cwkx" size="" style="margin-right: 5px" />驳回申请</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
</el-card>
|
|
|
|
<OrgUserPage v-if="openOrClose" v-model:openclose="openOrClose" :preset-personnel="presetPersonnel" :selected-people="selectedPeople" @update-node="updateNode" />
|
|
<OrgAllUserPage v-if="openclosebox" v-model:openclosebox="openclosebox" :selected-people="selectedPeople" @update-node="updateNode" />
|
|
</template>
|
|
<style lang='scss' scoped>
|
|
.card-header{
|
|
font-size: 1.4rem;
|
|
font-weight: 700;
|
|
color: #0020C2;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.flowBody{
|
|
padding: 10px 15px;
|
|
height: calc(100vh - 260px);
|
|
}
|
|
.flowBodyNofoot{
|
|
padding: 10px 15px;
|
|
height: calc(100vh - 140px);
|
|
}
|
|
.svgBox{
|
|
background: linear-gradient(135deg, #0020C2, #4d6cff);
|
|
color: white;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.2rem;
|
|
}
|
|
.bootemWorkFlow{
|
|
width: 100%;
|
|
text-align: center;
|
|
.bootemWorkFlowBut{
|
|
width: 100%;
|
|
padding: 10px 0;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.btn {
|
|
padding: 14px 16px;
|
|
border-radius: 10px;
|
|
font-weight: 600;
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
border: none;
|
|
}
|
|
.btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(0, 32, 194, 0.15);
|
|
}
|
|
.approve-btn {
|
|
background: linear-gradient(135deg, #00cc66, #33dd88);
|
|
color: white;
|
|
}
|
|
.reject-btn {
|
|
background: linear-gradient(135deg, #ff3366, #ff5588);
|
|
color: white;
|
|
}
|
|
.approval-steps {
|
|
flex: 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
.step {
|
|
display: flex;
|
|
margin-bottom: 25px;
|
|
position: relative;
|
|
margin-left: 7px;
|
|
margin-top: 7px;
|
|
}
|
|
|
|
.step:not(:last-child)::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: 20px;
|
|
top: 40px;
|
|
width: 2px;
|
|
height: calc(100% + 5px);
|
|
background: linear-gradient(to bottom, rgba(0, 32, 194, 0.2), rgba(0, 32, 194, 0.05));
|
|
}
|
|
|
|
.step-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 5px;
|
|
z-index: 1;
|
|
flex-shrink: 0;
|
|
|
|
}
|
|
|
|
.step.completed .step-icon {
|
|
background: linear-gradient(135deg, #0020C2, #4d6cff);
|
|
color: white;
|
|
}
|
|
|
|
.step.in-progress .step-icon {
|
|
background: linear-gradient(135deg, #ffcc00, #ffdd55);
|
|
color: #333;
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
.step.in-executor .step-icon {
|
|
background: linear-gradient(135deg, #67C23A, #95f764);
|
|
color: #fff;
|
|
}
|
|
|
|
.step.pending .step-icon {
|
|
background: #f0f4ff;
|
|
color: #a0a6c9;
|
|
}
|
|
|
|
.step-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.step-title {
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
color: #0020C2;
|
|
}
|
|
|
|
.step.completed .step-title {
|
|
color: #4d6cff;
|
|
}
|
|
|
|
.step.in-progress .step-title {
|
|
color: #ff9900;
|
|
}
|
|
.step.in-executor .step-title {
|
|
color: #67C23A;
|
|
}
|
|
|
|
.step.pending .step-title {
|
|
color: #a0a6c9;
|
|
}
|
|
|
|
.step-info {
|
|
font-size: 0.9rem;
|
|
color: #666;
|
|
margin-top: 0px;
|
|
}
|
|
|
|
.step-time {
|
|
font-size: 0.8rem;
|
|
color: #888;
|
|
margin-bottom: 4px;
|
|
}
|
|
/* 响应式设计 */
|
|
@media (max-width: 1200px) {
|
|
.container {
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: auto auto auto;
|
|
height: auto;
|
|
gap: 15px;
|
|
}
|
|
|
|
.card {
|
|
min-height: 400px;
|
|
}
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { box-shadow: 0 0 0 0 rgba(255, 204, 0, 0.7); }
|
|
70% { box-shadow: 0 0 0 10px rgba(255, 204, 0, 0); }
|
|
100% { box-shadow: 0 0 0 0 rgba(255, 204, 0, 0); }
|
|
}
|
|
.flowLogBox{
|
|
display: grid;
|
|
grid-template-columns: 45px 1fr;
|
|
}
|
|
.addUser{
|
|
display:block;
|
|
cursor:pointer;
|
|
}
|
|
.avatarBox{
|
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
}
|
|
</style>
|
|
|