Browse Source

处理审批节点

master
herenshan112 11 hours ago
parent
commit
931d93c2de
  1. 2
      src/api/taskapi/types.ts
  2. 8
      src/components/workflow/drwer/approverDrawer.vue
  3. 3
      src/utils/workflow/const.ts
  4. 5
      src/views/hr/orgUserRole/org.vue
  5. 35
      src/views/hr/orgUserRole/orgrole.vue
  6. 6
      src/views/hr/userBox.vue
  7. 2
      src/views/system/user/index.vue
  8. 5
      src/views/sysworkflow/codepage/index.vue
  9. 50
      src/views/sysworkflow/lowcodepage/appPage/appPageForm/appFlow/simulateTaskFlow.vue
  10. 24
      src/views/sysworkflow/lowcodepage/appPage/appPageForm/unitsPageFrom/attribute.vue
  11. 3
      src/views/sysworkflow/lowcodepage/newLowCode/appLayoutEdit/appContainerPage.vue
  12. 442
      src/views/taskplatform/taskmanagement/flowStepNew.vue

2
src/api/taskapi/types.ts

@ -66,6 +66,8 @@ export interface nodeFlow extends customerFormLogo{
conditionList?:conditionInfo[];
nodelPeople?:nodelPeoples[];
oldFlow?:any
isRun?:number;
userKey?:string;
}
export interface nodelPeoples {
factorid?:string;

8
src/components/workflow/drwer/approverDrawer.vue

@ -845,7 +845,7 @@ const isAllPick = (val:powerNokeInfo) => {
<template #label>
<span class="wordKeyCss">属性设置</span>
</template>
<!-- <el-divider content-position="left">节点属性</el-divider> -->
<!-- <el-divider content-position={{}"left">节点属性</el-divider> -->
<div class="approver_content">
<el-radio-group
v-model="approverConfig.settype"
@ -1132,6 +1132,12 @@ const isAllPick = (val:powerNokeInfo) => {
@check="handleNodeClickOrg"
/>
</div>
<!--指定成员-->
<el-divider v-if="approverConfig.settype == 11" content-position="left"
>指定角色</el-divider
>
<!-- <MatrixPage
v-if="approverConfig.settype == 9"
v-model:isshow="matrixIsShow"

3
src/utils/workflow/const.ts

@ -15,7 +15,8 @@ export let setTypes = [
{value: 10, label: '指定部门负责人'},
{value: 7, label: '指定前置审批为本节点设置审批人'},
{value: 8, label: '表单字段'},
{value: 9, label: '权限矩阵'}
{value: 9, label: '权限矩阵'},
{value: 11, label: '指定角色'}
]
export let selectModes = [

5
src/views/hr/orgUserRole/org.vue

@ -94,6 +94,7 @@ onMounted(() => {
@ 功能: 选择项目
*/
const pickCont = (val: userOrgRole) => {
console.log("选择项目-------->",val)
if (val.isPick != 1) {
val.isPick = 1;
if (pickLists.value.length > 0) {
@ -119,8 +120,8 @@ const pickCont = (val: userOrgRole) => {
});
}
}
// emits('update:pickList', pickLists)
// console.log("-------->",pickLists.value)
emits('update:pickList', pickLists)
console.log("选择项目-------->",pickLists.value)
emits("updataPickLog", pickLists.value);
};
/**

35
src/views/hr/orgUserRole/orgrole.vue

@ -271,35 +271,36 @@ defineExpose({
<ul>
<li v-for="item in listAry" :key="item.id" :class="item.isPick==1?'active':''">
<div v-if="orgTrue!=1&&item.types!=2" class="mianbaoxueDoy" @click="pickCont(item)">
<i v-if="item.isPick==1" class="fa fa-check-square-o"></i>
<i v-else class="fa fa-square-o"></i>
<svg-icon v-if="item.types==2" icon-class="fenZhu" :size="20" />
<svg-icon v-if="item.types==3" icon-class="tasp" :size="20" />
<el-avatar v-if="item.types==1" shape="square" :size="20" :src="circleUrl" />
<el-text>{{ item.title }}</el-text>
<div v-if="orgTrue!=1&&item.types!=2" class="mianbaoxueDoy">
<i v-if="item.isPick==1" class="fa fa-check-square-o" @click="pickCont(item)"></i>
<i v-else class="fa fa-square-o" @click="pickCont(item)"></i>
<svg-icon v-if="item.types==2" @click="pickCont(item)" icon-class="fenZhu" :size="20" />
<svg-icon v-if="item.types==3" @click="pickCont(item)"icon-class="tasp" :size="20" />
<el-avatar v-if="item.types==1" @click="pickCont(item)" shape="square" :size="20" :src="circleUrl" />
<el-text @click="pickCont(item)">{{ item.title }}</el-text>
<div v-if="item.types==2" class="contentLiLeft">
<svg-icon icon-class="cascader" />
<el-text @click="pickSunCont(item.id)">下级</el-text>
</div>
</div>
<div v-if="orgTrue!=1&&item.types==2" class="mianbaoxueDoy" >
<i class="fa fa-minus-square-o"></i>
<svg-icon v-if="item.types==2" icon-class="fenZhu" :size="20" />
<svg-icon v-if="item.types==3" icon-class="tasp" :size="20" />
<el-avatar v-if="item.types==1" shape="square" :size="20" :src="circleUrl" />
<el-text>{{ item.title }}</el-text>
<i class="fa fa-minus-square-o" @click="pickCont(item)"></i>
<svg-icon v-if="item.types==2" @click="pickCont(item)" icon-class="fenZhu" :size="20" />
<svg-icon v-if="item.types==3" @click="pickCont(item)" icon-class="tasp" :size="20" />
<el-avatar v-if="item.types==1" @click="pickCont(item)" shape="square" :size="20" :src="circleUrl" />
<el-text @click="pickCont(item)">{{ item.title }}</el-text>
<div v-if="item.types==2" class="contentLiLeft">
<svg-icon icon-class="cascader" />
<el-text @click="pickSunCont(item.id)">下级</el-text>
</div>
</div>
<div v-if="orgTrue==1" class="mianbaoxueDoy" >
<i v-if="item.isPick==1" class="fa fa-check-square-o"></i><i v-else class="fa fa-square-o"></i>
<svg-icon v-if="item.types==2" icon-class="fenZhu" :size="20" />
<svg-icon v-if="item.types==3" icon-class="tasp" :size="20" />
<el-avatar v-if="item.types==1" shape="square" :size="20" :src="circleUrl" />
<el-text>{{ item.title }}</el-text>
<i v-if="item.isPick==1" class="fa fa-check-square-o" @click="pickCont(item)"></i>
<i v-else class="fa fa-square-o" @click="pickCont(item)"></i>
<svg-icon v-if="item.types==2" @click="pickCont(item)" icon-class="fenZhu" :size="20" />
<svg-icon v-if="item.types==3" @click="pickCont(item)" icon-class="tasp" :size="20" />
<el-avatar v-if="item.types==1" @click="pickCont(item)" shape="square" :size="20" :src="circleUrl" />
<el-text @click="pickCont(item)">{{ item.title }}</el-text>
<div v-if="item.types==2" class="contentLiLeft">
<svg-icon icon-class="cascader" />
<el-text @click="pickSunCont(item.id)">下级</el-text>

6
src/views/hr/userBox.vue

@ -39,9 +39,9 @@ const taotleLog = ref(0);
const pickListAry = ref<userOrgRole[]>([]); //
const emits = defineEmits(["update:isOpen", "update:types", "pickInfo"]);
const searchQuery = ref<any>("");
const orgUs = ref(null);
const orgUsRole = ref(null);
const rolePage = ref(null);
const orgUs = ref();
const orgUsRole = ref();
const rolePage = ref();
/**
@ 作者: 秦东
@ 时间: 2024-05-23 13:56:28

2
src/views/system/user/index.vue

@ -6,7 +6,7 @@ defineOptions({
name: "User",
inheritAttrs: false,
});
import { UploadFile } from "element-plus";
import { ElTree, UploadFile } from "element-plus";
import {
getUserPage,
getUserForm,

5
src/views/sysworkflow/codepage/index.vue

@ -34,6 +34,7 @@ const editid = ref<string>("");
const formConfigCont = reactive<customerFormConfig>({
formname: "",
formlogo: "",
signCode: ""
});
const formId = ref<string>("");
//
@ -93,7 +94,7 @@ function handleDelete() {
//
function editCustomerFormState(cont: customerFormCont) {
// console.log("",cont)
editIds.push(cont.id);
editIds.push(cont.id.toString());
ElMessageBox.confirm("请问是否真的删除?删除后数据将无法找回!", "温馨提示!", {
confirmButtonText: "删除",
cancelButtonText: "取消",
@ -120,7 +121,7 @@ function handleSelectionChange(selection: any) {
}
//
function setupCustomerForm(cont: customerFormCont) {
editid.value = cont.id;
editid.value = cont.id.toString();
show.value = true;
}
onMounted(() => {

50
src/views/sysworkflow/lowcodepage/appPage/appPageForm/appFlow/simulateTaskFlow.vue

@ -4,10 +4,9 @@
@ 备注: 模拟任务
-->
<script lang="ts" setup>
import { Check, Delete, Edit, Message, Search, Star } from "@element-plus/icons-vue";
import { Search } from "@element-plus/icons-vue";
import {
haveCustomerFormVersion,
generateFlow,
echoTableFormPage,
realTimeUpdateFlow,
} from "@/api/taskapi/management";
@ -18,21 +17,20 @@ import {
fixedValueControl,
timeControl,
timeEquation,
submitButton,
afreshSubmitButton,
} from "@/utils/workflow/const";
import { judgeSubmitCancel, startRunFlow } from "@/api/DesignForm/requestapi";
import { string2json, stringToObj } from "@/utils/DesignForm/form";
import FlowStep from "@/views/taskplatform/taskmanagement/flowStep.vue";
import FlowStep from "@/views/taskplatform/taskmanagement/flowStepNew.vue";
import { orgform, criteriaForPeopleList } from "@/api/hr/org/type";
import {
getOrgFormTree,
getOrgFormUserList,
searchUserCustomerFormList,
} from "@/api/hr/org/index";
import RunFlowStep from "@/views/taskplatform/taskmanagement/runNewFlowStep.vue";
import AiPage from "@/views/sysworkflow/lowcodepage/pageFlow/aiPage.vue";
const props = defineProps({
@ -58,7 +56,12 @@ const props = defineProps({
type: Number,
default: 1,
},
versionid: {
type: String,
default: "",
},
});
const currentAgent=ref<{name:string,uuid:string,model:boolean,dbm:string}>()
const squareUrl = "https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png";
const emits = defineEmits(["update:simuIsTrue"]);
const isShow = computed({
@ -126,7 +129,7 @@ const beforeSubmit = (params: any) => {
@ 时间: 2024-04-09 16:41:20
@ 功能: 提交成功回调
*/
const afterSubmit = (type: string, val?: any) => {
const afterSubmit = (type: string) => {
// console.log("------------------>",type,val)
// console.log("------------------>",flowMap)
if (type === "success") {
@ -154,7 +157,7 @@ const changeKeyVal = (key: any, val: any, type: any, attribute: any) => {
// if(notAsA_BasisForJudgment.indexOf(type) != -1){
// console.log("--type--true->",notAsA_BasisForJudgment.indexOf(type))
// }
let unitsVal = val;
let isUpdateFlowChart = false;
//
if (notAsA_BasisForJudgment.indexOf(type) === -1) {
@ -347,15 +350,7 @@ const changeKeyVal = (key: any, val: any, type: any, attribute: any) => {
});
}
};
/**
@ 作者: 秦东
@ 时间: 2024-04-09 16:50:37
@ 功能: 保存草稿
*/
const saveDraftPage = (type: string, val?: any) => {
if (type === "success") {
}
};
const changeFlowPick = ref<string[]>([])
//
@ -519,11 +514,12 @@ const pickManInfo = (val: any) => {
});
handleClose();
};
const drawTitle = ref("")
</script>
<template>
<div>
<el-drawer v-model="isShow" title="模拟测试流程" direction="rtl" :size="drawBodyWidth">
<template #header="{ close, titleId, titleClass }">
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">模拟测试流程</h4>
<div>
<el-text v-if="pickSimMan.name != ''"
@ -558,11 +554,7 @@ const pickManInfo = (val: any) => {
:after-submit="afterSubmit"
:close-app-submit="closeAppSubmit"
:change-key-val="changeKeyVal"
:anew-submit="anewSubmit"
:save-edit-form-info="saveEditFormInfo"
:send-draft-submit="sendDraftSubmit"
:submit-edit="submitEdit"
@options-value3-get3="optionsValue3Get3"
/>
<FlowStep
v-model:flow-map="flowMap"
@ -595,7 +587,7 @@ const pickManInfo = (val: any) => {
highlight-current
check-strictly
clearable
@node-click="handleNodeClick"
/>
</el-col>
<el-col :span="8">
@ -618,7 +610,7 @@ const pickManInfo = (val: any) => {
height="400px"
element-loading-text="Loading..."
>
<div
<!-- <div
v-for="item in tableData"
class="userListBox active"
@click="pickManInfo(item)"
@ -635,7 +627,7 @@ const pickManInfo = (val: any) => {
"
/>
<el-text class="userText">{{ item.name }}{{ item.number }}</el-text>
</div>
</div> -->
</el-scrollbar>
</el-col>
<el-col :span="24" class="footPage">

24
src/views/sysworkflow/lowcodepage/appPage/appPageForm/unitsPageFrom/attribute.vue

@ -3378,12 +3378,12 @@ watch(() => props.formList, (dataList: any) => {
controlData.value.type == "associatedForms" &&
currentAsfChangeCount.value == 0
) {
asfsExpectCurrent.value = [];
asfs.value = [];
asfsExpectCurrent.values = [];
asfs.values = [];
currentAsfChangeCount.value = 1;
for (let i = 0; i < dataList.length; i++) {
if (dataList[i].type == "associatedForms") {
asfs.value.push(dataList[i]);
asfs.values.push(dataList[i]);
} else if (
dataList[i].type == "card" ||
dataList[i].type == "flex" ||
@ -3392,7 +3392,7 @@ watch(() => props.formList, (dataList: any) => {
) {
dataList[i].list.forEach((element: any) => {
if (element.type == "associatedForms") {
asfs.value.push(element);
asfs.values.push(element);
}
});
} else if (dataList[i].type == "grid") {
@ -3404,7 +3404,7 @@ watch(() => props.formList, (dataList: any) => {
let a = JSON.parse(JSON.stringify(columns[z].list[x]));
//console.log(a)
if (a.type == "associatedForms") {
asfs.value.push(a);
asfs.values.push(a);
}
}
}
@ -3418,14 +3418,14 @@ watch(() => props.formList, (dataList: any) => {
let a = JSON.parse(JSON.stringify(columns[z].list[x]));
//console.log(a)
if (a.type == "associatedForms") {
asfs.value.push(a);
asfs.values.push(a);
} else if (a.type == "flex" || a.type == "table") {
if (a.list.length > 0) {
for (let m = 0; m < a.list.length; m++) {
let q = JSON.parse(JSON.stringify(a.list[m]));
//console.log(q)
if (q.type == "associatedForms") {
asfs.value.push(q);
asfs.values.push(q);
}
}
}
@ -3435,8 +3435,8 @@ watch(() => props.formList, (dataList: any) => {
}
}
}
if (asfs.value.length > 0) {
asfsExpectCurrent.value = asfs.value.filter((item: any) => {
if (asfs.values.length > 0) {
asfsExpectCurrent.values = asfs.values.filter((item: any) => {
return item.name != controlData.value.name;
});
/* if(asfsExpectCurrent.value.length>0){
@ -4119,7 +4119,7 @@ const childRoleRightChanged = () => {
let selectedArr: any[] = [];
let otherMasterSelectedArr: any[] = [];
let otherChildSelectedArr: any[] = [];
asfsExpectCurrent.value.forEach(function (item: any) {
asfsExpectCurrent.values.forEach(function (item: any) {
let master = item.control.fillRoles.master;
let child = item.control.fillRoles.child;
/* console.log(master)
@ -7556,7 +7556,7 @@ const updataBase = (val: any) => {
placeholder="额外添加的表单class类名"
>
<el-option
v-for="item in optionsCss"
v-for="item in $options"
:key="item.value"
:label="item.label"
:value="item.value"
@ -8354,7 +8354,7 @@ const updataBase = (val: any) => {
>
<!-- #F5F7FA #E6F3FE #F0F0F0 #F5F7FA-->
<template
v-for="(, index) in controlData.control.fillRoles.master"
v-for="(_, index) in controlData.control.fillRoles.master"
:key="controlData.control.fillRoles.master[index].id"
>
<AssociatedFormsFillRole

3
src/views/sysworkflow/lowcodepage/newLowCode/appLayoutEdit/appContainerPage.vue

@ -159,6 +159,9 @@ const openAppPageInit = ref(null);
const updataPageInit = () => {
openAppPageInit.value.gainAppFormPageInit();
};
const accessRunApp = () => {
// window.open(props.pickAppInfo.signCodeStr);
}
</script>
<template>
<el-drawer

442
src/views/taskplatform/taskmanagement/flowStepNew.vue

@ -0,0 +1,442 @@
<!--
@ 作者: 秦东
@ 时间: 2023-11-17 16:43:55
@ 备注: 工作流步进图
-->
<script lang='ts' setup>
import { PropType, reactive, ref, toRefs, computed, watch, onMounted } from "vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
import OrgUserPage from "@/views/public/orguser/orguser.vue"
import OrgAllUserPage from "@/views/public/orguser/orgalluser.vue"
import squareUrlOne from "@/assets/images/1.png"
import squareUrlTwo from "@/assets/images/2.png"
interface OperatorItem {
id: string | number;
icon?: string;
iconbase64?: string;
departmentname?: string;
postname?: string;
name?: string;
log?: Array<{
state: number;
cause?: string;
time?: string;
}>;
}
interface FlowItem {
step: number;
type: number;
nodeName?: string;
operator?: OperatorItem[];
customNode?: string;
runscope?: number;
pendpers?: any[];
runtype?: number;
}
const state = reactive({
circleUrl:squareUrlTwo,
squareUrl: squareUrlOne,
sizeList: ['small', '', 'large'] as const,
})
const flowLoading = ref(false)
const { circleUrl, squareUrl, sizeList } = toRefs(state)
const openOrClose = ref(false)
const openclosebox = ref(false)
const props = defineProps({
flowMap: {
type: Array as PropType<FlowItem[]>,
default: () => []
},
nodeKey:{
type:String,
default:""
},
currentProgress:{
type:Number,
default:0
}
,
nextStep:{
type:Number,
default:0
}
})
const ifSendFlow = ref(false)
const presetPersonnel = ref<any>([]); //
const selectedPeople = ref<any>([]); //
const flowMaps = ref<FlowItem[]>();
const emits = defineEmits(["update:flowMap"]);
const flowList = computed({
get: () => props.flowMap,
set: (val: FlowItem[]) => {
emits("update:flowMap", val);
},
});
watch(()=>props.flowMap,(val: FlowItem[])=>{
emits("update:flowMap", val);
})
onMounted(()=>{
})
//
const judgeAddUser = (val: FlowItem):boolean =>{
// console.log("val----->",val)
if(val.customNode == "beginnode"){
val.runscope = val.runscope!=0?val.runscope:1
return true
}
switch(val.runtype){
case 1:
val.runscope = val.runscope!=0?val.runscope:1
return true
break;
case 3:
if(val.customNode == props.nodeKey){
val.runscope = val.runscope!=0?val.runscope:1
return true
}
break;
case 4:
if(val.runscope==1){
return true
}
break;
default:
// if(!val.operator){
// val.runscope = val.runscope!=0?val.runscope:1
// return true
// }else if(val.operator.length < 1){
// val.runscope = val.runscope!=0?val.runscope:1
// return true
// }
if(val.runscope != 0){
return true
}
return false
}
return false
}
let zhiXingStep = 1;
//
const addPeople = (val: FlowItem) =>{
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) =>{
// console.log("P",zhiXingStep,val)
flowList.value.forEach((item: FlowItem) =>{
if(item.step == zhiXingStep){
// console.log("P---1-->",item.step , zhiXingStep)
item.operator = val
}
})
// console.log("P--2--->",flowList)
}
/**
@ 作者: 秦东
@ 时间: 2026-01-29 16:37:56
@ 功能: 判断审批节点样式
*/
const judgeNodeClass = (val:number,stepVal:number):string => {
// console.log("",val,stepVal)
switch(val){
case 1:
if(props.currentProgress == stepVal){
return "in-progress"
}else{
return 'completed'
}
case 2:
return 'pending'
case 3:
// return 'in-progress'
if(props.currentProgress == stepVal){
return "in-progress"
}else{
return 'in-executor'
}
default:
if(props.currentProgress == stepVal){
return "in-progress"
}else{
return 'completed'
}
}
return 'completed'
}
</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..." element-loading-background="rgba(122, 122, 122, 0.8)" :class="ifSendFlow?'flowBody':'flowBodyNofoot'">
<div class="approval-steps">
<div v-for="item in flowList" :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>
</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 - 225px);
}
.flowBodyNofoot{
padding: 10px 15px;
height: calc(100vh - 105px);
}
.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>
Loading…
Cancel
Save