提交 89bf837f authored 作者: kxjia's avatar kxjia

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

<template>
<a-drawer
:title="drawerTitle"
:visible="visible"
:visible="visibleRef"
:width="drawerWidth"
:closable="true"
:mask-closable="maskClosable"
......@@ -11,11 +11,7 @@
<!-- 选项卡模式 -->
<a-tabs v-model:activeKey="activeTabKey" type="card" class="form-tabs">
<!-- 只读选项卡:索引小于 currentNodeIndex 的节点 -->
<a-tab-pane
v-for="node in readonlyNodes"
:key="node.id"
:tab="node.name"
>
<a-tab-pane v-for="node in readonlyNodes" :key="node.id" :tab="node.name">
<template #tab>
<span>
{{ node.name }}
......@@ -25,32 +21,29 @@
<div class="tab-content readonly-content">
<component
:is="getComponent(node.formUrl || node.formListUrl)"
:disabled="true"
:readonly="true"
:disabled="innerDisabled"
:readonly="innerReadonly"
:form-data="getFormData(node.id)"
:current-flow-node="node"
/>
</div>
</a-tab-pane>
<!-- 可编辑选项卡:索引等于 currentNodeIndex 的节点 -->
<a-tab-pane
v-if="editableNode"
:key="editableNode.id"
:tab="editableNode.name"
>
<!-- 当前节点选项卡:根据disabled/readonly状态决定是否可编辑 -->
<a-tab-pane v-if="editableNode" :key="editableNode.id" :tab="editableNode.name">
<template #tab>
<span>
{{ editableNode.name }}
<a-tag color="green" size="small" class="tab-tag">可编辑</a-tag>
<a-tag v-if="innerDisabled || innerReadonly" color="blue" size="small" class="tab-tag">只读</a-tag>
<a-tag v-else color="green" size="small" class="tab-tag">可编辑</a-tag>
</span>
</template>
<div class="tab-content editable-content">
<component
:is="getComponent(editableNode.formUrl || editableNode.formListUrl)"
:ref="setEditableFormRef"
:disabled="false"
:readonly="false"
:disabled="innerDisabled"
:readonly="innerReadonly"
:form-data="currentFormData"
:current-flow-node="editableNode"
@update:form-data="handleFormDataUpdate"
......@@ -64,579 +57,693 @@
<a-empty description="未找到有效表单节点" />
</div>
<template #footer>
<!-- <template #footer>
<div class="drawer-footer">
<a-button @click="handleClose">取消</a-button>
<a-button type="primary" :loading="submitLoading" @click="handleSubmit">
提交
</a-button>
<a-button type="primary" :loading="submitLoading" @click="handleSubmit"> 提交 </a-button>
</div>
</template>
</template> -->
</a-drawer>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, defineAsyncComponent, h, watch, ComponentPublicInstance } from 'vue'
import { message } from 'ant-design-vue'
interface WorkflowNode {
id: string
name: string
formUrl?: string
formListUrl?: string
procDefId?: string
[key: string]: any
}
// 表单组件实例类型
interface FormComponentInstance extends ComponentPublicInstance {
validate?: () => Promise<any>
getFormData?: () => any
submitForm?: () => Promise<any> // 添加 submitForm 方法类型
formData?: any
[key: string]: any
}
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '表单处理'
},
width: {
type: [Number, String],
default: 720
},
maskClosable: {
type: Boolean,
default: false
},
// 当前节点索引(从0开始),这个索引对应的节点是可编辑的
currentNodeIndex: {
type: Number,
required: true,
default: 2
},
workflowNodes: {
type: Array as () => WorkflowNode[],
required: true,
default: () => []
},
externalFormData: {
type: Object as () => Record<string, any>,
default: () => ({})
},
procDefId: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:visible', 'submit', 'close', 'form-data-update'])
// 组件缓存
const componentCache = new Map()
const modules = import.meta.glob('@/views/**/*.vue')
// 状态
const loading = ref(false)
const submitLoading = ref(false)
// 使用数组来存储表单组件实例
const editableFormRefs = ref<FormComponentInstance[]>([])
const currentFormData = ref<any>({})
const formDataMap = ref<Record<string, any>>({})
const activeTabKey = ref<string>('')
// 计算属性
const drawerTitle = computed(() => props.title)
const drawerWidth = computed(() => props.width)
// 只读节点:索引小于 currentNodeIndex 的节点
const readonlyNodes = computed(() => {
if (!props.workflowNodes || props.workflowNodes.length === 0) {
return []
}
const idx = props.currentNodeIndex
console.log('只读节点 - 当前索引:', idx)
console.log('只读节点 - 所有节点:', props.workflowNodes.map((n, i) => `${i}:${n.name}`))
if (idx <= 0) return []
const nodes = props.workflowNodes.slice(0, idx)
console.log('只读节点:', nodes.map(n => n.name))
return nodes
})
// 可编辑节点:索引等于 currentNodeIndex 的节点
const editableNode = computed(() => {
if (!props.workflowNodes || props.workflowNodes.length === 0) {
return null
}
const idx = props.currentNodeIndex
if (idx < 0 || idx >= props.workflowNodes.length) {
console.warn('可编辑节点索引无效:', idx)
return null
}
const node = props.workflowNodes[idx]
console.log('可编辑节点:', node?.name, '索引:', idx)
return node
})
// 设置表单组件 ref 的方法
function setEditableFormRef(el: any) {
if (el) {
// 清除旧的引用,只保留当前激活的可编辑表单
editableFormRefs.value = [el]
console.log('表单组件已挂载,组件方法:', Object.keys(el))
console.log('是否有 submitForm 方法:', typeof el.submitForm === 'function')
}
}
// 获取当前可编辑的表单组件实例
function getCurrentEditableForm(): FormComponentInstance | null {
return editableFormRefs.value[0] || null
}
// 获取表单数据
function getFormData(nodeId: string): any {
const data = formDataMap.value[nodeId] || {}
console.log('获取表单数据 - 节点:', nodeId, '数据:', data)
return data
}
// 获取或加载组件
function getComponent(url: string) {
if (!url) {
console.warn('URL为空,返回空组件')
return createEmptyComponent()
}
import { ref, computed, onMounted, defineAsyncComponent, h, watch, ComponentPublicInstance, getCurrentInstance } from 'vue';
import { message } from 'ant-design-vue';
import { useModalInner } from '/@/components/Modal';
interface WorkflowNode {
id: string;
name: string;
formUrl?: string;
formListUrl?: string;
procDefId?: string;
[key: string]: any;
}
// 表单组件实例类型
interface FormComponentInstance extends ComponentPublicInstance {
validate?: () => Promise<any>;
getFormData?: () => any;
submitForm?: () => Promise<any>; // 添加 submitForm 方法类型
formData?: any;
[key: string]: any;
}
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '表单处理',
},
width: {
type: [Number, String],
default: 900,
},
maskClosable: {
type: Boolean,
default: false,
},
// 当前节点索引(从0开始),这个索引对应的节点是可编辑的
currentNodeIndex: {
type: Number,
required: true,
default: 2,
},
workflowNodes: {
type: Array as () => WorkflowNode[],
required: true,
default: () => [],
},
externalFormData: {
type: Object as () => Record<string, any>,
default: () => ({}),
},
procDefId: {
type: String,
default: '',
},
// 表单是否禁用
disabled: {
type: Boolean,
default: false,
},
// 表单是否只读
readonly: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['update:visible', 'submit', 'close', 'form-data-update', 'register']);
if (componentCache.has(url)) {
return componentCache.get(url)
// expose minimal modal methods so useModal can register this drawer
const instance = getCurrentInstance();
const modalMethods = {
setModalProps: (props: Record<string, any>) => {
if (Reflect.has(props, 'open')) {
visibleRef.value = !!props.open;
emit('update:visible', !!props.open);
}
if (Reflect.has(props, 'visible')) {
visibleRef.value = !!props.visible;
emit('update:visible', !!props.visible);
}
if (Reflect.has(props, 'loading')) {
submitLoading.value = !!props.loading;
}
},
// allow parent to read current form data programmatically
getFormData: async () => {
try {
const data = await getFormDataFromComponent();
return data;
} catch (e) {
console.error('getFormData error:', e);
return null;
}
},
// will be set by useModal.register
emitVisible: undefined,
redoModalHeight: () => {},
};
// internal editable inputs provided via openModal data
const innerWorkflowNodes = ref<any[]>(props.workflowNodes || []);
const innerCurrentNodeIndex = ref<number>(props.currentNodeIndex || 0);
const innerExternalFormData = ref<Record<string, any>>(props.externalFormData || {});
const innerTitle = ref<string>(props.title || '表单处理');
const innerProcDefId = ref<string>(props.procDefId || '');
const innerDisabled = ref<boolean>(props.disabled || false);
const innerReadonly = ref<boolean>(props.readonly || false);
// useModalInner receives data written by useModal.openModal
const [registerInner, { setModalProps: innerSetModalProps }] = useModalInner(async (data: any) => {
if (!data) return;
if (data.workflowNodes) innerWorkflowNodes.value = data.workflowNodes;
if (data.currentNodeIndex !== undefined) innerCurrentNodeIndex.value = Number(data.currentNodeIndex);
if (data.externalFormData) innerExternalFormData.value = data.externalFormData;
if (data.procDefId) innerProcDefId.value = data.procDefId;
if (data.title) innerTitle.value = data.title;
if (data.disabled !== undefined) innerDisabled.value = !!data.disabled;
if (data.readonly !== undefined) innerReadonly.value = !!data.readonly;
// ensure drawer opens
visibleRef.value = true;
// reset form data for new nodes/data
resetFormData();
preloadComponents();
});
// 组件缓存
const componentCache = new Map();
const modules = import.meta.glob('@/views/**/*.vue');
// 状态
const loading = ref(false);
const submitLoading = ref(false);
// internal visible state so parent doesn't need v-model binding
const visibleRef = ref(false);
// 使用数组来存储表单组件实例
const editableFormRefs = ref<FormComponentInstance[]>([]);
const currentFormData = ref<any>({});
const formDataMap = ref<Record<string, any>>({});
const activeTabKey = ref<string>('');
// 计算属性
const drawerTitle = computed(() => innerTitle.value || props.title);
const drawerWidth = computed(() => props.width);
// 只读节点:根据disabled/readonly状态决定
const readonlyNodes = computed(() => {
if (!innerWorkflowNodes.value || innerWorkflowNodes.value.length === 0) {
return [];
}
// 如果设置了disabled或readonly参数,所有节点都视为只读
if (innerDisabled.value || innerReadonly.value) {
console.log('所有节点都设置为只读,因为disabled:', innerDisabled.value, '或readonly:', innerReadonly.value);
return innerWorkflowNodes.value;
}
const idx = innerCurrentNodeIndex.value;
console.log('只读节点 - 当前索引:', idx);
console.log(
'只读节点 - 所有节点:',
innerWorkflowNodes.value.map((n, i) => `${i}:${n.name}`)
);
if (idx <= 0) return [];
const nodes = innerWorkflowNodes.value.slice(0, idx);
console.log(
'只读节点:',
nodes.map((n: any) => n.name)
);
return nodes;
});
// 可编辑节点:根据disabled/readonly状态决定
const editableNode = computed(() => {
// 如果设置了disabled或readonly参数,不显示可编辑节点
if (innerDisabled.value || innerReadonly.value) {
console.log('不显示可编辑节点,因为disabled:', innerDisabled.value, '或readonly:', innerReadonly.value);
return null;
}
if (!innerWorkflowNodes.value || innerWorkflowNodes.value.length === 0) {
return null;
}
const idx = innerCurrentNodeIndex.value;
if (idx < 0 || idx >= innerWorkflowNodes.value.length) {
console.warn('可编辑节点索引无效:', idx);
return null;
}
const node = innerWorkflowNodes.value[idx];
console.log('可编辑节点:', node?.name, '索引:', idx);
return node;
});
// 设置表单组件 ref 的方法
function setEditableFormRef(el: any) {
if (el) {
// 清除旧的引用,只保留当前激活的可编辑表单
editableFormRefs.value = [el];
console.log('表单组件已挂载,组件方法:', Object.keys(el));
console.log('是否有 submitForm 方法:', typeof el.submitForm === 'function');
}
}
let componentPath = ''
if (url.includes('/views')) {
componentPath = `/src${url}`
} else {
componentPath = `/src/views${url}`
// 获取当前可编辑的表单组件实例
function getCurrentEditableForm(): FormComponentInstance | null {
return editableFormRefs.value[0] || null;
}
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) {
componentPath += '.vue'
// 获取表单数据
function getFormData(nodeId: string): any {
const data = formDataMap.value[nodeId] || {};
console.log('获取表单数据 - 节点:', nodeId, '数据:', data);
return data;
}
console.log('加载组件路径:', componentPath)
// 获取或加载组件
function getComponent(url: string) {
if (!url) {
console.warn('URL为空,返回空组件');
return createEmptyComponent();
}
const loader = modules[componentPath]
if (componentCache.has(url)) {
return componentCache.get(url);
}
if (!loader) {
console.error('未找到组件:', componentPath)
const ErrorComponent = createErrorComponent(`组件未找到: ${componentPath}`)
componentCache.set(url, ErrorComponent)
return ErrorComponent
}
let componentPath = '';
if (url.includes('/views')) {
componentPath = `/src${url}`;
} else {
componentPath = `/src/views${url}`;
}
const AsyncComponent = defineAsyncComponent({
loader: () => loader() as Promise<{ default: any }>,
loadingComponent: {
render: () => h('div', { style: 'text-align: center; padding: 20px;' }, '加载中...')
},
errorComponent: {
render: () => h('div', { style: 'color: red; padding: 20px;' }, '组件加载失败')
},
delay: 200,
timeout: 3000
})
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) {
componentPath += '.vue';
}
componentCache.set(url, AsyncComponent)
return AsyncComponent
}
console.log('加载组件路径:', componentPath);
function createEmptyComponent() {
return {
render: () => h('div', { style: 'color: #999; padding: 20px; text-align: center;' }, '该节点未配置表单')
}
}
const loader = modules[componentPath];
function createErrorComponent(msg: string) {
return {
render: () => h('div', { style: 'color: red; padding: 20px;' }, msg)
}
}
// 处理表单数据更新
function handleFormDataUpdate(data: any) {
currentFormData.value = { ...currentFormData.value, ...data }
emit('form-data-update', currentFormData.value)
}
/**
* 从表单组件获取数据
* 优先调用组件的 getFormData 方法,如果没有则返回 formData 属性或 currentFormData
*/
async function getFormDataFromComponent(): Promise<any> {
const formComponent = getCurrentEditableForm()
if (!formComponent) {
console.warn('未找到表单组件实例')
return currentFormData.value
}
if (!loader) {
console.error('未找到组件:', componentPath);
const ErrorComponent = createErrorComponent(`组件未找到: ${componentPath}`);
componentCache.set(url, ErrorComponent);
return ErrorComponent;
}
console.log('当前表单组件实例:', formComponent)
console.log('组件方法列表:', Object.keys(formComponent))
const AsyncComponent = defineAsyncComponent({
loader: () => loader() as Promise<{ default: any }>,
loadingComponent: {
render: () => h('div', { style: 'text-align: center; padding: 20px;' }, '加载中...'),
},
errorComponent: {
render: () => h('div', { style: 'color: red; padding: 20px;' }, '组件加载失败'),
},
delay: 200,
timeout: 3000,
});
// 方式1:调用组件的 getFormData 方法
if (typeof formComponent.getFormData === 'function') {
try {
const data = await formComponent.getFormData()
console.log('通过 getFormData 方法获取的数据:', data)
return data
} catch (error) {
console.error('调用 getFormData 失败:', error)
}
componentCache.set(url, AsyncComponent);
return AsyncComponent;
}
// 方式2:获取组件的 formData 属性
if (formComponent.formData !== undefined) {
console.log('通过 formData 属性获取的数据:', formComponent.formData)
return formComponent.formData
function createEmptyComponent() {
return {
render: () => h('div', { style: 'color: #999; padding: 20px; text-align: center;' }, '该节点未配置表单'),
};
}
// 方式3:如果组件有内部表单数据,尝试获取
if (formComponent.getValues && typeof formComponent.getValues === 'function') {
try {
const data = await formComponent.getValues()
console.log('通过 getValues 方法获取的数据:', data)
return data
} catch (error) {
console.error('调用 getValues 失败:', error)
}
function createErrorComponent(msg: string) {
return {
render: () => h('div', { style: 'color: red; padding: 20px;' }, msg),
};
}
// 方式4:返回本地维护的 currentFormData
console.log('使用本地维护的 currentFormData:', currentFormData.value)
return currentFormData.value
}
/**
* 验证表单数据
*/
async function validateForm(): Promise<boolean> {
const formComponent = getCurrentEditableForm()
if (!formComponent) {
return true
// 处理表单数据更新
function handleFormDataUpdate(data: any) {
currentFormData.value = { ...currentFormData.value, ...data };
emit('form-data-update', currentFormData.value);
}
// 方式1:调用组件的 validate 方法
if (typeof formComponent.validate === 'function') {
try {
await formComponent.validate()
return true
} catch (error) {
console.error('表单验证失败:', error)
return false
/**
* 从表单组件获取数据
* 优先调用组件的 getFormData 方法,如果没有则返回 formData 属性或 currentFormData
*/
async function getFormDataFromComponent(): Promise<any> {
const formComponent = getCurrentEditableForm();
if (!formComponent) {
console.warn('未找到表单组件实例');
return currentFormData.value;
}
}
// 方式2:如果组件有 vee-validate 或其他验证库的实例
if (formComponent.v$ && typeof formComponent.v$.$validate === 'function') {
try {
const isValid = await formComponent.v$.$validate()
return isValid
} catch (error) {
console.error('验证失败:', error)
return false
console.log('当前表单组件实例:', formComponent);
console.log('组件方法列表:', Object.keys(formComponent));
// 方式1:调用组件的 getFormData 方法
if (typeof formComponent.getFormData === 'function') {
try {
const data = await formComponent.getFormData();
console.log('通过 getFormData 方法获取的数据:', data);
return data;
} catch (error) {
console.error('调用 getFormData 失败:', error);
}
}
}
// 如果没有验证方法,默认通过
return true
}
// 方式2:获取组件的 formData 属性
if (formComponent.formData !== undefined) {
console.log('通过 formData 属性获取的数据:', formComponent.formData);
return formComponent.formData;
}
// 提交处理
async function handleSubmit() {
if (!editableNode.value) {
message.warning('没有可编辑的表单')
return
// 方式3:如果组件有内部表单数据,尝试获取
if (formComponent.getValues && typeof formComponent.getValues === 'function') {
try {
const data = await formComponent.getValues();
console.log('通过 getValues 方法获取的数据:', data);
return data;
} catch (error) {
console.error('调用 getValues 失败:', error);
}
}
// 方式4:返回本地维护的 currentFormData
console.log('使用本地维护的 currentFormData:', currentFormData.value);
return currentFormData.value;
}
submitLoading.value = true
try {
const formComponent = getCurrentEditableForm()
// 🔥 优先调用子组件的 submitForm 方法
if (formComponent && typeof formComponent.submitForm === 'function') {
console.log('调用子组件的 submitForm 方法')
// 调用子组件的 submitForm 方法,并等待返回结果
const result = await formComponent.submitForm()
// 如果子组件的 submitForm 返回了数据,则使用返回的数据
if (result !== undefined) {
console.log('submitForm 返回的数据:', result)
// 触发提交事件
emit('submit', {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
formData: result,
procDefId: props.procDefId,
formComponent: formComponent
})
message.success('提交成功')
handleClose()
return
/**
* 验证表单数据
*/
async function validateForm(): Promise<boolean> {
const formComponent = getCurrentEditableForm();
if (!formComponent) {
return true;
}
// 方式1:调用组件的 validate 方法
if (typeof formComponent.validate === 'function') {
try {
await formComponent.validate();
return true;
} catch (error) {
console.error('表单验证失败:', error);
return false;
}
}
// 如果没有 submitForm 方法或 submitForm 没有返回数据,则使用原来的逻辑
console.log('使用默认提交逻辑')
// 1. 先进行表单验证
const isValid = await validateForm()
if (!isValid) {
message.error('请完善表单信息')
return
// 方式2:如果组件有 vee-validate 或其他验证库的实例
if (formComponent.v$ && typeof formComponent.v$.$validate === 'function') {
try {
const isValid = await formComponent.v$.$validate();
return isValid;
} catch (error) {
console.error('验证失败:', error);
return false;
}
}
// 2. 获取表单数据
const submitData = await getFormDataFromComponent()
console.log('最终提交数据:', {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
formData: submitData,
procDefId: props.procDefId
})
// 3. 触发提交事件
emit('submit', {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
formData: submitData,
procDefId: props.procDefId,
formComponent: formComponent
})
message.success('提交成功')
// 提交成功后关闭抽屉
handleClose()
} catch (error: any) {
console.error('提交失败:', error)
message.error(error?.message || '提交失败,请重试')
} finally {
submitLoading.value = false
// 如果没有验证方法,默认通过
return true;
}
}
// 关闭抽屉
function handleClose() {
emit('update:visible', false)
emit('close')
// 关闭后清空表单引用
editableFormRefs.value = []
}
// 重置数据
function resetFormData() {
currentFormData.value = {}
const newFormDataMap: Record<string, any> = {}
readonlyNodes.value.forEach(node => {
newFormDataMap[node.id] = props.externalFormData[node.id] || {}
})
formDataMap.value = newFormDataMap
if (editableNode.value && props.externalFormData[editableNode.value.id]) {
currentFormData.value = { ...props.externalFormData[editableNode.value.id] }
// 提交处理
async function handleSubmit() {
if (!editableNode.value) {
message.warning('没有可编辑的表单');
return;
}
submitLoading.value = true;
try {
const formComponent = getCurrentEditableForm();
// 🔥 优先调用子组件的 submitForm 方法
if (formComponent && typeof formComponent.submitForm === 'function') {
console.log('调用子组件的 submitForm 方法');
// 调用子组件的 submitForm 方法,并等待返回结果
const result = await formComponent.submitForm();
// 如果子组件的 submitForm 返回了数据,则使用返回的数据
if (result !== undefined) {
console.log('submitForm 返回的数据:', result);
// 触发提交事件
emit('submit', {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
formData: result,
procDefId: props.procDefId,
formComponent: formComponent,
});
message.success('提交成功');
handleClose();
return;
}
}
// 如果没有 submitForm 方法或 submitForm 没有返回数据,则使用原来的逻辑
console.log('使用默认提交逻辑');
// 1. 先进行表单验证
const isValid = await validateForm();
if (!isValid) {
message.error('请完善表单信息');
return;
}
// 2. 获取表单数据
const submitData = await getFormDataFromComponent();
console.log('最终提交数据:', {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
formData: submitData,
procDefId: props.procDefId,
});
// 3. 触发提交事件
emit('submit', {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
formData: submitData,
procDefId: props.procDefId,
formComponent: formComponent,
});
message.success('提交成功');
// 提交成功后关闭抽屉
handleClose();
} catch (error: any) {
console.error('提交失败:', error);
message.error(error?.message || '提交失败,请重试');
} finally {
submitLoading.value = false;
}
}
// 设置默认激活的选项卡为可编辑的选项卡
if (editableNode.value) {
activeTabKey.value = editableNode.value.id
console.log('设置激活选项卡为可编辑节点:', editableNode.value.name)
} else if (readonlyNodes.value.length > 0) {
activeTabKey.value = readonlyNodes.value[0].id
console.log('设置激活选项卡为第一个只读节点:', readonlyNodes.value[0].name)
// 关闭抽屉
function handleClose() {
visibleRef.value = false;
emit('update:visible', false);
emit('close');
// 关闭后清空表单引用
editableFormRefs.value = [];
}
}
// 预加载组件
function preloadComponents() {
props.workflowNodes.forEach(node => {
const url = node.formUrl || node.formListUrl
if (url) {
getComponent(url)
// 重置数据
function resetFormData() {
currentFormData.value = {};
const newFormDataMap: Record<string, any> = {};
readonlyNodes.value.forEach((node) => {
newFormDataMap[node.id] = innerExternalFormData.value[node.id] || {};
});
formDataMap.value = newFormDataMap;
if (editableNode.value && innerExternalFormData.value[editableNode.value.id]) {
currentFormData.value = { ...innerExternalFormData.value[editableNode.value.id] };
}
// 设置默认激活的选项卡为可编辑的选项卡
if (editableNode.value) {
activeTabKey.value = editableNode.value.id;
console.log('设置激活选项卡为可编辑节点:', editableNode.value.name);
} else if (readonlyNodes.value.length > 0) {
activeTabKey.value = readonlyNodes.value[0].id;
console.log('设置激活选项卡为第一个只读节点:', readonlyNodes.value[0].name);
}
})
}
// 监听抽屉打开
watch(() => props.visible, (newVal) => {
if (newVal) {
console.log('抽屉打开,currentNodeIndex:', props.currentNodeIndex)
console.log('所有节点:', props.workflowNodes)
resetFormData()
preloadComponents()
// 清空之前的表单引用
editableFormRefs.value = []
}
}, { immediate: true })
// 监听外部数据变化
watch(() => props.externalFormData, (newData) => {
if (newData && Object.keys(newData).length > 0) {
console.log('外部数据变化:', newData)
const newFormDataMap = { ...formDataMap.value }
readonlyNodes.value.forEach(node => {
if (newData[node.id]) {
newFormDataMap[node.id] = newData[node.id]
// 预加载组件
function preloadComponents() {
innerWorkflowNodes.value.forEach((node) => {
const url = node.formUrl || node.formListUrl;
if (url) {
getComponent(url);
}
})
formDataMap.value = newFormDataMap
if (editableNode.value && newData[editableNode.value.id]) {
currentFormData.value = newData[editableNode.value.id]
}
});
}
}, { deep: true })
onMounted(() => {
console.log('组件挂载,workflowNodes:', props.workflowNodes)
console.log('currentNodeIndex:', props.currentNodeIndex)
resetFormData()
preloadComponents()
})
defineExpose({
resetFormData,
getFormData: () => currentFormData.value,
getCurrentFormData: getFormDataFromComponent,
validate: validateForm,
submit: handleSubmit
})
// 监听外部 visible prop 同步到内部 visibleRef
watch(
() => props.visible,
(newVal) => {
visibleRef.value = !!newVal;
},
{ immediate: true }
);
// 监听内部抽屉打开
watch(
() => visibleRef.value,
(newVal) => {
if (newVal) {
console.log('抽屉打开,currentNodeIndex:', innerCurrentNodeIndex.value);
console.log('所有节点:', innerWorkflowNodes.value);
resetFormData();
preloadComponents();
// 清空之前的表单引用
editableFormRefs.value = [];
}
},
{ immediate: true }
);
// 监听外部数据变化
watch(
() => innerExternalFormData.value,
(newData) => {
if (newData && Object.keys(newData).length > 0) {
console.log('外部数据变化:', newData);
const newFormDataMap = { ...formDataMap.value };
readonlyNodes.value.forEach((node) => {
if (newData[node.id]) {
newFormDataMap[node.id] = newData[node.id];
}
});
formDataMap.value = newFormDataMap;
if (editableNode.value && newData[editableNode.value.id]) {
currentFormData.value = newData[editableNode.value.id];
}
}
},
{ deep: true }
);
onMounted(() => {
console.log('组件挂载,workflowNodes:', props.workflowNodes);
console.log('currentNodeIndex:', props.currentNodeIndex);
resetFormData();
preloadComponents();
// register modal methods for useModal() via useModalInner.register
if (instance) {
try {
registerInner(modalMethods, instance.uid);
} catch (e) {
// fallback to emit if registerInner not available
emit('register', modalMethods, instance.uid);
}
}
});
defineExpose({
resetFormData,
getFormData: () => currentFormData.value,
getCurrentFormData: getFormDataFromComponent,
validate: validateForm,
submit: handleSubmit,
});
</script>
<style scoped lang="scss">
.workflow-form-drawer {
width: 100%;
height: 100%;
overflow-y: auto;
.form-tabs {
:deep(.ant-tabs-nav) {
margin-bottom: 0;
background-color: #fafbfc;
padding: 0 16px;
border-bottom: 1px solid #e8eef2;
}
.workflow-form-drawer {
width: 100%;
height: 100%;
overflow-y: auto;
.form-tabs {
:deep(.ant-tabs-nav) {
margin-bottom: 0;
background-color: #fafbfc;
padding: 0 16px;
border-bottom: 1px solid #e8eef2;
}
:deep(.ant-tabs-tab) {
padding: 12px 20px;
font-weight: 500;
transition: all 0.3s;
:deep(.ant-tabs-tab) {
padding: 12px 20px;
font-weight: 500;
transition: all 0.3s;
&:hover {
color: #1890ff;
&:hover {
color: #1890ff;
}
}
}
:deep(.ant-tabs-tab-active) {
.ant-tabs-tab-btn {
color: #1890ff;
:deep(.ant-tabs-tab-active) {
.ant-tabs-tab-btn {
color: #1890ff;
}
}
}
:deep(.ant-tabs-ink-bar) {
background: #1890ff;
:deep(.ant-tabs-ink-bar) {
background: #1890ff;
}
}
}
.tab-content {
padding: 24px;
min-height: 400px;
background-color: #fff;
&.readonly-content {
background-color: #f5f5f5;
:deep(input),
:deep(textarea),
:deep(.ant-input),
:deep(.ant-select-selector),
:deep(.ant-picker) {
background-color: #f5f5f5 !important;
cursor: not-allowed !important;
color: #666 !important;
.tab-content {
padding: 24px;
min-height: 400px;
background-color: #fff;
&.readonly-content {
background-color: #f5f5f5;
:deep(input),
:deep(textarea),
:deep(.ant-input),
:deep(.ant-select-selector),
:deep(.ant-picker) {
background-color: #f5f5f5 !important;
cursor: not-allowed !important;
color: #666 !important;
}
:deep(.ant-input-affix-wrapper) {
background-color: #f5f5f5 !important;
}
}
:deep(.ant-input-affix-wrapper) {
background-color: #f5f5f5 !important;
&.editable-content {
background-color: #fff;
}
}
&.editable-content {
background-color: #fff;
.tab-tag {
margin-left: 8px;
font-size: 12px;
transform: scale(0.9);
display: inline-block;
}
}
.tab-tag {
margin-left: 8px;
font-size: 12px;
transform: scale(0.9);
display: inline-block;
}
.empty-state {
padding: 60px 0;
text-align: center;
}
.empty-state {
padding: 60px 0;
text-align: center;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
&::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
&:hover {
background: #a8a8a8;
}
}
}
&::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
.drawer-footer {
text-align: right;
:deep(.ant-btn) {
margin-left: 8px;
&:hover {
background: #a8a8a8;
&:first-child {
margin-left: 0;
}
}
}
}
.drawer-footer {
text-align: right;
:deep(.ant-btn) {
margin-left: 8px;
&:first-child {
margin-left: 0;
}
:deep(.ant-drawer-body) {
padding: 16px;
background-color: #f5f7fa;
}
:deep(.ant-drawer-footer) {
padding: 12px 16px;
border-top: 1px solid #e8eef2;
}
}
:deep(.ant-drawer-body) {
padding: 16px;
background-color: #f5f7fa;
}
:deep(.ant-drawer-footer) {
padding: 12px 16px;
border-top: 1px solid #e8eef2;
}
</style>
\ No newline at end of file
</style>
......@@ -2,21 +2,21 @@
<div class="app-container">
<a-card class="box-card">
<template #title>
<span><FileTextOutlined /> 待办任务</span>
<!-- <span><FileTextOutlined /> 待办任务</span> -->
<div style="float: right">
<a-tag style="margin-left: 10px">发起人:{{ startUser }}</a-tag>
<a-tag>任务节点:{{ taskName }}</a-tag>
</div>
</template>
<a-tabs v-model:activeKey="activeName" @tabClick="handleClick">
<a-tabs v-model:activeKey="activeName" @tab-click="handleClick">
<!--表单信息-->
<a-tab-pane key="1" tab="主表单信息">
<div v-show="formUrl" class="iframe-container">
<iFrame :src="formUrl" class="responsive-iframe"></iFrame>
</div>
<div v-show="!formUrl" style="margin:10px;width:100%">
<FlowInnerForm ref="refInnerForm"></FlowInnerForm>
<div v-show="!formUrl" style="margin: 10px; width: 100%">
<FlowInnerForm ref="refInnerForm"></FlowInnerForm>
</div>
</a-tab-pane>
......@@ -24,37 +24,19 @@
<a-tab-pane key="2" tab="流转记录">
<div class="block">
<a-steps direction="vertical" size="small">
<a-step
v-for="(item, index) in flowRecordList"
:key="index"
:status="getStepStatus(item.finishTime)"
:title="item.taskName"
>
<a-step v-for="(item, index) in flowRecordList" :key="index" :status="getStepStatus(item.finishTime)" :title="item.taskName">
<template #description>
<a-card :body-style="{ padding: '0px', marginTop: '0px' }">
<a-descriptions
class="margin-top"
:column="1"
size="small"
bordered
>
<a-descriptions-item
v-if="item.assigneeName"
label="办理人"
>
<a-descriptions class="margin-top" :column="1" size="small" bordered>
<a-descriptions-item v-if="item.assigneeName" label="办理人">
<template #label>
<UserOutlined />
办理人
</template>
{{ item.assigneeName }}
<a-tag color="gray" size="small">{{
item.deptName
}}</a-tag>
<a-tag color="gray" size="small">{{ item.deptName }}</a-tag>
</a-descriptions-item>
<a-descriptions-item
v-if="item.candidate"
label="候选办理"
>
<a-descriptions-item v-if="item.candidate" label="候选办理">
<template #label>
<UserOutlined />
候选办理
......@@ -68,30 +50,21 @@
</template>
{{ item.createTime }}
</a-descriptions-item>
<a-descriptions-item
v-if="item.finishTime"
label="处理时间"
>
<a-descriptions-item v-if="item.finishTime" label="处理时间">
<template #label>
<CalendarOutlined />
处理时间
</template>
{{ item.finishTime }}
</a-descriptions-item>
<a-descriptions-item
v-if="item.duration"
label="耗时"
>
<a-descriptions-item v-if="item.duration" label="耗时">
<template #label>
<ClockCircleOutlined />
耗时
</template>
{{ item.duration }}
</a-descriptions-item>
<a-descriptions-item
v-if="item.comment"
label="处理意见"
>
<a-descriptions-item v-if="item.comment" label="处理意见">
<template #label>
<FormOutlined />
处理意见
......@@ -115,182 +88,163 @@
</template>
<script lang="ts" setup>
import { ref, reactive,nextTick } from 'vue'
import {
FileTextOutlined,
UserOutlined,
CalendarOutlined,
ClockCircleOutlined,
FormOutlined,
} from '@ant-design/icons-vue'
import { flowRecord } from "/@/components/Process/api/finished"
import { flowXmlAndNode } from "/@/components/Process/api/definition"
import { findFlowFormVal} from "/@/components/Process/api/todo"
import { flowFormData } from "/@/components/Process/api/process"
import BpmnViewer from '/@/components/Process/viewer/index.vue'
import FlowInnerForm from '/@/views/flowable/task/components/FlowInnerForm.vue'
interface FlowRecordItem {
taskName: string
finishTime: string | null
assigneeName: string
deptName: string
candidate: string
createTime: string
duration: string
comment: {
comment: string
import { ref, reactive, nextTick } from 'vue';
import { FileTextOutlined, UserOutlined, CalendarOutlined, ClockCircleOutlined, FormOutlined } from '@ant-design/icons-vue';
import { flowRecord } from '/@/components/Process/api/finished';
import { flowXmlAndNode } from '/@/components/Process/api/definition';
import { findFlowFormVal } from '/@/components/Process/api/todo';
import { flowFormData } from '/@/components/Process/api/process';
import BpmnViewer from '/@/components/Process/viewer/index.vue';
import FlowInnerForm from '/@/views/flowable/task/components/FlowInnerForm.vue';
interface FlowRecordItem {
taskName: string;
finishTime: string | null;
assigneeName: string;
deptName: string;
candidate: string;
createTime: string;
duration: string;
comment: {
comment: string;
};
}
}
interface TaskForm {
returnTaskShow: boolean
delegateTaskShow: boolean
defaultTaskShow: boolean
comment: string
procInsId: string
instanceId: string
deployId: string
taskId: string
procDefId: string
targetKey: string
variables: Record<string, any>
executionId?: string
}
const flowData = ref({})
const activeName = ref('1')
const formUrl = ref();
const refInnerForm = ref();
// 遮罩层
const loading = ref(true)
const flowRecordList = ref<FlowRecordItem[]>([])
const taskForm = reactive<TaskForm>({
returnTaskShow: false,
delegateTaskShow: false,
defaultTaskShow: true,
comment: '',
procInsId: '',
instanceId: '',
deployId: '',
taskId: '',
procDefId: '',
targetKey: '',
variables: {},
})
interface TaskForm {
returnTaskShow: boolean;
delegateTaskShow: boolean;
defaultTaskShow: boolean;
comment: string;
procInsId: string;
instanceId: string;
deployId: string;
taskId: string;
procDefId: string;
targetKey: string;
variables: Record<string, any>;
executionId?: string;
}
const taskName = ref<string | null>(null) // 任务节点
const startUser = ref<string | null>(null) // 发起人信息
const handleClick = (key: string) => {
if (key === '3') {
const flowData = ref({});
const activeName = ref('1');
const formUrl = ref();
const refInnerForm = ref();
// 遮罩层
const flowRecordList = ref<FlowRecordItem[]>([]);
const taskForm = reactive<TaskForm>({
returnTaskShow: false,
delegateTaskShow: false,
defaultTaskShow: true,
comment: '',
procInsId: '',
instanceId: '',
deployId: '',
taskId: '',
procDefId: '',
targetKey: '',
variables: {},
});
const taskName = ref<string | null>(null); // 任务节点
const startUser = ref<string | null>(null); // 发起人信息
const handleClick = (key: string) => {
if (key === '3') {
flowXmlAndNode({
procInsId: taskForm.procInsId,
deployId: taskForm.deployId,
}).then((res) => {
flowData.value = res
console.log("xml",JSON.stringify(res))
flowData.value = res;
console.log('xml', JSON.stringify(res));
});
}
};
// 修改 setColor 方法为 getStepStatus
const getStepStatus = (finishTime: string | null) => {
if (finishTime) {
return 'finish'; // 已完成
} else {
return 'wait'; // 等待中
}
};
const setFlowRecordList = async (procInsId: string, deployId: string) => {
const params = { procInsId, deployId };
await flowRecord(params)
.then((res) => {
console.log('flowList', res);
flowRecordList.value = res.flowList;
})
}
}
// 修改 setColor 方法为 getStepStatus
const getStepStatus = (finishTime: string | null) => {
if (finishTime) {
return 'finish' // 已完成
} else {
return 'wait' // 等待中
}
}
const setFlowRecordList = async (procInsId: string, deployId: string) => {
const params = { procInsId, deployId }
await flowRecord(params)
.then((res) => {
console.log("flowList",res)
flowRecordList.value = res.flowList
})
.catch(() => {
//goBack()
})
}
const iniData = async (data) => {
taskName.value = data.taskName as string
startUser.value = data.startUserName as string
taskForm.deployId = data.deployId as string
taskForm.taskId = data.taskId as string
taskForm.procInsId = data.procInsId as string
taskForm.executionId = data.executionId as string
taskForm.instanceId = data.procInsId as string
await flowFormData({deployId: taskForm.deployId }).then(resData => {
.catch(() => {
//goBack()
});
};
const iniData = async (data) => {
taskName.value = data.taskName as string;
startUser.value = data.startUserName as string;
taskForm.deployId = data.deployId as string;
taskForm.taskId = data.taskId as string;
taskForm.procInsId = data.procInsId as string;
taskForm.executionId = data.executionId as string;
taskForm.instanceId = data.procInsId as string;
await flowFormData({ deployId: taskForm.deployId }).then((resData) => {
nextTick(() => {
if(resData.formUrl) {
formUrl.value = resData.formUrl
if (resData.formUrl) {
formUrl.value = resData.formUrl;
} else {
formUrl.value = ""
refInnerForm.value.iniData(resData)
formUrl.value = '';
refInnerForm.value.iniData(resData);
}
})
})
});
});
if(taskForm.taskId) {
await findFlowFormVal({taskId: taskForm.taskId }).then((resValues) => {
if (taskForm.taskId) {
await findFlowFormVal({ taskId: taskForm.taskId }).then((resValues) => {
nextTick(() => {
refInnerForm.value.setFormData(resValues)
})
})
refInnerForm.value.setFormData(resValues);
});
});
}
setFlowRecordList(taskForm.procInsId, taskForm.deployId)
}
defineExpose({
iniData,
})
setFlowRecordList(taskForm.procInsId, taskForm.deployId);
};
defineExpose({
iniData,
});
</script>
<style lang="scss" scoped>
.clearfix:before,
.clearfix:after {
display: table;
content: '';
}
.clearfix:after {
clear: both;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.app-container {
height: calc(100vh - 150px);
margin: 0px;
padding: 5px;
}
.app-container {
height: calc(100vh - 150px);
margin: 0px;
padding: 5px;
}
.my-label {
background: #E1F3D8;
}
.my-label {
background: #e1f3d8;
}
.iframe-container {
height: calc(100vh - 200px);
width: 100%;
}
.responsive-iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
\ No newline at end of file
.iframe-container {
height: calc(100vh - 200px);
width: 100%;
}
.responsive-iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
......@@ -2,21 +2,21 @@
<div class="app-container">
<a-card class="box-card">
<template #title>
<span><FileTextOutlined /> 待办任务</span>
<!-- <span><FileTextOutlined /> 待办任务</span> -->
<div style="float: right">
<a-tag style="margin-left: 10px">发起人:{{ startUser }}</a-tag>
<a-tag>任务节点:{{ taskName }}</a-tag>
</div>
</template>
<a-tabs v-model:activeKey="activeName" @tabClick="handleClick">
<a-tabs v-model:activeKey="activeName" @tab-click="handleClick">
<!--表单信息-->
<a-tab-pane key="1" tab="主表单信息">
<div v-show="formUrl" class="iframe-container">
<iFrame :src="formUrl" class="responsive-iframe"></iFrame>
</div>
<div v-show="!formUrl" style="margin:10px;width:100%">
<FlowInnerForm ref="refInnerForm"></FlowInnerForm>
<div v-show="!formUrl" style="margin: 10px; width: 100%">
<FlowInnerForm ref="refInnerForm"></FlowInnerForm>
</div>
</a-tab-pane>
......@@ -24,43 +24,25 @@
<a-tab-pane key="2" tab="流转记录">
<div class="block">
<a-steps direction="vertical" size="small">
<a-step
v-for="(item, index) in flowRecordList"
:key="index"
:status="getStepStatus(item.finishTime)"
>
<a-step v-for="(item, index) in flowRecordList" :key="index" :status="getStepStatus(item.finishTime)">
<template #title>
<div style="margin: 5px">
<span style="margin-right: 50px;">节点名称:{{ item.taskName }} </span>
<span style="margin-right: 50px">节点名称:{{ item.taskName }} </span>
<a-button type="link" @click="showNodeFormData(item)">查看表单</a-button>
</div>
</template>
<template #description>
<a-card :body-style="{ padding: '0px', marginTop: '0px' }">
<a-descriptions
class="margin-top"
:column="1"
size="small"
bordered
>
<a-descriptions-item
v-if="item.assigneeName"
label="办理人"
>
<a-descriptions class="margin-top" :column="1" size="small" bordered>
<a-descriptions-item v-if="item.assigneeName" label="办理人">
<template #label>
<UserOutlined />
办理人
</template>
{{ item.assigneeName }}
<a-tag color="gray" size="small">{{
item.deptName
}}</a-tag>
<a-tag color="gray" size="small">{{ item.deptName }}</a-tag>
</a-descriptions-item>
<a-descriptions-item
v-if="item.candidate"
label="候选办理"
>
<a-descriptions-item v-if="item.candidate" label="候选办理">
<template #label>
<UserOutlined />
候选办理
......@@ -74,30 +56,21 @@
</template>
{{ item.createTime }}
</a-descriptions-item>
<a-descriptions-item
v-if="item.finishTime"
label="处理时间"
>
<a-descriptions-item v-if="item.finishTime" label="处理时间">
<template #label>
<CalendarOutlined />
处理时间
</template>
{{ item.finishTime }}
</a-descriptions-item>
<a-descriptions-item
v-if="item.duration"
label="耗时"
>
<a-descriptions-item v-if="item.duration" label="耗时">
<template #label>
<ClockCircleOutlined />
耗时
</template>
{{ item.duration }}
</a-descriptions-item>
<a-descriptions-item
v-if="item.comment?.comment"
label="处理意见"
>
<a-descriptions-item v-if="item.comment?.comment" label="处理意见">
<template #label>
<FormOutlined />
处理意见
......@@ -122,195 +95,224 @@
</template>
<script lang="ts" setup>
import { ref, reactive,nextTick } from 'vue'
import {
FileTextOutlined,
UserOutlined,
CalendarOutlined,
ClockCircleOutlined,
FormOutlined,
} from '@ant-design/icons-vue'
import { flowRecord } from "/@/components/Process/api/finished"
import { flowXmlAndNode } from "/@/components/Process/api/definition"
import { findFlowFormVal} from "/@/components/Process/api/todo"
import { useModal } from '/@/components/Modal';
import { flowFormData } from "/@/components/Process/api/process"
import BpmnViewer from '/@/components/Process/viewer/index.vue'
import FlowInnerForm from '/@/views/flowable/task/components/FlowInnerForm.vue'
import ShowFormModal from '/@/views/flowable/task/components/ShowFormModal.vue'
interface FlowRecordItem {
taskName: string
finishTime: string | null
assigneeName: string
deptName: string
candidate: string
createTime: string
duration: string
comment: {
comment: string
import { ref, reactive, nextTick } from 'vue';
import { FileTextOutlined, UserOutlined, CalendarOutlined, ClockCircleOutlined, FormOutlined } from '@ant-design/icons-vue';
import { flowRecord } from '/@/components/Process/api/finished';
import { flowXmlAndNode } from '/@/components/Process/api/definition';
import { findFlowFormVal } from '/@/components/Process/api/todo';
import { useModal } from '/@/components/Modal';
import { flowFormData } from '/@/components/Process/api/process';
import BpmnViewer from '/@/components/Process/viewer/index.vue';
import FlowInnerForm from '/@/views/flowable/task/components/FlowInnerForm.vue';
import ShowFormModal from '/@/views/flowable/task/components/ShowFormModal.vue';
interface FlowRecordItem {
taskName: string;
finishTime: string | null;
assigneeName: string;
deptName: string;
candidate: string;
createTime: string;
duration: string;
comment: {
comment: string;
};
}
}
interface TaskForm {
returnTaskShow: boolean
delegateTaskShow: boolean
defaultTaskShow: boolean
comment: string
procInsId: string
instanceId: string
deployId: string
taskId: string
procDefId: string
targetKey: string
variables: Record<string, any>
executionId?: string
}
const flowData = ref({})
const activeName = ref('1')
const formUrl = ref();
const refInnerForm = ref();
const [registerShowFormModal, { openModal, setModalProps }] = useModal();
// 遮罩层
const loading = ref(true)
const flowRecordList = ref<FlowRecordItem[]>([])
const taskForm = reactive<TaskForm>({
returnTaskShow: false,
delegateTaskShow: false,
defaultTaskShow: true,
comment: '',
procInsId: '',
instanceId: '',
deployId: '',
taskId: '',
procDefId: '',
targetKey: '',
variables: {},
})
const taskName = ref<string | null>(null) // 任务节点
const startUser = ref<string | null>(null) // 发起人信息
const handleClick = (key: string) => {
if (key === '3') {
interface TaskForm {
returnTaskShow: boolean;
delegateTaskShow: boolean;
defaultTaskShow: boolean;
comment: string;
procInsId: string;
instanceId: string;
deployId: string;
taskId: string;
procDefId: string;
targetKey: string;
variables: Record<string, any>;
executionId?: string;
}
const flowData = ref({});
const activeName = ref('1');
const formUrl = ref();
const refInnerForm = ref();
const [registerShowFormModal, showFormModalApi] = useModal();
// 遮罩层
const loading = ref(true);
const flowRecordList = ref<FlowRecordItem[]>([]);
const taskForm = reactive<TaskForm>({
returnTaskShow: false,
delegateTaskShow: false,
defaultTaskShow: true,
comment: '',
procInsId: '',
instanceId: '',
deployId: '',
taskId: '',
procDefId: '',
targetKey: '',
variables: {},
});
const taskName = ref<string | null>(null); // 任务节点
const startUser = ref<string | null>(null); // 发起人信息
const handleClick = (key: string) => {
if (key === '3') {
flowXmlAndNode({
procInsId: taskForm.procInsId,
deployId: taskForm.deployId,
}).then((res) => {
flowData.value = res
})
}
}
flowData.value = res;
});
}
};
// 修改 setColor 方法为 getStepStatus
const getStepStatus = (finishTime: string | null) => {
if (finishTime) {
return 'finish'; // 已完成
} else {
return 'wait'; // 等待中
}
};
// 修改 setColor 方法为 getStepStatus
const getStepStatus = (finishTime: string | null) => {
if (finishTime) {
return 'finish' // 已完成
} else {
return 'wait' // 等待中
}
}
const setFlowRecordList = async (procInsId: string, deployId: string) => {
const params = { procInsId, deployId }
await flowRecord(params)
.then((res) => {
console.log("flowList",res)
flowRecordList.value = res.flowList
})
.catch(() => {
//goBack()
})
}
const taskDataObj = ref()
const iniData = async (data) => {
taskDataObj.value = data
taskName.value = data.taskName as string
startUser.value = data.startUserName as string
taskForm.deployId = data.deployId as string
taskForm.taskId = data.taskId as string
taskForm.procInsId = data.procInsId as string
taskForm.executionId = data.executionId as string
taskForm.instanceId = data.procInsId as string
const setFlowRecordList = async (procInsId: string, deployId: string) => {
const params = { procInsId, deployId };
await flowRecord(params)
.then((res) => {
console.log('flowList', res);
flowRecordList.value = res.flowList;
})
.catch(() => {
//goBack()
});
};
const taskDataObj = ref();
const iniData = async (data) => {
taskDataObj.value = data;
taskName.value = data.taskName as string;
startUser.value = data.startUserName as string;
taskForm.deployId = data.deployId as string;
taskForm.taskId = data.taskId as string;
taskForm.procInsId = data.procInsId as string;
taskForm.executionId = data.executionId as string;
taskForm.instanceId = data.procInsId as string;
//console.log('taskForm.taskId:', taskForm.taskId);
await flowFormData({deployId: taskForm.deployId, taskId:taskForm.taskId}).then(resData => {
await flowFormData({ deployId: taskForm.deployId, taskId: taskForm.taskId }).then((resData) => {
nextTick(() => {
if(resData.formUrl) {
formUrl.value = resData.formUrl
if (resData.formUrl) {
formUrl.value = resData.formUrl;
} else {
formUrl.value = ""
refInnerForm.value.iniData(resData)
formUrl.value = '';
refInnerForm.value.iniData(resData);
}
})
})
});
});
if(taskForm.taskId) {
await findFlowFormVal({taskId: taskForm.taskId }).then((resValues) => {
if (taskForm.taskId) {
await findFlowFormVal({ taskId: taskForm.taskId }).then((resValues) => {
nextTick(() => {
refInnerForm.value.setFormData(resValues)
})
})
refInnerForm.value.setFormData(resValues);
});
});
}
setFlowRecordList(taskForm.procInsId, taskForm.deployId)
}
setFlowRecordList(taskForm.procInsId, taskForm.deployId);
};
const showNodeFormData = (data) => {
// 调试:打印传入的数据结构
console.log('showNodeFormData 传入数据:', data);
console.log('showNodeFormData taskDataObj:', taskDataObj.value);
const showNodeFormData = (data) => {
openModal(true, {
showFooter: true,
data
// Build workflowNodes list for the drawer. Adjust component paths as needed.
const nodes = [
{ id: 'stplanman', name: '主表单', formUrl: '/project/plan/components/StPlanManForm.vue' },
{ id: 'stplanexcute', name: '执行表单', formUrl: '/project/plan/components/StPlanExcuteForm.vue' },
];
// pick which node to show — customize the rule as necessary
const currentIndex = (data?.taskName || '').includes('执行') ? 1 : 0;
// 获取当前节点
const currentNode = nodes[currentIndex];
// 获取业务数据ID:优先级为 id -> dataId -> procInsId
let businessDataId = taskDataObj.value?.id || taskDataObj.value?.dataId || taskDataObj.value?.procInsId;
console.log('获取到的业务数据ID:', businessDataId);
// 准备表单数据
const formData = {
id: businessDataId || '',
// 将taskDataObj中的数据合并到formData中
...taskDataObj.value,
};
console.log('准备的表单数据:', formData);
// externalFormData: map node id -> form data object
const externalFormData = {};
// 所有节点都传递相同的formData,包括可编辑节点和只读节点
nodes.forEach((node) => {
externalFormData[node.id] = formData;
});
}
defineExpose({
iniData,
})
console.log('传递给ShowFormModal的externalFormData:', externalFormData);
</script>
showFormModalApi.openModal(true, {
workflowNodes: nodes,
currentNodeIndex: currentIndex,
externalFormData,
title: data?.taskName || '表单',
showFooter: false,
// 设置表单为只读状态
disabled: true,
readonly: true,
});
};
defineExpose({
iniData,
});
</script>
<style lang="scss" scoped>
.clearfix:before,
.clearfix:after {
display: table;
content: '';
}
.clearfix:after {
clear: both;
}
.app-container {
height: calc(100vh - 150px);
margin: 0px;
padding: 5px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.app-container {
height: calc(100vh - 150px);
margin: 0px;
padding: 5px;
}
.my-label {
background: #E1F3D8;
}
.iframe-container {
height: calc(100vh - 200px);
width: 100%;
}
.responsive-iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
\ No newline at end of file
.my-label {
background: #e1f3d8;
}
.iframe-container {
height: calc(100vh - 200px);
width: 100%;
}
.responsive-iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
......@@ -57,20 +57,20 @@
<div class="content-wrapper">
<!-- 左侧主表单 -->
<div class="main-form-section">
<a-card title="审批表单" :bordered="false" class="form-card">
<!-- <a-card title="审批表单" :bordered="false" class="form-card">
<template #extra>
<a-button type="link" @click="refreshForm"> <ReloadOutlined /> 刷新 </a-button>
</template>
</template> -->
<div class="form-content">
<div v-show="formTp == 1" class="form-wrapper">
<FlowInnerForm ref="refCruInnerForm" :key="formKey" style="width: 100%" />
</div>
<div v-show="formTp == 2" class="iframe-container" style="height: 470px">
<iFrame :key="formKey" :src="formUrl" class="responsive-iframe" style="width: 100%; height: 100%" />
</div>
<div class="form-card">
<div v-show="formTp == 1" class="form-wrapper">
<FlowInnerForm ref="refCruInnerForm" :key="formKey" style="width: 100%; height: 100%" />
</div>
</a-card>
<div v-show="formTp == 2" class="form-wrapper">
<iFrame :key="formKey" :src="formUrl" class="responsive-iframe" style="width: 100%; height: 100%" />
</div>
</div>
<!-- </a-card> -->
</div>
<!-- 右侧审批栏 -->
......@@ -220,7 +220,7 @@
// API
import { flowRecord } from '/@/components/Process/api/finished';
import { flowXmlAndNode } from '/@/components/Process/api/definition';
import { complete, flowTaskForm, getNextFlowNode, getMyTaskFlow,assign,assignRead } from '/@/components/Process/api/todo';
import { complete, flowTaskForm, getNextFlowNode, getMyTaskFlow, assign, assignRead } from '/@/components/Process/api/todo';
import { flowTaskInfo } from '/@/components/Process/api/process';
// 组件
......@@ -344,7 +344,7 @@
field: 'checkSendRole',
component: 'JSelectRole',
required: false,
ifShow: computed(() => userType.value === 'role'),
ifShow: computed(() => userType.value !== 'user'),
componentProps: {
labelKey: 'roleName',
rowKey: 'id',
......@@ -536,11 +536,13 @@
submitData.values['userTaskid'] = nextNodeNameSelevue.value;
}
if (!showApprovalUi.value) {
await complete(submitData);
emit('callback');
return false;
}
const formData = await validate();
Object.assign(submitData, formData);
......@@ -562,6 +564,7 @@
submitData.values['approvalType'] = 'role';
}
// }
console.log("-----------submitData ",submitData);
// 执行发送
const result = await complete(submitData);
......@@ -723,8 +726,8 @@
}
};
//转办处理
const handleAssignTask = async () => {
//转办处理
const handleAssignTask = async () => {
if (assigning.value) return;
try {
assigning.value = true;
......@@ -738,33 +741,32 @@
const formData = await validate();
Object.assign(submitData, formData);
submitData.comment = submitData.comment || '';
//test1 1958436761110269953
// submitData.values['targetUserId'] = '1959869916657950721';
if (userType.value === 'user') {
//test1 1958436761110269953
// submitData.values['targetUserId'] = '1959869916657950721';
if (userType.value === 'user') {
submitData.values['targetUserId'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
if (formData.checkSendUser) {
submitData.values['targetUserId'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
if (formData.checkSendUser) {
submitData.values['targetUserId'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
}
} else if (formData.checkSendRole) {
submitData.values['targetUserId'] = formData.checkSendRole;
submitData.values['approvalType'] = 'role';
}
const dataId = formUrl.value.split('=').pop();
submitData.values['dataId'] = dataId;
const lastEqualIndex = formUrl.value.lastIndexOf('=');
// 往前找到 & 或 ? 的位置
let startIndex = formUrl.value.lastIndexOf('&', lastEqualIndex);
if (startIndex === -1) {
startIndex = formUrl.value.lastIndexOf('?', lastEqualIndex);
}
const dataName = formUrl.value.substring(startIndex + 1, lastEqualIndex);
submitData.values['dataName'] = dataName;
} else if (formData.checkSendRole) {
submitData.values['targetUserId'] = formData.checkSendRole;
submitData.values['approvalType'] = 'role';
}
const dataId = formUrl.value.split('=').pop();
submitData.values['dataId'] = dataId;
const lastEqualIndex = formUrl.value.lastIndexOf('=');
// 往前找到 & 或 ? 的位置
let startIndex = formUrl.value.lastIndexOf('&', lastEqualIndex);
if (startIndex === -1) {
startIndex = formUrl.value.lastIndexOf('?', lastEqualIndex);
}
const dataName = formUrl.value.substring(startIndex + 1, lastEqualIndex);
submitData.values['dataName'] = dataName;
// 执行发送
const result = await assign(submitData);
......@@ -784,8 +786,8 @@
}
};
//转阅处理
const handleAssignReadTask = async () => {
//转阅处理
const handleAssignReadTask = async () => {
if (assiReadgning.value) return;
try {
assiReadgning.value = true;
......@@ -799,23 +801,21 @@
const formData = await validate();
Object.assign(submitData, formData);
submitData.comment = submitData.comment || '';
//test1 1958436761110269953
// submitData.values['targetUserId'] = '1959869916657950721';
if (userType.value === 'user') {
//test1 1958436761110269953
// submitData.values['targetUserId'] = '1959869916657950721';
if (userType.value === 'user') {
submitData.values['targetUserId'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
if (formData.checkSendUser) {
submitData.values['targetUserId'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
if (formData.checkSendUser) {
submitData.values['targetUserId'] = formData.checkSendUser;
submitData.values['approvalType'] = 'user';
}
} else if (formData.checkSendRole) {
submitData.values['targetUserId'] = formData.checkSendRole;
submitData.values['approvalType'] = 'role';
}
} else if (formData.checkSendRole) {
submitData.values['targetUserId'] = formData.checkSendRole;
submitData.values['approvalType'] = 'role';
}
// 执行发送
const result = await assignRead(submitData);
......@@ -835,8 +835,6 @@
}
};
defineExpose({
iniData,
});
......@@ -868,7 +866,7 @@
height: 100vh;
margin: 0;
padding: 0;
background: linear-gradient(135deg, #b0b3c2 0%, #764ba2 100%);
// background: linear-gradient(135deg, #b0b3c2 0%, #764ba2 100%);
overflow: hidden;
}
......@@ -945,6 +943,7 @@
}
.next-node-label {
width: 120px;
color: #666;
font-size: 14px;
}
......
<template>
<div class="app-container">
<vxe-grid
ref="xGrid"
v-bind="gridOptions"
@checkbox-change="onSelectChange"
@checkbox-all="onSelectChange"
>
<vxe-grid ref="xGrid" v-bind="gridOptions" @checkbox-change="onSelectChange" @checkbox-all="onSelectChange">
<!-- 搜索表单插槽 -->
<template #flowNameItem="{ data }">
<vxe-input v-model="data.procDefName" placeholder="请输入流程名称" clearable @keyup.enter="searchEvent"></vxe-input>
</template>
<template #taskNameItem="{ data }">
<vxe-input v-model="data.taskName" placeholder="请输入任务名称" clearable @keyup.enter="searchEvent"></vxe-input>
</template>
<template #startTimeItem="{ data }">
<vxe-input type="date" v-model="data.startTime" placeholder="请选择开始时间" clearable></vxe-input>
</template>
<template #actionItem>
<vxe-button status="primary" icon="vxe-icon-search" @click="searchEvent">搜索</vxe-button>
<vxe-button icon="vxe-icon-refresh" @click="resetEvent">重置</vxe-button>
......@@ -33,14 +28,9 @@
<span class="dept-tag">{{ row.startDeptName }}</span>
</div>
</template>
<template #action_default="{ row }">
<vxe-button
type="text"
icon="vxe-icon-edit"
status="primary"
@click="handleProcess(row)"
>处理</vxe-button>
<vxe-button type="text" icon="vxe-icon-edit" status="primary" @click="handleProcess(row)">处理</vxe-button>
</template>
</vxe-grid>
<div v-if="isShowDrawer">
......@@ -53,288 +43,283 @@
title="待办任务"
placement="right"
width="90%"
style="margin: 0px;padding: 0px;"
>
style="margin: 0px; padding: 0px"
>
<template #extra>
<div style="float: right">
<a-tag style="margin-left: 10px">发起人:{{ startUser }}</a-tag>
<a-tag>任务节点:{{ taskName }}</a-tag>
</div>
</template>
<TodoIndex v-if="isShowDrawer" ref="refTodoIndex" @callback="handleSuccess"/>
<TodoIndex v-if="isShowDrawer" ref="refTodoIndex" @callback="handleSuccess" />
</a-drawer>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted,nextTick } from 'vue'
import { message } from 'ant-design-vue'
import type { VxeGridProps, VxeGridInstance } from 'vxe-table'
import { todoList, delDeployment } from '/@/components/Process/api/todo'
import XEUtils from 'xe-utils'
import TodoIndex from './components/TodoIndex.vue'
const loading = ref(false)
const isShowDrawer = ref(false)
const refTodoIndex = ref()
const startUser = ref()
const taskName = ref()
interface QueryParams {
pageNum: number
pageSize: number
procDefName?: string
taskName?: string
startTime?: string
}
const queryParams = reactive<QueryParams>({
pageNum: 1,
pageSize: 10,
procDefName: undefined,
taskName: undefined,
startTime: undefined,
})
interface TodoItem {
taskId: string
procDefName: string
taskName: string
procDefVersion: number
startUserName: string
startDeptName: string
createTime: string
procInsId: string
executionId: string
deployId: string
}
const defaultData = reactive({
procDefName: '',
taskName: '',
startTime: ''
})
const selectedRowKeys = ref<string[]>([])
const selectRecords = ref<TodoItem[]>([])
const multiple = ref(true)
const xGrid = ref<VxeGridInstance>()
const gridOptions = reactive<VxeGridProps<any>>({
loading: loading.value,
showOverflow: true,
border: true,
rowConfig: {
keyField: 'taskId',
isHover: true
},
columnConfig: {
resizable: true
},
pagerConfig: {
enabled: true,
import { reactive, ref, onMounted, nextTick } from 'vue';
import { message } from 'ant-design-vue';
import type { VxeGridProps, VxeGridInstance } from 'vxe-table';
import { todoList, delDeployment } from '/@/components/Process/api/todo';
import XEUtils from 'xe-utils';
import TodoIndex from './components/TodoIndex.vue';
const loading = ref(false);
const isShowDrawer = ref(false);
const refTodoIndex = ref();
const startUser = ref();
const taskName = ref();
interface QueryParams {
pageNum: number;
pageSize: number;
procDefName?: string;
taskName?: string;
startTime?: string;
}
const queryParams = reactive<QueryParams>({
pageNum: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
layouts: ['PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']
},
checkboxConfig: {
highlight: true,
range: true
},
layouts: ['Top', 'Form', 'Toolbar','Table', 'Bottom', 'Pager'],
formConfig: {
data: XEUtils.clone(defaultData, true),
items: [
{ field: 'procDefName', title: '流程名称', span: 6, itemRender: {}, slots: { default: 'flowNameItem' } },
{ field: 'taskName', title: '任务名称', span: 6, itemRender: {}, slots: { default: 'taskNameItem' } },
{ field: 'startTime', title: '开始时间', span: 6, itemRender: {}, slots: { default: 'startTimeItem' } },
{ span: 6, align: 'center', itemRender: {}, slots: { default: 'actionItem' } }
]
},
proxyConfig: {
response: {
result: 'result',
total: 'page.total'
procDefName: undefined,
taskName: undefined,
startTime: undefined,
});
interface TodoItem {
taskId: string;
procDefName: string;
taskName: string;
procDefVersion: number;
startUserName: string;
startDeptName: string;
createTime: string;
procInsId: string;
executionId: string;
deployId: string;
}
const defaultData = reactive({
procDefName: '',
taskName: '',
startTime: '',
});
const selectedRowKeys = ref<string[]>([]);
const selectRecords = ref<TodoItem[]>([]);
const multiple = ref(true);
const xGrid = ref<VxeGridInstance>();
const gridOptions = reactive<VxeGridProps<any>>({
loading: loading.value,
showOverflow: true,
border: true,
rowConfig: {
keyField: 'taskId',
isHover: true,
},
ajax: {
query: ({ page }) => {
return findPageList(page.currentPage, page.pageSize)
}
columnConfig: {
resizable: true,
},
pagerConfig: {
enabled: true,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
layouts: ['PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total'],
},
checkboxConfig: {
highlight: true,
range: true,
},
layouts: ['Top', 'Form', 'Toolbar', 'Table', 'Bottom', 'Pager'],
formConfig: {
data: XEUtils.clone(defaultData, true),
items: [
{ field: 'procDefName', title: '流程名称', span: 6, itemRender: {}, slots: { default: 'flowNameItem' } },
{ field: 'taskName', title: '任务名称', span: 6, itemRender: {}, slots: { default: 'taskNameItem' } },
{ field: 'startTime', title: '开始时间', span: 6, itemRender: {}, slots: { default: 'startTimeItem' } },
{ span: 6, align: 'center', itemRender: {}, slots: { default: 'actionItem' } },
],
},
proxyConfig: {
response: {
result: 'result',
total: 'page.total',
},
ajax: {
query: ({ page }) => {
return findPageList(page.currentPage, page.pageSize);
},
},
},
columns: [
{ type: 'checkbox', width: 60, fixed: 'left' },
{ type: 'seq', width: 70, fixed: 'left' },
{ field: 'taskId', title: '任务编号', minWidth: 120, showOverflow: true },
{ field: 'procDefName', title: '流程名称', minWidth: 160, showOverflow: true },
{ field: 'taskName', title: '当前节点', minWidth: 140, showOverflow: true },
{ field: 'taskType', title: '节点类型', minWidth: 140, showOverflow: true },
{ field: 'procDefVersion', title: '流程版本', width: 100, align: 'center', slots: { default: 'procDefVersion_default' } },
{ field: 'startUser', title: '流程发起人', minWidth: 180, align: 'center', slots: { default: 'startUser_default' } },
{ field: 'createTime', title: '接收时间', width: 180, align: 'center' },
{
title: '操作',
width: 120,
fixed: 'right',
align: 'center',
slots: { default: 'action_default' },
},
],
});
const findPageList = async (currentPage: number, pageSize: number) => {
queryParams.pageNum = currentPage;
queryParams.pageSize = pageSize;
if (gridOptions.formConfig?.data) {
queryParams.procDefName = gridOptions.formConfig.data.procDefName;
queryParams.taskName = gridOptions.formConfig.data.taskName;
queryParams.startTime = gridOptions.formConfig.data.startTime;
}
},
columns: [
{ type: 'checkbox', width: 60, fixed: 'left' },
{ type: 'seq', width: 70, fixed: 'left' },
{ field: 'taskId', title: '任务编号', minWidth: 120, showOverflow: true },
{ field: 'procDefName', title: '流程名称', minWidth: 160, showOverflow: true },
{ field: 'taskName', title: '当前节点', minWidth: 140, showOverflow: true },
{ field: 'taskType', title: '节点类型', minWidth: 140, showOverflow: true },
{ field: 'procDefVersion', title: '流程版本', width: 100, align: 'center', slots: { default: 'procDefVersion_default' } },
{ field: 'startUser', title: '流程发起人', minWidth: 180, align: 'center', slots: { default: 'startUser_default' } },
{ field: 'createTime', title: '接收时间', width: 180, align: 'center' },
{
title: '操作',
width: 120,
fixed: 'right',
align: 'center',
slots: { default: 'action_default' }
try {
loading.value = true;
const retData = await todoList(queryParams);
return {
page: {
total: retData.total,
},
result: retData.records,
};
} catch (error) {
console.error('查询数据失败:', error);
message.error('查询数据失败');
return {
page: { total: 0 },
result: [],
};
} finally {
loading.value = false;
}
]
})
const findPageList = async (currentPage: number, pageSize: number) => {
queryParams.pageNum = currentPage
queryParams.pageSize = pageSize
if (gridOptions.formConfig?.data) {
queryParams.procDefName = gridOptions.formConfig.data.procDefName
queryParams.taskName = gridOptions.formConfig.data.taskName
queryParams.startTime = gridOptions.formConfig.data.startTime
}
try {
loading.value = true
const retData = await todoList(queryParams)
return {
page: {
total: retData.total
},
result: retData.records
};
const searchEvent = async () => {
queryParams.pageNum = 1;
if (xGrid.value) {
await xGrid.value.commitProxy('query');
}
};
const resetEvent = () => {
if (gridOptions.formConfig) {
gridOptions.formConfig.data = XEUtils.clone(defaultData, true);
}
} catch (error) {
console.error('查询数据失败:', error)
message.error('查询数据失败')
return {
page: { total: 0 },
result: []
searchEvent();
};
const onSelectChange = () => {
if (xGrid.value) {
const checkedRecords = xGrid.value.getCheckboxRecords();
selectRecords.value = checkedRecords;
selectedRowKeys.value = checkedRecords.map((item) => item.taskId);
multiple.value = !selectedRowKeys.value.length;
}
} finally {
loading.value = false
};
const handleProcess = async (row) => {
isShowDrawer.value = true;
startUser.value = row.startUserName;
taskName.value = row.taskName;
await nextTick(() => {
if (refTodoIndex.value) {
refTodoIndex.value.iniData(row);
} else {
isShowDrawer.value = false;
}
});
};
const handleSuccess = async () => {
isShowDrawer.value = false;
await searchEvent();
};
</script>
<style scoped>
.app-container {
padding: 16px;
width: 100%;
height: 100%;
}
}
const searchEvent = async () => {
queryParams.pageNum = 1
if (xGrid.value) {
await xGrid.value.commitProxy('query')
.user-info {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
flex-wrap: nowrap;
}
}
const resetEvent = () => {
if (gridOptions.formConfig) {
gridOptions.formConfig.data = XEUtils.clone(defaultData, true)
.user-name {
font-weight: 500;
white-space: nowrap;
}
searchEvent()
}
.dept-tag {
background: #f0f0f0;
color: #666;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
}
const onSelectChange = () => {
if (xGrid.value) {
const checkedRecords = xGrid.value.getCheckboxRecords()
selectRecords.value = checkedRecords
selectedRowKeys.value = checkedRecords.map(item => item.taskId)
multiple.value = !selectedRowKeys.value.length
.version-tag {
background: #1890ff;
color: white;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
}
const handleProcess = async (row) => {
isShowDrawer.value = true
startUser.value = row.startUserName
taskName.value = row.taskName
await nextTick(()=>{
if(refTodoIndex.value) {
refTodoIndex.value.iniData(row)
} else {
isShowDrawer.value = false
}
})
/* Vxe Grid 样式调整 */
:deep(.vxe-form--wrapper) {
background: #fafafa;
padding: 16px;
border-radius: 4px;
border: 1px solid #e8e8e8;
margin-bottom: 16px;
}
}
:deep(.vxe-form--item-title) {
font-weight: 500;
color: #333;
}
const handleSuccess = async () => {
isShowDrawer.value = false
await searchEvent()
}
:deep(.vxe-grid--wrapper) {
font-family: inherit;
}
</script>
:deep(.vxe-grid--header) {
background-color: #fafafa;
}
<style scoped>
.app-container {
padding: 16px;
width: 100%;
height: 100%;
}
.user-info {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
flex-wrap: nowrap;
}
.user-name {
font-weight: 500;
white-space: nowrap;
}
.dept-tag {
background: #f0f0f0;
color: #666;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
}
.version-tag {
background: #1890ff;
color: white;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
/* Vxe Grid 样式调整 */
:deep(.vxe-form--wrapper) {
background: #fafafa;
padding: 16px;
border-radius: 4px;
border: 1px solid #e8e8e8;
margin-bottom: 16px;
}
:deep(.vxe-form--item-title) {
font-weight: 500;
color: #333;
}
:deep(.vxe-grid--wrapper) {
font-family: inherit;
}
:deep(.vxe-grid--header) {
background-color: #fafafa;
}
:deep(.vxe-grid--body) {
background-color: #fff;
}
:deep(.vxe-cell) {
padding: 8px 4px;
}
:deep(.vxe-toolbar) {
background: transparent;
padding: 8px 0;
margin-bottom: 8px;
}
:deep(.vxe-table--render-wrapper) {
border-radius: 4px;
}
</style>
\ No newline at end of file
:deep(.vxe-grid--body) {
background-color: #fff;
}
:deep(.vxe-cell) {
padding: 8px 4px;
}
:deep(.vxe-toolbar) {
background: transparent;
padding: 8px 0;
margin-bottom: 8px;
}
:deep(.vxe-table--render-wrapper) {
border-radius: 4px;
}
</style>
......@@ -8,10 +8,10 @@
<!-- 表单主体 -->
<div class="form-body">
<a-form ref="formRef" :model="formData" layout="vertical" class="styled-form">
<a-form ref="formRef" :model="formFields" layout="vertical" class="styled-form">
<!-- 执行状态 -->
<a-form-item label="执行状态" name="executeStatus">
<a-select v-model:value="formData.executeStatus" placeholder="请选择执行状态">
<a-select v-model:value="formFields.executeStatus" :disabled="disabled || readonly" placeholder="请选择执行状态">
<a-select-option value="0">
<span class="status-option">
<span class="status-indicator pending"></span>
......@@ -42,28 +42,45 @@
<!-- 时间选择区域 -->
<div class="form-row">
<a-form-item label="实际开始时间" name="actualStartTime" class="form-item-half">
<a-date-picker v-model:value="formData.actualStartTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker>
<a-date-picker
v-model:value="formFields.actualStartTime"
:disabled="disabled || readonly"
placeholder="选择时间"
format="YYYY-MM-DD"
></a-date-picker>
</a-form-item>
<a-form-item label="实际结束时间" name="actualEndTime" class="form-item-half" :rules="endDateRules">
<a-date-picker v-model:value="formData.actualEndTime" placeholder="选择时间" format="YYYY-MM-DD"></a-date-picker>
<a-date-picker
v-model:value="formFields.actualEndTime"
:disabled="disabled || readonly"
placeholder="选择时间"
format="YYYY-MM-DD"
></a-date-picker>
</a-form-item>
</div>
<!-- 执行记录 -->
<a-form-item label="执行记录" name="executeEcord">
<a-textarea v-model:value="formData.executeEcord" :rows="3" placeholder="请输入执行记录和详细说明..." show-count :maxlength="500" />
<a-textarea
v-model:value="formFields.executeEcord"
:disabled="disabled || readonly"
:rows="3"
placeholder="请输入执行记录和详细说明..."
show-count
:maxlength="500"
/>
</a-form-item>
<!-- 附件上传 -->
<a-form-item label="相关附件" name="attachments">
<JUpload v-model:value="formData.attachments" desText="支持扩展名: .rar .zip .doc .docx .pdf .jpg..." />
<JUpload v-model:value="formFields.attachments" :disabled="disabled || readonly" desText="支持扩展名: .rar .zip .doc .docx .pdf .jpg..." />
</a-form-item>
</a-form>
</div>
<!-- 表单底部按钮 -->
<div class="form-footer">
<div v-if="!readonly" class="form-footer">
<a-space :size="12">
<a-button @click="resetForm">重置</a-button>
<a-button type="primary" @click="submitForm">保存</a-button>
......@@ -73,7 +90,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, watch, nextTick } from 'vue';
import { ref, reactive, onMounted, watch, nextTick, toRaw } from 'vue';
import { message } from 'ant-design-vue';
import dayjs from 'dayjs';
import { saveOrUpdate } from '../StPlanMan.api';
......@@ -90,15 +107,8 @@
const planFormStore = usePlanFormStore();
const isInitialized = ref(false);
// const formData = ref({
// id: '',
// executeStatus: '',
// actualStartTime: null,
// actualEndTime: null,
// executeEcord: '',
// attachments: [],
// });
const formData = reactive({
// 本地表单数据 - 使用 formFields 作为内部状态
const formFields = reactive({
id: '',
executeStatus: '',
actualStartTime: null as any,
......@@ -107,15 +117,50 @@
attachments: [] as any[],
});
// 监听formData变化,用于调试
watch(
() => formData,
(newVal) => {
console.log('[v0] formData 响应式更新触发:', JSON.stringify(newVal));
const props = defineProps({
formData: {
type: Object,
default: () => ({}),
},
{ deep: true, immediate: true }
);
disabled: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
});
// 安全日期转换函数 - 从字符串或Date或dayjs对象转换为dayjs对象
const safeDateParse = (dateValue) => {
if (!dateValue) return null;
// 如果已经是dayjs对象,直接返回
if (dateValue && typeof dateValue.format === 'function' && dateValue.isValid && dateValue.isValid()) {
return dateValue;
}
// 如果是Date对象,转换为dayjs
if (dateValue instanceof Date) {
return dayjs(dateValue);
}
// 如果是字符串,尝试解析
if (typeof dateValue === 'string' && dateValue.trim()) {
try {
const parsed = dayjs(dateValue);
if (parsed.isValid()) {
return parsed;
}
} catch (e) {
console.error('日期解析失败:', dateValue, e);
return null;
}
}
return null;
};
// 安全解析JSON
const safeJsonParse = (str) => {
......@@ -128,12 +173,45 @@
}
};
// sync incoming prop into internal reactive formData
watch(
() => props.formData,
(v) => {
if (v && Object.keys(v).length > 0) {
const preparationData = {
id: v.id || '',
executeStatus: v.executeStatus || '',
actualStartTime: safeDateParse(v.actualStartTime),
actualEndTime: safeDateParse(v.actualEndTime),
executeEcord: v.executeEcord || '',
attachments: safeJsonParse(v.attachments),
};
Object.assign(formFields, preparationData);
console.log('从props同步表单数据:', preparationData);
}
},
{ immediate: true, deep: true }
);
// 监听formData变化,用于调试
watch(
() => formFields,
(newVal) => {},
{ deep: true, immediate: true }
);
// 初始化表单数据
const initFormData = async (id?: string) => {
try {
// 优先检查props.formData中是否已有完整数据和id
if (props.formData && props.formData.id && Object.keys(props.formData).length > 1) {
console.log('表单数据已从props传入,使用props数据,跳过queryById');
return; // 数据已通过watch同步到formData
}
// 检查是否已正在加载,避免重复请求
if (loading.value && planId.value === (id || route.query.id)) {
console.log('[v0] 表单已在加载中,跳过重复初始化');
return;
}
......@@ -143,13 +221,11 @@
// 从参数或URL获取id
const targetId = id || (route.query.id as string);
if (!targetId) {
message.warning('未获取到计划ID');
console.warn('[v0] 未获取到计划ID, id参数:', id, '路由ID:', route.query.id);
loading.value = false;
return;
}
console.log('[v0] 开始初始化表单,ID:', targetId);
planId.value = targetId;
planFormStore.setLastLoadedId(targetId);
......@@ -166,27 +242,25 @@
const newFormData = {
id: data.id || '',
executeStatus: data.executeStatus || '',
actualStartTime: data.actualStartTime ? dayjs(data.actualStartTime) : null,
actualEndTime: data.actualEndTime ? dayjs(data.actualEndTime) : null,
actualStartTime: safeDateParse(data.actualStartTime),
actualEndTime: safeDateParse(data.actualEndTime),
executeEcord: data.executeEcord || '',
// 安全解析附件字段(可能是字符串或数组)
attachments: safeJsonParse(data.attachments),
};
// 直接替换整个 ref 值,确保触发响应式更新
Object.assign(formData, newFormData);
Object.assign(formFields, newFormData);
// 强制等待 DOM 更新
await nextTick();
planFormStore.updateFormDataCache(newFormData);
isInitialized.value = true;
console.log('[v0] 表单初始化成功,数据:', formData);
// Object.assign(formData, newFormData);
// planFormStore.updateFormDataCache(newFormData);
// isInitialized.value = true;
// console.log('[v0] 表单初始化成功,数据:', newFormData);
}
} catch (error: any) {
console.error('初始化表单数据失败:', error);
message.error('初始化表单数据失败');
// 只有在从URL获取id时才提示错误
if (id || route.query.id) {
message.error('初始化表单数据失败');
}
planFormStore.formLoadingState.isError = true;
planFormStore.formLoadingState.errorMessage = error?.message || '加载失败';
} finally {
......@@ -204,12 +278,12 @@
if (!value) {
return Promise.resolve();
}
if (!formData.actualStartTime) {
if (!formFields.actualStartTime) {
return Promise.resolve();
}
// 比较日期:结束日期不能早于开始日期
const endTime = dayjs(value);
const startTime = dayjs(formData.actualStartTime);
const startTime = dayjs(formFields.actualStartTime);
if (endTime.isBefore(startTime, 'day')) {
return Promise.reject(new Error('结束日期不能早于开始日期'));
}
......@@ -221,6 +295,12 @@
const submitForm = async () => {
try {
// 只读模式下不允许提交
if (props.readonly) {
message.warning('只读模式下无法提交表单');
return;
}
// 验证表单
try {
await formRef.value.validate();
......@@ -228,7 +308,7 @@
return;
}
if (!formData.id) {
if (!formFields.id) {
message.error('缺少计划ID,无法保存');
return;
}
......@@ -259,23 +339,22 @@
// 准备提交数据,将数组类型转换为字符串,日期格式化
const submitData = {
id: formData.id,
executeStatus: formData.executeStatus || '',
actualStartTime: formatDate(formData.actualStartTime),
actualEndTime: formatDate(formData.actualEndTime),
executeEcord: formData.executeEcord || '',
id: formFields.id,
executeStatus: formFields.executeStatus || '',
actualStartTime: formatDate(formFields.actualStartTime),
actualEndTime: formatDate(formFields.actualEndTime),
executeEcord: formFields.executeEcord || '',
// 将附件数组转换为JSON字符串(如果是数组)或保留原值(如果已是字符串)
attachments: Array.isArray(formData.attachments) ? JSON.stringify(formData.attachments) : formData.attachments || '',
attachments: Array.isArray(formFields.attachments) ? JSON.stringify(formFields.attachments) : formFields.attachments || '',
};
// 调用 saveOrUpdate 接口,isUpdate=true表示执行更新操作
const response = await saveOrUpdate(submitData, true);
if (response) {
message.success('保存成功');
// message.success('保存成功');
// 更新缓存
planFormStore.updateFormDataCache(formData);
console.log(response);
planFormStore.updateFormDataCache(formFields);
}
} catch (error: any) {
console.error('保存失败:', error);
......@@ -287,6 +366,11 @@
};
const resetForm = () => {
// 只读模式下不允许重置
if (props.readonly) {
message.warning('只读模式下无法重置表单');
return;
}
formRef.value.resetFields();
// 重置为初始化的数据
initFormData();
......@@ -297,13 +381,22 @@
// 等待 iframe DOM 完全渲染
await nextTick();
// 优先检查props.formData是否已有完整数据
if (props.formData && props.formData.id && Object.keys(props.formData).length > 1) {
console.log('props.formData已有完整数据,直接使用,不再查询接口');
initFormData();
// 只在非只读模式下注册提交回调
if (!props.readonly) {
planFormStore.registerSubmitCallback(submitForm);
}
return;
}
// 从 URL 直接解析 id 参数(iframe 场景)
const urlParams = new URLSearchParams(window.location.search);
const idFromUrl = urlParams.get('id');
const targetId = idFromUrl || (route.query.id as string);
console.log('[v0] StPlanExcuteForm mounted - idFromUrl:', idFromUrl, 'routeId:', route.query.id, 'finalId:', targetId);
// 如果从 URL 获取到 ID,直接初始化
if (idFromUrl) {
initFormData(idFromUrl);
......@@ -320,8 +413,10 @@
}
}
planFormStore.registerSubmitCallback(submitForm);
console.log('[StPlanExcuteForm] 已向 store 注册表单保存回调');
// 只在非只读模式下注册提交回调
if (!props.readonly) {
planFormStore.registerSubmitCallback(submitForm);
}
});
// 监听路由参数变化
......@@ -355,6 +450,7 @@
resetForm,
initFormData,
isInitialized,
getFormData: () => toRaw(formFields),
});
</script>
......@@ -489,10 +585,8 @@
/* ==================== 表单底部按钮 ==================== */
.form-footer {
padding: 14px 32px;
background: var(--color-bg-section);
border-top: 1px solid var(--color-border);
display: flex;
justify-content: flex-end;
justify-content: center;
}
/* ==================== 上传区域 ==================== */
......
......@@ -13,12 +13,7 @@
<template #planBasis="{ model, field }">
<div class="basis-container" v-if="model[field]">
<div v-if="isValidJson(model[field])" class="basis-tags">
<a-tag
v-for="item in safeJsonParse(model[field])"
@click="viewBasisDetail(item)"
:key="item.id"
class="basis-tag"
>
<a-tag v-for="item in safeJsonParse(model[field])" @click="viewBasisDetail(item)" :key="item.id" class="basis-tag">
{{ item.name }}
</a-tag>
</div>
......@@ -46,7 +41,7 @@
<script lang="ts" setup>
import { BasicForm, useForm } from '/@/components/Form/index';
import { computed, ref, onMounted, watchEffect, toRaw } from 'vue';
import { computed, ref, onMounted, watchEffect, toRaw, watch, nextTick } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { getBpmFormSchema } from '../StPlanMan.data';
import { saveOrUpdate } from '../StPlanMan.api';
......@@ -70,7 +65,6 @@
const loading = ref(false);
const submitting = ref(false);
const formData = ref<Record<string, any>>({});
const idsystoken = ref('');
const [registerForm, { setFieldsValue, setProps, getFieldsValue, resetFields, validate }] = useForm({
labelWidth: 150,
......@@ -79,6 +73,35 @@
baseColProps: { span: 24 },
});
// watch incoming prop and populate the form when provided
watch(
() => props.formData,
async (v) => {
if (v && Object.keys(v).length > 0) {
formData.value = { ...v };
// 延迟调用setFieldsValue,确保表单已经初始化
// 当选项卡切换时,表单可能还没有完全准备好
await nextTick();
try {
await setFieldsValue(v);
console.log('setFieldsValue 成功:', v);
} catch (e) {
console.warn('setFieldsValue failed (first attempt):', e);
// 如果第一次失败,延迟后重试
await new Promise((resolve) => setTimeout(resolve, 100));
try {
await setFieldsValue(v);
console.log('setFieldsValue 成功 (retry):', v);
} catch (e2) {
console.warn('setFieldsValue failed (retry):', e2);
}
}
}
},
{ immediate: true, deep: true }
);
const formDisabled = computed(() => {
return props.formData?.disabled !== false;
});
......@@ -126,7 +149,6 @@
const winurl = window.location.href;
const params = new URLSearchParams(winurl.split('?')[1]);
console.log('winurl:', winurl);
return {
cctoken: params.get('Token') || '',
WorkID: params.get('WorkID') || '',
......@@ -136,12 +158,24 @@
// 初始化表单数据
const initFormData = async () => {
try {
// 如果props.formData已经有数据,说明是从流程表单打开,不需要再调用接口
if (props.formData && Object.keys(props.formData).length > 0 && props.formData.id) {
console.log('表单数据已从props传入,跳过queryById接口调用');
return;
}
loading.value = true;
const timestamp = new Date().getTime();
let tid = toRaw(route.query).id;
console.log('tid:', tid);
// 如果URL中也没有id,则不调用接口
if (!tid) {
console.warn('未找到id参数,既非URL query参数也非props传入');
loading.value = false;
return;
}
const queryByIdUrl = '/plan.main/stPlanMan/queryById';
const data = await defHttp.get({
......@@ -159,7 +193,10 @@
await setProps({ disabled: formDisabled.value });
} catch (error) {
console.error('初始化表单数据失败:', error);
createMessage.error('初始化表单数据失败');
// 只有在非预期情况下才提示错误
if (!props.formData?.id) {
createMessage.error('初始化表单数据失败');
}
} finally {
loading.value = false;
}
......@@ -184,196 +221,209 @@
};
onMounted(() => {
initFormData();
// 如果formData已经通过props传入,则不需要再调用initFormData
if (props.formData && Object.keys(props.formData).length > 0 && props.formData.id) {
console.log('form data已通过props传入,跳过接口调用');
} else {
initFormData();
}
});
// expose getFormData so parent modal can read current values
defineExpose({
submitForm,
// resetForm,
initFormData,
getFormData: () => toRaw(formData.value),
});
</script>
<style scoped lang="less">
/* ==================== 柔和中性风格 - CSS 变量 ==================== */
.plan-form-container {
--color-primary: #3b5bdb;
--color-primary-hover: #364fc7;
--color-primary-light: #e8ecfd;
--color-success: #2f9e44;
--color-warning: #e67700;
--color-error: #c92a2a;
--color-text-primary: #1c1c1e;
--color-text-secondary: #555770;
--color-text-muted: #a0a3b1;
--color-border: #e4e4e9;
--color-border-strong: #c8c8d0;
--color-bg-page: #f5f5f7;
--color-bg-white: #ffffff;
--color-bg-section: #f0f0f4;
--radius: 6px;
background: var(--color-bg-white);
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
/* ==================== 表单头部 ==================== */
.form-header {
background: var(--color-bg-white);
border-bottom: 1px solid var(--color-border);
padding: 20px 32px;
border-left: 3px solid var(--color-primary);
padding-left: 28px;
}
.form-title {
font-size: 18px;
font-weight: 600;
color: var(--color-text-primary);
margin: 0 0 3px 0;
letter-spacing: -0.2px;
}
.form-subtitle {
font-size: 13px;
color: var(--color-text-muted);
margin: 0;
}
/* ==================== 表单主体 ==================== */
.form-body {
padding: 24px 32px;
background: var(--color-bg-white);
}
/* 依据标签样式 */
.basis-container {
padding: 8px 0;
}
.basis-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.basis-tag {
display: inline-flex;
align-items: center;
padding: 4px 10px;
background: var(--color-primary-light);
border: 1px solid var(--color-primary);
border-radius: var(--radius);
color: var(--color-primary);
font-size: 13px;
cursor: pointer;
transition: background 0.15s;
&:hover {
background: #d0d9f8;
/* ==================== 柔和中性风格 - CSS 变量 ==================== */
.plan-form-container {
--color-primary: #3b5bdb;
--color-primary-hover: #364fc7;
--color-primary-light: #e8ecfd;
--color-success: #2f9e44;
--color-warning: #e67700;
--color-error: #c92a2a;
--color-text-primary: #1c1c1e;
--color-text-secondary: #555770;
--color-text-muted: #a0a3b1;
--color-border: #e4e4e9;
--color-border-strong: #c8c8d0;
--color-bg-page: #f5f5f7;
--color-bg-white: #ffffff;
--color-bg-section: #f0f0f4;
--radius: 6px;
background: var(--color-bg-white);
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
}
}
.basis-alert {
border-radius: var(--radius);
}
.basis-empty {
padding: 16px;
background: var(--color-bg-section);
border-radius: var(--radius);
border: 1px dashed var(--color-border-strong);
}
/* ==================== 表单底部按钮 ==================== */
.form-footer {
padding: 14px 32px;
background: var(--color-bg-section);
border-top: 1px solid var(--color-border);
display: flex;
justify-content: flex-end;
}
/* ==================== 表单控件样式覆盖 ==================== */
:deep(.ant-form-item) {
margin-bottom: 18px;
}
:deep(.ant-form-item-label > label) {
font-size: 13px;
font-weight: 500;
color: var(--color-text-secondary);
}
:deep(.ant-input),
:deep(.ant-select-selector),
:deep(.ant-picker),
:deep(.ant-input-textarea textarea) {
border-radius: var(--radius) !important;
border-color: var(--color-border) !important;
font-size: 13px;
box-shadow: none !important;
&:hover {
border-color: var(--color-border-strong) !important;
/* ==================== 表单头部 ==================== */
.form-header {
background: var(--color-bg-white);
border-bottom: 1px solid var(--color-border);
padding: 20px 32px;
border-left: 3px solid var(--color-primary);
padding-left: 28px;
}
&:focus,
&.ant-input-focused {
border-color: var(--color-primary) !important;
box-shadow: 0 0 0 2px var(--color-primary-light) !important;
.form-title {
font-size: 18px;
font-weight: 600;
color: var(--color-text-primary);
margin: 0 0 3px 0;
letter-spacing: -0.2px;
}
.form-subtitle {
font-size: 13px;
color: var(--color-text-muted);
margin: 0;
}
/* ==================== 表单主体 ==================== */
.form-body {
padding: 24px 32px;
background: var(--color-bg-white);
}
}
:deep(.ant-select-focused .ant-select-selector) {
border-color: var(--color-primary) !important;
box-shadow: 0 0 0 2px var(--color-primary-light) !important;
}
/* ==================== 按钮样式 ==================== */
:deep(.ant-btn) {
border-radius: var(--radius);
font-weight: 500;
font-size: 13px;
height: 34px;
padding: 0 18px;
box-shadow: none;
&.ant-btn-primary {
background: var(--color-primary);
border-color: var(--color-primary);
/* 依据标签样式 */
.basis-container {
padding: 8px 0;
}
.basis-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.basis-tag {
display: inline-flex;
align-items: center;
padding: 4px 10px;
background: var(--color-primary-light);
border: 1px solid var(--color-primary);
border-radius: var(--radius);
color: var(--color-primary);
font-size: 13px;
cursor: pointer;
transition: background 0.15s;
&:hover {
background: var(--color-primary-hover);
border-color: var(--color-primary-hover);
background: #d0d9f8;
}
}
&.ant-btn-default {
border-color: var(--color-border-strong);
.basis-alert {
border-radius: var(--radius);
}
.basis-empty {
padding: 16px;
background: var(--color-bg-section);
border-radius: var(--radius);
border: 1px dashed var(--color-border-strong);
}
/* ==================== 表单底部按钮 ==================== */
.form-footer {
padding: 14px 32px;
background: var(--color-bg-section);
border-top: 1px solid var(--color-border);
display: flex;
justify-content: flex-end;
}
/* ==================== 表单控件样式覆盖 ==================== */
:deep(.ant-form-item) {
margin-bottom: 18px;
}
:deep(.ant-form-item-label > label) {
font-size: 13px;
font-weight: 500;
color: var(--color-text-secondary);
background: var(--color-bg-white);
}
:deep(.ant-input),
:deep(.ant-select-selector),
:deep(.ant-picker),
:deep(.ant-input-textarea textarea) {
border-radius: var(--radius) !important;
border-color: var(--color-border) !important;
font-size: 13px;
box-shadow: none !important;
&:hover {
border-color: var(--color-primary);
color: var(--color-primary);
background: var(--color-primary-light);
border-color: var(--color-border-strong) !important;
}
&:focus,
&.ant-input-focused {
border-color: var(--color-primary) !important;
box-shadow: 0 0 0 2px var(--color-primary-light) !important;
}
}
}
/* ==================== 加载状态 ==================== */
:deep(.ant-spin-container) {
min-height: 400px;
}
:deep(.ant-select-focused .ant-select-selector) {
border-color: var(--color-primary) !important;
box-shadow: 0 0 0 2px var(--color-primary-light) !important;
}
/* ==================== 响应式设计 ==================== */
@media (max-width: 768px) {
.form-header {
padding: 16px 20px;
/* ==================== 按钮样式 ==================== */
:deep(.ant-btn) {
border-radius: var(--radius);
font-weight: 500;
font-size: 13px;
height: 34px;
padding: 0 18px;
box-shadow: none;
&.ant-btn-primary {
background: var(--color-primary);
border-color: var(--color-primary);
&:hover {
background: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
}
&.ant-btn-default {
border-color: var(--color-border-strong);
color: var(--color-text-secondary);
background: var(--color-bg-white);
&:hover {
border-color: var(--color-primary);
color: var(--color-primary);
background: var(--color-primary-light);
}
}
}
.form-body {
padding: 20px;
/* ==================== 加载状态 ==================== */
:deep(.ant-spin-container) {
min-height: 400px;
}
.form-footer {
padding: 12px 20px;
/* ==================== 响应式设计 ==================== */
@media (max-width: 768px) {
.form-header {
padding: 16px 20px;
}
.form-body {
padding: 20px;
}
.form-footer {
padding: 12px 20px;
}
}
}
</style>
......@@ -253,12 +253,6 @@
const visible = ref(false);
const formRef = ref();
// const exeRules = ref([
// { value: 1, label: '事件触发' },
// { value: 2, label: '周期执行' },
// { value: 3, label: '一次性执行' },
// ]);
const formModel = reactive({
projectName: '',
projectType: '',
......@@ -347,7 +341,6 @@
resetForm();
closeModal();
} catch (e) {
console.log(e);
} finally {
setModalProps({ confirmLoading: false });
}
......
......@@ -4,12 +4,12 @@
<select id="selectSysDeployFormByDeployId" resultType="org.jeecg.modules.flowable.apithird.business.entity.FlowForm">
select t2.form_table_name as formTableName,t1.form_options as formContent,t1.form_name as formName,t1.form_id as formId from sys_form t1 left join sys_deploy_form t2 on t1.form_id = t2.form_id
where t2.deploy_id = #{deployId} limit 1
where t2.deploy_id = #{deployId} order by t2.id desc limit 1
</select>
<select id="selectSysFormByDeployId" resultType="org.jeecg.modules.flowable.apithird.business.entity.FlowForm">
select t1.* from sys_form t1 left join sys_deploy_form t2 on t1.form_id = t2.form_id
where t2.deploy_id = #{deployId} limit 1
where t2.deploy_id = #{deployId} order by t2.id desc limit 1
</select>
<update id="updateBisTabBpmStatus" parameterType="org.jeecg.modules.flowable.apithird.business.entity.FlowForm">
......@@ -18,9 +18,12 @@
bpm_status=#{formTp},
</if>
<if test="formTp != null and formTp == ''">
bpm_status=NULL,
bpm_status=NULL,deploy_id=null
</if>
deploy_id=#{formUrl} where ${formContent}
<if test="formUrl != null and formUrl != ''">
deploy_id=#{formUrl}
</if>
where ${formContent}
</update>
<update id="updateBisTabUid" parameterType="org.jeecg.modules.flowable.apithird.business.entity.FlowForm">
......
......@@ -19,4 +19,5 @@ public class FlowTaskVo {
private List<String> candidateUsers;
private List<String> candidateGroups;
private String userTaskid;
private boolean nodeisApprove;
}
package org.jeecg.modules.flowable.domain.vo;
import lombok.Data;
@Data
public class MyTaskFlowVo {
private java.lang.String formTableName;
private java.lang.String targetId;
private java.lang.String deployId;
private java.lang.String taskId;
private java.lang.String procInsId;
private java.lang.String executionId;
private java.lang.String procDefId;
private java.lang.String uid;
private java.lang.String roleid;
private java.lang.String taskDefinitionKey;
private boolean nodeisApprove;
}
......@@ -25,6 +25,8 @@ import org.jeecg.modules.flowable.common.constant.ProcessConstants;
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.entity.MyTaskFlowHis;
import org.jeecg.modules.stm.my.service.IMyTaskFlowHisService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -33,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
......@@ -50,6 +53,8 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
FlowTaskServiceImpl flowTaskService;
@Autowired
private ISysDeployFormService sysDeployFormService;
@Autowired
private IMyTaskFlowHisService myTaskFlowHisService;
private static final String BPMN_FILE_SUFFIX = ".bpmn";
......@@ -284,92 +289,6 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
}
}
// 给第一步申请人节点设置任务执行人和意见
// Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
// if (Objects.nonNull(task)) {
// taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getRealname() + "发起流程申请");
// taskService.setAssignee(task.getId(),sysUser.getUsername());
// taskService.complete(task.getId(), variables);
// }
/*======================todo 启动之后 回调以及关键数据保存======================*/
/**
//业务数据id
String dataId = variables.get("dataId").toString();
//如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
//设置数据
FlowNextDto nextFlowNode = flowTaskService.getNextFlowNode(task.getId(), variables);
taskService.complete(task.getId(), variables);
//下一个实例节点 多实例会是一个list,随意取一个即可 数组中定义Key是一致的
//Task task2 = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
List<Task> task2List = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().list();
Task task2 = null;
if(task2List.size()>0) task2 = task2List.get(0);
String doneUsers = business.getDoneUsers();
// 处理过流程的人
JSONArray doneUserList = new JSONArray();
if (StrUtil.isNotBlank(doneUsers)){
doneUserList = JSON.parseArray(doneUsers);
}
if (!doneUserList.contains(sysUser.getUsername())){
doneUserList.add(sysUser.getUsername());
}
if (nextFlowNode!=null){
//**有下一个节点
UserTask nextTask = nextFlowNode.getUserTask();
//能够处理下个节点的候选人
List<SysUser> nextFlowNodeUserList = nextFlowNode.getUserList();
List<String> collect_username = nextFlowNodeUserList.stream().map(SysUser::getUsername).collect(Collectors.toList());
//spring容器类名
String serviceImplName = business.getServiceImplName();
FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
List<String> beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(task2.getTaskDefinitionKey(), variables);
if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
// 删除后重写
for (Task task2One : task2List) {
for (String oldUser : collect_username) {
taskService.deleteCandidateUser(task2One.getId(),oldUser);
}
}
// 业务层有指定候选人,覆盖
for (Task task2One : task2List) {
for (String newUser : beforeParamsCandidateUsernames) {
taskService.addCandidateUser(task2One.getId(),newUser);
}
}
business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
}
business.setProcessDefinitionId(procDefId)
.setProcessInstanceId(processInstance.getProcessInstanceId())
.setActStatus(ActStatus.start)
.setProposer(sysUser.getUsername())
.setTaskId(task2.getId())
.setTaskName(nextTask.getName())
.setTaskNameId(nextTask.getId())
.setPriority(nextTask.getPriority())
.setDoneUsers(doneUserList.toJSONString())
.setTodoUsers(JSON.toJSONString(collect_username))
;
} else {
// **没有下一个节点,流程已经结束了
business.setProcessDefinitionId(procDefId)
.setProcessInstanceId(processInstance.getProcessInstanceId())
.setActStatus(ActStatus.pass)
.setProposer(sysUser.getUsername())
.setDoneUsers(doneUserList.toJSONString())
;
}
flowMyBusinessService.updateById(business);
//spring容器类名
String serviceImplName = business.getServiceImplName();
FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
// 流程处理完后,进行回调业务层
business.setValues(variables);
if (flowCallBackService!=null)flowCallBackService.afterFlowHandle(business);
*/
return Result.OK("流程启动成功");
} catch (Exception e) {
e.printStackTrace();
......@@ -439,6 +358,9 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
public Result startProcessInstanceByDeployId(String DeployId, Map<String, Object> variables) {
// 构建返回结果
Map<String, Object> result = new HashMap<>();
Date cdate = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String curdate = sdf.format(cdate);
try {
......@@ -517,87 +439,52 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl
}
MyTaskFlowHis taskFlowHis=new MyTaskFlowHis();
taskFlowHis.setTaskId(task.getId());
taskFlowHis.setProcDefId(task.getProcessDefinitionId());
taskFlowHis.setProcInsId(task.getProcessInstanceId());
taskFlowHis.setExecutionId(task.getExecutionId());
if(zdval!=null&&!zdval.equals("")){
taskFlowHis.setTargetId(zdval);
}
taskFlowHis.setDeployId(DeployId);
if(flowForm!=null&&flowForm.getFormTableName()!=null){
taskFlowHis.setFormTableName(flowForm.getFormTableName());
}
String userType="user";
String approvalId=sysUser.getId();
if(userType.equals("user")) {
taskFlowHis.setUid(approvalId);
} else {
taskFlowHis.setRoleid(approvalId);
}
taskFlowHis.setTaskDefinitionKey(task.getTaskDefinitionKey());
taskFlowHis.setOptionTime(cdate);
taskFlowHis.setOptionId(sysUser.getId());
taskFlowHis.setOptionType("操作类型");
taskFlowHis.setTaskType("发起");//发起 办理 转办 结束
taskFlowHis.setTaskName(task.getName());
taskFlowHis.setTaskTitle("标题");
taskFlowHis.setFlowName("");
myTaskFlowHisService.save(taskFlowHis);
}
/*======================todo 启动之后 回调以及关键数据保存======================*/
/**
//业务数据id
String dataId = variables.get("dataId").toString();
//如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
//设置数据
FlowNextDto nextFlowNode = flowTaskService.getNextFlowNode(task.getId(), variables);
taskService.complete(task.getId(), variables);
//下一个实例节点 多实例会是一个list,随意取一个即可 数组中定义Key是一致的
//Task task2 = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
List<Task> task2List = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active().list();
Task task2 = null;
if(task2List.size()>0) task2 = task2List.get(0);
String doneUsers = business.getDoneUsers();
// 处理过流程的人
JSONArray doneUserList = new JSONArray();
if (StrUtil.isNotBlank(doneUsers)){
doneUserList = JSON.parseArray(doneUsers);
}
if (!doneUserList.contains(sysUser.getUsername())){
doneUserList.add(sysUser.getUsername());
}
if (nextFlowNode!=null){
//**有下一个节点
UserTask nextTask = nextFlowNode.getUserTask();
//能够处理下个节点的候选人
List<SysUser> nextFlowNodeUserList = nextFlowNode.getUserList();
List<String> collect_username = nextFlowNodeUserList.stream().map(SysUser::getUsername).collect(Collectors.toList());
//spring容器类名
String serviceImplName = business.getServiceImplName();
FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
List<String> beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(task2.getTaskDefinitionKey(), variables);
if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
// 删除后重写
for (Task task2One : task2List) {
for (String oldUser : collect_username) {
taskService.deleteCandidateUser(task2One.getId(),oldUser);
}
}
// 业务层有指定候选人,覆盖
for (Task task2One : task2List) {
for (String newUser : beforeParamsCandidateUsernames) {
taskService.addCandidateUser(task2One.getId(),newUser);
}
}
business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
}
business.setProcessDefinitionId(procDefId)
.setProcessInstanceId(processInstance.getProcessInstanceId())
.setActStatus(ActStatus.start)
.setProposer(sysUser.getUsername())
.setTaskId(task2.getId())
.setTaskName(nextTask.getName())
.setTaskNameId(nextTask.getId())
.setPriority(nextTask.getPriority())
.setDoneUsers(doneUserList.toJSONString())
.setTodoUsers(JSON.toJSONString(collect_username))
;
} else {
// **没有下一个节点,流程已经结束了
business.setProcessDefinitionId(procDefId)
.setProcessInstanceId(processInstance.getProcessInstanceId())
.setActStatus(ActStatus.pass)
.setProposer(sysUser.getUsername())
.setDoneUsers(doneUserList.toJSONString())
;
}
flowMyBusinessService.updateById(business);
//spring容器类名
String serviceImplName = business.getServiceImplName();
FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
// 流程处理完后,进行回调业务层
business.setValues(variables);
if (flowCallBackService!=null)flowCallBackService.afterFlowHandle(business);
*/
return Result.OK(result);
} catch (Exception e) {
e.printStackTrace();
......
......@@ -139,7 +139,7 @@ public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlow
flowFormben.setFormTableName(tabname);
flowFormben.setFormContent(formContent);
flowFormben.setFormTp("");
flowFormben.setFormUrl(DeployId);
//flowFormben.setFormUrl(DeployId);
sysDeployFormService.updateBisTabBpmStatus(flowFormben);
}
}
......
......@@ -56,6 +56,7 @@ import org.jeecg.modules.flowable.domain.dto.FlowNextDto;
import org.jeecg.modules.flowable.domain.dto.FlowTaskDto;
import org.jeecg.modules.flowable.domain.dto.FlowViewerDto;
import org.jeecg.modules.flowable.domain.vo.FlowTaskVo;
import org.jeecg.modules.flowable.domain.vo.MyTaskFlowVo;
import org.jeecg.modules.flowable.factory.FlowServiceFactory;
import org.jeecg.modules.flowable.flow.CustomProcessDiagramGenerator;
import org.jeecg.modules.flowable.flow.FindNextNodeUtil;
......@@ -63,6 +64,8 @@ import org.jeecg.modules.flowable.flow.FlowableUtils;
import org.jeecg.modules.flowable.service.IFlowTaskService;
import org.jeecg.modules.stm.my.entity.MyTask;
import org.jeecg.modules.stm.my.entity.MyTaskFlow;
import org.jeecg.modules.stm.my.entity.MyTaskFlowHis;
import org.jeecg.modules.stm.my.service.IMyTaskFlowHisService;
import org.jeecg.modules.stm.my.service.IMyTaskFlowService;
import org.jeecg.modules.stm.my.service.IMyTaskService;
import org.jeecg.modules.system.service.ISysUserService;
......@@ -103,6 +106,8 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
private IMyTaskService myTaskService;
@Autowired
private IMyTaskFlowService myTaskFlowService;
@Autowired
private IMyTaskFlowHisService myTaskFlowHisService;
/**
* 完成任务
......@@ -113,52 +118,116 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
@Override
public Result complete(FlowTaskVo taskVo) {
Date cdate = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String curdate = sdf.format(cdate);
Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult();
if (Objects.isNull(task)) {
return Result.error("任务不存在");
}
// 根据流程定义ID查询流程定义对象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(task.getProcessDefinitionId())
.singleResult();
String deploymentId ="";
if (processDefinition != null) {
// 获取部署ID - 这就是你想要的deployId
deploymentId = processDefinition.getDeploymentId();
}
if (DelegationState.PENDING.equals(task.getDelegationState())) {
taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment());
taskService.resolveTask(taskVo.getTaskId(), taskVo.getValues());
//taskService.setAssignee(taskVo.getTaskId(),"e9ca23d68d884d4ebb19d07889727dae");
} else {
// 获取流程变量
Map<String, Object> formValues = getProcessVariables(taskVo.getTaskId());
String zdmc="";
String zdval="";
if(formValues.get("dataName")==null){
if(formValues.get("_value")!=null) {
Map zdv = (Map) formValues.get("_value");
zdmc = (String) zdv.get("dataName");
zdval = zdv.get("dataId").toString();
}
}else{
zdmc=(String)formValues.get("dataName");
zdval=formValues.get("dataId").toString();
}
taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment());
//Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
SysUser loginUser = iFlowThirdService.getLoginUser();
taskService.complete(taskVo.getTaskId(), taskVo.getValues());
String userType ="";
String approvalId="";
Object userTypeObject = taskVo.getValues().get("approvalType");
if(userTypeObject!=null) {
userType = userTypeObject.toString();
approvalId = taskVo.getValues().get("approval").toString();
}
MyTaskFlowHis taskFlowHis=new MyTaskFlowHis();
taskFlowHis.setTaskId(task.getId());
taskFlowHis.setProcDefId(task.getProcessDefinitionId());
taskFlowHis.setProcInsId(task.getProcessInstanceId());
taskFlowHis.setExecutionId(task.getExecutionId());
if(zdval!=null&&!zdval.equals("")){
taskFlowHis.setTargetId(zdval);
}
taskFlowHis.setDeployId(deploymentId);
Long tformId = Long.parseLong(task.getFormKey());
FlowForm tsysForm = flowFormService.getById(tformId); // 假设有这个方法
if(tsysForm!=null&&tsysForm.getFormTableName()!=null){
taskFlowHis.setFormTableName(tsysForm.getFormTableName());
}
if(userType.equals("user")) {
taskFlowHis.setUid(approvalId);
} else {
taskFlowHis.setRoleid(approvalId);
}
taskFlowHis.setTaskDefinitionKey(task.getTaskDefinitionKey());
taskFlowHis.setOptionTime(cdate);
taskFlowHis.setOptionId(loginUser.getId());
taskFlowHis.setOptionType("操作类型");
taskFlowHis.setTaskType("节点类型");//发起 办理 转办 结束
taskFlowHis.setTaskName(task.getName());
taskFlowHis.setTaskTitle("标题");
taskFlowHis.setFlowName("");
myTaskFlowHisService.save(taskFlowHis);
Task nextTask = taskService.createTaskQuery()
.processInstanceId(taskVo.getInstanceId())
.singleResult();
if(nextTask!=null){
String userType = userTypeObject.toString();
String approvalId = taskVo.getValues().get("approval").toString();
if(userType.equals("user")) {
taskService.setAssignee(nextTask.getId(), approvalId);
} else {
taskService.addCandidateGroup(nextTask.getId(),approvalId);
}
// 获取流程变量
Map<String, Object> formValues = getProcessVariables(taskVo.getTaskId());
String zdmc="";
String zdval="";
if(formValues.get("dataName")==null){
if(formValues.get("_value")!=null) {
Map zdv = (Map) formValues.get("_value");
zdmc = (String) zdv.get("dataName");
zdval = zdv.get("dataId").toString();
}
}else{
zdmc=(String)formValues.get("dataName");
zdval=formValues.get("dataId").toString();
}
// 处理表单数据
// Map<String, Object> formData = processFormData(task);
......@@ -168,7 +237,12 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
// 写入 待办
MyTask myTask = new MyTask();
myTask.setUid(approvalId);
if(userType.equals("user")) {
myTask.setUid(approvalId);
} else {
myTask.setRoleid(approvalId);
}
myTask.setTp(5);
myTask.setTarget(nextTask.getName());
if(zdval!=null&&!zdval.equals("")){
......@@ -183,21 +257,20 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
myTask.setDes("");
//myTask.setLinkAddr("/project/plan/StPlanManList?"+zdmc+"="+zdval);
if(sysForm!=null){
myTask.setLinkAddr(sysForm.getFormListurl());
if(sysForm.getFormTp().equals("2")){
myTask.setLinkAddr(sysForm.getFormListurl());
}else{
myTask.setLinkAddr("/flowable/task/todo/index");
}
}
myTaskService.save(myTask);
// 4. 根据流程定义ID查询流程定义对象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(task.getProcessDefinitionId())
.singleResult();
String deploymentId ="";
if (processDefinition != null) {
// 获取部署ID - 这就是你想要的deployId
deploymentId = processDefinition.getDeploymentId();
}
//有了以上信息,可以向相关表写入 相关信息了
MyTaskFlow taskFlow=new MyTaskFlow();
taskFlow.setTaskId(nextTask.getId());
......@@ -235,225 +308,31 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
}
}
}
//判断是否是最后一个节点
Task endnextTask = taskService.createTaskQuery()
.processInstanceId(taskVo.getInstanceId())
.singleResult();
if(endnextTask==null){
}else{
//是最后一个 id 判断是最后一个节点
// 获取流程变量
Map<String, Object> formValues = getProcessVariables(taskVo.getTaskId());
FlowForm flowForm=sysDeployFormService.selectSysDeployFormByDeployId(deploymentId);
String zdmc = Optional.ofNullable(formValues)
.map(map -> map.get("dataName"))
.map(Object::toString) // 安全转换为String
.orElse(null);
if(flowForm!=null){
String zdval = Optional.ofNullable(formValues)
.map(map -> map.get("dataId"))
.map(Object::toString) // 安全转换为String
.orElse(null);
// 通过流程定义ID查询流程定义对象
ProcessDefinition processDefinition = repositoryService
.createProcessDefinitionQuery()
.processDefinitionId(task.getProcessDefinitionId()) // 替换为你的流程定义ID
.singleResult();
if (processDefinition != null) {
// 从流程定义对象中获取部署ID
String DeployId = processDefinition.getDeploymentId();
FlowForm flowForm=sysDeployFormService.selectSysDeployFormByDeployId(DeployId);
if(flowForm!=null){
String tabname= flowForm.getFormTableName();
if(tabname!=null&&!tabname.equals("")&&zdmc!=null&&!zdmc.equals("")&&zdval!=null&&!zdval.equals("")){
String formContent=zdmc+"="+zdval;
FlowForm flowFormben=new FlowForm();
flowFormben.setFormTableName(tabname);
flowFormben.setFormContent(formContent);
flowFormben.setFormTp("3");
flowFormben.setFormUrl(DeployId);
sysDeployFormService.updateBisTabBpmStatus(flowFormben);
String tabname= flowForm.getFormTableName();
if(tabname!=null&&!tabname.equals("")&&zdmc!=null&&!zdmc.equals("")&&zdval!=null&&!zdval.equals("")){
String formContent=zdmc+"="+zdval;
FlowForm flowFormben=new FlowForm();
flowFormben.setFormTableName(tabname);
flowFormben.setFormContent(formContent);
flowFormben.setFormTp("3");
flowFormben.setFormUrl(deploymentId);
sysDeployFormService.updateBisTabBpmStatus(flowFormben);
}
}
}
}
}
}
return Result.OK();
/**
Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult();
if (Objects.isNull(task)){
return Result.error("任务不存在");
}
SysUser loginUser = iFlowThirdService.getLoginUser();
if (DelegationState.PENDING.equals(task.getDelegationState())) {
taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment());
//taskService.resolveTask(taskVo.getTaskId(), taskVo.getValues());
} else {
taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment());
taskService.setAssignee(taskVo.getTaskId(), loginUser.getUsername());
//taskService.complete(taskVo.getTaskId(), taskVo.getValues());
}
*/
/*======================审批通过 回调以及关键数据保存======================*/
/**
//业务数据id
String dataId = taskVo.getDataId();
//如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
//spring容器类名
String serviceImplName = business.getServiceImplName();
FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
// 流程变量
Map<String, Object> flowBeforeParamsValues = flowCallBackService.flowValuesOfTask(business.getTaskNameId(),taskVo.getValues());
//设置数据
Map<String, Object> values = taskVo.getValues();
if (MapUtil.isNotEmpty(flowBeforeParamsValues)){
// 业务层有设置变量,使用业务层的变量
values = flowBeforeParamsValues;
}
FlowNextDto nextFlowNode = this.getNextFlowNode(task.getId(), values);
//下一个实例节点
if (DelegationState.PENDING.equals(task.getDelegationState())) {
taskService.resolveTask(taskVo.getTaskId(), values);
} else {
taskService.complete(taskVo.getTaskId(), values);
}
List<Task> task2List = taskService.createTaskQuery().processInstanceId(business.getProcessInstanceId()).active().list();
Task task2 = null;
if (CollUtil.isNotEmpty(task2List)){
task2 = task2List.get(0);
}
// 下个节点候选人
List<String> beforeParamsCandidateUsernames = Lists.newArrayList();
if(task2!=null){
beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(task2.getTaskDefinitionKey(),taskVo.getValues());
}
List<String> candidateUsers = taskVo.getCandidateUsers();
if (CollUtil.isNotEmpty(candidateUsers)){
// 前端传入候选人 覆盖
beforeParamsCandidateUsernames = candidateUsers;
}
String doneUsers = business.getDoneUsers();
// 处理过流程的人
JSONArray doneUserList = new JSONArray();
if (StrUtil.isNotBlank(doneUsers)){
doneUserList = JSON.parseArray(doneUsers);
}
if (!doneUserList.contains(loginUser.getUsername())){
doneUserList.add(loginUser.getUsername());
}
if (task2!=null && task.getTaskDefinitionKey().equals(task2.getTaskDefinitionKey())){
// * 当前节点是会签节点,没有走完
business.setActStatus(ActStatus.doing)
.setTaskId(task2.getId())
.setDoneUsers(doneUserList.toJSONString())
;
String todoUsersStr = business.getTodoUsers();
JSONArray todosArr = JSON.parseArray(todoUsersStr);
// 删除后重写
for (Task task2One : task2List) {
for (Object oldUser : todosArr) {
taskService.deleteCandidateUser(task2One.getId(),oldUser.toString());
}
}
// 重写
if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
beforeParamsCandidateUsernames.remove(loginUser.getUsername());
// 业务层有指定候选人,覆盖
for (Task task2One : task2List) {
for (String newUser : beforeParamsCandidateUsernames) {
taskService.addCandidateUser(task2One.getId(),newUser);
}
}
business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
} else {
todosArr.remove(loginUser.getUsername());
for (Task task2One : task2List) {
for (Object oldUser : todosArr) {
taskService.addCandidateUser(task2One.getId(),oldUser.toString());
}
}
business.setTodoUsers(todosArr.toJSONString());
}
} else {
// * 下一节点是会签节点 或 普通用户节点,逻辑一致
if (nextFlowNode!=null){
//**有下一个节点
UserTask nextTask = nextFlowNode.getUserTask();
//能够处理下个节点的候选人
List<SysUser> nextFlowNodeUserList = nextFlowNode.getUserList();
List<String> collect_username = nextFlowNodeUserList.stream().map(SysUser::getUsername).collect(Collectors.toList());
if (CollUtil.isNotEmpty(candidateUsers)){
// 前端传入候选人
collect_username = candidateUsers;
}
business.setActStatus(ActStatus.doing)
.setTaskId(task2.getId())
.setTaskNameId(nextTask.getId())
.setTaskName(nextTask.getName())
.setPriority(nextTask.getPriority())
.setDoneUsers(doneUserList.toJSONString())
.setTodoUsers(JSON.toJSONString(collect_username))
;
// 删除后重写
for (Task task2One : task2List) {
for (String oldUser : collect_username) {
taskService.deleteCandidateUser(task2One.getId(),oldUser);
}
}
if (CollUtil.isEmpty(candidateUsers)&&CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
// 前端没有传入候选人 && 业务层有指定候选人,覆盖
for (Task task2One : task2List) {
for (String newUser : beforeParamsCandidateUsernames) {
taskService.addCandidateUser(task2One.getId(),newUser);
}
}
business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
} else {
for (Task task2One : task2List) {
for (String oldUser : collect_username) {
taskService.addCandidateUser(task2One.getId(),oldUser);
}
}
}
} else {
// **没有下一个节点,流程已经结束了
business.setActStatus(ActStatus.pass)
.setDoneUsers(doneUserList.toJSONString())
.setTodoUsers("")
.setTaskId("")
.setTaskNameId("")
.setTaskName("")
;
}
}
flowMyBusinessService.updateById(business);
// 流程处理完后,进行回调业务层
business.setValues(values);
if (flowCallBackService!=null)flowCallBackService.afterFlowHandle(business);
return Result.OK();
*/
}
@Override
public Result completeByDateId(FlowTaskVo flowTaskVo){
......@@ -591,223 +470,6 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
} catch (FlowableException e) {
throw new CustomException("无法取消或开始活动");
}
/**
if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) {
throw new CustomException("任务处于挂起状态");
}
// 当前任务 task
Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
// 获取流程定义信息
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
// 获取所有节点信息
Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
// 获取全部节点列表,包含子节点
Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
// 获取当前任务节点元素
FlowElement source = null;
if (allElements != null) {
for (FlowElement flowElement : allElements) {
// 类型为用户节点
if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
// 获取节点信息
source = flowElement;
}
}
}
// 目的获取所有跳转到的节点 targetIds
// 获取当前节点的所有父级用户任务节点
// 深度优先算法思想:延边迭代深入
List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
throw new CustomException("当前节点为初始任务节点,不能驳回");
}
// 获取活动 ID 即节点 Key
List<String> parentUserTaskKeyList = new ArrayList<>();
parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
// 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序
List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
// 数据清洗,将回滚导致的脏数据清洗掉
List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList);
// 此时历史任务实例为倒序,获取最后走的节点
List<String> targetIds = new ArrayList<>();
// 循环结束标识,遇到当前目标节点的次数
int number = 0;
StringBuilder parentHistoricTaskKey = new StringBuilder();
for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
// 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过
if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
continue;
}
parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
number++;
}
// 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次
// 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环
// number == 1,第一次遇到当前节点
// number == 2,第二次遇到,代表最后一次的循环范围
if (number == 2) {
break;
}
// 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点
if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
targetIds.add(historicTaskInstanceKey);
}
}
// 目的获取所有需要被跳转的节点 currentIds
// 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路
UserTask oneUserTask = parentUserTaskList.get(0);
// 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务
List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
List<String> runTaskKeyList = new ArrayList<>();
runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey()));
// 需驳回任务列表
List<String> currentIds = new ArrayList<>();
// 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务
List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
currentUserTaskList.forEach(item -> currentIds.add(item.getId()));
// 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
if (targetIds.size() > 1 && currentIds.size() > 1) {
throw new CustomException("任务出现多对多情况,无法撤回");
}
// 循环获取那些需要被撤回的节点的ID,用来设置驳回原因
List<String> currentTaskIds = new ArrayList<>();
currentIds.forEach(currentId -> runTaskList.forEach(runTask -> {
if (currentId.equals(runTask.getTaskDefinitionKey())) {
currentTaskIds.add(runTask.getId());
}
}));
// 设置驳回意见
currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment()));
SysUser loginUser = iFlowThirdService.getLoginUser();
try {
// 设置处理人
taskService.setAssignee(task.getId(), loginUser.getUsername());
// 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况
if (targetIds.size() > 1) {
// 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId()).
moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
}
// 如果父级任务只有一个,因此当前任务可能为网关中的任务
if (targetIds.size() == 1) {
// 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1)
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
}
*/
/*======================驳回 回调以及关键数据保存======================*/
/**
//业务数据id
String dataId = flowTaskVo.getDataId();
if (dataId==null) return;
//如果保存数据前未调用必调的FlowCommonService.initActBusiness方法,就会有问题
FlowMyBusiness business = flowMyBusinessService.getByDataId(dataId);
// 驳回到了上一个节点等待处理
List<Task> task2List = taskService.createTaskQuery().processInstanceId(business.getProcessInstanceId()).active().list();
Task task2 = task2List.get(0);
//spring容器类名
String serviceImplName = business.getServiceImplName();
FlowCallBackServiceI flowCallBackService = (FlowCallBackServiceI) SpringContextUtils.getBean(serviceImplName);
Map<String, Object> values = flowTaskVo.getValues();
if (values ==null){
values = MapUtil.newHashMap();
values.put("dataId",dataId);
} else {
values.put("dataId",dataId);
}
List<String> beforeParamsCandidateUsernames = flowCallBackService.flowCandidateUsernamesOfTask(task2.getTaskDefinitionKey(), values);
//设置数据
String doneUsers = business.getDoneUsers();
// 处理过流程的人
JSONArray doneUserList = new JSONArray();
if (StrUtil.isNotBlank(doneUsers)){
doneUserList = JSON.parseArray(doneUsers);
}
if (!doneUserList.contains(loginUser.getUsername())){
doneUserList.add(loginUser.getUsername());
}
business.setActStatus(ActStatus.reject)
.setTaskId(task2.getId())
.setTaskNameId(task2.getTaskDefinitionKey())
.setTaskName(task2.getName())
.setDoneUsers(doneUserList.toJSONString())
;
FlowElement targetElement = null;
if (allElements != null) {
for (FlowElement flowElement : allElements) {
// 类型为用户节点
if (flowElement.getId().equals(task2.getTaskDefinitionKey())) {
// 获取节点信息
targetElement = flowElement;
}
}
}
if (targetElement!=null){
UserTask targetTask = (UserTask) targetElement;
business.setPriority(targetTask.getPriority());
if (StrUtil.equals(business.getTaskNameId(),ProcessConstants.START_NODE)){
// 开始节点。设置处理人为申请人
business.setTodoUsers(JSON.toJSONString(Lists.newArrayList(business.getProposer())));
taskService.setAssignee(business.getTaskId(),business.getProposer());
} else {
List<SysUser> sysUserFromTask = getSysUserFromTask(targetTask);
List<String> collect_username = sysUserFromTask.stream().map(SysUser::getUsername).collect(Collectors.toList());
// 前端存入的候选人
List<String> candidateUsers = flowTaskVo.getCandidateUsers();
if (CollUtil.isNotEmpty(candidateUsers)){
collect_username = candidateUsers;
}
business.setTodoUsers(JSON.toJSONString(collect_username));
// 删除后重写
for (Task task2One : task2List) {
for (String oldUser : collect_username) {
taskService.deleteCandidateUser(task2One.getId(),oldUser);
}
}
if (CollUtil.isNotEmpty(beforeParamsCandidateUsernames)){
if (CollUtil.isNotEmpty(candidateUsers)){
beforeParamsCandidateUsernames = candidateUsers;
}
// 业务层有指定候选人,覆盖
for (Task task2One : task2List) {
for (String newUser : beforeParamsCandidateUsernames) {
taskService.addCandidateUser(task2One.getId(), newUser);
}
}
business.setTodoUsers(JSON.toJSONString(beforeParamsCandidateUsernames));
} else {
for (Task task2One : task2List) {
for (String oldUser : collect_username) {
taskService.addCandidateUser(task2One.getId(), oldUser);
}
}
}
}
}
flowMyBusinessService.updateById(business);
// 流程处理完后,进行回调业务层
business.setValues(values);
if (flowCallBackService!=null) flowCallBackService.afterFlowHandle(business);
} catch (FlowableObjectNotFoundException e) {
throw new CustomException("未找到流程实例,流程可能已发生变化");
} catch (FlowableException e) {
throw new CustomException("无法取消或开始活动");
}
*/
}
@Override
public void taskReturnByDataId(FlowTaskVo flowTaskVo){
......@@ -1991,18 +1653,22 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
//String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
String dataType="fixed";
String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE);
if(userType!=null) {
flowNextDto.setType(userType);
if (userType.equals("user") && (userTask.getAssignee() == null || (userTask.getAssignee() != null && userTask.getAssignee().equals("")))) {
dataType = "dynamic";
}
if (userType.equals("role") && (userTask.getCandidateGroups() == null || (userTask.getCandidateGroups() != null && userTask.getCandidateGroups().size() == 0))) {
dataType = "dynamic";
}
if (userType.equals("role") && (userTask.getCandidateGroups() != null && userTask.getCandidateGroups().size() > 0)) {
dataType = "fixed";
}
}else{
flowNextDto.setType("user");//why 2026-4-13 如果是null, 强制指定
}
flowNextDto.setDataType(dataType);
flowNextDto.setType(userType);
if(userType.equals("user")&&(userTask.getAssignee()==null||(userTask.getAssignee()!=null&&userTask.getAssignee().equals("")))){
dataType="dynamic";
}
if(userType.equals("role")&&(userTask.getCandidateGroups()==null||(userTask.getCandidateGroups()!=null&&userTask.getCandidateGroups().size()==0))){
dataType="dynamic";
}
if(userType.equals("role")&&(userTask.getCandidateGroups()!=null&&userTask.getCandidateGroups().size()>0)){
dataType="fixed";
}
flowNextDto.setDataType(dataType);
}
......@@ -2830,7 +2496,20 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask
//flowTaskVo.getDeploymentId()
//flowTaskVo.getDataId()
MyTaskFlow myTaskFlow= myTaskFlowService.selectMyTaskFlowByDeployId(flowTaskVo.getDeploymentId(),flowTaskVo.getDataId());
return Result.OK(myTaskFlow);
MyTaskFlowVo vo=new MyTaskFlowVo();
vo.setTaskId(myTaskFlow.getTaskId());
vo.setDeployId(myTaskFlow.getDeployId());
vo.setProcDefId(myTaskFlow.getProcDefId());
vo.setProcInsId(myTaskFlow.getProcInsId());
vo.setExecutionId(myTaskFlow.getExecutionId());
FlowNextDto flowtDto=getFlowNodeType(myTaskFlow.getTaskId());
if(flowtDto!=null){
vo.setNodeisApprove(flowtDto.isNodeisApprove());
}
return Result.OK(vo);
}
......
package org.jeecg.modules.stm.my.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.stm.my.entity.MyTaskFlowHis;
import org.jeecg.modules.stm.my.service.IMyTaskFlowHisService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: my_task_flow_his
* @Author: jeecg-boot
* @Date: 2026-04-10
* @Version: V1.0
*/
@Tag(name="my_task_flow_his")
@RestController
@RequestMapping("/my/myTaskFlowHis")
@Slf4j
public class MyTaskFlowHisController extends JeecgController<MyTaskFlowHis, IMyTaskFlowHisService> {
@Autowired
private IMyTaskFlowHisService myTaskFlowHisService;
/**
* 分页列表查询
*
* @param myTaskFlowHis
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "my_task_flow_his-分页列表查询")
@Operation(summary="my_task_flow_his-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<MyTaskFlowHis>> queryPageList(MyTaskFlowHis myTaskFlowHis,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<MyTaskFlowHis> queryWrapper = QueryGenerator.initQueryWrapper(myTaskFlowHis, req.getParameterMap());
Page<MyTaskFlowHis> page = new Page<MyTaskFlowHis>(pageNo, pageSize);
IPage<MyTaskFlowHis> pageList = myTaskFlowHisService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param myTaskFlowHis
* @return
*/
@AutoLog(value = "my_task_flow_his-添加")
@Operation(summary="my_task_flow_his-添加")
@RequiresPermissions("my:my_task_flow_his:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody MyTaskFlowHis myTaskFlowHis) {
myTaskFlowHisService.save(myTaskFlowHis);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param myTaskFlowHis
* @return
*/
@AutoLog(value = "my_task_flow_his-编辑")
@Operation(summary="my_task_flow_his-编辑")
@RequiresPermissions("my:my_task_flow_his:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody MyTaskFlowHis myTaskFlowHis) {
myTaskFlowHisService.updateById(myTaskFlowHis);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "my_task_flow_his-通过id删除")
@Operation(summary="my_task_flow_his-通过id删除")
@RequiresPermissions("my:my_task_flow_his:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
myTaskFlowHisService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "my_task_flow_his-批量删除")
@Operation(summary="my_task_flow_his-批量删除")
@RequiresPermissions("my:my_task_flow_his:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.myTaskFlowHisService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "my_task_flow_his-通过id查询")
@Operation(summary="my_task_flow_his-通过id查询")
@GetMapping(value = "/queryById")
public Result<MyTaskFlowHis> queryById(@RequestParam(name="id",required=true) String id) {
MyTaskFlowHis myTaskFlowHis = myTaskFlowHisService.getById(id);
if(myTaskFlowHis==null) {
return Result.error("未找到对应数据");
}
return Result.OK(myTaskFlowHis);
}
/**
* 导出excel
*
* @param request
* @param myTaskFlowHis
*/
@RequiresPermissions("my:my_task_flow_his:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, MyTaskFlowHis myTaskFlowHis) {
return super.exportXls(request, myTaskFlowHis, MyTaskFlowHis.class, "my_task_flow_his");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("my:my_task_flow_his:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, MyTaskFlowHis.class);
}
}
......@@ -96,4 +96,6 @@ public class MyTask implements Serializable {
@Excel(name = "描述", width = 15)
private String des;
private String roleid;
}
......@@ -74,4 +74,6 @@ public class MyTaskFlow implements Serializable {
private java.lang.String taskDefinitionKey;
}
package org.jeecg.modules.stm.my.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: my_task_flow_his
* @Author: jeecg-boot
* @Date: 2026-04-10
* @Version: V1.0
*/
@Data
@TableName("my_task_flow_his")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="my_task_flow_his")
public class MyTaskFlowHis implements Serializable {
private static final long serialVersionUID = 1L;
/**id*/
@TableId(type = IdType.AUTO)
@Schema(description = "id")
private java.lang.Integer id;
/**表单的表名ID*/
@Excel(name = "表单的表名ID", width = 15)
@Schema(description = "表单的表名ID")
private java.lang.String formTableName;
/**业务主表ID*/
@Excel(name = "业务主表ID", width = 15)
@Schema(description = "业务主表ID")
private java.lang.String targetId;
/**部署ID*/
@Excel(name = "部署ID", width = 15)
@Schema(description = "部署ID")
private java.lang.String deployId;
/**任务ID*/
@Excel(name = "任务ID", width = 15)
@Schema(description = "任务ID")
private java.lang.String taskId;
/**实例ID*/
@Excel(name = "实例ID", width = 15)
@Schema(description = "实例ID")
private java.lang.String procInsId;
/**执行ID*/
@Excel(name = "执行ID", width = 15)
@Schema(description = "执行ID")
private java.lang.String executionId;
/**流程定义ID*/
@Excel(name = "流程定义ID", width = 15)
@Schema(description = "流程定义ID")
private java.lang.String procDefId;
/**代办人*/
@Excel(name = "代办人", width = 15)
@Schema(description = "代办人")
private java.lang.String uid;
/**代办角色*/
@Excel(name = "代办角色", width = 15)
@Schema(description = "代办角色")
private java.lang.String roleid;
/**节点ID*/
@Excel(name = "节点ID", width = 15)
@Schema(description = "节点ID")
private java.lang.String taskDefinitionKey;
/**操作时间*/
@Excel(name = "操作时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "操作时间")
private java.util.Date optionTime;
/**操作人*/
@Excel(name = "操作人", width = 15)
@Schema(description = "操作人")
private java.lang.String optionId;
/**操作类型*/
@Excel(name = "操作类型", width = 15)
@Schema(description = "操作类型")
private java.lang.String optionType;
/**节点类型*/
@Excel(name = "节点类型", width = 15)
@Schema(description = "节点类型")
private java.lang.String taskType;
/**节点名称*/
@Excel(name = "节点名称", width = 15)
@Schema(description = "节点名称")
private java.lang.String taskName;
/**标题*/
@Excel(name = "标题", width = 15)
@Schema(description = "标题")
private java.lang.String taskTitle;
/**流程名称*/
@Excel(name = "流程名称", width = 15)
@Schema(description = "流程名称")
private java.lang.String flowName;
}
package org.jeecg.modules.stm.my.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.stm.my.entity.MyTaskFlowHis;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @Description: my_task_flow_his
* @Author: jeecg-boot
* @Date: 2026-04-10
* @Version: V1.0
*/
public interface MyTaskFlowHisMapper extends BaseMapper<MyTaskFlowHis> {
}
package org.jeecg.modules.stm.my.service;
import org.jeecg.modules.stm.my.entity.MyTaskFlowHis;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @Description: my_task_flow_his
* @Author: jeecg-boot
* @Date: 2026-04-10
* @Version: V1.0
*/
public interface IMyTaskFlowHisService extends IService<MyTaskFlowHis> {
}
package org.jeecg.modules.stm.my.service.impl;
import org.jeecg.modules.stm.my.entity.MyTaskFlowHis;
import org.jeecg.modules.stm.my.mapper.MyTaskFlowHisMapper;
import org.jeecg.modules.stm.my.service.IMyTaskFlowHisService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
/**
* @Description: my_task_flow_his
* @Author: jeecg-boot
* @Date: 2026-04-10
* @Version: V1.0
*/
@Service
public class MyTaskFlowHisServiceImpl extends ServiceImpl<MyTaskFlowHisMapper, MyTaskFlowHis> implements IMyTaskFlowHisService {
}
......@@ -13,6 +13,8 @@ import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.stm.baosong.entity.BaosongTaskRecord;
import org.jeecg.modules.stm.baosong.service.IBaosongTaskRecordService;
import org.jeecg.modules.stm.page.entity.PageTitleconfig;
import org.jeecg.modules.system.entity.SysUserRole;
import org.jeecg.modules.system.service.ISysUserRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
......@@ -24,10 +26,8 @@ import org.jeecg.modules.stm.rectify.plan.service.IStRectifyPlanService;
import org.jeecg.modules.stm.utils.UserUtil;
import jakarta.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
......@@ -46,6 +46,8 @@ public class PageTitleconfigController extends JeecgController<PageTitleconfig,
@Autowired
private IBaosongTaskRecordService baosongTaskRecordService;
@Autowired
private ISysUserRoleService sysUserRoleService;
@GetMapping(value = "/list")
public Result<IPage<PageTitleconfig>> queryPageList(PageTitleconfig pageTitleconfig,
......@@ -90,7 +92,30 @@ public class PageTitleconfigController extends JeecgController<PageTitleconfig,
//获取待办事项列表,待审批和待填报
QueryWrapper<MyTask> queryWrapper = new QueryWrapper();
String userId = UserUtil.getUserId();
queryWrapper.eq("sta",0).eq("uid",userId).orderByDesc("priority").orderByDesc("st_time");
String roleids="";
List<SysUserRole> userRole = sysUserRoleService.list(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId, userId));
for(int u=0;u<userRole.size();u++){
SysUserRole sysUserRole=userRole.get(u);
roleids=roleids+sysUserRole.getRoleId()+",";
}
if(!roleids.equals("")){
roleids=roleids.substring(0,roleids.length()-1);
}
// 创建一个最终变量或实际上的最终变量用于 lambda
final String finalRoleids = roleids;
//queryWrapper.eq("sta",0).eq("uid",userId).orderByDesc("priority").orderByDesc("st_time");
// 修改:uid 和 roleid 是 OR 关系
queryWrapper.eq("sta", 0)
.and(wrapper -> {
wrapper.eq("uid", userId);
if(!"".equals(finalRoleids)){
wrapper.or().in("roleid", Arrays.asList(finalRoleids.split(",")));
}
})
.orderByDesc("priority")
.orderByDesc("st_time");
queryWrapper.last("limit 5");
List<MyTask> list = myTaskService.list(queryWrapper);
return Result.OK(list);
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.stm.my.mapper.MyTaskFlowHisMapper">
</mapper>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论