提交 54c6cef5 authored 作者: kxjia's avatar kxjia

完善程序

上级 fe7183fd
...@@ -200,7 +200,7 @@ export function getNodesByTableName(tableName) { ...@@ -200,7 +200,7 @@ export function getNodesByTableName(tableName) {
export function addMyTaskFlow(data) { export function addMyTaskFlow(data) {
return defHttp.post({ return defHttp.post({
url: '//my/myTaskFlow/add', url: '/my/myTaskFlow/add',
data: data data: data
}) })
} }
...@@ -149,7 +149,6 @@ export function getMyTaskFlow(data) { ...@@ -149,7 +149,6 @@ export function getMyTaskFlow(data) {
// 得到退回的节点 // 得到退回的节点
export function getRejectNode(query) { export function getRejectNode(query) {
alert(JSON.stringify(query))
return defHttp.get({ return defHttp.get({
url: '/flowable/form/getRejectTargetNode', url: '/flowable/form/getRejectTargetNode',
params: query params: query
......
...@@ -3,13 +3,15 @@ ...@@ -3,13 +3,15 @@
<a-card class="form-card" :bordered="false"> <a-card class="form-card" :bordered="false">
<template #title> <template #title>
<div class="form-header"> <div class="form-header">
<span class="form-title">当前待办[{{ editableNode?.name || '无' }}]</span> <span class="form-title">当前待办<font color="red">[{{ editableNode?.name || '无' }}]</font></span>
<a-button color="blue" v-if="!isEditStatus" @click="handleSetEditStatus">转阅</a-button> <a-space>
<a-button color="blue" v-if="!isEditStatus" @click="handleSetEditStatus">转发</a-button> <a-button v-if="props.showApprovalPanel" type="primary" ghost @click="handleApproval">审批</a-button>
<a-button color="blue" v-if="!isEditStatus" @click="handleSetEditStatus">退回</a-button> <a-button v-else type="primary" ghost @click="handleSend">发送</a-button>
<a-button type="primary" ghost @click="handleReject">驳回</a-button>
<a-button color="blue" v-if="!isEditStatus" @click="handleSetEditStatus">编辑</a-button> <a-button type="primary" ghost @click="handleTransmit">转办</a-button>
<a-button color="blue" v-if="isEditStatus" @click="handleSetEditStatus">只读</a-button> <a-button type="primary" ghost @click="handleRead">转阅</a-button>
</a-space>
</div> </div>
</template> </template>
<div class="form-content" v-if="editableNode"> <div class="form-content" v-if="editableNode">
...@@ -20,18 +22,28 @@ ...@@ -20,18 +22,28 @@
:readonly="false" :readonly="false"
:form-data="props.formData" :form-data="props.formData"
:current-flow-node="editableNode" :current-flow-node="editableNode"
:showApprovalPanel="props.showApprovalPanel"
@update:form-data="handleFormDataUpdate" @update:form-data="handleFormDataUpdate"
/> />
</div> </div>
<div v-else class="empty-form-state"> <div v-else class="empty-form-state">
<a-empty description="未找到可编辑的表单节点" /> <a-empty description="未找到可编辑的表单节点" />
</div> </div>
</a-card> </a-card>
<ApprovalModal @register="registerApprovalModal" />
<TaskModal @register="registerTaskModal" @success="handleFormDataUpdate"/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, defineAsyncComponent, h, watch, ComponentPublicInstance, nextTick } from 'vue' import { ref, defineAsyncComponent, h, watch, ComponentPublicInstance, nextTick } from 'vue'
import ApprovalModal from './components/ApprovalModal.vue';
import TaskModal from './components/TaskModal.vue';
import { useModal } from '/@/components/Modal';
const [registerApprovalModal, {openModal}] = useModal();
const [registerTaskModal, {openModal: taskOpenModal}] = useModal();
interface WorkflowNode { interface WorkflowNode {
id: string id: string
...@@ -41,6 +53,7 @@ interface WorkflowNode { ...@@ -41,6 +53,7 @@ interface WorkflowNode {
[key: string]: any [key: string]: any
} }
// 表单组件实例类型 // 表单组件实例类型
interface FormComponentInstance extends ComponentPublicInstance { interface FormComponentInstance extends ComponentPublicInstance {
validate?: () => Promise<any> validate?: () => Promise<any>
...@@ -59,6 +72,18 @@ const props = defineProps({ ...@@ -59,6 +72,18 @@ const props = defineProps({
type: String, type: String,
default: '' default: ''
}, },
deployId: {
type: String,
default: ''
},
taskId: {
type: String,
default: ''
},
procInsId: {
type: String,
default: ''
},
externalFormData: { externalFormData: {
type: Object as () => Record<string, any>, type: Object as () => Record<string, any>,
default: () => ({}) default: () => ({})
...@@ -70,22 +95,96 @@ const props = defineProps({ ...@@ -70,22 +95,96 @@ const props = defineProps({
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false
},
assignee: {
type: String,
default: ''
},
userType: {
type: String,
default: ''
},
showApprovalPanel: {
type: Boolean,
default: false
} }
}) })
const emit = defineEmits(['update:form-data', 'form-mounted']) function handleApproval() {
openModal(true, {
isUpdate: false,
showFooter: true,
taskType: 'approval',
});
}
function handleSend() {
taskOpenModal(true, {
taskTitle: '发送',
isUpdate: false,
showFooter: true,
taskType: 'send',
deployId: props.deployId,
taskId: props.taskId,
procInsId: props.procInsId,
dataId: props.dataId,
assignee: props.assignee,
userType: props.userType,
});
}
function handleReject() {
taskOpenModal(true, {
taskTitle: '驳回',
isUpdate: false,
showFooter: true,
taskType: 'reject',
deployId: props.deployId,
taskId: props.taskId,
procInsId: props.procInsId,
dataId: props.dataId,
assignee: props.assignee,
userType: props.userType,
});
}
function handleTransmit() {
taskOpenModal(true, {
taskTitle: '转办',
isUpdate: false,
showFooter: true,
taskType: 'transmit',
deployId: props.deployId,
taskId: props.taskId,
procInsId: props.procInsId,
dataId: props.dataId,
});
}
function handleRead() {
taskOpenModal(true, {
taskTitle: '转阅',
isUpdate: false,
showFooter: true,
taskType: 'read',
deployId: props.deployId,
taskId: props.taskId,
procInsId: props.procInsId,
dataId: props.dataId,
assignee: props.assignee,
userType: props.userType,
});
}
// 组件缓存 const emit = defineEmits(['update:form-data', 'form-mounted'])
const componentCache = new Map() const componentCache = new Map()
const modules = import.meta.glob('@/views/**/*.vue') const modules = import.meta.glob('@/views/**/*.vue')
// 表单组件实例
const formComponentRef = ref<FormComponentInstance | null>(null) const formComponentRef = ref<FormComponentInstance | null>(null)
const formData = ref<any>({}) const formData = ref<any>({})
const isEditStatus = ref(false); const isEditStatus = ref(false);
// 设置表单组件 ref
function setFormRef(el: any, nodeId: string) { function setFormRef(el: any, nodeId: string) {
if (el) { if (el) {
formComponentRef.value = el formComponentRef.value = el
...@@ -109,8 +208,10 @@ async function callInitFormData(formComponent: FormComponentInstance) { ...@@ -109,8 +208,10 @@ async function callInitFormData(formComponent: FormComponentInstance) {
} }
// 处理表单数据更新 // 处理表单数据更新
function handleFormDataUpdate(data: any) { async function handleFormDataUpdate(data: any) {
formData.value = { ...formData.value, ...data } const curFormData = await getFormData();
formData.value = { ...curFormData, ...data }
await handleSaveCurForm(curFormData.value)
emit('update:form-data', formData.value) emit('update:form-data', formData.value)
} }
...@@ -122,11 +223,9 @@ function getFormInstance(): FormComponentInstance | null { ...@@ -122,11 +223,9 @@ function getFormInstance(): FormComponentInstance | null {
// 获取表单数据 // 获取表单数据
async function getFormData(): Promise<any> { async function getFormData(): Promise<any> {
const formComponent = formComponentRef.value const formComponent = formComponentRef.value
if (!formComponent) { if (!formComponent) {
return formData.value return formData.value
} }
if (typeof formComponent.getFormData === 'function') { if (typeof formComponent.getFormData === 'function') {
try { try {
const data = await formComponent.getFormData() const data = await formComponent.getFormData()
...@@ -135,7 +234,6 @@ async function getFormData(): Promise<any> { ...@@ -135,7 +234,6 @@ async function getFormData(): Promise<any> {
console.error('调用 getFormData 失败:', error) console.error('调用 getFormData 失败:', error)
} }
} }
if (formComponent.formData !== undefined) { if (formComponent.formData !== undefined) {
return formComponent.formData return formComponent.formData
} }
...@@ -146,11 +244,9 @@ async function getFormData(): Promise<any> { ...@@ -146,11 +244,9 @@ async function getFormData(): Promise<any> {
// 验证表单 // 验证表单
async function validateForm(): Promise<boolean> { async function validateForm(): Promise<boolean> {
const formComponent = formComponentRef.value const formComponent = formComponentRef.value
if (!formComponent) { if (!formComponent) {
return true return true
} }
if (typeof formComponent.validate === 'function') { if (typeof formComponent.validate === 'function') {
try { try {
await formComponent.validate() await formComponent.validate()
...@@ -159,7 +255,6 @@ async function validateForm(): Promise<boolean> { ...@@ -159,7 +255,6 @@ async function validateForm(): Promise<boolean> {
return false return false
} }
} }
return true return true
} }
...@@ -256,16 +351,17 @@ function createErrorComponent(msg: string) { ...@@ -256,16 +351,17 @@ function createErrorComponent(msg: string) {
} }
} }
// 处理编辑状态切换
function handleSetEditStatus() { const handleSaveCurForm = async (curFormData) => {
isEditStatus.value = !isEditStatus.value const valid = await validateForm();
if (formComponentRef.value&&typeof formComponentRef.value.setFormDisabledStatus === 'function') { if (!valid) {
return
formComponentRef.value.setFormDisabledStatus(false) }
if (formComponentRef.value&&typeof formComponentRef.value.saveForm === 'function') {
await formComponentRef.value.saveForm(curFormData)
} }
} }
// 暴露方法给父组件
defineExpose({ defineExpose({
getFormData, getFormData,
validateForm, validateForm,
......
...@@ -31,30 +31,16 @@ ...@@ -31,30 +31,16 @@
:class="{ 'last-card': index === readonlyNodes.length - 1 }" :class="{ 'last-card': index === readonlyNodes.length - 1 }"
> >
<template #extra> <template #extra>
<a-tag :color="index === readonlyNodes.length - 1 ? 'orange' : 'green'" size="small"> <a-button type="link" size="small" @click="togglePreview(node.id)">
{{ expandedPreviewId === node.id ? '收起' : '展开' }}
</a-button>
<!-- <a-tag :color="index === readonlyNodes.length - 1 ? 'orange' : 'green'" size="small">
{{ index === readonlyNodes.length - 1 ? '已通过' : '已完成' }} {{ index === readonlyNodes.length - 1 ? '已通过' : '已完成' }}
</a-tag> </a-tag> -->
</template> </template>
<div class="history-card-content">
<div class="node-info">
<span class="node-label">表单名称:</span>
<span class="node-value">{{ node.formName || node.name }}</span>
</div>
<div class="node-info">
<span class="node-label">处理时间:</span>
<span class="node-value">{{ node.processTime || '--' }}</span>
</div>
</div>
<!-- 显示历史表单数据 --> <!-- 显示历史表单数据 -->
<template v-if="showHistoryFormData"> <template v-if="showHistoryFormData">
<a-divider style="margin: 12px 0" />
<div class="history-form-preview"> <div class="history-form-preview">
<div class="preview-header">
<span>表单数据预览</span>
<a-button type="link" size="small" @click="togglePreview(node.id)">
{{ expandedPreviewId === node.id ? '收起' : '展开' }}
</a-button>
</div>
<div v-show="expandedPreviewId === node.id" class="form-preview-content"> <div v-show="expandedPreviewId === node.id" class="form-preview-content">
<component <component
:is="getComponent(node)" :is="getComponent(node)"
......
...@@ -25,11 +25,8 @@ ...@@ -25,11 +25,8 @@
<!-- 用户/角色选择区域(未选择时) --> <!-- 用户/角色选择区域(未选择时) -->
<a-form-item <a-form-item
v-if="!hasAssignee" v-if="!assigneeId"
:label="localUserType === 'user' ? '选择用户' : '选择角色'" :label="localUserType === 'user' ? '选择用户' : '选择角色'"
:required="required"
:validate-status="validateStatus"
:help="validateHelp"
> >
<div class="assignee-selector"> <div class="assignee-selector">
<a-input <a-input
...@@ -46,7 +43,7 @@ ...@@ -46,7 +43,7 @@
</a-form-item> </a-form-item>
<!-- 已指定信息展示 --> <!-- 已指定信息展示 -->
<div v-if="hasAssignee" class="assignee-info-wrapper"> <div v-if="assigneeId" class="assignee-info-wrapper">
<div class="assignee-info-label">已指定{{ localUserType === 'user' ? '用户' : '角色' }}:</div> <div class="assignee-info-label">已指定{{ localUserType === 'user' ? '用户' : '角色' }}:</div>
<a-descriptions :column="1" size="small" bordered> <a-descriptions :column="1" size="small" bordered>
<a-descriptions-item :label="localUserType === 'user' ? '用户ID' : '角色ID'"> <a-descriptions-item :label="localUserType === 'user' ? '用户ID' : '角色ID'">
...@@ -122,9 +119,12 @@ const assigneeDisplayName = ref('') ...@@ -122,9 +119,12 @@ const assigneeDisplayName = ref('')
const confirmLoading = ref(false) const confirmLoading = ref(false)
const dataId = ref('') const dataId = ref('')
const deployId = ref('') const deployId = ref('')
const taskId = ref('')
const hasAssignee = ref(false)
// 使用 useDrawerInner 接收外部传入的数据 // 使用 useDrawerInner 接收外部传入的数据
const [registerBasicDrawer, { closeDrawer, setDrawerProps }] = useDrawerInner((data) => { const [registerBasicDrawer, { closeDrawer, setDrawerProps }] = useDrawerInner((data) => {
alert(JSON.stringify(data))
if (data) { if (data) {
dataId.value = data.dataId || '' dataId.value = data.dataId || ''
deployId.value = data.deployId || '' deployId.value = data.deployId || ''
...@@ -186,7 +186,7 @@ const handleConfirm = async () => { ...@@ -186,7 +186,7 @@ const handleConfirm = async () => {
deployId: myTaskFlow.deployId || '', deployId: myTaskFlow.deployId || '',
taskId: myTaskFlow.taskId, taskId: myTaskFlow.taskId,
dataId: dataId.value, dataId: dataId.value,
comment: 'eeeeeee', comment: '',
values: { values: {
approval: assigneeId.value, approval: assigneeId.value,
approvalType: localUserType.value approvalType: localUserType.value
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<a-drawer <a-drawer
:title="drawerTitle" :title="drawerTitle"
:visible="visible" :visible="visible"
:width="drawerWidth" width="80%"
:closable="true" :closable="true"
:mask-closable="maskClosable" :mask-closable="maskClosable"
:header-style="{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }" :header-style="{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }"
...@@ -13,13 +13,12 @@ ...@@ -13,13 +13,12 @@
<div class="drawer-container"> <div class="drawer-container">
<a-row :gutter="10" class="drawer-row" :wrap="false"> <a-row :gutter="10" class="drawer-row" :wrap="false">
<!-- 左侧历史面板 --> <!-- 左侧历史面板 -->
<a-col :width="props.leftPanelWidth"> <a-col :width="props.leftPanelWidth" v-if="showHistoryVisible">
<HistoryPanel style="width: 100%;height: 100%;" <HistoryPanel style="width: 100%;height: 100%;"
:readonly-nodes="readonlyNodes" :readonly-nodes="readonlyNodes"
:data-id="dataId" :data-id="dataId"
:show-history-form-data="props.showHistoryFormData" :show-history-form-data="props.showHistoryFormData"
/> />
</a-col> </a-col>
<!-- 中间表单区域 --> <!-- 中间表单区域 -->
...@@ -32,14 +31,21 @@ ...@@ -32,14 +31,21 @@
:form-data="props.formData" :form-data="props.formData"
:form-bpm="formBpm" :form-bpm="formBpm"
:disabled="showApprovalPanel || false" :disabled="showApprovalPanel || false"
:deploy-id="props.deployId"
:task-id="props.taskId"
:proc-ins-id="props.procInsId"
:assignee="props.assignee"
:user-type="props.userType"
:show-approval-panel="props.showApprovalPanel"
@update:form-data="handleFormDataUpdate" @update:form-data="handleFormDataUpdate"
@form-mounted="handleFormMounted" @form-mounted="handleFormMounted"
/> />
</a-col> </a-col>
<!-- 右侧审核面板 --> <!-- 右侧审核面板 -->
<a-col v-if="showApprovalPanel" :width="props.rightPanelWidth"> <!-- <a-col v-if="showApprovalPanel" :width="props.rightPanelWidth">
<ApprovalPanel style="width: 100%;height: 100%;" <ApprovalPanel style="width: 100%;height: 100%;"
ref="approvalPanelRef" ref="approvalPanelRef"
:current-node="editableNode" :current-node="editableNode"
...@@ -52,13 +58,14 @@ ...@@ -52,13 +58,14 @@
@success="handleApprovalSuccess" @success="handleApprovalSuccess"
@approval-fail="handleApprovalFail" @approval-fail="handleApprovalFail"
/> />
</a-col> -->
</a-col>
</a-row> </a-row>
</div> </div>
<template #extra> <template #extra>
<a-button @click="handleHistoryHidden()" v-if="showHistoryVisible" type="primary">隐藏历史</a-button>
<a-button @click="handleHistoryHidden()" v-else type="primary">显示历史</a-button>
<a-button @click="openHistoryDrawer()" type="primary">流程追踪</a-button> <a-button @click="openHistoryDrawer()" type="primary">流程追踪</a-button>
<a-button @click="handleClose()" type="primary">关闭抽屉</a-button> <a-button @click="handleClose()" type="primary">关闭</a-button>
</template> </template>
<FlowHistoryDrawer @register="refFlowHistoryDrawer"/> <FlowHistoryDrawer @register="refFlowHistoryDrawer"/>
</a-drawer> </a-drawer>
...@@ -75,7 +82,9 @@ import { useDrawer } from '/@/components/Drawer'; ...@@ -75,7 +82,9 @@ import { useDrawer } from '/@/components/Drawer';
const formBpm = ref(true) const formBpm = ref(true)
const formDisabled = ref(false) const formDisabled = ref(false)
const drawerHistoryVisible = ref(false) const showHistoryVisible = ref(true)
const [refFlowHistoryDrawer, { openDrawer }] = useDrawer(); const [refFlowHistoryDrawer, { openDrawer }] = useDrawer();
...@@ -96,30 +105,25 @@ interface WorkflowNode { ...@@ -96,30 +105,25 @@ interface WorkflowNode {
} }
const props = defineProps({ const props = defineProps({
// 基础配置
visible: {
type: Boolean,
default: false
},
title: { title: {
type: String, type: String,
default: '表单处理' default: '工作流表单'
}, },
width: { visible: {
type: [Number, String], type: Boolean,
default: "90%" default: false
}, },
maskClosable: { maskClosable: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 工作流配置
currentNodeIndex: { currentNodeIndex: {
type: Number, type: Number,
required: true, required: true,
default: 2 default: 2
}, },
workflowNodes: { workflowNodes: {
type: Array as () => WorkflowNode[], type: Array as () => WorkflowNode[],
required: true, required: true,
...@@ -137,12 +141,14 @@ const props = defineProps({ ...@@ -137,12 +141,14 @@ const props = defineProps({
type: String, type: String,
default: '' default: ''
}, },
taskId: {
type: String,
default: ''
},
procInsId: { procInsId: {
type: String, type: String,
default: '' default: ''
}, },
dataId: { dataId: {
type: String, type: String,
default: '' default: ''
...@@ -151,37 +157,10 @@ const props = defineProps({ ...@@ -151,37 +157,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false default: false
}, },
// 审核面板配置
showApprovalPanel: { showApprovalPanel: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 审核结果配置
approvalResultOptions: {
type: Array,
default: () => [
{
value: 'approved',
label: '通过',
icon: 'CheckCircleOutlined',
className: 'approved'
},
{
value: 'rejected',
label: '退回',
icon: 'CloseCircleOutlined',
className: 'rejected'
}
]
},
defaultApprovalResult: {
type: String,
default: 'approved'
},
flowNodes: { flowNodes: {
type: Array as () => WorkflowNode[], type: Array as () => WorkflowNode[],
default: () => [] default: () => []
...@@ -192,11 +171,9 @@ const props = defineProps({ ...@@ -192,11 +171,9 @@ const props = defineProps({
}, },
assignee: { type: String, default: null }, assignee: { type: String, default: null },
userType: { type: String, default: 'user' }, userType: { type: String, default: 'user' },
// 面板宽度配置
leftPanelWidth: { type: String,default: '40%'}, leftPanelWidth: { type: String,default: '40%'},
centerPanelWidth: { type: String, default: '40%'}, centerPanelWidth: { type: String, default: '40%'},
rightPanelWidth: { type: String,default: '20%'} rightPanelWidth: { type: String,default: '20%'}
}) })
const emit = defineEmits(['update:visible', 'submit', 'close', 'form-data-update','success','approval-fail']) const emit = defineEmits(['update:visible', 'submit', 'close', 'form-data-update','success','approval-fail'])
...@@ -210,7 +187,7 @@ const approvalPanelRef = ref<InstanceType<typeof ApprovalPanel> | null>(null) ...@@ -210,7 +187,7 @@ const approvalPanelRef = ref<InstanceType<typeof ApprovalPanel> | null>(null)
// 计算属性 // 计算属性
const drawerTitle = computed(() => props.title) const drawerTitle = computed(() => props.title)
const drawerWidth = computed(() => props.width)
// 只读节点:索引小于 currentNodeIndex 的节点 // 只读节点:索引小于 currentNodeIndex 的节点
const readonlyNodes = computed(() => { const readonlyNodes = computed(() => {
...@@ -258,74 +235,18 @@ function handleApprovalSuccess(dataId: string) { ...@@ -258,74 +235,18 @@ function handleApprovalSuccess(dataId: string) {
} }
} }
// 提交处理
async function handleSubmit() {
if (!editableNode.value) {
message.warning('没有可编辑的表单')
return
}
if (!currentFormPanelRef.value) {
message.warning('表单组件未加载')
return
}
if (props.showApprovalPanel && approvalPanelRef.value) {
const result = await approvalPanelRef.value.validate()
if (!result.valid) {
message.error(result.errors[0] || '请填写完整的审核信息')
return
}
}
submitLoading.value = true
try {
const isValid = await currentFormPanelRef.value.validateForm()
if (!isValid) {
message.error('请完善表单信息')
return
}
const submitData = await currentFormPanelRef.value.getFormData()
const finalSubmitData: any = {
nodeId: editableNode.value.id,
nodeName: editableNode.value.name,
formData: submitData,
procDefId: props.procDefId,
formComponent: currentFormPanelRef.value.getFormInstance()
}
// 如果显示审核面板,添加审核数据
if (props.showApprovalPanel && approvalPanelRef.value) {
const approval = await approvalPanelRef.value.getApprovalData()
finalSubmitData.approval = approval
}
console.log('最终提交数据:', finalSubmitData)
// 4. 触发提交事件
emit('submit', finalSubmitData)
message.success(props.showApprovalPanel ? '审核提交成功' : '提交成功')
handleClose()
} catch (error: any) {
console.error('提交失败:', error)
message.error(error?.message || '提交失败,请重试')
} finally {
submitLoading.value = false
}
}
// 关闭抽屉 // 关闭抽屉
function handleClose() { function handleClose() {
emit('update:visible', false) emit('update:visible', false)
emit('close') emit('close')
} }
function handleHistoryHidden() {
showHistoryVisible.value = !showHistoryVisible.value
}
// 处理审核失败 // 处理审核失败
function handleApprovalFail(dataId: string) { function handleApprovalFail(dataId: string) {
if (dataId === props.dataId) { if (dataId === props.dataId) {
message.error('审核失败')
emit('approval-fail',dataId) emit('approval-fail',dataId)
handleClose() handleClose()
} }
...@@ -391,18 +312,6 @@ defineExpose({ ...@@ -391,18 +312,6 @@ defineExpose({
} }
return currentFormData.value return currentFormData.value
}, },
validate: async () => {
if (currentFormPanelRef.value) {
const formValid = await currentFormPanelRef.value.validateForm()
if (props.showApprovalPanel && approvalPanelRef.value) {
const approvalResult = await approvalPanelRef.value.validate()
return formValid && approvalResult.valid
}
return formValid
}
return true
},
submit: handleSubmit
}) })
</script> </script>
......
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
title="审批"
:centered="true"
width="40%"
:minHeight="400"
:useWrapper="true"
:wrapperFooterOffset="20"
@cancel=""
@ok="handleOk"
>
<ApprovalPanel ref="approvalPanelRef" />
</BasicModal>
</template>
<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
import { BasicModal, useModalInner } from '/@/components/Modal';
import ApprovalPanel from './ApprovalPanel.vue';
import { complete, getMyTaskFlow,rejectTask, assignRead,assign } from '/@/components/Process/api/todo';
const approvalPanelRef = ref();
const emit = defineEmits(['success','approval-fail','update-form-data'])
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
});
function handleOk() {
alert('handleOk');
setModalProps({confirmLoading: true});
approvalPanelRef.value.handleSaveApp();
setModalProps({confirmLoading: false});
}
</script>
\ No newline at end of file
<template> <template>
<div class="approval-panel"> <div class="approval-panel">
<a-card class="approval-card" :bordered="false"> <a-card class="approval-card">
<template #title> <!-- <template #title>
<div class="approval-header"> <div class="approval-header">
<span class="approval-title">审批操作</span> <span class="approval-title">审批操作</span>
</div> </div>
</template> </template>
<template #extra> <template #extra>
<a-button @click="handleSaveApp" type="primary" size="small">保存审批</a-button> <a-button @click="handleSaveApp" type="primary" size="small">保存审批</a-button>
</template> </template> -->
<div class="approval-content"> <div class="approval-content" style="width: 100%;">
<BasicForm @register="registerForm" style="width: 100%;"> <BasicForm @register="registerForm">
<template #slot_passOrReturn="{ model }"> <template #slot_passOrReturn="{ model }">
<a-radio-group v-model:value="model.reviewStatus" button-style="solid" class="approval-radio-group"> <a-radio-group v-model:value="model.reviewStatus" button-style="solid" class="approval-radio-group">
<a-radio-button :value="true" class="radio-button"> <a-radio-button :value="true" class="radio-button" @click="handleClickPass">
<CheckCircleOutlined /> <CheckCircleOutlined />
通过 通过
</a-radio-button> </a-radio-button>
<a-radio-button :value="false" class="radio-button"> <a-radio-button :value="false" class="radio-button" @click="handleClickReject">
<CloseCircleOutlined /> <CloseCircleOutlined />
退回 退回
</a-radio-button> </a-radio-button>
...@@ -30,25 +30,23 @@ ...@@ -30,25 +30,23 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref,onMounted,nextTick } from 'vue' import { ref, onMounted, nextTick } from 'vue';
import { useForm, BasicForm, FormSchema } from '/@/components/Form' import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue' import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
import { complete, getMyTaskFlow,getRejectNode,rejectTask } from '/@/components/Process/api/todo' import { complete, getMyTaskFlow, getRejectNode, rejectTask } from '/@/components/Process/api/todo';
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue';
const emit = defineEmits(['update:visible', 'success', 'error', 'close','approval-fail']) const emit = defineEmits(['update:visible', 'success', 'error', 'close', 'approval-fail']);
const props = defineProps({ const props = defineProps({
beforeFlowNode: { beforeFlowNode: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
}, },
assignee: { type: String, default: null }, assignee: { type: String, default: null },
userType: { type: String, default: '' }, userType: { type: String, default: '' },
deployId: { type: String, default: '' }, deployId: { type: String, default: '' },
dataId: { type: String, default: '' }, dataId: { type: String, default: '' },
}) });
const formSchemas: FormSchema[] = [ const formSchemas: FormSchema[] = [
{ {
...@@ -75,8 +73,8 @@ ...@@ -75,8 +73,8 @@
showCount: true, showCount: true,
maxlength: 512, maxlength: 512,
autoSize: { minRows: 7, maxRows: 10 }, autoSize: { minRows: 7, maxRows: 10 },
style: { width: '100%' } style: { width: '100%' },
} },
}, },
{ {
label: '接收人', label: '接收人',
...@@ -85,11 +83,11 @@ ...@@ -85,11 +83,11 @@
required: true, required: true,
componentProps: { componentProps: {
allowClear: true, allowClear: true,
rowKey: 'username', rowKey: 'id',
labelKey: 'realname', labelKey: 'realname',
showButton: false, showButton: false,
modalTitle: '用户', modalTitle: '用户',
style: { width: '100%' } style: { width: '100%' },
}, },
}, },
{ {
...@@ -98,34 +96,33 @@ ...@@ -98,34 +96,33 @@
component: 'RoleSelect', component: 'RoleSelect',
required: true, required: true,
componentProps: { componentProps: {
rowKey: 'id',
labelKey: 'roleName',
allowClear: true, allowClear: true,
maxSelectCount: 3, maxSelectCount: 1,
isRadioSelection: false, isRadioSelection: false,
style: { width: '100%' } style: { width: '100%' },
}, },
} },
] ];
const [registerForm, { validate,updateSchema,setFieldsValue }] = useForm({ const [registerForm, { validate, updateSchema, setFieldsValue,setProps }] = useForm({
schemas: formSchemas, schemas: formSchemas,
showActionButtonGroup: false, showActionButtonGroup: false,
layout: 'vertical',
labelWidth: '120px', labelWidth: '120px',
size: 'small', size: 'large',
baseColProps: { span: 24 }, baseColProps: {style: { width: '100%' } },
}) });
const handleClose = () => { const handleClose = () => {
emit('update:visible', false) emit('update:visible', false);
emit('close') emit('close');
} };
const handleSaveApp = async () => { const handleSaveApp = async () => {
const formDataTmp = await validate();
const formDataTmp = await validate() const myTaskFlow = await getMyTaskFlow({ deploymentId: props.deployId, dataId: props.dataId });
const myTaskFlow = await getMyTaskFlow({ deploymentId: props.deployId, dataId: props.dataId })
if (myTaskFlow?.taskId) { if (myTaskFlow?.taskId) {
const reviewStatus = formDataTmp.reviewStatus; const reviewStatus = formDataTmp.reviewStatus;
if (reviewStatus === true) { if (reviewStatus === true) {
await complete({ await complete({
...@@ -134,149 +131,196 @@ ...@@ -134,149 +131,196 @@
taskId: myTaskFlow.taskId, taskId: myTaskFlow.taskId,
comment: formDataTmp.comment, comment: formDataTmp.comment,
values: { values: {
approval: formDataTmp.approvalUser || formDataTmp.approvalRole, approval: formDataTmp.approvalUser || formDataTmp.approvalRole,
approvalType: props.userType approvalType: props.userType,
}, },
}) });
} else { } else {
await rejectTask({ await rejectTask({
instanceId: myTaskFlow.procInsId || '', instanceId: myTaskFlow.procInsId || '',
deployId: myTaskFlow.deployId || '', deployId: myTaskFlow.deployId || '',
taskId: myTaskFlow.taskId, taskId: myTaskFlow.taskId,
comment: formDataTmp.comment, comment: formDataTmp.comment,
values: { values: {
rejectNode: props.beforeFlowNode, rejectNode: props.beforeFlowNode,
}, },
}) });
alert(3333)
emit('approval-fail', props.dataId) emit('approval-fail', props.dataId);
}
} }
}
emit('success', props.dataId) emit('success', props.dataId);
nextTick(() => { nextTick(() => {
message.success('任务发送成功') message.success('任务发送成功');
handleClose() handleClose();
}) });
} };
onMounted(async() => { const handleClickReject = async () => {
if (props.userType === 'user') { updateSchema([
updateSchema([{ {
field: 'approvalUser', field: 'approvalUser',
required: true,
ifShow: true,
},{
field: 'approvalRole',
required: false, required: false,
ifShow:false, ifShow: false,
}]) },
} else { {
updateSchema([{
field: 'approvalRole', field: 'approvalRole',
required: true,
ifShow: true,
},{
field: 'approvalUser',
required: false, required: false,
ifShow:false, ifShow: false,
}]) },
]);
};
const handleClickPass = async () => {
updateField(props.userType);
};
const updateField = (userTp) => {
if (userTp === 'user') {
updateSchema([
{
field: 'approvalUser',
required: true,
ifShow: true,
},
{
field: 'approvalRole',
required: false,
ifShow: false,
},
]);
} else {
updateSchema([
{
field: 'approvalRole',
required: true,
ifShow: true,
},
{
field: 'approvalUser',
required: false,
ifShow: false,
},
]);
} }
};
onMounted(async () => {
updateField(props.userType);
});
defineExpose({
handleSaveApp,
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.approval-panel { .approval-panel {
flex: 1; flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: #f5f7fa;
.approval-card {
height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: #fff; overflow: hidden;
background-color: #f5f7fa;
:deep(.ant-card-head) { .approval-card {
padding: 0 24px; height: 100%;
background-color: #e9ecef; display: flex;
border-bottom: 1px solid #e8eef2; flex-direction: column;
} background-color: #fff;
:deep(.ant-card-body) { :deep(.ant-card-head) {
flex: 1; padding: 0 16px;
padding: 24px; background-color: #e9ecef;
overflow-y: auto; border-bottom: 1px solid #e8eef2;
}
:deep(.ant-card-body) {
flex: 1;
padding: 16px;
overflow-y: auto;
}
} }
}
.approval-header { .approval-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 16px 0; padding: 12px 0;
.approval-title { .approval-title {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: #1f2f3d; color: #1f2f3d;
}
} }
}
.approval-content {
flex: 1;
width: 100%;
.approval-radio-group { .approval-content {
flex: 1;
width: 100%; width: 100%;
display: flex; // 确保表单容器内部无多余间距
gap: 8px; :deep(.ant-form) {
margin: 0;
padding: 0;
}
.radio-button { :deep(.ant-form-item) {
flex: 1; margin-bottom: 16px;
text-align: center; }
height: 36px;
line-height: 36px; :deep(.ant-form-item:last-child) {
margin-bottom: 0;
}
.approval-radio-group {
width: 100%;
display: flex; display: flex;
align-items: center; gap: 8px;
justify-content: center; margin-bottom: 8px;
gap: 6px;
font-weight: 500;
&:first-child:not(.ant-radio-button-wrapper-checked) { .radio-button {
border-color: #52c41a; flex: 1;
color: #52c41a; text-align: center;
height: 36px;
line-height: 36px;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-weight: 500;
&:hover { &:first-child:not(.ant-radio-button-wrapper-checked) {
border-color: #73d13d; border-color: #52c41a;
color: #73d13d; color: #52c41a;
&:hover {
border-color: #73d13d;
color: #73d13d;
}
} }
}
&:last-child:not(.ant-radio-button-wrapper-checked) { &:last-child:not(.ant-radio-button-wrapper-checked) {
border-color: #ff4d4f; border-color: #ff4d4f;
color: #ff4d4f; color: #ff4d4f;
&:hover { &:hover {
border-color: #ff7875; border-color: #ff7875;
color: #ff7875; color: #ff7875;
}
} }
}
&.ant-radio-button-wrapper-checked:first-child { &.ant-radio-button-wrapper-checked:first-child {
background: #52c41a; background: #52c41a;
border-color: #52c41a; border-color: #52c41a;
} }
&.ant-radio-button-wrapper-checked:last-child { &.ant-radio-button-wrapper-checked:last-child {
background: #ff4d4f; background: #ff4d4f;
border-color: #ff4d4f; border-color: #ff4d4f;
}
} }
} }
} }
} }
} </style>
</style>
\ No newline at end of file
<template>
<a-form layout="vertical">
<a-form-item label="用户类型" required>
<a-radio-group v-model:value="localUserType" disabled>
<a-radio value="user">
<UserOutlined /> 用户
</a-radio>
<a-radio value="role">
<TeamOutlined /> 角色
</a-radio>
</a-radio-group>
</a-form-item>
<!-- 用户/角色选择区域(未选择时) -->
<a-form-item
v-if="!assigneeId"
:label="localUserType === 'user' ? '选择用户' : '选择角色'"
>
<div class="assignee-selector">
<a-input
:value="assigneeDisplayName"
:placeholder="`点击'选择'按钮选择${localUserType === 'user' ? '用户' : '角色'}`"
readonly
class="assignee-input"
>
<template #suffix>
<a-button type="link" size="small" @click="handleSelect">选择</a-button>
</template>
</a-input>
</div>
</a-form-item>
<!-- 已指定信息展示 -->
<div v-if="assigneeId" class="assignee-info-wrapper">
<div class="assignee-info-label">已指定{{ localUserType === 'user' ? '用户' : '角色' }}:</div>
<a-descriptions :column="2" size="small" bordered>
<a-descriptions-item :label="localUserType === 'user' ? '用户ID' : '角色ID'">
{{ assigneeId }}
</a-descriptions-item>
<a-descriptions-item :label="localUserType === 'user' ? '用户名' : '角色名'">
{{ assigneeDisplayName || '--' }}
</a-descriptions-item>
<a-descriptions-item>
<a-button type="link" size="small" @click="clearAssignee" class="change-btn">
重新选择
</a-button>
</a-descriptions-item>
</a-descriptions>
</div>
<a-form-item label="说明">
<a-textarea
v-model:value="assigneeRemark"
placeholder="请输入说明"
:rows="4"
allow-clear
show-count
:maxlength="200"
/>
</a-form-item>
</a-form>
<RoleSelectModal
rowKey="id"
@register="registerSelRoleModal"
@getSelectResult="onSelectRoleOk"
isRadioSelection
:showButton="false"
labelKey="roleName"
/>
<UserSelectModal
rowKey="id"
@register="registerSelUserModal"
@getSelectResult="onSelectUserOk"
isRadioSelection
:showButton="false"
labelKey="realname"
/>
</template>
<script lang="ts" setup>
import { ref, computed, watch, nextTick } from 'vue'
import { useModal } from '/@/components/Modal'
import { UserOutlined, TeamOutlined } from '@ant-design/icons-vue'
import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue'
import RoleSelectModal from '/@/components/Form/src/jeecg/components/modal/RoleSelectModal.vue'
const emit = defineEmits(['success', 'error', 'close'])
const [registerSelUserModal, { openModal: userOpenModal }] = useModal()
const [registerSelRoleModal, { openModal: roleOpenModal }] = useModal()
// 状态
const localUserType = ref<'user' | 'role'>('user')
const assigneeId = ref('')
const assigneeDisplayName = ref('')
const assigneeRemark = ref('')
const setAssigneeData = ((data) => {
if (data) {
if (data.assignee) {
localUserType.value = data.userType === 'role' ? 'role' : 'user'
assigneeId.value = data.assignee
if (data.assigneeName) {
assigneeDisplayName.value = data.assigneeName
}
} else {
localUserType.value = data.userType === 'role' ? 'role' : 'user'
}
}
})
const clearAssignee = () => {
assigneeId.value = ''
assigneeDisplayName.value = ''
}
const handleSelect = () => {
if (localUserType.value === 'user') {
userOpenModal()
} else {
roleOpenModal()
}
}
const onSelectUserOk = (options: any[], values: any[]) => {
if (!values?.length) return
assigneeId.value = values[0]
assigneeDisplayName.value = options[0]?.label || ''
localUserType.value = 'user'
}
const onSelectRoleOk = (options: any[], values: any[]) => {
if (!values?.length) return
assigneeId.value = values[0]
assigneeDisplayName.value = options[0]?.label || ''
localUserType.value = 'role'
}
// 对外暴露的方法
const getAssigneeData = () => ({
userType: localUserType.value,
assignee: assigneeId.value,
name: assigneeDisplayName.value,
remark: assigneeRemark.value
})
defineExpose({
setAssigneeData,
getAssigneeData,
})
</script>
<style scoped lang="scss">
.drawer-content {
height: 100%;
overflow-y: auto;
padding: 16px 20px;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 2px;
&:hover {
background: #a8a8a8;
}
}
}
.assignee-card {
height: 100%;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
:deep(.ant-card-head) {
background-color: #fafbfc;
border-bottom: 1px solid #e8eef2;
.ant-card-head-title {
font-size: 16px;
font-weight: 500;
}
}
:deep(.ant-card-body) {
padding: 24px;
height: calc(100% - 57px);
display: flex;
flex-direction: column;
}
}
.assignee-selector {
display: flex;
align-items: center;
gap: 8px;
.assignee-input {
flex: 1;
:deep(.ant-input) {
background-color: #fafafa;
cursor: pointer;
&:hover {
background-color: #fff;
}
}
}
}
.assignee-info-wrapper {
margin-top: 12px;
padding: 12px;
background-color: #fafbfc;
border-radius: 8px;
.assignee-info-label {
margin-bottom: 8px;
font-weight: 500;
color: #1f2f3d;
}
.change-btn {
margin-top: 8px;
padding-left: 0;
}
:deep(.ant-descriptions) {
.ant-descriptions-item-label {
background-color: #f5f5f5;
width: 80px;
}
}
}
.assignee-actions {
margin-top: auto;
padding-top: 24px;
text-align: center;
border-top: 1px solid #f0f0f0;
}
</style>
\ No newline at end of file
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
title="审批"
:centered="true"
width="40%"
:minHeight="400"
:useWrapper="true"
:wrapperFooterOffset="20"
>
<ApprovalPanel />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal';
import ApprovalPanel from './ApprovalPanel.vue';
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
});
</script>
\ No newline at end of file
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
title="审批"
:centered="true"
width="40%"
:minHeight="400"
:useWrapper="true"
:wrapperFooterOffset="20"
>
<ApprovalPanel />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal';
import ApprovalPanel from './ApprovalPanel.vue';
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
});
</script>
\ No newline at end of file
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
title="审批"
:centered="true"
width="40%"
:minHeight="400"
:useWrapper="true"
:wrapperFooterOffset="20"
>
<ApprovalPanel />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal';
import ApprovalPanel from './ApprovalPanel.vue';
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
});
</script>
\ No newline at end of file
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
title="审批"
:centered="true"
width="40%"
:minHeight="400"
:useWrapper="true"
:wrapperFooterOffset="20"
>
<ApprovalPanel />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal';
import ApprovalPanel from './ApprovalPanel.vue';
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
});
</script>
\ No newline at end of file
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:title="taskTitle"
:centered="true"
width="40%"
:minHeight="400"
:useWrapper="true"
:wrapperFooterOffset="0"
@ok="handleTaskOk"
@cancel="closeThisModal"
>
<div class="assignee-panel-wrapper">
<AssigneePanel ref="refAssigneePanel"/>
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { BasicModal, useModalInner } from '/@/components/Modal';
import AssigneePanel from './AssigneePanel.vue';
import { complete, getMyTaskFlow,rejectTask, assignRead,assign } from '/@/components/Process/api/todo';
import { message } from 'ant-design-vue';
const emit = defineEmits(['success','approval-fail','update-form-data'])
const taskTitle = ref('任务')
const sending = ref(false);
const taskType = ref<any>({});
const refAssigneePanel = ref<any>(null);
const myTaskFlow = ref<any>({});
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
taskTitle.value = data?.taskTitle || '任务'
taskType.value = data?.taskType || "";
refAssigneePanel.value.setAssigneeData(data);
myTaskFlow.value = await getMyTaskFlow({
deploymentId: data?.deployId || '',
dataId: data?.dataId || '',
})
});
const handleTaskOk = async () => {
if (sending.value) return;
try {
sending.value = true;
const assigneeData = refAssigneePanel.value.getAssigneeData();
const submitData: any = {
instanceId: myTaskFlow.value.procInsId,
deployId: myTaskFlow.value.deployId,
taskId: myTaskFlow.value.taskId,
comment: assigneeData.remark || '',
values: {
approval: assigneeData.assignee,
approvalType: assigneeData.userType,
}
};
if (taskType.value === 'send') {
await handleSendTask(submitData);
} else if (taskType.value === 'reject') {
await handleRejectTask(submitData);
} else if (taskType.value === 'transmit') {
await handleTransmitTask(submitData);
} else if (taskType.value === 'read') {
await handleReadTask(submitData);
}
//message.success('操作成功');
emit('success', submitData);
closeThisModal();
} catch (error: any) {
} finally {
sending.value = false;
}
}
const handleSendTask = async (submitData: any) => {
const result = await complete(submitData);
return false;
};
const handleRejectTask = async (submitData) => {
const result = await rejectTask(submitData);
return false;
};
const handleReadTask = async (submitData) => {
const result = await assignRead(submitData);
return false;
};
const handleTransmitTask = async (submitData) => {
const result = await assign(submitData);
return false;
};
const closeThisModal = () => {
closeModal();
}
</script>
<style scoped lang="scss">
.assignee-panel-wrapper{
padding: 0px;
}
</style>
\ No newline at end of file
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</template> </template>
<script lang="ts" name="problem-stProblemCheck" setup> <script lang="ts" name="problem-stProblemCheck" setup>
import { ref } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
...@@ -28,18 +28,10 @@ ...@@ -28,18 +28,10 @@
const emit = defineEmits(['callback','openMultiForm']) const emit = defineEmits(['callback','openMultiForm'])
const props = defineProps({ const props = defineProps({
beforeFlowNode: {
type: Object,
default: () => ({})
},
currentFlowNode: { currentFlowNode: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
}, },
nextFlowNode: {
type: Object,
default: () => ({})
}
}) })
......
...@@ -49,6 +49,10 @@ ...@@ -49,6 +49,10 @@
nextFlowNode: { nextFlowNode: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
},
todoList: {
type: Array,
default: () => []
} }
}) })
...@@ -64,15 +68,9 @@ ...@@ -64,15 +68,9 @@
api: list, api: list,
columns, columns,
canResize: false, canResize: false,
// formConfig: {
// schemas: searchFormSchema,
// autoSubmitOnEnter: true,
// showAdvancedButton: true,
// fieldMapToNumber: [],
// fieldMapToTime: [],
// },
beforeFetch(params) { beforeFetch(params) {
params['bmpNodeId'] = props.currentFlowNode.id params['bmpNodeId'] = props.currentFlowNode.id
params['todoList'] = props.todoList
}, },
actionColumn: { actionColumn: {
width: 200, width: 200,
...@@ -101,7 +99,7 @@ ...@@ -101,7 +99,7 @@
record: { record: {
bmpNodeId: props.currentFlowNode.id, bmpNodeId: props.currentFlowNode.id,
deployId: props.currentFlowNode.deployId, deployId: props.currentFlowNode.deployId,
bpmStatus: 1, bpmStatus: 0,
} }
}); });
} }
...@@ -185,7 +183,8 @@ ...@@ -185,7 +183,8 @@
async function handleStartUpdate(flowData) { async function handleStartUpdate(flowData) {
let record = { let record = {
procInsId: flowData.procInsId, procInsId: flowData.procInsId,
id:flowData.dataId id:flowData.dataId,
bpmStatus: 0,
} }
await saveOrUpdate(record,true).then(res => { await saveOrUpdate(record,true).then(res => {
handleSuccess(null); handleSuccess(null);
......
...@@ -30,7 +30,10 @@ ...@@ -30,7 +30,10 @@
:show-history-form-data="true" :show-history-form-data="true"
:data-id="dataId" :data-id="dataId"
:deploy-id="deployId" :deploy-id="deployId"
:task-id="taskId"
:show-approval-panel="isShowApprovalPanel" :show-approval-panel="isShowApprovalPanel"
:assignee="assignee"
:user-type="userType"
@submit="handleMultiFormSubmit" @submit="handleMultiFormSubmit"
@close="handleDrawerClose" @close="handleDrawerClose"
@form-data-update="handleMultiFormDataUpdate" @form-data-update="handleMultiFormDataUpdate"
...@@ -67,6 +70,7 @@ ...@@ -67,6 +70,7 @@
const isShowApprovalPanel = ref(true); const isShowApprovalPanel = ref(true);
const deployId = ref(''); const deployId = ref('');
const procInsId = ref(''); const procInsId = ref('');
const taskId = ref('');
// 改为动态 ref 对象,存储每个节点的组件实例 // 改为动态 ref 对象,存储每个节点的组件实例
const formComponentRefs = ref<Map<number, any>>(new Map()); const formComponentRefs = ref<Map<number, any>>(new Map());
...@@ -123,6 +127,7 @@ ...@@ -123,6 +127,7 @@
componentPath += '.vue'; componentPath += '.vue';
} }
let loader = modules[componentPath]; let loader = modules[componentPath];
if (!loader) { if (!loader) {
const ErrorComponent = { const ErrorComponent = {
...@@ -156,9 +161,10 @@ ...@@ -156,9 +161,10 @@
const deployId = currentNode.value.deployId || ''; const deployId = currentNode.value.deployId || '';
const startResRaw = await definitionStartByDeployId(deployId, formData); const startResRaw = await definitionStartByDeployId(deployId, formData);
let myTaskFlow = {} let myTaskFlow = {}
if (startResRaw?.instanceId) { if (startResRaw?.instanceId) {
procInsId.value = startResRaw.instanceId; procInsId.value = startResRaw.instanceId;
taskId.value = startResRaw.taskId;
myTaskFlow["taskId"] = startResRaw.taskId; myTaskFlow["taskId"] = startResRaw.taskId;
myTaskFlow["deployId"] = startResRaw.deployId; myTaskFlow["deployId"] = startResRaw.deployId;
myTaskFlow["procInsId"] = startResRaw.instanceId; myTaskFlow["procInsId"] = startResRaw.instanceId;
...@@ -173,7 +179,7 @@ ...@@ -173,7 +179,7 @@
if(userType.value==="role"){ if(userType.value==="role"){
myTaskFlow["roleid"] = currentNode.value.assignee; myTaskFlow["roleid"] = currentNode.value.assignee;
} else { } else {
myTaskFlow["uid"] = currentNode.value.assignee; myTaskFlow["uid"] = currentNode.value.assignee;;
} }
} }
await addMyTaskFlow(myTaskFlow); await addMyTaskFlow(myTaskFlow);
...@@ -221,8 +227,6 @@ ...@@ -221,8 +227,6 @@
} }
} }
} }
const handleMultiFormSubmit = async (submitData: { const handleMultiFormSubmit = async (submitData: {
nodeId: string;nodeName: string;formData: any;procDefId: string; nodeId: string;nodeName: string;formData: any;procDefId: string;
}) => { }) => {
...@@ -245,12 +249,10 @@ ...@@ -245,12 +249,10 @@
}; };
const handleMultiFormDataUpdate = (data: any) => { const handleMultiFormDataUpdate = (data: any) => {
//alert(JSON.stringify(data))
console.log('多表单数据更新:', data); console.log('多表单数据更新:', data);
// 可以在这里实时保存数据到本地 // 可以在这里实时保存数据到本地
}; };
function handlSendSuccess(dataId: any) { function handlSendSuccess(dataId: any) {
const currentFormComponent = getCurrentFormComponent(); const currentFormComponent = getCurrentFormComponent();
if (currentFormComponent && typeof currentFormComponent.handleUpdate === 'function') { if (currentFormComponent && typeof currentFormComponent.handleUpdate === 'function') {
...@@ -269,8 +271,6 @@ ...@@ -269,8 +271,6 @@
const currentFormComponent = getCurrentFormComponent(); const currentFormComponent = getCurrentFormComponent();
if (currentFormComponent && typeof currentFormComponent.handleUpdate === 'function') { if (currentFormComponent && typeof currentFormComponent.handleUpdate === 'function') {
const beforeNode = workflowNodes.value[currentMultiFormIndex.value-1]; const beforeNode = workflowNodes.value[currentMultiFormIndex.value-1];
alert(currentMultiFormIndex.value)
alert(JSON.stringify(beforeNode))
await currentFormComponent.handleUpdate(dataId,beforeNode); await currentFormComponent.handleUpdate(dataId,beforeNode);
} else { } else {
console.warn('当前组件实例不存在或没有 handleUpdate 方法'); console.warn('当前组件实例不存在或没有 handleUpdate 方法');
...@@ -278,10 +278,13 @@ ...@@ -278,10 +278,13 @@
} }
async function handleOpenMultiForm(data: any) { async function handleOpenMultiForm(data: any) {
deployId.value = currentNode.value.deployId || ''; deployId.value = currentNode.value.deployId || '';
procInsId.value = data.procInsId || ''; procInsId.value = data.procInsId || '';
drawerVisible.value = true; drawerVisible.value = true;
drawerTitle.value = currentNode.value.name || '表单处理';
dataId.value = data.id || ''; dataId.value = data.id || '';
currentNode.value = workflowNodes.value[currentMultiFormIndex.value]; currentNode.value = workflowNodes.value[currentMultiFormIndex.value];
currentProcDefId.value = currentNode.value.procDefId || ''; currentProcDefId.value = currentNode.value.procDefId || '';
...@@ -293,6 +296,8 @@ ...@@ -293,6 +296,8 @@
isShowApprovalPanel.value = false; isShowApprovalPanel.value = false;
} }
await setNextNodeUser(); await setNextNodeUser();
} }
......
...@@ -7,12 +7,11 @@ ...@@ -7,12 +7,11 @@
</div> </div>
<!-- 底部按钮区域 --> <!-- 底部按钮区域 -->
<div style="text-align: center;" v-if="!formDisabled"> <!-- <div style="text-align: center;" v-if="!formDisabled">
<a-space> <a-space>
<a-button block @click="saveForm" pre-icon="ant-design:save" type="primary" ghost>保 存</a-button> <a-button block @click="saveForm" pre-icon="ant-design:save" type="primary" ghost>保 存</a-button>
<a-button block @click="saveAndSendForm" pre-icon="ant-design:send" type="primary" ghost>保存并发送</a-button>
</a-space> </a-space>
</div> </div> -->
</a-card> </a-card>
</div> </div>
</template> </template>
...@@ -122,13 +121,18 @@ ...@@ -122,13 +121,18 @@
return isEdit return isEdit
}; };
const getFormData = () => {
return getFieldsValue();
};
return { return {
setFormDisabledStatus, setFormDisabledStatus,
registerForm, registerForm,
formDisabled, formDisabled,
saveForm, saveForm,
saveAndSendForm, saveAndSendForm,
initFormData initFormData,
getFormData
}; };
}, },
}); });
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
"target": "esnext", "target": "esnext",
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"noEmit": true, // 或者使用这行,只做类型检查不生成文件
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
...@@ -41,5 +42,11 @@ ...@@ -41,5 +42,11 @@
"mock/**/*.ts", "mock/**/*.ts",
"vite.config.ts" "vite.config.ts"
], ],
"exclude": ["node_modules", "tests/server/**/*.ts", "dist", "**/*.js"] "exclude": [
"node_modules",
"tests/server/**/*.ts",
"dist",
"src/**/*.js",
"**/*.js"
]
} }
...@@ -24,6 +24,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -24,6 +24,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, root); const env = loadEnv(mode, root);
// The boolean type read by loadEnv is a string. This function can be converted to boolean type
const viteEnv = wrapperEnv(env); const viteEnv = wrapperEnv(env);
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = viteEnv; const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY } = viteEnv;
...@@ -32,12 +33,14 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -32,12 +33,14 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
const serverOptions: Recordable = {} const serverOptions: Recordable = {}
// ----- [begin] 【JEECG作为乾坤子应用】 -----
const {VITE_GLOB_QIANKUN_MICRO_APP_NAME, VITE_GLOB_QIANKUN_MICRO_APP_ENTRY} = viteEnv; const {VITE_GLOB_QIANKUN_MICRO_APP_NAME, VITE_GLOB_QIANKUN_MICRO_APP_ENTRY} = viteEnv;
const isQiankunMicro = VITE_GLOB_QIANKUN_MICRO_APP_NAME != null && VITE_GLOB_QIANKUN_MICRO_APP_NAME !== ''; const isQiankunMicro = VITE_GLOB_QIANKUN_MICRO_APP_NAME != null && VITE_GLOB_QIANKUN_MICRO_APP_NAME !== '';
if (isQiankunMicro && !isBuild) { if (isQiankunMicro && !isBuild) {
serverOptions.cors = true; serverOptions.cors = true;
serverOptions.origin = VITE_GLOB_QIANKUN_MICRO_APP_ENTRY!.split('/').slice(0, 3).join('/'); serverOptions.origin = VITE_GLOB_QIANKUN_MICRO_APP_ENTRY!.split('/').slice(0, 3).join('/');
} }
// ----- [end] 【JEECG作为乾坤子应用】 -----
console.log('[init] Start Port: ', VITE_PORT); console.log('[init] Start Port: ', VITE_PORT);
console.debug('[init] Vite Proxy Config: ', VITE_PROXY); console.debug('[init] Vite Proxy Config: ', VITE_PROXY);
...@@ -52,14 +55,17 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -52,14 +55,17 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
find: 'vue-i18n', find: 'vue-i18n',
replacement: 'vue-i18n/dist/vue-i18n.cjs.js', replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
}, },
// 添加 Vue 别名,确保使用正确的版本
{ {
find: 'vue', find: 'vue',
replacement: 'vue/dist/vue.esm-bundler.js', replacement: 'vue/dist/vue.esm-bundler.js',
}, },
// /@/xxxx => src/xxxx
{ {
find: /\/@\//, find: /\/@\//,
replacement: pathResolve('src') + '/', replacement: pathResolve('src') + '/',
}, },
// /#/xxxx => types/xxxx
{ {
find: /\/#\//, find: /\/#\//,
replacement: pathResolve('types') + '/', replacement: pathResolve('types') + '/',
...@@ -68,6 +74,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -68,6 +74,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
find: /@\//, find: /@\//,
replacement: pathResolve('src') + '/', replacement: pathResolve('src') + '/',
}, },
// /#/xxxx => types/xxxx
{ {
find: /#\//, find: /#\//,
replacement: pathResolve('types') + '/', replacement: pathResolve('types') + '/',
...@@ -75,14 +82,20 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -75,14 +82,20 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
], ],
}, },
server: { server: {
// Listening on all local IPs
host: true, host: true,
// @ts-ignore
https: false, https: false,
port: VITE_PORT, port: VITE_PORT,
// Load proxy configuration from .env
proxy: createProxy(VITE_PROXY), proxy: createProxy(VITE_PROXY),
// 合并 server 配置
...serverOptions, ...serverOptions,
// 预热常用文件,提高首次加载速度
warmup: { warmup: {
clientFiles: ['./index.html', './src/main.ts', './src/App.vue'], clientFiles: ['./index.html', './src/main.ts', './src/App.vue'],
}, },
// 启用更快的源文件变更检测
fs: { fs: {
strict: false, strict: false,
}, },
...@@ -93,12 +106,16 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -93,12 +106,16 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
cssTarget: 'chrome80', cssTarget: 'chrome80',
outDir: OUTPUT_DIR, outDir: OUTPUT_DIR,
rollupOptions: { rollupOptions: {
// 关闭除屑优化,防止删除重要代码,导致打包后功能出现异常
treeshake: false, treeshake: false,
output: { output: {
// 提供命名导出,解决默认导出问题
exports: 'named', exports: 'named',
chunkFileNames: 'js/[name]-[hash].js', chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名称
entryFileNames: 'js/[name]-[hash].js', entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名称
// manualChunks配置 (依赖包从大到小排列)
manualChunks: { manualChunks: {
// vue vue-router合并打包
'vue-vendor': ['vue', 'vue-router'], 'vue-vendor': ['vue', 'vue-router'],
'antd-vue-vendor': ['ant-design-vue','@ant-design/icons-vue','@ant-design/colors'], 'antd-vue-vendor': ['ant-design-vue','@ant-design/icons-vue','@ant-design/colors'],
'vxe-table-vendor': ['vxe-table','vxe-table-plugin-antd','xe-utils'], 'vxe-table-vendor': ['vxe-table','vxe-table-plugin-antd','xe-utils'],
...@@ -107,19 +124,27 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -107,19 +124,27 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
}, },
}, },
}, },
// 关闭brotliSize显示可以稍微减少打包时间
reportCompressedSize: false, reportCompressedSize: false,
// 提高超大静态资源警告大小
chunkSizeWarningLimit: 2000, chunkSizeWarningLimit: 2000,
// 添加 CommonJS 配置
commonjsOptions: { commonjsOptions: {
transformMixedEsModules: true, transformMixedEsModules: true,
include: [/node_modules/], include: [/node_modules/],
// 特别处理 form-create 的依赖
requireReturnsDefault: 'auto', requireReturnsDefault: 'auto',
// 忽略导入检查
ignoreTryCatch: false, ignoreTryCatch: false,
}, },
}, },
esbuild: { esbuild: {
//清除全局的console.log和debug
drop: isBuild ? ['console', 'debugger'] : [], drop: isBuild ? ['console', 'debugger'] : [],
}, },
define: { define: {
// setting vue-i18-next
// Suppress warning
__INTLIFY_PROD_DEVTOOLS__: false, __INTLIFY_PROD_DEVTOOLS__: false,
__APP_INFO__: JSON.stringify(__APP_INFO__), __APP_INFO__: JSON.stringify(__APP_INFO__),
}, },
...@@ -131,20 +156,27 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -131,20 +156,27 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
}, },
}, },
}, },
// The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
// 预加载构建配置(首屏性能)
plugins: createVitePlugins(viteEnv, isBuild, isQiankunMicro), plugins: createVitePlugins(viteEnv, isBuild, isQiankunMicro),
optimizeDeps: { optimizeDeps: {
esbuildOptions: { esbuildOptions: {
target: 'es2020', target: 'es2020',
// 为 ES 模块添加支持
supported: { supported: {
'dynamic-import': true, 'dynamic-import': true,
'import-meta': true, 'import-meta': true,
}, },
}, },
force: false, // 强制重新预构建,确保缓存是最新的
force: false, // 设置为 false,避免每次都重新预构建
exclude: [ exclude: [
//升级vite4后,需要排除online依赖
'@jeecg/online', '@jeecg/online',
'@jeecg/aiflow', '@jeecg/aiflow',
], ],
// 只包含最核心的依赖,让 Vite 自动处理其他依赖
include: [ include: [
'vue', 'vue',
'vue-router', 'vue-router',
...@@ -152,6 +184,5 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { ...@@ -152,6 +184,5 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
'ant-design-vue', 'ant-design-vue',
] ]
}, },
cacheDir: '.vite',
}; };
}; };
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论