提交 62b97b82 authored 作者: liuluyu's avatar liuluyu

Merge branch 'master' of http://47.97.51.208/root/zrch-risk-39

......@@ -147,6 +147,17 @@ export function getMyTaskFlow(data) {
})
}
// 得到退回的节点
export function getRejectNode(query) {
alert(JSON.stringify(query))
return defHttp.get({
url: '/flowable/form/getRejectTargetNode',
params: query
})
}
<template>
<div class="form-panel">
<a-card class="form-card" :bordered="false">
<template #title>
<div class="form-header">
<span class="form-title">当前待办</span>
<a-tag color="green" v-if="editableNode">可编辑</a-tag>
<span class="form-title">当前待办[{{ editableNode?.name || '无' }}]</span>
<a-button color="blue" v-if="!isEditStatus" @click="handleSetEditStatus">编辑</a-button>
<a-button color="blue" v-if="isEditStatus" @click="handleSetEditStatus">只读</a-button>
</div>
<div class="form-content" v-if="editableNode">
<a-card :title="editableNode.name" :bordered="false" class="current-form-card">
<template #extra>
<a-tag color="processing">待处理</a-tag>
</template>
<div class="form-content" v-if="editableNode">
<component
:is="getComponent(editableNode.formUrl)"
:ref="(el) => setFormRef(el, editableNode.id)"
......@@ -18,11 +18,11 @@
:current-flow-node="editableNode"
@update:form-data="handleFormDataUpdate"
/>
</a-card>
</div>
<div v-else class="empty-form-state">
<a-empty description="未找到可编辑的表单节点" />
</div>
</a-card>
</div>
</template>
......@@ -79,6 +79,8 @@ const modules = import.meta.glob('@/views/**/*.vue')
const formComponentRef = ref<FormComponentInstance | null>(null)
const formData = ref<any>({})
const isEditStatus = ref(false);
// 设置表单组件 ref
function setFormRef(el: any, nodeId: string) {
if (el) {
......@@ -250,6 +252,14 @@ function createErrorComponent(msg: string) {
}
}
// 处理编辑状态切换
function handleSetEditStatus() {
isEditStatus.value = !isEditStatus.value
if (formComponentRef.value&&typeof formComponentRef.value.formDisabled === 'function') {
//formComponentRef.value.formDisabled(isEditStatus.value)
}
}
// 暴露方法给父组件
defineExpose({
getFormData,
......@@ -266,16 +276,40 @@ defineExpose({
display: flex;
flex-direction: column;
overflow: hidden;
background-color: #f5f7fa;
.form-card {
height: 100%;
display: flex;
flex-direction: column;
background-color: #fff;
.form-header {
padding: 16px 24px;
:deep(.ant-card-head) {
padding: 0 24px;
background-color: #e9ecef;
border-bottom: 1px solid #e8eef2;
background-color: #fff;
min-height: auto;
.ant-card-head-wrapper {
height: auto;
}
}
:deep(.ant-card-body) {
flex: 1;
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
}
.form-header {
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
width: 100%;
padding: 16px 0;
.form-title {
font-size: 16px;
......@@ -308,28 +342,6 @@ defineExpose({
}
}
.current-form-card {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border: 1px solid #e8eef2;
:deep(.ant-card-head) {
background-color: #fafbfc;
border-bottom: 1px solid #e8eef2;
padding: 12px 20px;
.ant-card-head-title {
font-size: 15px;
font-weight: 500;
color: #096dd9;
}
}
:deep(.ant-card-body) {
padding: 24px;
}
}
.empty-form-state {
flex: 1;
display: flex;
......
<template>
<div class="flowchart-container">
<div v-show="loading" class="loading-state">
<a-spin tip="加载流程图中..." size="large" />
</div>
<div v-show="!loading" class="bpmn-wrapper">
<div class="bpmn-content">
<bpmn-viewer :flow-data="flowData" :procInsId="procInsId" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { message } from 'ant-design-vue';
import { flowXmlAndNode } from '/@/components/Process/api/definition';
import BpmnViewer from '/@/components/Process/viewer/index.vue';
// Emits
const emit = defineEmits(['loaded']);
// 响应式数据
const loading = ref(false);
const flowData = ref<any>({});
const procInsId = ref('');
const loadData = async (data) => {
procInsId.value = data.procInsId || '';
const deployId = data.deployId || '';
try {
loading.value = true;
const res = await flowXmlAndNode({ procInsId: procInsId.value,deployId: deployId });
loading.value = false;
flowData.value = res;
} catch (error) {
console.error('加载流程图失败:', error);
message.error('加载流程图失败');
throw error;
} finally {
loading.value = false;
}
};
// 重置数据
const resetData = () => {
flowData.value = {};
};
// 暴露方法给父组件
defineExpose({
loadData,
resetData,
});
</script>
<style lang="scss" scoped>
.flowchart-container {
height: 100%;
padding: 24px;
background: #f5f7fa;
overflow: hidden;
}
.bpmn-wrapper {
height: 100%;
.bpmn-content {
height: 100%;
min-height: 500px;
border: 1px solid #e8e8e8;
border-radius: 6px;
overflow: hidden;
background: white;
}
}
.loading-state {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
min-height: 300px;
margin-top: 20px;
}
@media (max-width: 768px) {
.flowchart-container {
padding: 16px;
}
}
</style>
\ No newline at end of file
<template>
<BasicDrawer v-bind="$attrs" title="流程追踪" width="100%"
@register="registerBasicDrawer"
:header-style="{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }">
<div class="drawer-content">
<FlowHistoryChart ref="refFlowHistoryChart" />
<FlowHistoryRecord :procInsId="procInsId" />
</div>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { defineComponent,ref } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import FlowHistoryChart from './FlowHistoryChart.vue';
import FlowHistoryRecord from './FlowHistoryRecord.vue';
const procInsId = ref('');
const dataId = ref('');
const deployId = ref('');
const refFlowHistoryChart = ref();
const callback = (data) => {
refFlowHistoryChart.value.loadData(data);
}
const [registerBasicDrawer, { closeDrawer, setDrawerProps }] = useDrawerInner(callback);
const handleCloseDrawer = () => {
closeDrawer();
}
</script>
\ No newline at end of file
<template>
<div class="record-container">
<div v-if="loading" class="loading-state">
<a-spin tip="加载流转记录中..." />
</div>
<div v-else-if="flowRecordList.length === 0" class="empty-state">
<a-empty description="暂无流转记录" />
</div>
<a-timeline v-else class="flow-timeline">
<a-timeline-item
v-for="(item, index) in flowRecordList"
:key="index"
:color="getTimelineColor(item.finishTime)"
>
<div class="timeline-item">
<div class="timeline-header">
<div class="task-name">
<a-tag :color="getTaskStatusColor(item.finishTime)" class="status-tag">
{{ item.finishTime ? '已完成' : '进行中' }}
</a-tag>
<span class="task-title">{{ item.taskName }}</span>
</div>
<div class="task-time">
<ClockCircleOutlined />
<span>{{ formatDuration(item.duration) }}</span>
</div>
</div>
<div class="timeline-content">
<a-descriptions size="small" :column="1" bordered>
<a-descriptions-item label="办理人" v-if="item.assigneeName">
<UserOutlined class="desc-icon" />
{{ item.assigneeName }}
<a-tag v-if="item.deptName" color="default" size="small">
{{ item.deptName }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="候选办理" v-if="item.candidate">
<TeamOutlined class="desc-icon" />
{{ item.candidate }}
</a-descriptions-item>
<a-descriptions-item label="接收时间">
<CalendarOutlined class="desc-icon" />
{{ formatTime(item.createTime) }}
</a-descriptions-item>
<a-descriptions-item label="处理时间" v-if="item.finishTime">
<CheckCircleOutlined class="desc-icon" />
{{ formatTime(item.finishTime) }}
</a-descriptions-item>
<a-descriptions-item label="处理意见" v-if="item.comment?.comment">
<FormOutlined class="desc-icon" />
<div class="comment-box">
{{ item.comment.comment }}
</div>
</a-descriptions-item>
</a-descriptions>
</div>
</div>
</a-timeline-item>
</a-timeline>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { message } from 'ant-design-vue';
import {
UserOutlined,
CalendarOutlined,
ClockCircleOutlined,
FormOutlined,
TeamOutlined,
CheckCircleOutlined,
} from '@ant-design/icons-vue';
import { flowRecord } from '/@/components/Process/api/finished';
import dayjs from 'dayjs';
// Props
const props = defineProps<{
procInsId: string;
}>();
// 响应式数据
const loading = ref(false);
const flowRecordList = ref<any[]>([]);
// 方法
const formatTime = (time: string) => {
if (!time) return '';
return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
};
const formatDuration = (duration: string) => {
if (!duration) return '';
return duration.replace('PT', '').toLowerCase();
};
const getTimelineColor = (finishTime: string | null) => {
return finishTime ? 'green' : 'blue';
};
const getTaskStatusColor = (finishTime: string | null) => {
return finishTime ? 'success' : 'processing';
};
const loadData = async (data) => {
try {
loading.value = true;
const res = await flowRecord({ procInsId: props.procInsId });
flowRecordList.value = res.flowList || [];
} catch (error) {
console.error('加载流转记录失败:', error);
message.error('加载流转记录失败');
throw error;
} finally {
loading.value = false;
}
};
// 重置数据
const resetData = () => {
flowRecordList.value = [];
};
// 暴露方法给父组件
defineExpose({
loadData,
resetData,
});
</script>
<style lang="scss" scoped>
.record-container {
height: 100%;
padding: 24px;
background: #f5f7fa;
overflow-y: auto;
}
.flow-timeline {
:deep(.ant-timeline-item) {
padding-bottom: 24px;
&:last-child {
padding-bottom: 0;
}
}
}
.timeline-item {
background: white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
.timeline-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
flex-wrap: wrap;
gap: 8px;
.task-name {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
.status-tag {
border-radius: 12px;
font-size: 12px;
padding: 2px 10px;
}
.task-title {
font-weight: 500;
color: #333;
}
}
.task-time {
color: #666;
font-size: 13px;
.anticon {
margin-right: 4px;
}
}
}
.timeline-content {
:deep(.ant-descriptions) {
border-radius: 6px;
overflow: hidden;
.ant-descriptions-item-label {
background: #fafafa;
font-weight: 500;
min-width: 80px;
}
.desc-icon {
margin-right: 8px;
color: #666;
}
}
}
.comment-box {
white-space: pre-wrap;
word-break: break-word;
line-height: 1.6;
color: #333;
}
}
.loading-state,
.empty-state {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
min-height: 300px;
}
@media (max-width: 768px) {
.record-container {
padding: 16px;
}
.timeline-item {
padding: 12px;
}
}
</style>
\ No newline at end of file
<template>
<div class="history-panel" :class="{ 'empty-history': readonlyNodes.length === 0 }">
<div class="history-panel-wrapper">
<a-card class="history-card-container" :bordered="false" :body-style="{ padding: 0, height: '100%', display: 'flex', flexDirection: 'column' }">
<template #title>
<div class="history-header">
<span class="history-title">历史节点</span>
<a-tag color="blue" v-if="readonlyNodes.length > 0">{{ readonlyNodes.length }}个节点</a-tag>
</div>
<div class="history-content" :class="{ 'has-scroll': readonlyNodes.length > 3 }">
</template>
<div class="history-content" :class="{ 'empty-history': readonlyNodes.length === 0, 'has-scroll': readonlyNodes.length > 3 }">
<div v-if="readonlyNodes.length === 0" class="empty-history-state">
<a-empty description="暂无历史节点" :image="simpleImage" />
</div>
......@@ -24,7 +27,7 @@
:title="node.name"
:bordered="false"
size="small"
class="history-card"
class="history-node-card"
:class="{ 'last-card': index === readonlyNodes.length - 1 }"
>
<template #extra>
......@@ -70,6 +73,7 @@
</a-timeline-item>
</a-timeline>
</div>
</a-card>
</div>
</template>
......@@ -314,7 +318,7 @@ watch(expandedPreviewId, async (newId, oldId) => {
</script>
<style scoped lang="scss">
.history-panel {
.history-panel-wrapper {
width: 50%;
background-color: #f5f7fa;
border-right: 1px solid #e8eef2;
......@@ -322,22 +326,35 @@ watch(expandedPreviewId, async (newId, oldId) => {
flex-direction: column;
overflow: hidden;
&.empty-history {
.history-content {
.history-card-container {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: #f5f7fa;
:deep(.ant-card-head) {
padding: 0 20px;
background-color: #e9ecef; // 灰色背景,比整体背景稍深
border-bottom: 1px solid #e8eef2;
min-height: auto;
.ant-card-head-wrapper {
height: auto;
}
}
:deep(.ant-card-body) {
flex: 1;
overflow: hidden;
}
}
.history-header {
padding: 16px 20px;
border-bottom: 1px solid #e8eef2;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
width: 100%;
padding: 16px 0;
.history-title {
font-size: 16px;
......@@ -351,6 +368,12 @@ watch(expandedPreviewId, async (newId, oldId) => {
overflow-y: auto;
padding: 16px 12px;
&.empty-history {
display: flex;
align-items: center;
justify-content: center;
}
&.has-scroll {
padding-right: 8px;
}
......@@ -406,7 +429,7 @@ watch(expandedPreviewId, async (newId, oldId) => {
}
}
.history-card {
.history-node-card {
background-color: #fff;
border-radius: 8px;
margin-bottom: 8px;
......
......@@ -8,55 +8,57 @@
:header-style="{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }"
@close="handleClose"
class="workflow-form-drawer"
style="padding: 0px;"
:body-style="{ padding: '5px' }"
>
<div class="drawer-layout" :class="{ 'three-columns': showApprovalPanel }">
<HistoryPanel
<div class="drawer-container">
<a-row :gutter="10" class="drawer-row" :wrap="false">
<!-- 左侧历史面板 -->
<a-col :width="props.leftPanelWidth">
<HistoryPanel style="width: 100%;height: 100%;"
:readonly-nodes="readonlyNodes"
:data-id="dataId"
:show-history-form-data="props.showHistoryFormData"
/>
<div class="form-wrapper">
<CurrentFormPanel
</a-col>
<!-- 中间表单区域 -->
<a-col :width="props.centerPanelWidth">
<CurrentFormPanel style="width: 100%;height: 100%;"
ref="currentFormPanelRef"
:editable-node="editableNode"
:data-id="dataId"
:external-form-data="externalFormData"
:form-data="props.formData"
:form-bpm="formBpm"
:disabled="showApprovalPanel||false"
:disabled="showApprovalPanel || false"
@update:form-data="handleFormDataUpdate"
@form-mounted="handleFormMounted"
/>
</div>
<div v-if="showApprovalPanel" class="approval-wrapper">
<slot name="approval-panel" :approval-data="approvalData" :node="editableNode">
<ApprovalPanel
</a-col>
<!-- 右侧审核面板 -->
<a-col v-if="showApprovalPanel" :width="props.rightPanelWidth">
<ApprovalPanel style="width: 100%;height: 100%;"
ref="approvalPanelRef"
:title="approvalPanelTitle"
:current-node="editableNode"
:current-user="currentUser"
:reject-nodes="rejectNodes"
:can-select-reject-node="canSelectRejectNode"
:initial-data="initialApprovalData"
:show-summary="showApprovalSummary"
:result-options="approvalResultOptions"
:default-result="defaultApprovalResult"
:comment-placeholder="commentPlaceholder"
:require-comment-on-reject="requireCommentOnReject"
:show-reject-node-select="showRejectNodeSelect"
:show-approval-panel="true"
:deploy-id="props.deployId"
:data-id="props.dataId"
:assignee="props.assignee"
:user-type="props.userType"
@update:approval-data="handleApprovalDataUpdate"
@success="handleApprovalSuccess"
/>
</slot>
</div>
</div>
<template #footer>
<div class="drawer-footer">
</a-col>
</a-row>
</div>
<template #extra>
<a-button @click="openHistoryDrawer()" type="primary">流程追踪</a-button>
<a-button @click="handleClose()" type="primary">关闭抽屉</a-button>
</template>
<FlowHistoryDrawer @register="refFlowHistoryDrawer"/>
</a-drawer>
</template>
......@@ -66,12 +68,14 @@ import { message } from 'ant-design-vue'
import HistoryPanel from './HistoryPanel.vue'
import CurrentFormPanel from './CurrentFormPanel.vue'
import ApprovalPanel from './ApprovalPanel.vue'
import FlowHistoryDrawer from './FlowHistoryDrawer.vue';
import { useDrawer } from '/@/components/Drawer';
const formBpm = ref(true)
const formDisabled = ref(false)
const drawerHistoryVisible = ref(false)
const [refFlowHistoryDrawer, { openDrawer }] = useDrawer();
interface WorkflowNode {
id: string
......@@ -80,6 +84,7 @@ interface WorkflowNode {
formUrl?: string
formListUrl?: string
procDefId?: string
procInsId?: string
dataId?: string
processTime?: string
processor?: string
......@@ -126,10 +131,16 @@ const props = defineProps({
type: Object as () => Record<string, any>,
default: () => ({})
},
procDefId: {
deployId: {
type: String,
default: ''
},
procInsId: {
type: String,
default: ''
},
dataId: {
type: String,
default: ''
......@@ -144,14 +155,6 @@ const props = defineProps({
type: Boolean,
default: false
},
approvalPanelTitle: {
type: String,
default: '审核意见'
},
submitButtonText: {
type: String,
default: '提交'
},
// 审核结果配置
approvalResultOptions: {
......@@ -171,52 +174,27 @@ const props = defineProps({
}
]
},
defaultApprovalResult: {
type: String,
default: 'approved'
},
// 审核意见配置
commentPlaceholder: {
type: String,
default: '请输入审核意见...'
},
requireCommentOnReject: {
type: Boolean,
default: true
},
// 退回节点配置
showRejectNodeSelect: {
type: Boolean,
default: true
},
rejectNodes: {
flowNodes: {
type: Array as () => WorkflowNode[],
default: () => []
},
canSelectRejectNode: {
type: Boolean,
default: true
currentFlowNodeIdx: {
type: Number,
default: 0
},
assignee: { type: String, default: null },
userType: { type: String, default: 'user' },
// 面板宽度配置
leftPanelWidth: { type: String,default: '40%'},
centerPanelWidth: { type: String, default: '40%'},
rightPanelWidth: { type: String,default: '20%'}
// 其他配置
currentUser: {
type: String,
default: ''
},
initialApprovalData: {
type: Object as () => {
result?: string
comment?: string
rejectNode?: string
},
default: () => ({})
},
showApprovalSummary: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['update:visible', 'submit', 'close', 'form-data-update'])
......@@ -232,7 +210,6 @@ const approvalPanelRef = ref<InstanceType<typeof ApprovalPanel> | null>(null)
const drawerTitle = computed(() => props.title)
const drawerWidth = computed(() => props.width)
// 只读节点:索引小于 currentNodeIndex 的节点
const readonlyNodes = computed(() => {
if (!props.workflowNodes || props.workflowNodes.length === 0) {
......@@ -266,11 +243,18 @@ function handleApprovalDataUpdate(data: any) {
approvalData.value = data
}
// 处理表单组件挂载
function handleFormMounted({ nodeId, instance }: { nodeId: string; instance: any }) {
console.log('表单组件已挂载 - 节点:', nodeId)
}
// 处理审核成功
function handleApprovalSuccess(dataId: string) {
if (dataId === props.dataId) {
message.success('审核成功')
handleClose()
}
}
// 提交处理
async function handleSubmit() {
if (!editableNode.value) {
......@@ -283,7 +267,6 @@ async function handleSubmit() {
return
}
// 如果显示审核面板,验证审核数据
if (props.showApprovalPanel && approvalPanelRef.value) {
const result = await approvalPanelRef.value.validate()
if (!result.valid) {
......@@ -295,17 +278,12 @@ async function handleSubmit() {
submitLoading.value = true
try {
// 1. 表单验证
const isValid = await currentFormPanelRef.value.validateForm()
if (!isValid) {
message.error('请完善表单信息')
return
}
// 2. 获取表单数据
const submitData = await currentFormPanelRef.value.getFormData()
// 3. 构建提交数据
const finalSubmitData: any = {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
......@@ -346,13 +324,21 @@ function resetFormData() {
if (currentFormPanelRef.value) {
currentFormPanelRef.value.resetFormData()
}
if (approvalPanelRef.value) {
approvalPanelRef.value.resetForm()
if (approvalPanelRef.value&&props.showApprovalPanel) {
(approvalPanelRef.value as any).resetForm()
}
currentFormData.value = {}
approvalData.value = {}
}
const openHistoryDrawer = () => {
openDrawer(true,{
procInsId: props.procInsId,
dataId: props.dataId,
deployId: props.deployId,
} );
};
// 监听抽屉打开
watch(() => props.visible, async (newVal) => {
if (newVal) {
......@@ -413,8 +399,7 @@ defineExpose({
:deep(.ant-drawer-header) {
background-color: #f5f7fa;
border-bottom: 1px solid #e8eef2;
padding: 16px 24px;
padding: 10px 10px;
.ant-drawer-title {
font-size: 16px;
font-weight: 500;
......@@ -431,66 +416,53 @@ defineExpose({
}
:deep(.ant-drawer-body) {
padding: 0;
padding: 5px;
height: 100%;
overflow: hidden;
}
:deep(.ant-drawer-footer) {
padding: 12px 24px;
padding: 5px 10px;
border-top: 1px solid #e8eef2;
background-color: red;
background-color: #fff;
}
}
.drawer-layout {
display: flex;
.drawer-container {
height: 100%;
overflow: hidden;
background: #eef5f680;
padding: 1px;
margin: 0;
}
// 三栏布局
&.three-columns {
.history-panel {
width: 30%;
}
.form-wrapper {
width: 40%;
}
.approval-wrapper {
width: 30%;
}
}
.drawer-row {
height: 100%;
flex-wrap: nowrap;
background: #0c37a418;
}
// 两栏布局
&:not(.three-columns) {
.history-panel {
width: 40%;
}
.drawer-col {
height: 100%;
.form-wrapper {
flex: 1;
}
&:not(:last-child) {
margin-right: 16px;
}
}
.form-wrapper {
overflow: hidden;
display: flex;
flex-direction: column;
}
.panel-card {
height: 100%;
.approval-wrapper {
overflow: hidden;
:deep(.ant-card-body) {
height: 100%;
display: flex;
flex-direction: column;
}
}
.drawer-footer {
display: flex;
justify-content: flex-end;
height: 2px;
background: #018ffb;
gap: 12px;
}
</style>
\ No newline at end of file
......@@ -206,7 +206,7 @@ if (key === '3') {
deployId: taskForm.deployId,
}).then((res) => {
flowData.value = res
console.log("xml",JSON.stringify(res))
console.log("xml88888888888888888",JSON.stringify(res))
})
}
}
......
......@@ -92,6 +92,7 @@ const handleClick = (key: string) => {
flowXmlAndNode({ deployId: deployId.value }).then(res => {
flowData.value = res
})
}
}
......
......@@ -36,6 +36,7 @@
<StProblemCheckArchiveModal @register="registerModal" @success="handleSuccess"></StProblemCheckArchiveModal>
<!-- 审批记录 -->
<BpmPictureModal @register="registerBpmModal" />
</div>
</template>
......@@ -56,9 +57,13 @@
const [registerBpmModal, { openModal: bpmPicModal }] = useModal();
const queryParam = reactive<any>({});
//注册model
const refFlowHistory = ref();
const deployId = ref('');
const dataId = ref('');
// 审批记录抽屉
const drawerHistoryVisible = ref(false);
const [registerModal, {openModal}] = useModal();
//注册table数据
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '问题归档',
......@@ -102,31 +107,20 @@
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
// 高级查询配置
const superQueryConfig = reactive(superQuerySchema);
/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
......@@ -134,9 +128,6 @@
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
......@@ -144,39 +135,40 @@
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function handleShowHistory(record) {
drawerHistoryVisible.value = true;
deployId.value = record.deployId;
dataId.value = record.id;
refFlowHistory.value.iniData(record);
}
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'problem:st_problem_check_archive:edit'
},
{
label: '查看流程',
onClick: handleShowHistory.bind(null, record),
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
let dropDownAction = [
{
......@@ -191,47 +183,11 @@
},
auth: 'problem:st_problem_check_archive:delete'
},
{
label: '审批进度',
onClick: handlePreviewPic.bind(null, record),
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
];
if(record.bpmStatus == '1'){
dropDownAction.push({
label: '发起流程',
popConfirm: {
title: '确认提交流程吗?',
confirm: handleProcess.bind(null, record),
placement: 'topLeft',
}
})
}
return dropDownAction;
}
/**
* 提交流程
*/
async function handleProcess(record) {
let params = {
flowCode: 'dev_st_problem_check_archive_001',
id: record.id,
formUrl: 'problem/components/StProblemCheckArchiveForm',
formUrlMobile: ''
}
await startProcess(params);
handleSuccess();
}
/**
* 审批进度
*/
async function handlePreviewPic(record) {
bpmPicModal(true, {
flowCode: 'dev_st_problem_check_archive_001',
dataId: record.id,
});
}
......
......@@ -25,7 +25,7 @@
import { list,problemArchive} from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback'])
const emit = defineEmits(['callback','openMultiForm'])
const props = defineProps({
beforeFlowNode: {
......
......@@ -187,10 +187,9 @@ export const formSchema: FormSchema[] = [
component: 'JEditor',
required: true,
componentProps: {
rows: 4,
showCount: true,
maxlength: 30000,
height: 300,
maxlength: 512,
height: 220,
},
},
{
......
......@@ -52,7 +52,7 @@
}
})
const emit = defineEmits(['callback','startWorkFlow','sendWorkFlow'])
const emit = defineEmits(['callback','startWorkFlow','sendWorkFlow','openMultiForm'])
//注册model
const [registerModal, { openModal }] = useModal();
......@@ -182,8 +182,21 @@
})
}
async function handleStartUpdate(flowData) {
let record = {
procInsId: flowData.procInsId,
id:flowData.dataId
}
await saveOrUpdate(record,true).then(res => {
handleSuccess(null);
})
}
defineExpose({
handleUpdate,
handleStartUpdate
})
</script>
......
......@@ -25,7 +25,7 @@
import { list, saveOrUpdate } from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback','sendWorkFlow'])
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm'])
const props = defineProps({
beforeFlowNode: {
......@@ -64,7 +64,7 @@
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handleExecuteApproval(record: Recordable) {
emit("callback",record)
emit("openMultiForm",record)
}
function handleSuccess() {
......
......@@ -25,7 +25,7 @@
import { list, saveOrUpdate } from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback','sendWorkFlow'])
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm'])
const props = defineProps({
beforeFlowNode: {
......@@ -64,7 +64,7 @@
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handleExecute(record: Recordable) {
emit("callback",record)
emit("openMultiForm",record)
}
function handleSuccess() {
......
......@@ -40,7 +40,7 @@
}
})
const emit = defineEmits(['callback','sendWorkFlow'])
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm'])
//注册model
const [registerExecuteModal, { openModal: openExecuteModal }] = useModal();
......@@ -64,7 +64,7 @@
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handlePlanApproval(record: Recordable) {
emit("callback",record)
emit("openMultiForm",record)
}
function handleSuccess() {
......
......@@ -25,7 +25,7 @@
import { list,saveOrUpdate} from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback','sendWorkFlow'])
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm'])
const props = defineProps({
beforeFlowNode: {
......@@ -62,7 +62,7 @@
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handlePlan(record: Recordable) {
emit("callback",record)
emit("openMultiForm",record)
}
async function handleSendNext(record: Recordable) {
......@@ -87,7 +87,6 @@
}
async function handleUpdate(dataId) {
//alert(dataId)
let record = {
bmpNodeId: props.nextFlowNode.id,
deployId: props.nextFlowNode.deployId,
......
......@@ -14,6 +14,7 @@
import { propTypes } from '/@/utils/propTypes';
import { getExecuteFormSchema } from '../StProblemCheck.data';
import { saveOrUpdate, } from '../StProblemCheck.api';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
name: 'StProblemCheckForm',
......@@ -27,7 +28,8 @@
disabled: propTypes.bool.def(false),
},
setup(props) {
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
const { createMessage } = useMessage();
const [registerForm, { validate, setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
schemas: getExecuteFormSchema(props.formData),
showActionButtonGroup: false,
......@@ -53,7 +55,16 @@
async function submitForm() {
let data = getFieldsValue();
let params = Object.assign({}, formData, data);
await saveOrUpdate(params, true);
const result = await saveOrUpdate(params, true);
if (result && result.id) {
await initFormData(result.id);
createMessage.success('保存成功!');
//emit('save-success', result);
} else {
await initFormData(props.dataId);
createMessage.success('保存成功!');
}
}
initFormData(props.dataId);
......
......@@ -28,7 +28,7 @@
},
setup(props) {
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
labelWidth: 100,
schemas: getBpmFormSchema(props.formData),
showActionButtonGroup: false,
baseColProps: { span: 24 },
......
<template>
<div style="min-height: 400px">
<div style="height: 100%; display: flex; flex-direction: column">
<a-card :style="{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'auto' }" :bordered="false" :body-style="{ flex: 1, display: 'flex', flexDirection: 'column' }">
<!-- 表单内容区域 -->
<div style="flex: 1; overflow: auto">
<BasicForm @register="registerForm" />
<div style="width: 100%; text-align: center" v-if="!formDisabled">
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button>
</div>
<!-- 底部按钮区域 -->
<div style="text-align: center;" v-if="!formDisabled">
<a-space>
<a-button block @click="saveForm" pre-icon="ant-design:save" type="primary" ghost>保 存</a-button>
<a-button block @click="saveAndSendForm" pre-icon="ant-design:send" type="primary" ghost>保存并发送</a-button>
</a-space>
</div>
</a-card>
</div>
</template>
<script lang="ts">
import { BasicForm, useForm } from '/@/components/Form/index';
import { computed, defineComponent } from 'vue';
import { computed, defineComponent, ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
import { getPlanFormSchema } from '../StProblemCheck.data';
import { saveOrUpdate, } from '../StProblemCheck.api';
import { saveOrUpdate } from '../StProblemCheck.api';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
name: 'StProblemCheckForm',
......@@ -26,9 +37,11 @@
dataId: propTypes.string.def(''),
disabled: propTypes.bool.def(false),
},
setup(props) {
emits: ['save-success', 'send-success'],
setup(props, { emit }) {
const { createMessage } = useMessage();
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
labelWidth: 100,
schemas: getPlanFormSchema(props.formData),
showActionButtonGroup: false,
baseColProps: { span: 24 },
......@@ -41,32 +54,94 @@
return true;
});
let formData = {};
let currentFormData = ref({});
const queryById = '/problem/stProblemCheck/queryById';
async function initFormData(did) {
let params = { id: props.dataId||did };
let params = { id: props.dataId || did };
const data = await defHttp.get({ url: queryById, params });
formData = { ...data };
await setFieldsValue(formData);
currentFormData.value = { ...data };
await setFieldsValue(currentFormData.value);
await setProps({ disabled: formDisabled.value });
}
async function submitForm() {
// 保存按钮
async function saveForm() {
try {
let data = getFieldsValue();
let params = Object.assign({}, formData, data);
await saveOrUpdate(params, true);
alert(1)
let params = Object.assign({}, currentFormData.value, data);
const result = await saveOrUpdate(params, true);
// 保存成功后更新表单
if (result && result.id) {
await initFormData(result.id);
createMessage.success('保存成功!');
emit('save-success', result);
} else {
await initFormData(props.dataId);
createMessage.success('保存成功!');
}
} catch (error) {
createMessage.error('保存失败,请重试!');
console.error('保存失败:', error);
}
}
// 保存并发送按钮
async function saveAndSendForm() {
try {
let data = getFieldsValue();
let params = Object.assign({}, currentFormData.value, data);
const result = await saveOrUpdate(params, true);
// 保存成功后更新表单
if (result && result.id) {
await initFormData(result.id);
createMessage.success('保存成功!');
// TODO: 调用发送接口
// await sendData(result.id);
createMessage.success('发送成功!');
emit('send-success', result);
} else {
await initFormData(props.dataId);
createMessage.success('保存成功!');
// TODO: 调用发送接口
createMessage.success('发送成功!');
}
} catch (error) {
createMessage.error('保存失败,请重试!');
console.error('保存失败:', error);
}
}
initFormData(props.dataId);
const setFormDisabledStatus = (isEdit: boolean) => {
if (props.disabled === false) {
return false;
}
return true;
};
return {
setFormDisabledStatus,
registerForm,
formDisabled,
submitForm,
saveForm,
saveAndSendForm,
initFormData
};
},
});
</script>
<style scoped>
/* 确保父容器高度100% */
:deep(.ant-card-body) {
flex: 1;
display: flex;
flex-direction: column;
padding: 24px;
}
</style>
\ No newline at end of file
......@@ -16,6 +16,7 @@ import org.jeecg.modules.flowable.apithird.business.entity.FlowForm;
import org.jeecg.modules.flowable.apithird.business.entity.SysDeployForm;
import org.jeecg.modules.flowable.apithird.business.service.IFlowFormService;
import org.jeecg.modules.flowable.apithird.business.service.ISysDeployFormService;
import org.jeecg.modules.flowable.service.IFlowRejectService;
import org.jeecg.modules.stm.my.entity.MyTaskFlow;
import org.jeecg.modules.stm.my.service.IMyTaskFlowService;
import org.jeecg.modules.stm.utils.UserUtil;
......@@ -23,25 +24,23 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
import org.flowable.task.api.Task;
@Slf4j
@RestController
@RequestMapping("/flowable/form")
public class FlowFormController {
@Autowired
private IFlowFormService flowFormService;
@Autowired
private ISysDeployFormService sysDeployFormService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private IMyTaskFlowService myTaskFlowService;
@Autowired
private IFlowRejectService flowRejectService;
@RequestMapping("/list")
public Result<IPage<FlowForm>> getFormList(
......@@ -237,6 +236,9 @@ public class FlowFormController {
nodeInfo.put("isApprove",isApprove);
String formKey = ((UserTask) element).getFormKey();
nodeInfo.put("assignee", userTask.getAssignee());
nodeInfo.put("candidateGroups", userTask.getCandidateGroups());
if (formKey != null) {
try {
Long formId = Long.parseLong(formKey);
......@@ -394,4 +396,106 @@ public class FlowFormController {
return extensionProperties;
}
/**
* 根据当前任务节点,判断审批不通过时走向哪个节点
* @param taskId 当前任务节点ID(如 "userTask1")
* @param deployId 部署ID
* @param isApproved 是否通过(true=通过,false=不通过)
* @return 目标节点信息
*/
@GetMapping("/appReject")
public void appReject(
@RequestParam String taskId,
@RequestParam String deployId,
@RequestParam String currentTaskId,
@RequestParam String rejectReason,
@RequestParam String targetTaskId,
@RequestParam boolean isApproved) {
Task task = flowRejectService.rejectToTarget(currentTaskId,targetTaskId,rejectReason);
}
@GetMapping("/getRejectTargetNode")
public Result<Map<String, Object>> getRejectTargetNode(
@RequestParam String taskId,
@RequestParam String deployId,
@RequestParam boolean isApproved) {
try {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployId).singleResult();
if (processDefinition == null) {
return Result.error("未找到流程定义");
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
if (bpmnModel == null) {
return Result.error("无法获取BPMN模型");
}
// 找到当前节点
FlowElement currentElement = findFlowElementById(bpmnModel, taskId);
if (!(currentElement instanceof UserTask)) {
return Result.error("当前节点不是用户任务");
}
UserTask userTask = (UserTask) currentElement;
// 遍历所有 outgoing 连线,找到条件匹配的连线
for (SequenceFlow sequenceFlow : userTask.getOutgoingFlows()) {
String conditionExpression = sequenceFlow.getConditionExpression();
if (conditionExpression != null && !conditionExpression.trim().isEmpty()) {
// 根据条件表达式判断是“通过”还是“不通过”
boolean matches = evaluateCondition(conditionExpression, isApproved);
if (matches) {
// 找到目标节点
FlowElement targetElement = findFlowElementById(bpmnModel, sequenceFlow.getTargetRef());
Map<String, Object> result = new HashMap<>();
result.put("targetNodeId", targetElement.getId());
result.put("targetNodeName", targetElement.getName());
result.put("targetNodeType", targetElement.getClass().getSimpleName());
result.put("conditionExpression", conditionExpression);
return Result.OK(result);
}
}
}
return Result.error("未找到匹配的连线条件");
} catch (Exception e) {
log.error("解析拒绝目标节点失败", e);
return Result.error("解析失败: " + e.getMessage());
}
}
private FlowElement findFlowElementById(BpmnModel bpmnModel, String elementId) {
for (org.flowable.bpmn.model.Process process : bpmnModel.getProcesses()) {
FlowElement element = process.getFlowElement(elementId);
if (element != null) {
return element;
}
}
return null;
}
private boolean evaluateCondition(String conditionExpression, boolean isApproved) {
// 条件表达式示例:${approved == true} 或 ${approved == false}
// 这里简化处理:根据字符串内容判断
if (conditionExpression == null) return false;
String expr = conditionExpression.trim();
if (isApproved) {
// 通过条件:通常包含 true, 1, 'yes', 'pass' 等
return expr.matches(".*\\$\\{.*(true|1|'yes'|'pass').*}.*");
} else {
// 不通过条件:通常包含 false, 0, 'no', 'reject' 等
return expr.matches(".*\\$\\{.*(false|0|'no'|'reject').*}.*");
}
}
}
package org.jeecg.modules.flowable.service;
import org.flowable.task.api.Task;
public interface IFlowRejectService {
/**
* 驳回任务到指定历史节点
*
* @param currentTaskId 当前任务ID
* @param targetTaskId 目标历史任务ID(要驳回到的节点)
* @param rejectReason 驳回原因
* @return 新创建的目标节点任务
* @throws RuntimeException 当目标任务不存在、当前任务不存在或驳回到当前节点时抛出
*/
Task rejectToTarget(String currentTaskId, String targetTaskId, String rejectReason);
}
\ No newline at end of file
package org.jeecg.modules.flowable.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.*;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
......@@ -19,22 +15,16 @@ import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.flowable.apithird.business.entity.FlowForm;
import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness;
import org.jeecg.modules.flowable.apithird.business.service.ISysDeployFormService;
import org.jeecg.modules.flowable.apithird.business.service.impl.FlowMyBusinessServiceImpl;
import org.jeecg.modules.flowable.apithird.entity.ActStatus;
import org.jeecg.modules.flowable.apithird.entity.SysUser;
import org.jeecg.modules.flowable.apithird.service.FlowCallBackServiceI;
import org.jeecg.modules.flowable.apithird.service.IFlowThirdService;
import org.jeecg.modules.flowable.common.constant.ProcessConstants;
import org.jeecg.modules.flowable.common.enums.FlowComment;
import org.jeecg.modules.flowable.domain.dto.FlowNextDto;
import org.jeecg.modules.flowable.domain.dto.FlowProcDefDto;
import org.jeecg.modules.flowable.factory.FlowServiceFactory;
import org.jeecg.modules.flowable.service.IFlowDefinitionService;
import org.jeecg.modules.stm.my.service.IMyTaskService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -44,8 +34,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import org.jeecg.modules.stm.my.entity.MyTask;
/**
......
package org.jeecg.modules.flowable.service.impl;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.jeecg.modules.flowable.factory.FlowServiceFactory;
import org.jeecg.modules.flowable.service.IFlowRejectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class FlowRejectServiceImpl extends FlowServiceFactory implements IFlowRejectService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 驳回任务到指定历史节点
* @param currentTaskId 当前任务ID
* @param targetTaskId 目标历史任务ID(要驳回到的节点)
* @param rejectReason 驳回原因
*/
public Task rejectToTarget(String currentTaskId, String targetTaskId, String rejectReason) {
HistoricTaskInstance targetTask = historyService
.createHistoricTaskInstanceQuery()
.taskId(targetTaskId)
.singleResult();
if (targetTask == null) {
throw new RuntimeException("驳回目的任务不存在");
}
// 2. 获取当前任务
Task currentTask = taskService.createTaskQuery()
.taskId(currentTaskId)
.singleResult();
if (currentTask == null) {
throw new RuntimeException("当前任务不存在");
}
// 3. 不能驳回到当前节点
if (currentTask.getTaskDefinitionKey().equals(targetTask.getTaskDefinitionKey())) {
throw new RuntimeException("不能驳回到当前节点");
}
String processInstanceId = currentTask.getProcessInstanceId();
// 4. 添加驳回意见(可选)
taskService.addComment(currentTaskId, processInstanceId, "reject", rejectReason);
// 5. 核心API:回退节点
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(processInstanceId)
.moveActivityIdTo(currentTask.getTaskDefinitionKey(), targetTask.getTaskDefinitionKey())
.changeState();
// 6. 清理脏数据(中间节点的历史任务和变量)
clearDirtyData(processInstanceId, currentTask, targetTask);
// 7. 返回新创建的目标节点任务
return taskService.createTaskQuery()
.processInstanceId(processInstanceId)
.taskDefinitionKey(targetTask.getTaskDefinitionKey())
.singleResult();
}
/**
* 清理驳回节点之间的脏数据
*/
private void clearDirtyData(String processInstanceId, Task sourceTask, HistoricTaskInstance targetTask) {
// 获取历史任务列表(按开始时间降序)
List<HistoricTaskInstance> historyTasks = historyService
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.orderByHistoricTaskInstanceStartTime().desc()
.list();
// 获取需要删除的任务节点(从目标节点到当前节点之间的所有节点)
List<HistoricTaskInstance> betweenNodes = getBetweenNodes(historyTasks,
sourceTask.getId(), targetTask.getId());
// 收集需要清理的流程变量Key
List<String> variableKeys = new ArrayList<>();
for (HistoricTaskInstance task : betweenNodes) {
// 根据实际业务,清理相关变量
variableKeys.add("approveResult");
variableKeys.add("approveComment");
// 删除历史任务记录
historyService.deleteHistoricTaskInstance(task.getId());
}
// 删除流程变量
if (!variableKeys.isEmpty()) {
runtimeService.removeVariables(processInstanceId, variableKeys);
}
}
/**
* 获取两个节点之间的所有历史任务
*/
private List<HistoricTaskInstance> getBetweenNodes(List<HistoricTaskInstance> historyTasks,
String sourceTaskId, String targetTaskId) {
List<HistoricTaskInstance> betweenNodes = new ArrayList<>();
boolean startRecord = false;
for (HistoricTaskInstance task : historyTasks) {
if (task.getId().equals(sourceTaskId)) {
startRecord = true;
}
if (startRecord) {
betweenNodes.add(task);
}
if (task.getId().equals(targetTaskId)) {
break;
}
}
return betweenNodes;
}
}
\ No newline at end of file
......@@ -134,12 +134,33 @@ public class StProblemCheck implements Serializable {
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern="yyyy-MM-dd")
private java.util.Date planEndDate;
/**整改开始时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern="yyyy-MM-dd")
private java.util.Date execStartDate;
/**整改结束时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern="yyyy-MM-dd")
private java.util.Date execEndDate;
/**整蛊执行人ID*/
private java.lang.String execUserId;
/**执行部门*/
private java.lang.String execDepCode;
/**整改落实情况*/
private java.lang.String execRemark;
/**发现人*/
private java.lang.String findUser;
/**流程状态*/
private java.lang.String bpmStatus;
/**部署ID*/
private java.lang.String deployId;
/**流程实例ID*/
private java.lang.String procInsId;
/**风险等级*/
private java.lang.Integer riskLevel;
/**流程节点ID*/
......
......@@ -21,16 +21,16 @@ management:
flowable:
database-schema-update: false
cmmn:
enabled: false
app:
enabled: false
content:
enabled: false
dmn:
enabled: false
form:
enabled: false
# cmmn:
# enabled: false
# app:
# enabled: false
# content:
# enabled: false
# dmn:
# enabled: false
# form:
# enabled: false
spring:
# main:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论